📄 m88k.c
字号:
/* Subroutines for insn-output.c for Motorola 88000. Copyright (C) 1988, 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002 Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@mcc.com) Currently maintained by (gcc@dg-rtp.dg.com)This file is part of GNU CC.GNU CC is free software; you can redistribute it and/or modifyit under the terms of the GNU General Public License as published bythe Free Software Foundation; either version 2, or (at your option)any later version.GNU CC is distributed in the hope that it will be useful,but WITHOUT ANY WARRANTY; without even the implied warranty ofMERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See theGNU General Public License for more details.You should have received a copy of the GNU General Public Licensealong with GNU CC; see the file COPYING. If not, write tothe Free Software Foundation, 59 Temple Place - Suite 330,Boston, MA 02111-1307, USA. */#include "config.h"#include "system.h"#include "rtl.h"#include "regs.h"#include "hard-reg-set.h"#include "real.h"#include "insn-config.h"#include "conditions.h"#include "output.h"#include "insn-attr.h"#include "tree.h"#include "function.h"#include "expr.h"#include "libfuncs.h"#include "c-tree.h"#include "flags.h"#include "recog.h"#include "toplev.h"#include "tm_p.h"#include "target.h"#include "target-def.h"extern FILE *asm_out_file;const char *m88k_pound_sign = ""; /* Either # for SVR4 or empty for SVR3 */const char *m88k_short_data;const char *m88k_version;char m88k_volatile_code;unsigned m88k_gp_threshold = 0;int m88k_prologue_done = 0; /* Ln directives can now be emitted */int m88k_function_number = 0; /* Counter unique to each function */int m88k_fp_offset = 0; /* offset of frame pointer if used */int m88k_stack_size = 0; /* size of allocated stack (including frame) */int m88k_case_index;rtx m88k_compare_reg; /* cmp output pseudo register */rtx m88k_compare_op0; /* cmpsi operand 0 */rtx m88k_compare_op1; /* cmpsi operand 1 */enum processor_type m88k_cpu; /* target cpu */static void m88k_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));static void m88k_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));static void m88k_output_function_end_prologue PARAMS ((FILE *));static void m88k_output_function_begin_epilogue PARAMS ((FILE *));#if defined (CTOR_LIST_BEGIN) && !defined (OBJECT_FORMAT_ELF)static void m88k_svr3_asm_out_constructor PARAMS ((rtx, int));static void m88k_svr3_asm_out_destructor PARAMS ((rtx, int));#endifstatic void m88k_select_section PARAMS ((tree, int, unsigned HOST_WIDE_INT));static int m88k_adjust_cost PARAMS ((rtx, rtx, rtx, int));static void m88k_encode_section_info PARAMS ((tree, int));/* Initialize the GCC target structure. */#undef TARGET_ASM_BYTE_OP#define TARGET_ASM_BYTE_OP "\tbyte\t"#undef TARGET_ASM_ALIGNED_HI_OP#define TARGET_ASM_ALIGNED_HI_OP "\thalf\t"#undef TARGET_ASM_ALIGNED_SI_OP#define TARGET_ASM_ALIGNED_SI_OP "\tword\t"#undef TARGET_ASM_UNALIGNED_HI_OP#define TARGET_ASM_UNALIGNED_HI_OP "\tuahalf\t"#undef TARGET_ASM_UNALIGNED_SI_OP#define TARGET_ASM_UNALIGNED_SI_OP "\tuaword\t"#undef TARGET_ASM_FUNCTION_PROLOGUE#define TARGET_ASM_FUNCTION_PROLOGUE m88k_output_function_prologue#undef TARGET_ASM_FUNCTION_END_PROLOGUE#define TARGET_ASM_FUNCTION_END_PROLOGUE m88k_output_function_end_prologue#undef TARGET_ASM_FUNCTION_BEGIN_EPILOGUE#define TARGET_ASM_FUNCTION_BEGIN_EPILOGUE m88k_output_function_begin_epilogue#undef TARGET_ASM_FUNCTION_EPILOGUE#define TARGET_ASM_FUNCTION_EPILOGUE m88k_output_function_epilogue#undef TARGET_SCHED_ADJUST_COST#define TARGET_SCHED_ADJUST_COST m88k_adjust_cost#undef TARGET_ENCODE_SECTION_INFO#define TARGET_ENCODE_SECTION_INFO m88k_encode_section_infostruct gcc_target targetm = TARGET_INITIALIZER;/* Determine what instructions are needed to manufacture the integer VALUE in the given MODE. */enum m88k_instructionclassify_integer (mode, value) enum machine_mode mode; register int value;{ if (value == 0) return m88k_zero; else if (SMALL_INTVAL (value)) return m88k_or; else if (SMALL_INTVAL (-value)) return m88k_subu; else if (mode == HImode) return m88k_or_lo16; else if (mode == QImode) return m88k_or_lo8; else if ((value & 0xffff) == 0) return m88k_oru_hi16; else if (integer_ok_for_set (value)) return m88k_set; else return m88k_oru_or;}/* Return the bit number in a compare word corresponding to CONDITION. */intcondition_value (condition) rtx condition;{ switch (GET_CODE (condition)) { case EQ: return 2; case NE: return 3; case GT: return 4; case LE: return 5; case LT: return 6; case GE: return 7; case GTU: return 8; case LEU: return 9; case LTU: return 10; case GEU: return 11; default: abort (); }}intinteger_ok_for_set (value) register unsigned value;{ /* All the "one" bits must be contiguous. If so, MASK + 1 will be a power of two or zero. */ register unsigned mask = (value | (value - 1)); return (value && POWER_OF_2_or_0 (mask + 1));}const char *output_load_const_int (mode, operands) enum machine_mode mode; rtx *operands;{ static const char *const patterns[] = { "or %0,%#r0,0", "or %0,%#r0,%1", "subu %0,%#r0,%n1", "or %0,%#r0,%h1", "or %0,%#r0,%q1", "set %0,%#r0,%s1", "or.u %0,%#r0,%X1", "or.u %0,%#r0,%X1\n\tor %0,%0,%x1", }; if (! REG_P (operands[0]) || GET_CODE (operands[1]) != CONST_INT) abort (); return patterns[classify_integer (mode, INTVAL (operands[1]))];}/* These next two routines assume that floating point numbers are represented in a manner which is consistent between host and target machines. */const char *output_load_const_float (operands) rtx *operands;{ /* These can return 0 under some circumstances when cross-compiling. */ operands[0] = operand_subword (operands[0], 0, 0, SFmode); operands[1] = operand_subword (operands[1], 0, 0, SFmode); return output_load_const_int (SImode, operands);}const char *output_load_const_double (operands) rtx *operands;{ rtx latehalf[2]; /* These can return zero on some cross-compilers, but there's nothing we can do about it. */ latehalf[0] = operand_subword (operands[0], 1, 0, DFmode); latehalf[1] = operand_subword (operands[1], 1, 0, DFmode); operands[0] = operand_subword (operands[0], 0, 0, DFmode); operands[1] = operand_subword (operands[1], 0, 0, DFmode); output_asm_insn (output_load_const_int (SImode, operands), operands); operands[0] = latehalf[0]; operands[1] = latehalf[1]; return output_load_const_int (SImode, operands);}const char *output_load_const_dimode (operands) rtx *operands;{ rtx latehalf[2]; latehalf[0] = operand_subword (operands[0], 1, 0, DImode); latehalf[1] = operand_subword (operands[1], 1, 0, DImode); operands[0] = operand_subword (operands[0], 0, 0, DImode); operands[1] = operand_subword (operands[1], 0, 0, DImode); output_asm_insn (output_load_const_int (SImode, operands), operands); operands[0] = latehalf[0]; operands[1] = latehalf[1]; return output_load_const_int (SImode, operands);}/* Emit insns to move operands[1] into operands[0]. Return 1 if we have written out everything that needs to be done to do the move. Otherwise, return 0 and the caller will emit the move normally. SCRATCH if nonzero can be used as a scratch register for the move operation. It is provided by a SECONDARY_RELOAD_* macro if needed. */intemit_move_sequence (operands, mode, scratch) rtx *operands; enum machine_mode mode; rtx scratch;{ register rtx operand0 = operands[0]; register rtx operand1 = operands[1]; if (CONSTANT_P (operand1) && flag_pic && pic_address_needs_scratch (operand1)) operands[1] = operand1 = legitimize_address (1, operand1, 0, 0); /* Handle most common case first: storing into a register. */ if (register_operand (operand0, mode)) { if (register_operand (operand1, mode) || (GET_CODE (operand1) == CONST_INT && SMALL_INT (operand1)) || GET_CODE (operand1) == HIGH /* Only `general_operands' can come here, so MEM is ok. */ || GET_CODE (operand1) == MEM) { /* Run this case quickly. */ emit_insn (gen_rtx_SET (VOIDmode, operand0, operand1)); return 1; } } else if (GET_CODE (operand0) == MEM) { if (register_operand (operand1, mode) || (operand1 == const0_rtx && GET_MODE_SIZE (mode) <= UNITS_PER_WORD)) { /* Run this case quickly. */ emit_insn (gen_rtx_SET (VOIDmode, operand0, operand1)); return 1; } if (! reload_in_progress && ! reload_completed) { operands[0] = validize_mem (operand0); operands[1] = operand1 = force_reg (mode, operand1); } } /* Simplify the source if we need to. */ if (GET_CODE (operand1) != HIGH && immediate_operand (operand1, mode)) { if (GET_CODE (operand1) != CONST_INT && GET_CODE (operand1) != CONST_DOUBLE) { rtx temp = ((reload_in_progress || reload_completed) ? operand0 : 0); operands[1] = legitimize_address (flag_pic && symbolic_address_p (operand1), operand1, temp, scratch); if (mode != SImode) operands[1] = gen_rtx_SUBREG (mode, operands[1], 0); } } /* Now have insn-emit do whatever it normally does. */ return 0;}/* Return a legitimate reference for ORIG (either an address or a MEM) using the register REG. If PIC and the address is already position-independent, use ORIG. Newly generated position-independent addresses go into a reg. This is REG if nonzero, otherwise we allocate register(s) as necessary. If this is called during reload, and we need a second temp register, then we use SCRATCH, which is provided via the SECONDARY_INPUT_RELOAD_CLASS mechanism. */struct rtx_def *legitimize_address (pic, orig, reg, scratch) int pic; rtx orig; rtx reg; rtx scratch;{ rtx addr = (GET_CODE (orig) == MEM ? XEXP (orig, 0) : orig); rtx new = orig; rtx temp, insn; if (pic) { if (GET_CODE (addr) == SYMBOL_REF || GET_CODE (addr) == LABEL_REF) { if (reg == 0) { if (reload_in_progress || reload_completed) abort (); else reg = gen_reg_rtx (Pmode); } if (flag_pic == 2) { /* If not during reload, allocate another temp reg here for loading in the address, so that these instructions can be optimized properly. */ temp = ((reload_in_progress || reload_completed) ? reg : gen_reg_rtx (Pmode)); emit_insn (gen_rtx_SET (VOIDmode, temp, gen_rtx_HIGH (SImode, gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 0)))); emit_insn (gen_rtx_SET (VOIDmode, temp, gen_rtx_LO_SUM (SImode, temp, gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 0)))); addr = temp; } new = gen_rtx_MEM (Pmode, gen_rtx_PLUS (SImode, pic_offset_table_rtx, addr)); current_function_uses_pic_offset_table = 1; RTX_UNCHANGING_P (new) = 1; insn = emit_move_insn (reg, new); /* Put a REG_EQUAL note on this insn, so that it can be optimized by loop. */ REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig, REG_NOTES (insn)); new = reg; } else if (GET_CODE (addr) == CONST) { rtx base; if (GET_CODE (XEXP (addr, 0)) == PLUS && XEXP (XEXP (addr, 0), 0) == pic_offset_table_rtx) return orig; if (reg == 0) { if (reload_in_progress || reload_completed) abort (); else reg = gen_reg_rtx (Pmode); } if (GET_CODE (XEXP (addr, 0)) != PLUS) abort (); base = legitimize_address (1, XEXP (XEXP (addr, 0), 0), reg, 0); addr = legitimize_address (1, XEXP (XEXP (addr, 0), 1), base == reg ? 0 : reg, 0); if (GET_CODE (addr) == CONST_INT) { if (ADD_INT (addr)) return plus_constant (base, INTVAL (addr)); else if (! reload_in_progress && ! reload_completed) addr = force_reg (Pmode, addr); /* We can't create any new registers during reload, so use the SCRATCH reg provided by the reload_insi pattern. */ else if (scratch) { emit_move_insn (scratch, addr); addr = scratch; } else /* If we reach here, then the SECONDARY_INPUT_RELOAD_CLASS macro needs to be adjusted so that a scratch reg is provided for this address. */ abort (); } new = gen_rtx_PLUS (SImode, base, addr); /* Should we set special REG_NOTEs here? */ } } else if (! SHORT_ADDRESS_P (addr, temp)) { if (reg == 0) { if (reload_in_progress || reload_completed) abort (); else reg = gen_reg_rtx (Pmode); } emit_insn (gen_rtx_SET (VOIDmode, reg, gen_rtx_HIGH (SImode, addr))); new = gen_rtx_LO_SUM (SImode, reg, addr); } if (new != orig && GET_CODE (orig) == MEM) { new = gen_rtx_MEM (GET_MODE (orig), new); MEM_COPY_ATTRIBUTES (new, orig); } return new;}/* Support functions for code to emit a block move. There are four methods used to perform the block move: + call memcpy + call the looping library function, e.g. __movstrSI64n8 + call a non-looping library function, e.g. __movstrHI15x11 + produce an inline sequence of ld/st instructions The parameters below describe the library functions produced by movstr-m88k.sh. */#define MOVSTR_LOOP 64 /* __movstrSI64n68 .. __movstrSI64n8 */#define MOVSTR_QI 16 /* __movstrQI16x16 .. __movstrQI16x2 */#define MOVSTR_HI 48 /* __movstrHI48x48 .. __movstrHI48x4 */#define MOVSTR_SI 96 /* __movstrSI96x96 .. __movstrSI96x8 */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -