📄 iq2000.c
字号:
else (offset) = cfun->machine->gp_sp_offset + ((UNITS_PER_WORD - (POINTER_SIZE / BITS_PER_UNIT)) * (BYTES_BIG_ENDIAN != 0)); } return offset;}/* Common code to emit the insns (or to write the instructions to a file) to save/restore registers. Other parts of the code assume that IQ2000_TEMP1_REGNUM (aka large_reg) is not modified within save_restore_insns. */#define BITSET_P(VALUE,BIT) (((VALUE) & (1L << (BIT))) != 0)/* Emit instructions to load the value (SP + OFFSET) into IQ2000_TEMP2_REGNUM and return an rtl expression for the register. Write the assembly instructions directly to FILE if it is not null, otherwise emit them as rtl. This function is a subroutine of save_restore_insns. It is used when OFFSET is too large to add in a single instruction. */static rtxiq2000_add_large_offset_to_sp (HOST_WIDE_INT offset){ rtx reg = gen_rtx_REG (Pmode, IQ2000_TEMP2_REGNUM); rtx offset_rtx = GEN_INT (offset); emit_move_insn (reg, offset_rtx); emit_insn (gen_addsi3 (reg, reg, stack_pointer_rtx)); return reg;}/* Make INSN frame related and note that it performs the frame-related operation DWARF_PATTERN. */static voidiq2000_annotate_frame_insn (rtx insn, rtx dwarf_pattern){ RTX_FRAME_RELATED_P (insn) = 1; REG_NOTES (insn) = alloc_EXPR_LIST (REG_FRAME_RELATED_EXPR, dwarf_pattern, REG_NOTES (insn));}/* Emit a move instruction that stores REG in MEM. Make the instruction frame related and note that it stores REG at (SP + OFFSET). */static voidiq2000_emit_frame_related_store (rtx mem, rtx reg, HOST_WIDE_INT offset){ rtx dwarf_address = plus_constant (stack_pointer_rtx, offset); rtx dwarf_mem = gen_rtx_MEM (GET_MODE (reg), dwarf_address); iq2000_annotate_frame_insn (emit_move_insn (mem, reg), gen_rtx_SET (GET_MODE (reg), dwarf_mem, reg));}/* Emit instructions to save/restore registers, as determined by STORE_P. */static voidsave_restore_insns (int store_p){ long mask = cfun->machine->mask; int regno; rtx base_reg_rtx; HOST_WIDE_INT base_offset; HOST_WIDE_INT gp_offset; HOST_WIDE_INT end_offset; if (frame_pointer_needed && ! BITSET_P (mask, HARD_FRAME_POINTER_REGNUM - GP_REG_FIRST)) abort (); if (mask == 0) { base_reg_rtx = 0, base_offset = 0; return; } /* Save registers starting from high to low. The debuggers prefer at least the return register be stored at func+4, and also it allows us not to need a nop in the epilog if at least one register is reloaded in addition to return address. */ /* Save GP registers if needed. */ /* Pick which pointer to use as a base register. For small frames, just use the stack pointer. Otherwise, use a temporary register. Save 2 cycles if the save area is near the end of a large frame, by reusing the constant created in the prologue/epilogue to adjust the stack frame. */ gp_offset = cfun->machine->gp_sp_offset; end_offset = gp_offset - (cfun->machine->gp_reg_size - GET_MODE_SIZE (gpr_mode)); if (gp_offset < 0 || end_offset < 0) internal_error ("gp_offset (%ld) or end_offset (%ld) is less than zero.", (long) gp_offset, (long) end_offset); else if (gp_offset < 32768) base_reg_rtx = stack_pointer_rtx, base_offset = 0; else { int regno; int reg_save_count = 0; for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--) if (BITSET_P (mask, regno - GP_REG_FIRST)) reg_save_count += 1; base_offset = gp_offset - ((reg_save_count - 1) * 4); base_reg_rtx = iq2000_add_large_offset_to_sp (base_offset); } for (regno = GP_REG_LAST; regno >= GP_REG_FIRST; regno--) { if (BITSET_P (mask, regno - GP_REG_FIRST)) { rtx reg_rtx; rtx mem_rtx = gen_rtx_MEM (gpr_mode, gen_rtx_PLUS (Pmode, base_reg_rtx, GEN_INT (gp_offset - base_offset))); reg_rtx = gen_rtx_REG (gpr_mode, regno); if (store_p) iq2000_emit_frame_related_store (mem_rtx, reg_rtx, gp_offset); else { emit_move_insn (reg_rtx, mem_rtx); } gp_offset -= GET_MODE_SIZE (gpr_mode); } }}/* Expand the prologue into a bunch of separate insns. */voidiq2000_expand_prologue (void){ int regno; HOST_WIDE_INT tsize; int last_arg_is_vararg_marker = 0; tree fndecl = current_function_decl; tree fntype = TREE_TYPE (fndecl); tree fnargs = DECL_ARGUMENTS (fndecl); rtx next_arg_reg; int i; tree next_arg; tree cur_arg; CUMULATIVE_ARGS args_so_far; int store_args_on_stack = (iq2000_can_use_return_insn ()); /* If struct value address is treated as the first argument. */ if (aggregate_value_p (DECL_RESULT (fndecl), fndecl) && ! current_function_returns_pcc_struct && targetm.calls.struct_value_rtx (TREE_TYPE (fndecl), 1) == 0) { tree type = build_pointer_type (fntype); tree function_result_decl = build_decl (PARM_DECL, NULL_TREE, type); DECL_ARG_TYPE (function_result_decl) = type; TREE_CHAIN (function_result_decl) = fnargs; fnargs = function_result_decl; } /* For arguments passed in registers, find the register number of the first argument in the variable part of the argument list, otherwise GP_ARG_LAST+1. Note also if the last argument is the varargs special argument, and treat it as part of the variable arguments. This is only needed if store_args_on_stack is true. */ INIT_CUMULATIVE_ARGS (args_so_far, fntype, NULL_RTX, 0, 0); regno = GP_ARG_FIRST; for (cur_arg = fnargs; cur_arg != 0; cur_arg = next_arg) { tree passed_type = DECL_ARG_TYPE (cur_arg); enum machine_mode passed_mode = TYPE_MODE (passed_type); rtx entry_parm; if (TREE_ADDRESSABLE (passed_type)) { passed_type = build_pointer_type (passed_type); passed_mode = Pmode; } entry_parm = FUNCTION_ARG (args_so_far, passed_mode, passed_type, 1); FUNCTION_ARG_ADVANCE (args_so_far, passed_mode, passed_type, 1); next_arg = TREE_CHAIN (cur_arg); if (entry_parm && store_args_on_stack) { if (next_arg == 0 && DECL_NAME (cur_arg) && ((0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)), "__builtin_va_alist")) || (0 == strcmp (IDENTIFIER_POINTER (DECL_NAME (cur_arg)), "va_alist")))) { last_arg_is_vararg_marker = 1; break; } else { int words; if (GET_CODE (entry_parm) != REG) abort (); /* Passed in a register, so will get homed automatically. */ if (GET_MODE (entry_parm) == BLKmode) words = (int_size_in_bytes (passed_type) + 3) / 4; else words = (GET_MODE_SIZE (GET_MODE (entry_parm)) + 3) / 4; regno = REGNO (entry_parm) + words - 1; } } else { regno = GP_ARG_LAST+1; break; } } /* In order to pass small structures by value in registers we need to shift the value into the high part of the register. Function_arg has encoded a PARALLEL rtx, holding a vector of adjustments to be made as the next_arg_reg variable, so we split up the insns, and emit them separately. */ next_arg_reg = FUNCTION_ARG (args_so_far, VOIDmode, void_type_node, 1); if (next_arg_reg != 0 && GET_CODE (next_arg_reg) == PARALLEL) { rtvec adjust = XVEC (next_arg_reg, 0); int num = GET_NUM_ELEM (adjust); for (i = 0; i < num; i++) { rtx insn, pattern; pattern = RTVEC_ELT (adjust, i); if (GET_CODE (pattern) != SET || GET_CODE (SET_SRC (pattern)) != ASHIFT) abort_with_insn (pattern, "Insn is not a shift"); PUT_CODE (SET_SRC (pattern), ASHIFTRT); insn = emit_insn (pattern); /* Global life information isn't valid at this point, so we can't check whether these shifts are actually used. Mark them MAYBE_DEAD so that flow2 will remove them, and not complain about dead code in the prologue. */ REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX, REG_NOTES (insn)); } } tsize = compute_frame_size (get_frame_size ()); /* If this function is a varargs function, store any registers that would normally hold arguments ($4 - $7) on the stack. */ if (store_args_on_stack && ((TYPE_ARG_TYPES (fntype) != 0 && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) != void_type_node)) || last_arg_is_vararg_marker)) { int offset = (regno - GP_ARG_FIRST) * UNITS_PER_WORD; rtx ptr = stack_pointer_rtx; for (; regno <= GP_ARG_LAST; regno++) { if (offset != 0) ptr = gen_rtx_PLUS (Pmode, stack_pointer_rtx, GEN_INT (offset)); emit_move_insn (gen_rtx_MEM (gpr_mode, ptr), gen_rtx_REG (gpr_mode, regno)); offset += GET_MODE_SIZE (gpr_mode); } } if (tsize > 0) { rtx tsize_rtx = GEN_INT (tsize); rtx adjustment_rtx, insn, dwarf_pattern; if (tsize > 32767) { adjustment_rtx = gen_rtx_REG (Pmode, IQ2000_TEMP1_REGNUM); emit_move_insn (adjustment_rtx, tsize_rtx); } else adjustment_rtx = tsize_rtx; insn = emit_insn (gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, adjustment_rtx)); dwarf_pattern = gen_rtx_SET (Pmode, stack_pointer_rtx, plus_constant (stack_pointer_rtx, -tsize)); iq2000_annotate_frame_insn (insn, dwarf_pattern); save_restore_insns (1); if (frame_pointer_needed) { rtx insn = 0; insn = emit_insn (gen_movsi (hard_frame_pointer_rtx, stack_pointer_rtx)); if (insn) RTX_FRAME_RELATED_P (insn) = 1; } } emit_insn (gen_blockage ());}/* Expand the epilogue into a bunch of separate insns. */voidiq2000_expand_epilogue (void){ HOST_WIDE_INT tsize = cfun->machine->total_size; rtx tsize_rtx = GEN_INT (tsize); rtx tmp_rtx = (rtx)0; if (iq2000_can_use_return_insn ()) { emit_jump_insn (gen_return ()); return; } if (tsize > 32767) { tmp_rtx = gen_rtx_REG (Pmode, IQ2000_TEMP1_REGNUM); emit_move_insn (tmp_rtx, tsize_rtx); tsize_rtx = tmp_rtx; } if (tsize > 0) { if (frame_pointer_needed) { emit_insn (gen_blockage ()); emit_insn (gen_movsi (stack_pointer_rtx, hard_frame_pointer_rtx)); } save_restore_insns (0); if (current_function_calls_eh_return) { rtx eh_ofs = EH_RETURN_STACKADJ_RTX; emit_insn (gen_addsi3 (eh_ofs, eh_ofs, tsize_rtx)); tsize_rtx = eh_ofs; } emit_insn (gen_blockage ()); if (tsize != 0 || current_function_calls_eh_return) { emit_insn (gen_addsi3 (stack_pointer_rtx, stack_pointer_rtx, tsize_rtx)); } } if (current_function_calls_eh_return) { /* Perform the additional bump for __throw. */ emit_move_insn (gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM), stack_pointer_rtx); emit_insn (gen_rtx_USE (VOIDmode, gen_rtx_REG (Pmode, HARD_FRAME_POINTER_REGNUM))); emit_jump_insn (gen_eh_return_internal ()); } else emit_jump_insn (gen_return_internal (gen_rtx_REG (Pmode, GP_REG_FIRST + 31)));}voidiq2000_expand_eh_return (rtx address){ HOST_WIDE_INT gp_offset = cfun->machine->gp_sp_offset; rtx scratch; scratch = plus_constant (stack_pointer_rtx, gp_offset); emit_move_insn (gen_rtx_MEM (GET_MODE (address), scratch), address);}/* Return nonzero if this function is known to have a null epilogue. This allows the optimizer to omit jumps to jumps if no stack was created. */intiq2000_can_use_return_insn (void){ if (! reload_completed) return 0; if (regs_ever_live[31] || profile_flag) return 0; if (cfun->machine->initialized) return cfun->machine->total_size == 0; return compute_frame_size (get_frame_size ()) == 0;}/* Returns nonzero if X contains a SYMBOL_REF. */static intsymbolic_expression_p (rtx x){ if (GET_CODE (x) == SYMBOL_REF) return 1; if (GET_CODE (x) == CONST) return symbolic_expression_p (XEXP (x, 0)); if (UNARY_P (x)) return symbolic_expression_p (XEXP (x, 0)); if (ARITHMETIC_P (x)) return (symbolic_expression_p (XEXP (x, 0)) || symbolic_expression_p (XEXP (x, 1))); return 0;}/* Choose the section to use for the constant rtx expression X that has mode MODE. */static voidiq2000_select_rtx_section (enum machine_mode mode, rtx x ATTRIBUTE_UNUSED, unsigned HOST_WIDE_INT align){ /* For embedded applications, always put constants in read-only data, in order to reduce RAM usage. */ /* For embedded applications, always put constants in read-only data, in order to reduce RAM usage. */ mergeable_constant_section (mode, align, 0);}/* Choose the section to use for DECL. RELOC is true if its value contains any relocatable expression. Some of the logic used here needs to be replicated in ENCODE_SECTION_INFO in iq2000.h so that references to these symbols are done correctly. */static voidiq2000_select_section (tree decl, int reloc ATTRIBUTE_UNUSED, unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED){ if (TARGET_EMBEDDED_DATA) { /* For embedded applications, always put an object in read-only data if possible, in order to reduce RAM usage. */ if ((TREE_CODE (decl) == VAR_DECL && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl) && DECL_INITIAL (decl) && (DECL_INITIAL (decl) == error_mark_node || TREE_CONSTANT (DECL_INITIAL (decl)))) /* Deal with calls from output_constant_def_contents. */ || TREE_CODE (decl) != VAR_DECL) readonly_data_section (); else data_section (); } else { /* For hosted applications, always put an object in small data if possible, as this gives the best performance. */ if ((TREE_CODE (decl) == VAR_DECL && TREE_READONLY (decl) && !TREE_SIDE_EFFECTS (decl) && DECL_INITIAL (decl) && (DECL_INITIAL (decl) == error_mark_node || TREE_CONSTANT (DECL_INITIAL (decl)))) /* Deal with calls from output_constant_def_contents. */ || TREE_CODE (decl) != VAR_DECL) readonly_dat
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -