📄 h8300.c
字号:
default: return NO_REGS; }}/* Return the byte register name for a register rtx X. B should be 0 if you want a lower byte register. B should be 1 if you want an upper byte register. */static const char *byte_reg (rtx x, int b){ static const char *const names_small[] = { "r0l", "r0h", "r1l", "r1h", "r2l", "r2h", "r3l", "r3h", "r4l", "r4h", "r5l", "r5h", "r6l", "r6h", "r7l", "r7h" }; if (!REG_P (x)) abort (); return names_small[REGNO (x) * 2 + b];}/* REGNO must be saved/restored across calls if this macro is true. */#define WORD_REG_USED(regno) \ (regno < SP_REG \ /* No need to save registers if this function will not return. */ \ && ! TREE_THIS_VOLATILE (current_function_decl) \ && (h8300_saveall_function_p (current_function_decl) \ /* Save any call saved register that was used. */ \ || (regs_ever_live[regno] && !call_used_regs[regno]) \ /* Save the frame pointer if it was used. */ \ || (regno == HARD_FRAME_POINTER_REGNUM && regs_ever_live[regno]) \ /* Save any register used in an interrupt handler. */ \ || (h8300_current_function_interrupt_function_p () \ && regs_ever_live[regno]) \ /* Save call clobbered registers in non-leaf interrupt \ handlers. */ \ || (h8300_current_function_interrupt_function_p () \ && call_used_regs[regno] \ && !current_function_is_leaf)))/* Output assembly language to FILE for the operation OP with operand size SIZE to adjust the stack pointer. */static voidh8300_emit_stack_adjustment (int sign, unsigned int size){ /* If the frame size is 0, we don't have anything to do. */ if (size == 0) return; /* H8/300 cannot add/subtract a large constant with a single instruction. If a temporary register is available, load the constant to it and then do the addition. */ if (TARGET_H8300 && size > 4 && !h8300_current_function_interrupt_function_p () && !(cfun->static_chain_decl != NULL && sign < 0)) { rtx r3 = gen_rtx_REG (Pmode, 3); emit_insn (gen_movhi (r3, GEN_INT (sign * size))); emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx, r3)); } else { /* The stack adjustment made here is further optimized by the splitter. In case of H8/300, the splitter always splits the addition emitted here to make the adjustment interrupt-safe. */ if (Pmode == HImode) emit_insn (gen_addhi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (sign * size))); else emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (sign * size))); }}/* Round up frame size SIZE. */static intround_frame_size (int size){ return ((size + STACK_BOUNDARY / BITS_PER_UNIT - 1) & -STACK_BOUNDARY / BITS_PER_UNIT);}/* Compute which registers to push/pop. Return a bit vector of registers. */static unsigned intcompute_saved_regs (void){ unsigned int saved_regs = 0; int regno; /* Construct a bit vector of registers to be pushed/popped. */ for (regno = 0; regno <= HARD_FRAME_POINTER_REGNUM; regno++) { if (WORD_REG_USED (regno)) saved_regs |= 1 << regno; } /* Don't push/pop the frame pointer as it is treated separately. */ if (frame_pointer_needed) saved_regs &= ~(1 << HARD_FRAME_POINTER_REGNUM); return saved_regs;}/* Emit an insn to push register RN. */static voidpush (int rn){ rtx reg = gen_rtx_REG (word_mode, rn); rtx x; if (TARGET_H8300) x = gen_push_h8300 (reg); else if (!TARGET_NORMAL_MODE) x = gen_push_h8300hs_advanced (reg); else x = gen_push_h8300hs_normal (reg); x = emit_insn (x); REG_NOTES (x) = gen_rtx_EXPR_LIST (REG_INC, stack_pointer_rtx, 0);}/* Emit an insn to pop register RN. */static voidpop (int rn){ rtx reg = gen_rtx_REG (word_mode, rn); rtx x; if (TARGET_H8300) x = gen_pop_h8300 (reg); else if (!TARGET_NORMAL_MODE) x = gen_pop_h8300hs_advanced (reg); else x = gen_pop_h8300hs_normal (reg); x = emit_insn (x); REG_NOTES (x) = gen_rtx_EXPR_LIST (REG_INC, stack_pointer_rtx, 0);}/* Emit an instruction to push or pop NREGS consecutive registers starting at register REGNO. POP_P selects a pop rather than a push and RETURN_P is true if the instruction should return. It must be possible to do the requested operation in a single instruction. If NREGS == 1 && !RETURN_P, use a normal push or pop insn. Otherwise emit a parallel of the form: (parallel [(return) ;; if RETURN_P (save or restore REGNO) (save or restore REGNO + 1) ... (save or restore REGNO + NREGS - 1) (set sp (plus sp (const_int adjust)))] */static voidh8300_push_pop (int regno, int nregs, int pop_p, int return_p){ int i, j; rtvec vec; rtx sp, offset; /* See whether we can use a simple push or pop. */ if (!return_p && nregs == 1) { if (pop_p) pop (regno); else push (regno); return; } /* We need one element for the return insn, if present, one for each register, and one for stack adjustment. */ vec = rtvec_alloc ((return_p != 0) + nregs + 1); sp = stack_pointer_rtx; i = 0; /* Add the return instruction. */ if (return_p) { RTVEC_ELT (vec, i) = gen_rtx_RETURN (VOIDmode); i++; } /* Add the register moves. */ for (j = 0; j < nregs; j++) { rtx lhs, rhs; if (pop_p) { /* Register REGNO + NREGS - 1 is popped first. Before the stack adjustment, its slot is at address @sp. */ lhs = gen_rtx_REG (SImode, regno + j); rhs = gen_rtx_MEM (SImode, plus_constant (sp, (nregs - j - 1) * 4)); } else { /* Register REGNO is pushed first and will be stored at @(-4,sp). */ lhs = gen_rtx_MEM (SImode, plus_constant (sp, (j + 1) * -4)); rhs = gen_rtx_REG (SImode, regno + j); } RTVEC_ELT (vec, i + j) = gen_rtx_SET (VOIDmode, lhs, rhs); } /* Add the stack adjustment. */ offset = GEN_INT ((pop_p ? nregs : -nregs) * 4); RTVEC_ELT (vec, i + j) = gen_rtx_SET (VOIDmode, sp, gen_rtx_PLUS (Pmode, sp, offset)); emit_insn (gen_rtx_PARALLEL (VOIDmode, vec));}/* Return true if X has the value sp + OFFSET. */static inth8300_stack_offset_p (rtx x, int offset){ if (offset == 0) return x == stack_pointer_rtx; return (GET_CODE (x) == PLUS && XEXP (x, 0) == stack_pointer_rtx && GET_CODE (XEXP (x, 1)) == CONST_INT && INTVAL (XEXP (x, 1)) == offset);}/* A subroutine of h8300_ldm_stm_parallel. X is one pattern in something that may be an ldm or stm instruction. If it fits the required template, return the register it loads or stores, otherwise return -1. LOAD_P is true if X should be a load, false if it should be a store. NREGS is the number of registers that the whole instruction is expected to load or store. INDEX is the index of the register that X should load or store, relative to the lowest-numbered register. */static inth8300_ldm_stm_regno (rtx x, int load_p, int index, int nregs){ int regindex, memindex, offset; if (load_p) regindex = 0, memindex = 1, offset = (nregs - index - 1) * 4; else memindex = 0, regindex = 1, offset = (index + 1) * -4; if (GET_CODE (x) == SET && GET_CODE (XEXP (x, regindex)) == REG && GET_CODE (XEXP (x, memindex)) == MEM && h8300_stack_offset_p (XEXP (XEXP (x, memindex), 0), offset)) return REGNO (XEXP (x, regindex)); return -1;}/* Return true if the elements of VEC starting at FIRST describe an ldm or stm instruction (LOAD_P says which). */static inth8300_ldm_stm_parallel (rtvec vec, int load_p, int first){ rtx last; int nregs, i, regno, adjust; /* There must be a stack adjustment, a register move, and at least one other operation (a return or another register move). */ if (GET_NUM_ELEM (vec) < 3) return false; /* Get the range of registers to be pushed or popped. */ nregs = GET_NUM_ELEM (vec) - first - 1; regno = h8300_ldm_stm_regno (RTVEC_ELT (vec, first), load_p, 0, nregs); /* Check that the call to h8300_ldm_stm_regno succeeded and that we're only dealing with GPRs. */ if (regno < 0 || regno + nregs > 8) return false; /* 2-register h8s instructions must start with an even-numbered register. 3- and 4-register instructions must start with er0 or er4. */ if (!TARGET_H8300SX) { if ((regno & 1) != 0) return false; if (nregs > 2 && (regno & 3) != 0) return false; } /* Check the other loads or stores. */ for (i = 1; i < nregs; i++) if (h8300_ldm_stm_regno (RTVEC_ELT (vec, first + i), load_p, i, nregs) != regno + i) return false; /* Check the stack adjustment. */ last = RTVEC_ELT (vec, first + nregs); adjust = (load_p ? nregs : -nregs) * 4; return (GET_CODE (last) == SET && SET_DEST (last) == stack_pointer_rtx && h8300_stack_offset_p (SET_SRC (last), adjust));}/* Return true if X is an ldm.l pattern. X is known to be parallel. */inth8300_ldm_parallel (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED){ return h8300_ldm_stm_parallel (XVEC (x, 0), 1, 0);}/* Likewise stm.l. */inth8300_stm_parallel (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED){ return h8300_ldm_stm_parallel (XVEC (x, 0), 0, 0);}/* Likewise rts/l and rte/l. Note that the .md pattern will check for the return so there's no need to do that here. */inth8300_return_parallel (rtx x, enum machine_mode mode ATTRIBUTE_UNUSED){ return h8300_ldm_stm_parallel (XVEC (x, 0), 1, 1);}/* This is what the stack looks like after the prolog of a function with a frame has been set up: <args> PC FP <- fp <locals> <saved registers> <- sp This is what the stack looks like after the prolog of a function which doesn't have a frame: <args> PC <locals> <saved registers> <- sp*//* Generate RTL code for the function prologue. */voidh8300_expand_prologue (void){ int regno; int saved_regs; int n_regs; /* If the current function has the OS_Task attribute set, then we have a naked prologue. */ if (h8300_os_task_function_p (current_function_decl)) return; if (h8300_monitor_function_p (current_function_decl)) /* My understanding of monitor functions is they act just like interrupt functions, except the prologue must mask interrupts. */ emit_insn (gen_monitor_prologue ()); if (frame_pointer_needed) { /* Push fp. */ push (HARD_FRAME_POINTER_REGNUM); emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx); } /* Push the rest of the registers in ascending order. */ saved_regs = compute_saved_regs (); for (regno = 0; regno < FIRST_PSEUDO_REGISTER; regno += n_regs) { n_regs = 1; if (saved_regs & (1 << regno)) { if (TARGET_H8300S) { /* See how many registers we can push at the same time. */ if ((!TARGET_H8300SX || (regno & 3) == 0) && ((saved_regs >> regno) & 0x0f) == 0x0f) n_regs = 4; else if ((!TARGET_H8300SX || (regno & 3) == 0) && ((saved_regs >> regno) & 0x07) == 0x07) n_regs = 3; else if ((!TARGET_H8300SX || (regno & 1) == 0) && ((saved_regs >> regno) & 0x03) == 0x03) n_regs = 2; } h8300_push_pop (regno, n_regs, 0, 0); } } /* Leave room for locals. */ h8300_emit_stack_adjustment (-1, round_frame_size (get_frame_size ()));}/* Return nonzero if we can use "rts" for the function currently being compiled. */inth8300_can_use_return_insn_p (void){ return (reload_completed && !frame_pointer_needed && get_frame_size () == 0 && compute_saved_regs () == 0);}/* Generate RTL code for the function epilogue. */voidh8300_expand_epilogue (void){ int regno; int saved_regs; int n_regs; HOST_WIDE_INT frame_size; bool returned_p; if (h8300_os_task_function_p (current_function_decl)) /* OS_Task epilogues are nearly naked -- they just have an rts instruction. */ return; frame_size = round_frame_size (get_frame_size ()); returned_p = false; /* Deallocate locals. */ h8300_emit_stack_adjustment (1, frame_size); /* Pop the saved registers in descending order. */ saved_regs = compute_saved_regs (); for (regno = FIRST_PSEUDO_REGISTER - 1; regno >= 0; regno -= n_regs) { n_regs = 1; if (saved_regs & (1 << regno)) { if (TARGET_H8300S) { /* See how many registers we can pop at the same time. */ if ((TARGET_H8300SX || (regno & 3) == 3) && ((saved_regs << 3 >> regno) & 0x0f) == 0x0f) n_regs = 4; else if ((TARGET_H8300SX || (regno & 3) == 2) && ((saved_regs << 2 >> regno) & 0x07) == 0x07) n_regs = 3; else if ((TARGET_H8300SX || (regno & 1) == 1) && ((saved_regs << 1 >> regno) & 0x03) == 0x03) n_regs = 2; } /* See if this pop would be the last insn before the return. If so, use rte/l or rts/l instead of pop or ldm.l. */ if (TARGET_H8300SX && !frame_pointer_needed && frame_size == 0 && (saved_regs & ((1 << (regno - n_regs + 1)) - 1)) == 0) returned_p = true; h8300_push_pop (regno - n_regs + 1, n_regs, 1, returned_p); } } /* Pop frame pointer if we had one. */ if (frame_pointer_needed) { if (TARGET_H8300SX) returned_p = true; h8300_push_pop (HARD_FRAME_POINTER_REGNUM, 1, 1, returned_p); } if (!returned_p)
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -