📄 romp.c
字号:
/* Subroutines used for code generation on ROMP. Copyright (C) 1990, 1991, 1992, 1993, 1997, 1998, 1999, 2000, 2002 Free Software Foundation, Inc. Contributed by Richard Kenner (kenner@nyu.edu)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 "flags.h"#include "recog.h"#include "obstack.h"#include "tree.h"#include "function.h"#include "expr.h"#include "ggc.h"#include "toplev.h"#include "tm_p.h"#include "target.h"#include "target-def.h"#define min(A,B) ((A) < (B) ? (A) : (B))#define max(A,B) ((A) > (B) ? (A) : (B))static int unsigned_comparisons_p PARAMS ((rtx));static void output_loadsave_fpregs PARAMS ((FILE *, enum rtx_code, rtx));static void output_fpops PARAMS ((FILE *));static void init_fpops PARAMS ((void));static int memory_offset_in_range_p PARAMS ((rtx, enum machine_mode, int, int));static unsigned int hash_rtx PARAMS ((rtx));static void romp_output_function_prologue PARAMS ((FILE *, HOST_WIDE_INT));static void romp_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));static void romp_select_rtx_section PARAMS ((enum machine_mode, rtx, unsigned HOST_WIDE_INT));static void romp_encode_section_info PARAMS ((tree, int));/* Initialize the GCC target structure. */#undef TARGET_ASM_FUNCTION_PROLOGUE#define TARGET_ASM_FUNCTION_PROLOGUE romp_output_function_prologue#undef TARGET_ASM_FUNCTION_EPILOGUE#define TARGET_ASM_FUNCTION_EPILOGUE romp_output_function_epilogue#undef TARGET_ASM_SELECT_RTX_SECTION#define TARGET_ASM_SELECT_RTX_SECTION romp_select_rtx_section#undef TARGET_ENCODE_SECTION_INFO#define TARGET_ENCODE_SECTION_INFO romp_encode_section_infostruct gcc_target targetm = TARGET_INITIALIZER;/* Return 1 if the insn using CC0 set by INSN does not contain any unsigned tests applied to the condition codes. Based on `next_insn_tests_no_inequality' in recog.c. */intnext_insn_tests_no_unsigned (insn) rtx insn;{ register rtx next = next_cc0_user (insn); if (next == 0) { if (find_reg_note (insn, REG_UNUSED, cc0_rtx)) return 1; else abort (); } return ((GET_CODE (next) == JUMP_INSN || GET_CODE (next) == INSN || GET_CODE (next) == CALL_INSN) && ! unsigned_comparisons_p (PATTERN (next)));}static intunsigned_comparisons_p (x) rtx x;{ register const char *fmt; register int len, i; register enum rtx_code code = GET_CODE (x); switch (code) { case REG: case PC: case CC0: case CONST_INT: case CONST_DOUBLE: case CONST: case LABEL_REF: case SYMBOL_REF: return 0; case LTU: case GTU: case LEU: case GEU: return (XEXP (x, 0) == cc0_rtx || XEXP (x, 1) == cc0_rtx); default: break; } len = GET_RTX_LENGTH (code); fmt = GET_RTX_FORMAT (code); for (i = 0; i < len; i++) { if (fmt[i] == 'e') { if (unsigned_comparisons_p (XEXP (x, i))) return 1; } else if (fmt[i] == 'E') { register int j; for (j = XVECLEN (x, i) - 1; j >= 0; j--) if (unsigned_comparisons_p (XVECEXP (x, i, j))) return 1; } } return 0;}/* Update the condition code from the insn. Look mostly at the first byte of the machine-specific insn description information. cc_state.value[12] refer to two possible values that might correspond to the CC. We only store register values. */voidupdate_cc (body, insn) rtx body ATTRIBUTE_UNUSED; rtx insn;{ switch (get_attr_cc (insn)) { case CC_NONE: /* Insn does not affect the CC at all. */ break; case CC_CHANGE0: /* Insn doesn't affect the CC but does modify operand[0], known to be a register. */ if (cc_status.value1 != 0 && reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1)) cc_status.value1 = 0; if (cc_status.value2 != 0 && reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value2)) cc_status.value2 = 0; break; case CC_COPY1TO0: /* Insn copies operand[1] to operand[0], both registers, but doesn't affect the CC. */ if (cc_status.value1 != 0 && reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value1)) cc_status.value1 = 0; if (cc_status.value2 != 0 && reg_overlap_mentioned_p (recog_data.operand[0], cc_status.value2)) cc_status.value2 = 0; if (cc_status.value1 != 0 && rtx_equal_p (cc_status.value1, recog_data.operand[1])) cc_status.value2 = recog_data.operand[0]; if (cc_status.value2 != 0 && rtx_equal_p (cc_status.value2, recog_data.operand[1])) cc_status.value1 = recog_data.operand[0]; break; case CC_CLOBBER: /* Insn clobbers CC. */ CC_STATUS_INIT; break; case CC_SETS: /* Insn sets CC to recog_data.operand[0], but overflow is impossible. */ CC_STATUS_INIT; cc_status.flags |= CC_NO_OVERFLOW; cc_status.value1 = recog_data.operand[0]; break; case CC_COMPARE: /* Insn is a compare which sets the CC fully. Update CC_STATUS for this compare and mark whether the test will be signed or unsigned. */ { register rtx p = PATTERN (insn); CC_STATUS_INIT; if (GET_CODE (p) == PARALLEL) p = XVECEXP (p, 0, 0); cc_status.value1 = SET_SRC (p); if (GET_CODE (SET_SRC (p)) == REG) cc_status.flags |= CC_NO_OVERFLOW; if (! next_insn_tests_no_unsigned (insn)) cc_status.flags |= CC_UNSIGNED; } break; case CC_TBIT: /* Insn sets T bit if result is nonzero. Next insn must be branch. */ CC_STATUS_INIT; cc_status.flags = CC_IN_TB | CC_NOT_NEGATIVE; break; default: abort (); }}/* Return 1 if a previous compare needs to be re-issued. This will happen if two compares tested the same objects, but one was signed and the other unsigned. OP is the comparison operation being performed. */intrestore_compare_p (op) rtx op;{ enum rtx_code code = GET_CODE (op); return (((code == GEU || code == LEU || code == GTU || code == LTU) && ! (cc_status.flags & CC_UNSIGNED)) || ((code == GE || code == LE || code == GT || code == LT) && (cc_status.flags & CC_UNSIGNED)));}/* Generate the (long) string corresponding to an inline multiply insn. Note that `r10' does not refer to the register r10, but rather to the SCR used as the MQ. */const char *output_in_line_mul (){ static char insns[200]; int i; strcpy (insns, "s %0,%0\n"); strcat (insns, "\tmts r10,%1\n"); for (i = 0; i < 16; i++) strcat (insns, "\tm %0,%2\n"); strcat (insns, "\tmfs r10,%0"); return insns;}/* Returns 1 if OP is a memory reference with an offset from a register within the range specified. The offset must also be a multiple of the size of the mode. */static intmemory_offset_in_range_p (op, mode, low, high) register rtx op; enum machine_mode mode; int low, high;{ int offset = 0; if (! memory_operand (op, mode)) return 0; while (GET_CODE (op) == SUBREG) { offset += SUBREG_BYTE (op); op = SUBREG_REG (op); } /* We must now have either (mem (reg (x)), (mem (plus (reg (x)) (c))), or a constant pool address. */ if (GET_CODE (op) != MEM) abort (); /* Now use the actual mode and get the address. */ mode = GET_MODE (op); op = XEXP (op, 0); if (GET_CODE (op) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (op)) offset = get_pool_offset (op) + 12; else if (GET_CODE (op) == PLUS) { if (GET_CODE (XEXP (op, 1)) != CONST_INT || ! register_operand (XEXP (op, 0), Pmode)) return 0; offset += INTVAL (XEXP (op, 1)); } else if (! register_operand (op, Pmode)) return 0; return (offset >= low && offset <= high && (offset % GET_MODE_SIZE (mode) == 0));}/* Return 1 if OP is a valid operand for a memory reference insn that can only reference indirect through a register. */intzero_memory_operand (op, mode) rtx op; enum machine_mode mode;{ return memory_offset_in_range_p (op, mode, 0, 0);}/* Return 1 if OP is a valid operand for a `short' memory reference insn. */intshort_memory_operand (op, mode) rtx op; enum machine_mode mode;{ if (mode == VOIDmode) mode = GET_MODE (op); return memory_offset_in_range_p (op, mode, 0, 15 * min (UNITS_PER_WORD, GET_MODE_SIZE (mode)));}/* Returns 1 if OP is a memory reference involving a symbolic constant that is not in the constant pool. */intsymbolic_memory_operand (op, mode) register rtx op; enum machine_mode mode;{ if (! memory_operand (op, mode)) return 0; while (GET_CODE (op) == SUBREG) op = SUBREG_REG (op); if (GET_CODE (op) != MEM) abort (); op = XEXP (op, 0); if (constant_pool_address_operand (op, VOIDmode)) return 0; else return romp_symbolic_operand (op, Pmode) || (GET_CODE (op) == PLUS && register_operand (XEXP (op, 0), Pmode) && romp_symbolic_operand (XEXP (op, 1), Pmode));}/* Returns 1 if OP is a constant pool reference to the current function. */intcurrent_function_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ if (GET_CODE (op) != MEM || GET_CODE (XEXP (op, 0)) != SYMBOL_REF || ! CONSTANT_POOL_ADDRESS_P (XEXP (op, 0))) return 0; op = get_pool_constant (XEXP (op, 0)); return (GET_CODE (op) == SYMBOL_REF && ! strcmp (current_function_name, XSTR (op, 0)));}/* Return nonzero if this function is known to have a null epilogue. */intnull_epilogue (){ return (reload_completed && first_reg_to_save () == 16 && ! romp_pushes_stack ());}/* Returns 1 if OP is the address of a location in the constant pool. */intconstant_pool_address_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return ((GET_CODE (op) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (op)) || (GET_CODE (op) == CONST && GET_CODE (XEXP (op, 0)) == PLUS && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT && GET_CODE (XEXP (XEXP (op, 0), 0)) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (XEXP (XEXP (op, 0), 0))));}/* Returns 1 if OP is either a symbol reference or a sum of a symbol reference and a constant. */intromp_symbolic_operand (op, mode) register rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ switch (GET_CODE (op)) { case SYMBOL_REF: case LABEL_REF: return ! op->integrated; case CONST: op = XEXP (op, 0); return (GET_CODE (XEXP (op, 0)) == SYMBOL_REF || GET_CODE (XEXP (op, 0)) == LABEL_REF) && GET_CODE (XEXP (op, 1)) == CONST_INT; default: return 0; }}/* Returns 1 if OP is a valid constant for the ROMP. */intconstant_operand (op, mode) register rtx op; enum machine_mode mode;{ switch (GET_CODE (op)) { case LABEL_REF: case SYMBOL_REF: case PLUS: case CONST: return romp_symbolic_operand (op,mode); case CONST_INT: return (unsigned int) (INTVAL (op) + 0x8000) < 0x10000 || (INTVAL (op) & 0xffff) == 0 || (INTVAL (op) & 0xffff0000) == 0; default: return 0; }}/* Returns 1 if OP is either a constant integer valid for the ROMP or a register. If a register, it must be in the proper mode unless MODE is VOIDmode. */intreg_or_cint_operand (op, mode) register rtx op; enum machine_mode mode;{ if (GET_CODE (op) == CONST_INT) return constant_operand (op, mode); return register_operand (op, mode);}/* Return 1 is the operand is either a register or ANY constant integer. */intreg_or_any_cint_operand (op, mode) register rtx op; enum machine_mode mode;{ return GET_CODE (op) == CONST_INT || register_operand (op, mode);}/* Return 1 if the operand is either a register or a valid D-type operand. */intreg_or_D_operand (op, mode) register rtx op; enum machine_mode mode;{ if (GET_CODE (op) == CONST_INT) return (unsigned) (INTVAL (op) + 0x8000) < 0x10000; return register_operand (op, mode);}/* Return 1 if the operand is either a register or an item that can be used as the operand of an SI add insn. */intreg_or_add_operand (op, mode) register rtx op; enum machine_mode mode;{ return reg_or_D_operand (op, mode) || romp_symbolic_operand (op, mode) || (GET_CODE (op) == CONST_INT && (INTVAL (op) & 0xffff) == 0);}/* Return 1 if the operand is either a register or an item that can be used as the operand of a ROMP logical AND insn. */intreg_or_and_operand (op, mode) register rtx op; enum machine_mode mode;{ if (reg_or_cint_operand (op, mode)) return 1;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -