📄 caller-save.c
字号:
tempreg = 0; else break; } } /* Push it on the stack. */#ifdef STACK_GROWS_DOWNWARD decrement = UNITS_PER_WORD;#else decrement = - UNITS_PER_WORD;#endif emit_insn_before (gen_add2_insn (stack_pointer_rtx, gen_rtx (CONST_INT, VOIDmode, -decrement)), insn); emit_insn_before (gen_move_insn (gen_rtx (MEM, Pmode, stack_pointer_rtx), tempreg), insn); } /* Save the regs we are supposed to save, aside from TEMPREG. Use TEMPREG for address calculations when needed. */ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) if (offset[regno] >= 0 && ! already_saved[regno] && tempreg != 0 && REGNO (tempreg) != regno) { rtx reg = save_reg_rtx[regno]; rtx addr1 = plus_constant (addr, offset[regno]); rtx temp; if (! memory_address_p (GET_MODE (reg), addr1)) { if (GET_CODE (addr1) != PLUS) abort (); if (GET_CODE (XEXP (addr1, 1)) != CONST_INT || GET_CODE (XEXP (addr1, 0)) != REG) abort (); emit_insn_before (gen_move_insn (tempreg, XEXP (addr1, 0)), insn); emit_insn_before (gen_add2_insn (tempreg, XEXP (addr1, 1)), insn); addr1 = tempreg; } temp = gen_rtx (MEM, GET_MODE (reg), addr1); emit_insn_before (gen_move_insn (temp, reg), insn); already_saved[regno] = 1; } /* If we pushed TEMPREG to make it free, pop it. */ if (needpush) { emit_insn_before (gen_move_insn (tempreg, gen_rtx (MEM, Pmode, stack_pointer_rtx)), insn); emit_insn_before (gen_add2_insn (stack_pointer_rtx, gen_rtx (CONST_INT, VOIDmode, decrement)), insn); } /* If TEMPREG itself needs saving, go back and save it. There are plenty of free regs now, those already saved. */ if (tempreg != 0 && offset[REGNO (tempreg)] >= 0 && ! already_saved[REGNO (tempreg)]) goto retry;}/* Emit a string of loads to restore the hard regs listed in OFFSET[] from address ADDR; insert the loads after INSN. OFFSET[reg] is -1 if reg should not be loaded, or a suitably-aligned offset from ADDR. The offsets actually used do not need to be those provided in OFFSET, but should agree with whatever emit_mult_save does. */static voidemit_mult_restore (insn, addr, offset) rtx insn, addr; int offset[];{ int regno; /* Number of regs now needing to be restored. */ int restore_count; /* A register to use as a temporary for address calculations. */ rtx tempreg; /* A register available for that purpose but less desirable. */ rtx maybe_tempreg; /* A register that could be used as that temp if we push and pop it. */ rtx can_push_reg; /* Nonzero means we need to push and pop a register to use it as TEMPREG. */ int needpush; /* The amount the stack is decremented to save that register (if we do). */ int decrement; /* Record which regs we restore, in case we branch to retry. */ char already_restored[FIRST_PSEUDO_REGISTER]; bzero (already_restored, sizeof already_restored); /* Note: INSN can't be the last insn, since if it were, no regs would live across it. */ insn = NEXT_INSN (insn); if (insn == 0) abort (); /* Now we can insert before INSN. That is convenient because we can insert them in the order that they should ultimately appear. */ /* Hair is needed because sometimes the addresses to restore from are not valid (offsets too big). So we need a reg, TEMPREG, to compute addresses in. We look first for an empty reg to use. Sometimes no reg is empty. Then we push a reg, use it, and pop it. If all the suitable regs need to be restored, that strategy won't work. So we restore all but one, using that one as a temporary. Then we jump to `retry' to restore that one, pushing and popping another (already restored) as a temporary. */ retry: needpush = 0; tempreg = 0; can_push_reg = 0; restore_count = 0; /* Set NEEDPUSH if any restore-addresses are not valid memory addresses. If any register is available, record it in TEMPREG. Otherwise, one register yet to be restored goes in MAYBE_TEMPREG, and can be used as TEMPREG for any other regs to be restored. If any register doesn't need restoring, record it in CAN_PUSH_REG. */ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) { if (offset[regno] >= 0 && ! already_restored[regno]) { rtx reg = save_reg_rtx[regno]; rtx addr1 = plus_constant (addr, offset[regno]); restore_count++; if (memory_address_p (GET_MODE (reg), addr1)) needpush = 1; /* Find a call-clobbered reg that needs restoring. We can use it as a temporary if we defer restoring it. */ if (maybe_tempreg == 0) { maybe_tempreg = gen_rtx (REG, Pmode, regno); /* Don't use it if not valid for addressing. */ if (! strict_memory_address_p (QImode, maybe_tempreg)) maybe_tempreg = 0; } } /* If any call-clobbered reg is dead, put it in TEMPREG. It can be used as a temporary at no extra cost. */ if (tempreg == 0 && call_used_regs[regno] && ! fixed_regs[regno] && offset[regno] < 0 && HARD_REGNO_MODE_OK (regno, Pmode)) { tempreg = gen_rtx (REG, Pmode, regno); /* Don't use it if not valid for addressing. */ if (! strict_memory_address_p (QImode, tempreg)) tempreg = 0; } /* Any non-call-clobbered reg, put in CAN_PUSH_REG. It can be used as a temporary if we push and pop it. */ if (can_push_reg == 0 && ! call_used_regs[regno] && HARD_REGNO_MODE_OK (regno, Pmode)) { can_push_reg = gen_rtx (REG, Pmode, regno); /* Don't use it if not valid for addressing. */ if (! strict_memory_address_p (QImode, can_push_reg)) can_push_reg = 0; } /* Any reg we already restored can be a temporary if we push and pop it. */ if (can_push_reg == 0 && already_restored[regno] && HARD_REGNO_MODE_OK (regno, Pmode)) { can_push_reg = gen_rtx (REG, Pmode, regno); /* Don't use it if not valid for addressing. */ if (! strict_memory_address_p (QImode, can_push_reg)) can_push_reg = 0; } } /* If 2 or more regs need to be restored, use one as a temp reg for the rest (if we need a tempreg). */ if (tempreg == 0 && maybe_tempreg != 0 && restore_count > 1) tempreg = maybe_tempreg; /* Clear NEEDPUSH if we already found an empty reg. */ if (tempreg != 0) needpush = 0; /* If we need a temp reg and none is free, make one free. */ if (needpush) { tempreg = can_push_reg; /* Push it on the stack. */#ifdef STACK_GROWS_DOWNWARD decrement = UNITS_PER_WORD;#else decrement = - UNITS_PER_WORD;#endif emit_insn_before (gen_add2_insn (stack_pointer_rtx, gen_rtx (CONST_INT, VOIDmode, -decrement)), insn); emit_insn_before (gen_move_insn (gen_rtx (MEM, Pmode, stack_pointer_rtx), tempreg), insn); } /* Restore the regs we are supposed to restore, aside from TEMPREG. Use TEMPREG for address calculations when needed. */ for (regno = 0; regno < FIRST_PSEUDO_REGISTER; ++regno) if (offset[regno] >= 0 && ! already_restored[regno] && tempreg != 0 && REGNO (tempreg) != regno) { rtx reg = save_reg_rtx[regno]; rtx addr1 = plus_constant (addr, offset[regno]); rtx temp; if (! memory_address_p (GET_MODE (reg), addr1)) { if (GET_CODE (addr1) != PLUS) abort (); if (GET_CODE (XEXP (addr1, 1)) != CONST_INT || GET_CODE (XEXP (addr1, 0)) != REG) abort (); emit_insn_before (gen_move_insn (tempreg, XEXP (addr1, 0)), insn); emit_insn_before (gen_add2_insn (tempreg, XEXP (addr1, 1)), insn); addr1 = tempreg; } temp = gen_rtx (MEM, GET_MODE (reg), addr1); emit_insn_before (gen_move_insn (reg, temp), insn); already_restored[regno] = 1; } /* If we pushed TEMPREG to make it free, pop it. */ if (needpush) { emit_insn_before (gen_move_insn (tempreg, gen_rtx (MEM, Pmode, stack_pointer_rtx)), insn); emit_insn_before (gen_add2_insn (stack_pointer_rtx, gen_rtx (CONST_INT, VOIDmode, decrement)), insn); } /* If TEMPREG itself needs restoring, go back and restore it. We can find a reg already restored to push and use as a temporary. */ if (tempreg != 0 && offset[REGNO (tempreg)] >= 0 && ! already_restored[REGNO (tempreg)]) goto retry;}/* Return the address of a new block of size SIZE on the stack. The old save block is at ADDR; ADDR is 0 if no block exists yet. */static rtxgrow_save_block (addr, size) rtx addr; int size;{ rtx newaddr; /* Keep the size a multiple of the main allocation unit. */ size = (((size + (BIGGEST_ALIGNMENT / BITS_PER_UNIT) - 1) / (BIGGEST_ALIGNMENT / BITS_PER_UNIT)) * (BIGGEST_ALIGNMENT / BITS_PER_UNIT)); /* If no save block exists yet, create one and return it. */ if (! addr) { save_block_size = size; return XEXP (assign_stack_local (BLKmode, size), 0); } /* Get a new block and coalesce it with the old one. */ newaddr = XEXP (assign_stack_local (BLKmode, size - save_block_size), 0); if (GET_CODE (newaddr) == PLUS && XEXP (newaddr, 0) == frame_pointer_rtx && GET_CODE (XEXP (newaddr, 1)) == CONST_INT && GET_CODE (addr) == PLUS && XEXP (addr, 0) == frame_pointer_rtx && GET_CODE (XEXP (addr, 1)) == CONST_INT && ((INTVAL (XEXP (newaddr, 1)) - INTVAL (XEXP (addr, 1)) == size - save_block_size) || (INTVAL (XEXP (addr, 1)) - INTVAL (XEXP (newaddr, 1)) == size - save_block_size))) { save_block_size = size; if (INTVAL (XEXP (newaddr, 1)) < INTVAL (XEXP (addr, 1))) return newaddr; else return addr; } /* They didn't coalesce, find out why */ abort (); save_block_size = size; return XEXP (assign_stack_local (BLKmode, size), 0);}/* Return a machine mode that is legitimate for hard reg REGNO and large enough to save the whole register. */static enum machine_modechoose_hard_reg_mode (regno) int regno;{ enum reg_class class = REGNO_REG_CLASS (regno); if (CLASS_MAX_NREGS (class, DImode) == 1 && HARD_REGNO_MODE_OK (regno, DImode)) return DImode; else if (CLASS_MAX_NREGS (class, DFmode) == 1 && HARD_REGNO_MODE_OK (regno, DFmode)) return DFmode; else if (CLASS_MAX_NREGS (class, SImode) == 1 && HARD_REGNO_MODE_OK (regno, SImode)) return SImode; else if (CLASS_MAX_NREGS (class, SFmode) == 1 && HARD_REGNO_MODE_OK (regno, SFmode)) return SFmode; else if (CLASS_MAX_NREGS (class, HImode) == 1 && HARD_REGNO_MODE_OK (regno, HImode)) return HImode; else abort ();}
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -