?? mips.c
字號:
intconsttable_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return CONSTANT_P (op);}/* Coprocessor operand; return true if rtx is a REG and refers to a coprocessor. */intcoprocessor_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return (GET_CODE (op) == REG && COP0_REG_FIRST <= REGNO (op) && REGNO (op) <= COP3_REG_LAST);}intcoprocessor2_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return (GET_CODE (op) == REG && COP2_REG_FIRST <= REGNO (op) && REGNO (op) <= COP2_REG_LAST);}/* Returns 1 if OP is a symbolic operand, i.e. a symbol_ref or a label_ref, possibly with an offset. */intsymbolic_operand (op, mode) register rtx op; enum machine_mode mode;{ if (mode != VOIDmode && GET_MODE (op) != VOIDmode && mode != GET_MODE (op)) return 0; if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF) return 1; if (GET_CODE (op) == CONST && GET_CODE (XEXP (op,0)) == PLUS && GET_CODE (XEXP (XEXP (op,0), 0)) == SYMBOL_REF && GET_CODE (XEXP (XEXP (op,0), 1)) == CONST_INT) return 1; return 0;}/* Return nonzero if we split the address into high and low parts. *//* ??? We should also handle reg+array somewhere. We get four instructions currently, lui %hi/addui %lo/addui reg/lw. Better is lui %hi/addui reg/lw %lo. Fixing GO_IF_LEGITIMATE_ADDRESS to accept (plus (reg) (symbol_ref)) doesn't work because the SYMBOL_REF is broken out of the address, then we have 4 instructions to combine. Perhaps add a 3->2 define_split for combine. *//* ??? We could also split a CONST_INT here if it is a large_int(). However, it doesn't seem to be very useful to have %hi(constant). We would be better off by doing the masking ourselves and then putting the explicit high part of the constant in the RTL. This will give better optimization. Also, %hi(constant) needs assembler changes to work. There is already a define_split that does this. */intmips_check_split (address, mode) rtx address; enum machine_mode mode;{ /* ??? This is the same check used in simple_memory_operand. We use it here because LO_SUM is not offsettable. */ if (GET_MODE_SIZE (mode) > (unsigned) UNITS_PER_WORD) return 0; if ((GET_CODE (address) == SYMBOL_REF && ! SYMBOL_REF_FLAG (address)) || (GET_CODE (address) == CONST && GET_CODE (XEXP (XEXP (address, 0), 0)) == SYMBOL_REF && ! SYMBOL_REF_FLAG (XEXP (XEXP (address, 0), 0))) || GET_CODE (address) == LABEL_REF) return 1; return 0;}/* This function is used to implement REG_MODE_OK_FOR_BASE_P. */intmips_reg_mode_ok_for_base_p (reg, mode, strict) rtx reg; enum machine_mode mode; int strict;{ return (strict ? REGNO_MODE_OK_FOR_BASE_P (REGNO (reg), mode) : GP_REG_OR_PSEUDO_NONSTRICT_P (REGNO (reg), mode));}/* This function is used to implement GO_IF_LEGITIMATE_ADDRESS. It returns a nonzero value if XINSN is a legitimate address for a memory operand of the indicated MODE. STRICT is nonzero if this function is called during reload. */intmips_legitimate_address_p (mode, xinsn, strict) enum machine_mode mode; rtx xinsn; int strict;{ if (TARGET_DEBUG_B_MODE) { GO_PRINTF2 ("\n========== GO_IF_LEGITIMATE_ADDRESS, %sstrict\n", strict ? "" : "not "); GO_DEBUG_RTX (xinsn); } /* Check for constant before stripping off SUBREG, so that we don't accept (subreg (const_int)) which will fail to reload. */ if (CONSTANT_ADDRESS_P (xinsn) && ! (mips_split_addresses && mips_check_split (xinsn, mode)) && (! TARGET_MIPS16 || mips16_constant (xinsn, mode, 1, 0))) return 1; while (GET_CODE (xinsn) == SUBREG) xinsn = SUBREG_REG (xinsn); /* The mips16 can only use the stack pointer as a base register when loading SImode or DImode values. */ if (GET_CODE (xinsn) == REG && mips_reg_mode_ok_for_base_p (xinsn, mode, strict)) return 1; if (GET_CODE (xinsn) == LO_SUM && mips_split_addresses) { register rtx xlow0 = XEXP (xinsn, 0); register rtx xlow1 = XEXP (xinsn, 1); while (GET_CODE (xlow0) == SUBREG) xlow0 = SUBREG_REG (xlow0); if (GET_CODE (xlow0) == REG && mips_reg_mode_ok_for_base_p (xlow0, mode, strict) && mips_check_split (xlow1, mode)) return 1; } if (GET_CODE (xinsn) == PLUS) { register rtx xplus0 = XEXP (xinsn, 0); register rtx xplus1 = XEXP (xinsn, 1); register enum rtx_code code0; register enum rtx_code code1; while (GET_CODE (xplus0) == SUBREG) xplus0 = SUBREG_REG (xplus0); code0 = GET_CODE (xplus0); while (GET_CODE (xplus1) == SUBREG) xplus1 = SUBREG_REG (xplus1); code1 = GET_CODE (xplus1); /* The mips16 can only use the stack pointer as a base register when loading SImode or DImode values. */ if (code0 == REG && mips_reg_mode_ok_for_base_p (xplus0, mode, strict)) { if (code1 == CONST_INT && SMALL_INT (xplus1)) return 1; /* On the mips16, we represent GP relative offsets in RTL. These are 16 bit signed values, and can serve as register offsets. */ if (TARGET_MIPS16 && mips16_gp_offset_p (xplus1)) return 1; /* For some code sequences, you actually get better code by pretending that the MIPS supports an address mode of a constant address + a register, even though the real machine doesn't support it. This is because the assembler can use $r1 to load just the high 16 bits, add in the register, and fold the low 16 bits into the memory reference, whereas the compiler generates a 4 instruction sequence. On the other hand, CSE is not as effective. It would be a win to generate the lui directly, but the MIPS assembler does not have syntax to generate the appropriate relocation. */ /* Also accept CONST_INT addresses here, so no else. */ /* Reject combining an embedded PIC text segment reference with a register. That requires an additional instruction. */ /* ??? Reject combining an address with a register for the MIPS 64 bit ABI, because the SGI assembler can not handle this. */ if (!TARGET_DEBUG_A_MODE && (mips_abi == ABI_32 || mips_abi == ABI_O64 || mips_abi == ABI_EABI) && CONSTANT_ADDRESS_P (xplus1) && ! mips_split_addresses && (!TARGET_EMBEDDED_PIC || code1 != CONST || GET_CODE (XEXP (xplus1, 0)) != MINUS) /* When assembling for machines with 64 bit registers, the assembler will sign-extend the constant "foo" in "la x, foo(x)" yielding the wrong result for: (set (blah:DI) (plus x y)). */ && (!TARGET_64BIT || (code1 == CONST_INT && trunc_int_for_mode (INTVAL (xplus1), SImode) == INTVAL (xplus1))) && !TARGET_MIPS16) return 1; } } if (TARGET_DEBUG_B_MODE) GO_PRINTF ("Not a legitimate address\n"); /* The address was not legitimate. */ return 0;}/* We need a lot of little routines to check constant values on the mips16. These are used to figure out how long the instruction will be. It would be much better to do this using constraints, but there aren't nearly enough letters available. */static intm16_check_op (op, low, high, mask) rtx op; int low; int high; int mask;{ return (GET_CODE (op) == CONST_INT && INTVAL (op) >= low && INTVAL (op) <= high && (INTVAL (op) & mask) == 0);}intm16_uimm3_b (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, 0x1, 0x8, 0);}intm16_simm4_1 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, - 0x8, 0x7, 0);}intm16_nsimm4_1 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, - 0x7, 0x8, 0);}intm16_simm5_1 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, - 0x10, 0xf, 0);}intm16_nsimm5_1 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, - 0xf, 0x10, 0);}intm16_uimm5_4 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, (- 0x10) << 2, 0xf << 2, 3);}intm16_nuimm5_4 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, (- 0xf) << 2, 0x10 << 2, 3);}intm16_simm8_1 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, - 0x80, 0x7f, 0);}intm16_nsimm8_1 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, - 0x7f, 0x80, 0);}intm16_uimm8_1 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, 0x0, 0xff, 0);}intm16_nuimm8_1 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, - 0xff, 0x0, 0);}intm16_uimm8_m1_1 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, - 0x1, 0xfe, 0);}intm16_uimm8_4 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, 0x0, 0xff << 2, 3);}intm16_nuimm8_4 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, (- 0xff) << 2, 0x0, 3);}intm16_simm8_8 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, (- 0x80) << 3, 0x7f << 3, 7);}intm16_nsimm8_8 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return m16_check_op (op, (- 0x7f) << 3, 0x80 << 3, 7);}/* References to the string table on the mips16 only use a small offset if the function is small. See the comment in the SYMBOL_REF case in simple_memory_operand. We can't check for LABEL_REF here, because the offset is always large if the label is before the referencing instruction. */intm16_usym8_4 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ if (GET_CODE (op) == SYMBOL_REF && SYMBOL_REF_FLAG (op) && cfun->machine->insns_len > 0 && XSTR (op, 0)[0] == '*' && strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX, sizeof LOCAL_LABEL_PREFIX - 1) == 0 && (cfun->machine->insns_len + get_pool_size () + mips_string_length < 4 * 0x100)) { struct string_constant *l; /* Make sure this symbol is on thelist of string constants to be output for this function. It is possible that it has already been output, in which case this requires a large offset. */ for (l = string_constants; l != NULL; l = l->next) if (strcmp (l->label, XSTR (op, 0)) == 0) return 1; } return 0;}intm16_usym5_4 (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ if (GET_CODE (op) == SYMBOL_REF && SYMBOL_REF_FLAG (op) && cfun->machine->insns_len > 0 && XSTR (op, 0)[0] == '*' && strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX, sizeof LOCAL_LABEL_PREFIX - 1) == 0 && (cfun->machine->insns_len + get_pool_size () + mips_string_length < 4 * 0x20)) { struct string_constant *l; /* Make sure this symbol is on thelist of string constants to be output for this function. It is possible that it has already been output, in which case this requires a large offset. */ for (l = string_constants; l != NULL; l = l->next) if (strcmp (l->label, XSTR (op, 0)) == 0) return 1; } return 0;}/* Returns an operand string for the given instruction's delay slot, after updating filled delay slot statistics. We assume that operands[0] is the target register that is set. In order to check the next insn, most of this functionality is moved to FINAL_PRESCAN_INSN, and we just set the global variables that it needs. *//* ??? This function no longer does anything useful, because final_prescan_insn now will never emit a nop. */const char *mips_fill_delay_slot (ret, type, operands, cur_insn) const char *ret; /* normal string to return */ enum delay_type type; /* type of delay */ rtx operands[]; /* operands to use */ rtx cur_insn; /* current insn */{ register rtx set_reg; register enum machine_mode mode; register rtx next_insn = cur_insn ? NEXT_INSN (cur_insn) : NULL_RTX; register int num_nops;
?? 快捷鍵說明
復制代碼
Ctrl + C
搜索代碼
Ctrl + F
全屏模式
F11
切換主題
Ctrl + Shift + D
顯示快捷鍵
?
增大字號
Ctrl + =
減小字號
Ctrl + -