📄 mips.c
字号:
|| (GET_CODE (XEXP (op, 0)) == CONST_INT && GET_MODE (XEXP (op, 0)) == VOIDmode)) && (register_operand (XEXP (op, 0), SImode) || immediate_operand (XEXP (op, 0), SImode))) return 1; return (general_operand (op, mode) && ! (TARGET_MIPS16 && GET_CODE (op) == SYMBOL_REF && ! mips16_constant (op, mode, 1, 0)));}/* Like register_operand, but when in 64 bit mode also accept a sign extend of a 32 bit register, since the value is known to be already sign extended. */intse_register_operand (op, mode) rtx op; enum machine_mode mode;{ if (TARGET_64BIT && mode == DImode && GET_CODE (op) == SIGN_EXTEND && GET_MODE (op) == DImode && GET_MODE (XEXP (op, 0)) == SImode && register_operand (XEXP (op, 0), SImode)) return 1; return register_operand (op, mode);}/* Like reg_or_0_operand, but when in 64 bit mode also accept a sign extend of a 32 bit register, since the value is known to be already sign extended. */intse_reg_or_0_operand (op, mode) rtx op; enum machine_mode mode;{ if (TARGET_64BIT && mode == DImode && GET_CODE (op) == SIGN_EXTEND && GET_MODE (op) == DImode && GET_MODE (XEXP (op, 0)) == SImode && register_operand (XEXP (op, 0), SImode)) return 1; return reg_or_0_operand (op, mode);}/* Like uns_arith_operand, but when in 64 bit mode also accept a sign extend of a 32 bit register, since the value is known to be already sign extended. */intse_uns_arith_operand (op, mode) rtx op; enum machine_mode mode;{ if (TARGET_64BIT && mode == DImode && GET_CODE (op) == SIGN_EXTEND && GET_MODE (op) == DImode && GET_MODE (XEXP (op, 0)) == SImode && register_operand (XEXP (op, 0), SImode)) return 1; return uns_arith_operand (op, mode);}/* Like arith_operand, but when in 64 bit mode also accept a sign extend of a 32 bit register, since the value is known to be already sign extended. */intse_arith_operand (op, mode) rtx op; enum machine_mode mode;{ if (TARGET_64BIT && mode == DImode && GET_CODE (op) == SIGN_EXTEND && GET_MODE (op) == DImode && GET_MODE (XEXP (op, 0)) == SImode && register_operand (XEXP (op, 0), SImode)) return 1; return arith_operand (op, mode);}/* Like nonmemory_operand, but when in 64 bit mode also accept a sign extend of a 32 bit register, since the value is known to be already sign extended. */intse_nonmemory_operand (op, mode) rtx op; enum machine_mode mode;{ if (TARGET_64BIT && mode == DImode && GET_CODE (op) == SIGN_EXTEND && GET_MODE (op) == DImode && GET_MODE (XEXP (op, 0)) == SImode && register_operand (XEXP (op, 0), SImode)) return 1; return nonmemory_operand (op, mode);}/* Like nonimmediate_operand, but when in 64 bit mode also accept a sign extend of a 32 bit register, since the value is known to be already sign extended. */intse_nonimmediate_operand (op, mode) rtx op; enum machine_mode mode;{ if (TARGET_64BIT && mode == DImode && GET_CODE (op) == SIGN_EXTEND && GET_MODE (op) == DImode && GET_MODE (XEXP (op, 0)) == SImode && register_operand (XEXP (op, 0), SImode)) return 1; return nonimmediate_operand (op, mode);}/* Accept any operand that can appear in a mips16 constant table instruction. We can't use any of the standard operand functions because for these instructions we accept values that are not accepted by LEGITIMATE_CONSTANT, such as arbitrary SYMBOL_REFs. */intconsttable_operand (op, mode) rtx op; enum machine_mode mode ATTRIBUTE_UNUSED;{ return CONSTANT_P (op);}/* 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) > 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;}/* 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) && current_frame_info.insns_len > 0 && XSTR (op, 0)[0] == '*' && strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX, sizeof LOCAL_LABEL_PREFIX - 1) == 0 && (current_frame_info.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) && current_frame_info.insns_len > 0 && XSTR (op, 0)[0] == '*' && strncmp (XSTR (op, 0) + 1, LOCAL_LABEL_PREFIX, sizeof LOCAL_LABEL_PREFIX - 1) == 0 && (current_frame_info.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. */char *mips_fill_delay_slot (ret, type, operands, cur_insn) 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; if (type == DELAY_LOAD || type == DELAY_FCMP) num_nops = 1; else if (type == DELAY_HILO) num_nops = 2; else num_nops = 0; /* Make sure that we don't put nop's after labels. */ next_insn = NEXT_INSN (cur_insn); while (next_insn != 0 && GET_CODE (next_insn) == NOTE) next_insn = NEXT_INSN (next_insn); dslots_load_total += num_nops; if (TARGET_DEBUG_F_MODE || !optimize || type == DELAY_NONE || operands == 0 || cur_insn == 0 || next_insn == 0 || GET_CODE (next_insn) == CODE_LABEL || (set_reg = operands[0]) == 0) { dslots_number_nops = 0; mips_load_reg = 0; mips_load_reg2 = 0; mips_load_reg3 = 0; mips_load_reg4 = 0; return ret; } set_reg = operands[0]; if (set_reg == 0) return ret; while (GET_CODE (set_reg) == SUBREG) set_reg = SUBREG_REG (set_reg); mode = GET_MODE (set_reg); dslots_number_nops = num_nops; mips_load_reg = set_reg; if (GET_MODE_SIZE (mode) > (FP_REG_P (REGNO (set_reg)) ? UNITS_PER_FPREG : UNITS_PER_WORD)) mips_load_reg2 = gen_rtx (REG, SImode, REGNO (set_reg) + 1); else mips_load_reg2 = 0; if (type == DELAY_HILO) { mips_load_reg3 = gen_rtx (REG, SImode, MD_REG_FIRST); mips_load_reg4 = gen_rtx (REG, SImode, MD_REG_FIRST+1); } else { mips_load_reg3 = 0; mips_load_reg4 = 0; } return ret;}/* Determine whether a memory reference takes one (based off of the GP pointer), two (normal), or three (label + reg) instructions, and bump the appropriate counter for -mstats. */voidmips_count_memory_refs (op, num) rtx op; int num;{ int additional = 0; int n_words = 0; rtx addr, plus0, plus1; enum rtx_code code0, code1; int looping; if (TARGET_DEBUG_B_MODE) { fprintf (stderr, "\n========== mips_count_memory_refs:\n"); debug_rtx (op); } /* Skip MEM if passed, otherwise handle movsi of address. */ addr = (GET_CODE (op) != MEM) ? op : XEXP (op, 0); /* Loop, going through the address RTL. */ do { looping = FALSE; switch (GET_CODE (addr)) { case REG: case CONST_INT: case LO_SUM: break; case PLUS: plus0 = XEXP (addr, 0); plus1 = XEXP (addr, 1); code0 = GET_CODE (plus0); code1 = GET_CODE (plus1);
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -