📄 mips.c
字号:
/* 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. */ if (GET_MODE_SIZE (GET_MODE (op)) > (HAVE_64BIT_P () ? 8 : 4)) return FALSE; /* Decode the address now. */ addr = XEXP (op, 0); switch (GET_CODE (addr)) { default: break; case REG: 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 (mips_section_threshold == 0 || !optimize || !TARGET_GP_OPT) return FALSE; { rtx offset = const0_rtx; addr = eliminate_constant_term (addr, &offset); if (GET_CODE (op) != SYMBOL_REF) return FALSE; /* let's be paranoid.... */ if (INTVAL (offset) < 0 || INTVAL (offset) > 0xffff) 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 (classify_op (op, mode) & CLASS_EQUALITY_OP) != 0;}/* 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 (classify_op (op, mode) & CLASS_CMP_OP) != 0;}/* Genrecog does not take the type of match_operator into consideration, and would complain about two patterns being the same if the same function is used, so make it believe they are different. */intcmp2_op (op, mode) rtx op; enum machine_mode mode;{ if (mode != GET_MODE (op)) return FALSE; return (classify_op (op, mode) & CLASS_CMP_OP) != 0;}/* Return true if the code is an unsigned relational operations (LEU, etc.) */intuns_cmp_op (op,mode) rtx op; enum machine_mode mode;{ if (mode != GET_MODE (op)) return FALSE; return (classify_op (op, mode) & CLASS_UNS_CMP_OP) == CLASS_UNS_CMP_OP;}/* Return true if the code is a relational operation FP can use. */intfcmp_op (op, mode) rtx op; enum machine_mode mode;{ if (mode != GET_MODE (op)) return FALSE; return (classify_op (op, mode) & CLASS_FCMP_OP) != 0;}/* 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;}/* Return an operand string if the given instruction's delay slot or wrap it in a .set noreorder section. This is for filling delay slots on load type instructions under GAS, which does no reordering on its own. For the MIPS assembler, all we do is update the 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. */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; mips_load_reg2 = (mode == DImode || mode == DFmode) ? gen_rtx (REG, SImode, REGNO (set_reg) + 1) : (rtx)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; } if (TARGET_GAS && set_noreorder++ == 0) fputs ("\t.set\tnoreorder\n", asm_out_file); 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: break; case PLUS: plus0 = XEXP (addr, 0); plus1 = XEXP (addr, 1); code0 = GET_CODE (plus0); code1 = GET_CODE (plus1); if (code0 == REG) { additional++; addr = plus1; looping = TRUE; continue; } if (code0 == CONST_INT) { addr = plus1; looping = TRUE; continue; } if (code1 == REG) { additional++; addr = plus0; looping = TRUE; continue; } if (code1 == CONST_INT) { addr = plus0; looping = TRUE; continue; } if (code0 == SYMBOL_REF || code0 == LABEL_REF || code0 == CONST) { addr = plus0; looping = TRUE; continue; } if (code1 == SYMBOL_REF || code1 == LABEL_REF || code1 == CONST) { addr = plus1; looping = TRUE; continue; } break; case LABEL_REF: n_words = 2; /* always 2 words */ break; case CONST: addr = XEXP (addr, 0); looping = TRUE; continue; case SYMBOL_REF: n_words = SYMBOL_REF_FLAG (addr) ? 1 : 2; break; } } while (looping); if (n_words == 0) return; n_words += additional; if (n_words > 3) n_words = 3; num_refs[n_words-1] += num;}/* Return the appropriate instructions to move one operand to another. */char *mips_move_1word (operands, insn, unsignedp) rtx operands[]; rtx insn; int unsignedp;{ char *ret = 0; rtx op0 = operands[0]; rtx op1 = operands[1]; enum rtx_code code0 = GET_CODE (op0); enum rtx_code code1 = GET_CODE (op1); enum machine_mode mode = GET_MODE (op0); int subreg_word0 = 0; int subreg_word1 = 0; enum delay_type delay = DELAY_NONE; while (code0 == SUBREG) { subreg_word0 += SUBREG_WORD (op0); op0 = SUBREG_REG (op0); code0 = GET_CODE (op0); } while (code1 == SUBREG) { subreg_word1 += SUBREG_WORD (op1); op1 = SUBREG_REG (op1); code1 = GET_CODE (op1); } if (code0 == REG) { int regno0 = REGNO (op0) + subreg_word0; if (code1 == REG) { int regno1 = REGNO (op1) + subreg_word1; /* Just in case, don't do anything for assigning a register to itself, unless we are filling a delay slot. */ if (regno0 == regno1 && set_nomacro == 0) ret = ""; else if (GP_REG_P (regno0)) { if (GP_REG_P (regno1)) ret = "move\t%0,%1"; else if (MD_REG_P (regno1)) { delay = DELAY_HILO; ret = "mf%1\t%0"; } else { delay = DELAY_LOAD; if (FP_REG_P (regno1)) ret = "mfc1\t%0,%1"; else if (regno1 == FPSW_REGNUM) ret = "cfc1\t%0,$31"; } } else if (FP_REG_P (regno0)) { if (GP_REG_P (regno1)) { delay = DELAY_LOAD; ret = "mtc1\t%1,%0"; } if (FP_REG_P (regno1)) ret = "mov.s\t%0,%1"; } else if (MD_REG_P (regno0)) { if (GP_REG_P (regno1)) { delay = DELAY_HILO; ret = "mt%0\t%1"; } } else if (regno0 == FPSW_REGNUM) { if (GP_REG_P (regno1)) { delay = DELAY_LOAD; ret = "ctc1\t%0,$31"; } } } else if (code1 == MEM) { delay = DELAY_LOAD; if (TARGET_STATS) mips_count_memory_refs (op1, 1); if (GP_REG_P (regno0)) { /* For loads, use the mode of the memory item, instead of the target, so zero/sign extend can use this code as well. */ switch (GET_MODE (op1)) { default: break; case SFmode: ret = "lw\t%0,%1"; break; case SImode: ret = "lw\t%0,%1"; break; case HImode: ret = (unsignedp) ? "lhu\t%0,%1" : "lh\t%0,%1"; break; case QImode: ret = (unsignedp) ? "lbu\t%0,%1" : "lb\t%0,%1"; break; } } else if (FP_REG_P (regno0) && (mode == SImode || mode == SFmode)) ret = "l.s\t%0,%1"; if (ret != (char *)0 && MEM_VOLATILE_P (op1)) { int i = strlen (ret); if (i > sizeof (volatile_buffer) - sizeof ("%{%}")) abort (); sprintf (volatile_buffer, "%%{%s%%}", ret); ret = volatile_buffer; } } else if (code1 == CONST_INT) { if (INTVAL (op1) == 0) { if (GP_REG_P (regno0)) ret = "move\t%0,%z1"; else if (FP_REG_P (regno0)) { delay = DELAY_LOAD; ret = "mtc1\t%z1,%0"; } } else if (GP_REG_P (regno0))
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -