📄 caller-save.c
字号:
for (j = regno; j < regno + HARD_REGNO_NREGS (regno, PSEUDO_REGNO_MODE (i)); j++) SET_HARD_REG_BIT (hard_regs_live, j); } /* Now scan the insns in the block, keeping track of what hard regs are live as we go. When we see a call, save the live call-clobbered hard regs. */ for (insn = basic_block_head[b]; ; insn = NEXT_INSN (insn)) { RTX_CODE code = GET_CODE (insn); if (GET_RTX_CLASS (code) == 'i') { rtx link; /* If some registers have been saved, see if INSN references any of them. We must restore them before the insn if so. */ if (n_regs_saved) restore_referenced_regs (PATTERN (insn), insn, insn_mode); /* NB: the normal procedure is to first enliven any registers set by insn, then deaden any registers that had their last use at insn. This is incorrect now, since multiple pseudos may have been mapped to the same hard reg, and the death notes are ambiguous. So it must be done in the other, safe, order. */ for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) if (REG_NOTE_KIND (link) == REG_DEAD) clear_reg_live (XEXP (link, 0)); /* When we reach a call, we need to save all registers that are live, call-used, not fixed, and not already saved. We must test at this point because registers that die in a CALL_INSN are not live across the call and likewise for registers that are born in the CALL_INSN. If registers are filled with parameters for this function, and some of these are also being set by this function, then they will not appear to die (no REG_DEAD note for them), to check if in fact they do, collect the set registers in hard_regs_live first. */ if (code == CALL_INSN) { HARD_REG_SET this_call_sets; { HARD_REG_SET old_hard_regs_live; /* Save the hard_regs_live information. */ COPY_HARD_REG_SET (old_hard_regs_live, hard_regs_live); /* Now calculate hard_regs_live for this CALL_INSN only. */ CLEAR_HARD_REG_SET (hard_regs_live); note_stores (PATTERN (insn), set_reg_live); COPY_HARD_REG_SET (this_call_sets, hard_regs_live); /* Restore the hard_regs_live information. */ COPY_HARD_REG_SET (hard_regs_live, old_hard_regs_live); } for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (call_used_regs[regno] && ! call_fixed_regs[regno] && TEST_HARD_REG_BIT (hard_regs_live, regno) /* It must not be set by this instruction. */ && ! TEST_HARD_REG_BIT (this_call_sets, regno) && ! TEST_HARD_REG_BIT (hard_regs_saved, regno)) regno += insert_save_restore (insn, 1, regno, insn_mode, 0); /* Put the information for this CALL_INSN on top of what we already had. */ IOR_HARD_REG_SET (hard_regs_live, this_call_sets); COPY_HARD_REG_SET (hard_regs_need_restore, hard_regs_saved); /* Must recompute n_regs_saved. */ n_regs_saved = 0; for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (TEST_HARD_REG_BIT (hard_regs_saved, regno)) n_regs_saved++; } else { note_stores (PATTERN (insn), set_reg_live);#ifdef AUTO_INC_DEC for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) if (REG_NOTE_KIND (link) == REG_INC) set_reg_live (XEXP (link, 0), NULL_RTX);#endif } for (link = REG_NOTES (insn); link; link = XEXP (link, 1)) if (REG_NOTE_KIND (link) == REG_UNUSED) clear_reg_live (XEXP (link, 0)); } if (insn == basic_block_end[b]) break; } /* At the end of the basic block, we must restore any registers that remain saved. If the last insn in the block is a JUMP_INSN, put the restore before the insn, otherwise, put it after the insn. */ if (n_regs_saved) for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno++) if (TEST_HARD_REG_BIT (hard_regs_need_restore, regno)) regno += insert_save_restore ((GET_CODE (insn) == JUMP_INSN ? insn : NEXT_INSN (insn)), 0, regno, insn_mode, MOVE_MAX / UNITS_PER_WORD); /* If we added any insns at the start of the block, update the start of the block to point at those insns. */ basic_block_head[b] = NEXT_INSN (prev_block_last); }}/* Here from note_stores when an insn stores a value in a register. Set the proper bit or bits in hard_regs_live. All pseudos that have been assigned hard regs have had their register number changed already, so we can ignore pseudos. */static voidset_reg_live (reg, setter) rtx reg, setter;{ register int regno, endregno, i; enum machine_mode mode = GET_MODE (reg); int word = 0; if (GET_CODE (reg) == SUBREG) { word = SUBREG_WORD (reg); reg = SUBREG_REG (reg); } if (GET_CODE (reg) != REG || REGNO (reg) >= FIRST_PSEUDO_REGISTER) return; regno = REGNO (reg) + word; endregno = regno + HARD_REGNO_NREGS (regno, mode); for (i = regno; i < endregno; i++) { SET_HARD_REG_BIT (hard_regs_live, i); CLEAR_HARD_REG_BIT (hard_regs_saved, i); CLEAR_HARD_REG_BIT (hard_regs_need_restore, i); }}/* Here when a REG_DEAD note records the last use of a reg. Clear the appropriate bit or bits in hard_regs_live. Again we can ignore pseudos. */static voidclear_reg_live (reg) rtx reg;{ register int regno, endregno, i; if (GET_CODE (reg) != REG || REGNO (reg) >= FIRST_PSEUDO_REGISTER) return; regno = REGNO (reg); endregno= regno + HARD_REGNO_NREGS (regno, GET_MODE (reg)); for (i = regno; i < endregno; i++) { CLEAR_HARD_REG_BIT (hard_regs_live, i); CLEAR_HARD_REG_BIT (hard_regs_need_restore, i); CLEAR_HARD_REG_BIT (hard_regs_saved, i); }} /* If any register currently residing in the save area is referenced in X, which is part of INSN, emit code to restore the register in front of INSN. INSN_MODE is the mode to assign to any insns that we add. */static voidrestore_referenced_regs (x, insn, insn_mode) rtx x; rtx insn; enum machine_mode insn_mode;{ enum rtx_code code = GET_CODE (x); char *fmt; int i, j; if (code == CLOBBER) return; if (code == REG) { int regno = REGNO (x); /* If this is a pseudo, scan its memory location, since it might involve the use of another register, which might be saved. */ if (regno >= FIRST_PSEUDO_REGISTER && reg_equiv_mem[regno] != 0) restore_referenced_regs (XEXP (reg_equiv_mem[regno], 0), insn, insn_mode); else if (regno >= FIRST_PSEUDO_REGISTER && reg_equiv_address[regno] != 0) restore_referenced_regs (reg_equiv_address[regno], insn, insn_mode); /* Otherwise if this is a hard register, restore any piece of it that is currently saved. */ else if (regno < FIRST_PSEUDO_REGISTER) { int numregs = HARD_REGNO_NREGS (regno, GET_MODE (x)); /* Save at most SAVEREGS at a time. This can not be larger than MOVE_MAX, because that causes insert_save_restore to fail. */ int saveregs = MIN (numregs, MOVE_MAX / UNITS_PER_WORD); int endregno = regno + numregs; for (i = regno; i < endregno; i++) if (TEST_HARD_REG_BIT (hard_regs_need_restore, i)) i += insert_save_restore (insn, 0, i, insn_mode, saveregs); } return; } fmt = GET_RTX_FORMAT (code); for (i = GET_RTX_LENGTH (code) - 1; i >= 0; i--) { if (fmt[i] == 'e') restore_referenced_regs (XEXP (x, i), insn, insn_mode); else if (fmt[i] == 'E') for (j = XVECLEN (x, i) - 1; j >= 0; j--) restore_referenced_regs (XVECEXP (x, i, j), insn, insn_mode); }}/* Insert a sequence of insns to save or restore, SAVE_P says which, REGNO. Place these insns in front of INSN. INSN_MODE is the mode to assign to these insns. MAXRESTORE is the maximum number of registers which should be restored during this call (when SAVE_P == 0). It should never be less than 1 since we only work with entire registers. Note that we have verified in init_caller_save that we can do this with a simple SET, so use it. Set INSN_CODE to what we save there since the address might not be valid so the insn might not be recognized. These insns will be reloaded and have register elimination done by find_reload, so we need not worry about that here. Return the extra number of registers saved. */static intinsert_save_restore (insn, save_p, regno, insn_mode, maxrestore) rtx insn; int save_p; int regno; enum machine_mode insn_mode; int maxrestore;{ rtx pat; enum insn_code code; int i, numregs; /* A common failure mode if register status is not correct in the RTL is for this routine to be called with a REGNO we didn't expect to save. That will cause us to write an insn with a (nil) SET_DEST or SET_SRC. Instead of doing so and causing a crash later, check for this common case and abort here instead. This will remove one step in debugging such problems. */ if (regno_save_mem[regno][1] == 0) abort ();#ifdef HAVE_cc0 /* If INSN references CC0, put our insns in front of the insn that sets CC0. This is always safe, since the only way we could be passed an insn that references CC0 is for a restore, and doing a restore earlier isn't a problem. We do, however, assume here that CALL_INSNs don't reference CC0. Guard against non-INSN's like CODE_LABEL. */ if ((GET_CODE (insn) == INSN || GET_CODE (insn) == JUMP_INSN) && reg_referenced_p (cc0_rtx, PATTERN (insn))) insn = prev_nonnote_insn (insn);#endif /* Get the pattern to emit and update our status. */ if (save_p) { int i, j, k; int ok; /* See if we can save several registers with a single instruction. Work backwards to the single register case. */ for (i = MOVE_MAX / UNITS_PER_WORD; i > 0; i--) { ok = 1; if (regno_save_mem[regno][i] != 0) for (j = 0; j < i; j++) { if (! call_used_regs[regno + j] || call_fixed_regs[regno + j] || ! TEST_HARD_REG_BIT (hard_regs_live, regno + j) || TEST_HARD_REG_BIT (hard_regs_saved, regno + j)) ok = 0; } else continue; /* Must do this one save at a time */ if (! ok) continue; pat = gen_rtx (SET, VOIDmode, regno_save_mem[regno][i], gen_rtx (REG, GET_MODE (regno_save_mem[regno][i]), regno)); code = reg_save_code[regno][i]; /* Set hard_regs_saved for all the registers we saved. */ for (k = 0; k < i; k++) { SET_HARD_REG_BIT (hard_regs_saved, regno + k); SET_HARD_REG_BIT (hard_regs_need_restore, regno + k); n_regs_saved++; } numregs = i; break; } } else { int i, j, k; int ok; /* See if we can restore `maxrestore' registers at once. Work backwards to the single register case. */ for (i = maxrestore; i > 0; i--) { ok = 1; if (regno_save_mem[regno][i]) for (j = 0; j < i; j++) { if (! TEST_HARD_REG_BIT (hard_regs_need_restore, regno + j)) ok = 0; } else continue; /* Must do this one restore at a time */ if (! ok) continue; pat = gen_rtx (SET, VOIDmode, gen_rtx (REG, GET_MODE (regno_save_mem[regno][i]), regno), regno_save_mem[regno][i]); code = reg_restore_code[regno][i]; /* Clear status for all registers we restored. */ for (k = 0; k < i; k++) { CLEAR_HARD_REG_BIT (hard_regs_need_restore, regno + k); n_regs_saved--; } numregs = i; break; } } /* Emit the insn and set the code and mode. */ insn = emit_insn_before (pat, insn); PUT_MODE (insn, insn_mode); INSN_CODE (insn) = code; /* Tell our callers how many extra registers we saved/restored */ return numregs - 1;}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -