📄 mips.c
字号:
ret = "sd\t%.,%0"; else { operands[2] = adj_offsettable_operand (op0, 4); ret = "sw\t%.,%0\n\tsw\t%.,%2"; } } if (TARGET_STATS) mips_count_memory_refs (op0, 2); if (ret != (char *)0 && MEM_VOLATILE_P (op0)) { int i = strlen (ret); if (i > sizeof (volatile_buffer) - sizeof ("%{%}")) abort (); sprintf (volatile_buffer, "%%{%s%%}", ret); ret = volatile_buffer; } } if (ret == (char *)0) { abort_with_insn (insn, "Bad move"); return 0; } if (delay != DELAY_NONE) return mips_fill_delay_slot (ret, delay, operands, insn); return ret;}/* Provide the costs of an addressing mode that contains ADDR. If ADDR is not a valid address, its cost is irrelevant. */intmips_address_cost (addr) rtx addr;{ switch (GET_CODE (addr)) { default: break; case LO_SUM: case HIGH: return 1; case LABEL_REF: return 2; case CONST: { rtx offset = const0_rtx; addr = eliminate_constant_term (XEXP (addr, 0), &offset); if (GET_CODE (addr) == LABEL_REF) return 2; if (GET_CODE (addr) != SYMBOL_REF) return 4; if (! SMALL_INT (offset)) return 2; } /* fall through */ case SYMBOL_REF: return SYMBOL_REF_FLAG (addr) ? 1 : 2; case PLUS: { register rtx plus0 = XEXP (addr, 0); register rtx plus1 = XEXP (addr, 1); if (GET_CODE (plus0) != REG && GET_CODE (plus1) == REG) { plus0 = XEXP (addr, 1); plus1 = XEXP (addr, 0); } if (GET_CODE (plus0) != REG) break; switch (GET_CODE (plus1)) { default: break; case CONST_INT: return (SMALL_INT (plus1) ? 1 : 2); case CONST: case SYMBOL_REF: case LABEL_REF: case HIGH: case LO_SUM: return mips_address_cost (plus1) + 1; } } } return 4;}/* Return true if X is an address which needs a temporary register when reloaded while generating PIC code. */intpic_address_needs_scratch (x) rtx x;{ /* An address which is a symbolic plus a non SMALL_INT needs a temp reg. */ if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT && ! SMALL_INT (XEXP (XEXP (x, 0), 1))) return 1; return 0;}/* Make normal rtx_code into something we can index from an array */static enum internal_testmap_test_to_internal_test (test_code) enum rtx_code test_code;{ enum internal_test test = ITEST_MAX; switch (test_code) { default: break; case EQ: test = ITEST_EQ; break; case NE: test = ITEST_NE; break; case GT: test = ITEST_GT; break; case GE: test = ITEST_GE; break; case LT: test = ITEST_LT; break; case LE: test = ITEST_LE; break; case GTU: test = ITEST_GTU; break; case GEU: test = ITEST_GEU; break; case LTU: test = ITEST_LTU; break; case LEU: test = ITEST_LEU; break; } return test;}/* Generate the code to compare two integer values. The return value is: (reg:SI xx) The pseudo register the comparison is in (rtx)0 No register, generate a simple branch. ??? This is called with result nonzero by the Scond patterns in mips.md. These patterns are called with a target in the mode of the Scond instruction pattern. Since this must be a constant, we must use SImode. This means that if RESULT is non-zero, it will always be an SImode register, even if TARGET_64BIT is true. We cope with this by calling convert_move rather than emit_move_insn. This will sometimes lead to an unnecessary extension of the result; for example: long long foo (long long i) { return i < 5; } */rtxgen_int_relational (test_code, result, cmp0, cmp1, p_invert) enum rtx_code test_code; /* relational test (EQ, etc) */ rtx result; /* result to store comp. or 0 if branch */ rtx cmp0; /* first operand to compare */ rtx cmp1; /* second operand to compare */ int *p_invert; /* NULL or ptr to hold whether branch needs */ /* to reverse its test */{ struct cmp_info { enum rtx_code test_code; /* code to use in instruction (LT vs. LTU) */ int const_low; /* low bound of constant we can accept */ int const_high; /* high bound of constant we can accept */ int const_add; /* constant to add (convert LE -> LT) */ int reverse_regs; /* reverse registers in test */ int invert_const; /* != 0 if invert value if cmp1 is constant */ int invert_reg; /* != 0 if invert value if cmp1 is register */ int unsignedp; /* != 0 for unsigned comparisons. */ }; static struct cmp_info info[ (int)ITEST_MAX ] = { { XOR, 0, 65535, 0, 0, 0, 0, 0 }, /* EQ */ { XOR, 0, 65535, 0, 0, 1, 1, 0 }, /* NE */ { LT, -32769, 32766, 1, 1, 1, 0, 0 }, /* GT */ { LT, -32768, 32767, 0, 0, 1, 1, 0 }, /* GE */ { LT, -32768, 32767, 0, 0, 0, 0, 0 }, /* LT */ { LT, -32769, 32766, 1, 1, 0, 1, 0 }, /* LE */ { LTU, -32769, 32766, 1, 1, 1, 0, 1 }, /* GTU */ { LTU, -32768, 32767, 0, 0, 1, 1, 1 }, /* GEU */ { LTU, -32768, 32767, 0, 0, 0, 0, 1 }, /* LTU */ { LTU, -32769, 32766, 1, 1, 0, 1, 1 }, /* LEU */ }; enum internal_test test; enum machine_mode mode; struct cmp_info *p_info; int branch_p; int eqne_p; int invert; rtx reg; rtx reg2; test = map_test_to_internal_test (test_code); if (test == ITEST_MAX) abort (); p_info = &info[ (int)test ]; eqne_p = (p_info->test_code == XOR); mode = GET_MODE (cmp0); if (mode == VOIDmode) mode = GET_MODE (cmp1); /* Eliminate simple branches */ branch_p = (result == (rtx)0); if (branch_p) { if (GET_CODE (cmp0) == REG || GET_CODE (cmp0) == SUBREG) { /* Comparisons against zero are simple branches */ if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0) return (rtx)0; /* Test for beq/bne. */ if (eqne_p) return (rtx)0; } /* allocate a pseudo to calculate the value in. */ result = gen_reg_rtx (mode); } /* Make sure we can handle any constants given to us. */ if (GET_CODE (cmp0) == CONST_INT) cmp0 = force_reg (mode, cmp0); if (GET_CODE (cmp1) == CONST_INT) { HOST_WIDE_INT value = INTVAL (cmp1); if (value < p_info->const_low || value > p_info->const_high /* ??? Why? And why wasn't the similar code below modified too? */ || (TARGET_64BIT && HOST_BITS_PER_WIDE_INT < 64 && p_info->const_add != 0 && ((p_info->unsignedp ? ((unsigned HOST_WIDE_INT) (value + p_info->const_add) > INTVAL (cmp1)) : (value + p_info->const_add) > INTVAL (cmp1)) != (p_info->const_add > 0)))) cmp1 = force_reg (mode, cmp1); } /* See if we need to invert the result. */ invert = (GET_CODE (cmp1) == CONST_INT) ? p_info->invert_const : p_info->invert_reg; if (p_invert != (int *)0) { *p_invert = invert; invert = FALSE; } /* Comparison to constants, may involve adding 1 to change a LT into LE. Comparison between two registers, may involve switching operands. */ if (GET_CODE (cmp1) == CONST_INT) { if (p_info->const_add != 0) { HOST_WIDE_INT new = INTVAL (cmp1) + p_info->const_add; /* If modification of cmp1 caused overflow, we would get the wrong answer if we follow the usual path; thus, x > 0xffffffffU would turn into x > 0U. */ if ((p_info->unsignedp ? (unsigned HOST_WIDE_INT) new > INTVAL (cmp1) : new > INTVAL (cmp1)) != (p_info->const_add > 0)) { /* This test is always true, but if INVERT is true then the result of the test needs to be inverted so 0 should be returned instead. */ emit_move_insn (result, invert ? const0_rtx : const_true_rtx); return result; } else cmp1 = GEN_INT (new); } } else if (p_info->reverse_regs) { rtx temp = cmp0; cmp0 = cmp1; cmp1 = temp; } if (test == ITEST_NE && GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) == 0) reg = cmp0; else { reg = (invert || eqne_p) ? gen_reg_rtx (mode) : result; convert_move (reg, gen_rtx (p_info->test_code, mode, cmp0, cmp1), 0); } if (test == ITEST_NE) { convert_move (result, gen_rtx (GTU, mode, reg, const0_rtx), 0); invert = FALSE; } else if (test == ITEST_EQ) { reg2 = (invert) ? gen_reg_rtx (mode) : result; convert_move (reg2, gen_rtx (LTU, mode, reg, const1_rtx), 0); reg = reg2; } if (invert) convert_move (result, gen_rtx (XOR, mode, reg, const1_rtx), 0); return result;}/* Emit the common code for doing conditional branches. operand[0] is the label to jump to. The comparison operands are saved away by cmp{si,di,sf,df}. */voidgen_conditional_branch (operands, test_code) rtx operands[]; enum rtx_code test_code;{ static enum machine_mode mode_map[(int)CMP_MAX][(int)ITEST_MAX] = { { /* CMP_SI */ SImode, /* eq */ SImode, /* ne */ SImode, /* gt */ SImode, /* ge */ SImode, /* lt */ SImode, /* le */ SImode, /* gtu */ SImode, /* geu */ SImode, /* ltu */ SImode, /* leu */ }, { /* CMP_DI */ DImode, /* eq */ DImode, /* ne */ DImode, /* gt */ DImode, /* ge */ DImode, /* lt */ DImode, /* le */ DImode, /* gtu */ DImode, /* geu */ DImode, /* ltu */ DImode, /* leu */ }, { /* CMP_SF */ CC_FPmode, /* eq */ CC_REV_FPmode, /* ne */ CC_FPmode, /* gt */ CC_FPmode, /* ge */ CC_FPmode, /* lt */ CC_FPmode, /* le */ VOIDmode, /* gtu */ VOIDmode, /* geu */ VOIDmode, /* ltu */ VOIDmode, /* leu */ }, { /* CMP_DF */ CC_FPmode, /* eq */ CC_REV_FPmode, /* ne */ CC_FPmode, /* gt */ CC_FPmode, /* ge */ CC_FPmode, /* lt */ CC_FPmode, /* le */ VOIDmode, /* gtu */ VOIDmode, /* geu */ VOIDmode, /* ltu */ VOIDmode, /* leu */ }, }; enum machine_mode mode; enum cmp_type type = branch_type; rtx cmp0 = branch_cmp[0]; rtx cmp1 = branch_cmp[1]; rtx label1 = gen_rtx (LABEL_REF, VOIDmode, operands[0]); rtx label2 = pc_rtx; rtx reg = (rtx)0; int invert = 0; enum internal_test test = map_test_to_internal_test (test_code); if (test == ITEST_MAX) { mode = word_mode; goto fail; } /* Get the machine mode to use (CCmode, CC_EQmode, CC_FPmode, or CC_REV_FPmode). */ mode = mode_map[(int)type][(int)test]; if (mode == VOIDmode) goto fail; switch (type) { default: goto fail; case CMP_SI: case CMP_DI: reg = gen_int_relational (test_code, (rtx)0, cmp0, cmp1, &invert); if (reg != (rtx)0) { cmp0 = reg; cmp1 = const0_rtx; test_code = NE; } /* Make sure not non-zero constant if ==/!= */ else if (GET_CODE (cmp1) == CONST_INT && INTVAL (cmp1) != 0) cmp1 = force_reg (mode, cmp1); break; case CMP_DF: case CMP_SF: { rtx reg = gen_rtx (REG, mode, FPSW_REGNUM); emit_insn (gen_rtx (SET, VOIDmode, reg, gen_rtx (test_code, mode, cmp0, cmp1))); cmp0 = reg; cmp1 = const0_rtx; test_code = NE; } break; } /* Generate the jump */ if (invert) { label2 = label1; label1 = pc_rtx; } emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, gen_rtx (test_code, mode, cmp0, cmp1), label1, label2))); return;fail: abort_with_insn (gen_rtx (test_code, mode, cmp0, cmp1), "bad test");}#if 0/* Internal code to generate the load and store of one word/short/byte. The load is emitted directly, and the store insn is returned. */#define UNITS_PER_MIPS_DWORD 8#define UNITS_PER_MIPS_WORD 4#define UNITS_PER_MIPS_HWORD 2static rtxblock_move_load_store (dest_reg, src_reg, p_bytes, p_offset, align, orig_src) rtx src_reg; /* register holding source memory address */ rtx dest_reg; /* register holding dest. memory address */ int *p_bytes; /* pointer to # bytes remaining */ int *p_offset; /* pointer to current offset */ int align; /* alignment */ rtx orig_src; /* original source for making a reg note */{ int bytes; /* # bytes remaining */ int offset; /* offset to use */ int size; /* size in bytes of load/store */ enum machine_mode mode; /* mode to use for load/store */ rtx reg; /* temporary register */ rtx src_addr; /* source address */ rtx dest_addr; /* destination address */ rtx insn; /* insn of the load */ rtx orig_src_addr; /* original source address */ rtx (*load_func)(); /* function to generate load insn */ rtx (*store_func)(); /* function to generate destination insn */ bytes = *p_bytes; if (bytes <= 0 || align <= 0) abort (); if (bytes >= UNITS_PER_MIPS_DWORD && align >= UNIS_PER_MIPS_DWORD) { mode = DImode; size = UNITS_PER_MIPS_DWORD; load_func = gen_movdi; store_func = gen_movdi; } else if (bytes >= UNITS_PER_MIPS_WORD && align >= UNITS_PER_MIPS_WORD) { mode = SImode; size = UNITS_PER_MIPS_WORD; load_func = gen_movsi; store_func = gen_movsi; }
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -