📄 mips.c
字号:
&& REAL_VALUES_LESS (dflow, d)) return TRUE; } else { if (REAL_VALUES_LESS (d, sfhigh) && REAL_VALUES_LESS (sflow, d)) return TRUE; } return FALSE;}/* Accept the floating point constant 1 in the appropriate mode. */intconst_float_1_operand (op, mode) rtx op; enum machine_mode mode;{ REAL_VALUE_TYPE d; static REAL_VALUE_TYPE onedf; static REAL_VALUE_TYPE onesf; static int one_initialized; if (GET_CODE (op) != CONST_DOUBLE || mode != GET_MODE (op) || (mode != DFmode && mode != SFmode)) return FALSE; REAL_VALUE_FROM_CONST_DOUBLE (d, op); /* We only initialize these values if we need them, since we will never get called unless mips_isa >= 4. */ if (! one_initialized) { onedf = REAL_VALUE_ATOF ("1.0", DFmode); onesf = REAL_VALUE_ATOF ("1.0", SFmode); one_initialized = TRUE; } if (mode == DFmode) return REAL_VALUES_EQUAL (d, onedf); else return REAL_VALUES_EQUAL (d, onesf);}/* Return truth value if a memory operand fits in a single instruction (ie, register + small offset). */intsimple_memory_operand (op, mode) rtx op; enum machine_mode mode;{ rtx addr, plus0, plus1; /* Eliminate non-memory operations */ if (GET_CODE (op) != MEM) return FALSE; /* dword operations really put out 2 instructions, so eliminate them. */ /* ??? This isn't strictly correct. It is OK to accept multiword modes here, since the length attributes are being set correctly, but only if the address is offsettable. LO_SUM is not offsettable. */ if (GET_MODE_SIZE (GET_MODE (op)) > UNITS_PER_WORD) return FALSE; /* Decode the address now. */ addr = XEXP (op, 0); switch (GET_CODE (addr)) { default: break; case REG: case LO_SUM: return TRUE; case CONST_INT: return SMALL_INT (op); case PLUS: plus0 = XEXP (addr, 0); plus1 = XEXP (addr, 1); if (GET_CODE (plus0) == REG && GET_CODE (plus1) == CONST_INT && SMALL_INT (plus1)) return TRUE; else if (GET_CODE (plus1) == REG && GET_CODE (plus0) == CONST_INT && SMALL_INT (plus0)) return TRUE; else return FALSE;#if 0 /* We used to allow small symbol refs here (ie, stuff in .sdata or .sbss), but this causes some bugs in G++. Also, it won't interfere if the MIPS linker rewrites the store instruction because the function is PIC. */ case LABEL_REF: /* never gp relative */ break; case CONST: /* If -G 0, we can never have a GP relative memory operation. Also, save some time if not optimizing. */ if (!TARGET_GP_OPT) return FALSE; { rtx offset = const0_rtx; addr = eliminate_constant_term (XEXP (addr, 0), &offset); if (GET_CODE (op) != SYMBOL_REF) return FALSE; /* let's be paranoid.... */ if (! SMALL_INT (offset)) return FALSE; } /* fall through */ case SYMBOL_REF: return SYMBOL_REF_FLAG (addr);#endif } return FALSE;}/* Return true if the code of this rtx pattern is EQ or NE. */intequality_op (op, mode) rtx op; enum machine_mode mode;{ if (mode != GET_MODE (op)) return FALSE; return (GET_CODE (op) == EQ || GET_CODE (op) == NE);}/* Return true if the code is a relational operations (EQ, LE, etc.) */intcmp_op (op, mode) rtx op; enum machine_mode mode;{ if (mode != GET_MODE (op)) return FALSE; return (GET_RTX_CLASS (GET_CODE (op)) == '<');}/* Return true if the operand is either the PC or a label_ref. */intpc_or_label_operand (op, mode) rtx op; enum machine_mode mode;{ if (op == pc_rtx) return TRUE; if (GET_CODE (op) == LABEL_REF) return TRUE; return FALSE;}/* Test for a valid operand for a call instruction. Don't allow the arg pointer register or virtual regs since they may change into reg + const, which the patterns can't handle yet. */intcall_insn_operand (op, mode) rtx op; enum machine_mode mode;{ if (CONSTANT_ADDRESS_P (op) || (GET_CODE (op) == REG && op != arg_pointer_rtx && ! (REGNO (op) >= FIRST_PSEUDO_REGISTER && REGNO (op) <= LAST_VIRTUAL_REGISTER))) return 1; return 0;}/* Return true if OPERAND is valid as a source operand for a move instruction. */intmove_operand (op, mode) rtx op; enum machine_mode mode;{ /* Accept any general operand after reload has started; doing so avoids losing if reload does an in-place replacement of a register with a SYMBOL_REF or CONST. */ return (general_operand (op, mode) && (! (mips_split_addresses && mips_check_split (op, mode)) || reload_in_progress || reload_completed)); }/* Return true if OPERAND is valid as a source operand for movdi. This accepts not only general_operand, but also sign extended constants and registers. We need to accept sign extended constants in case a sign extended register which is used in an expression, and is equivalent to a constant, is spilled. */intmovdi_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 || (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);}/* 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);}/* Return true 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;}/* 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) : (rtx)0; 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 != (rtx)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 == (rtx *)0 || cur_insn == (rtx)0 || next_insn == (rtx)0 || GET_CODE (next_insn) == CODE_LABEL || (set_reg = operands[0]) == (rtx)0) { dslots_number_nops = 0; mips_load_reg = (rtx)0; mips_load_reg2 = (rtx)0; mips_load_reg3 = (rtx)0; mips_load_reg4 = (rtx)0; return ret; } set_reg = operands[0]; if (set_reg == (rtx)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)) { default: break; case REG: case CONST_INT: case LO_SUM: break;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -