📄 iq2000.c
字号:
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; gcc_assert (GET_CODE (entry_parm) == REG); /* 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_data_section (); else data_section (); }}/* Return register to use for a function return value with VALTYPE for function FUNC. */rtxiq2000_function_value (tree valtype, tree func ATTRIBUTE_UNUSED){ int reg = GP_RETURN; enum machine_mode mode = TYPE_MODE (valtype); int unsignedp = TYPE_UNSIGNED (valtype); /* Since we define TARGET_PROMOTE_FUNCTION_RETURN that returns true, we must promote the mode just as PROMOTE_MODE does. */ mode = promote_mode (valtype, mode, &unsignedp, 1); return gen_rtx_REG (mode, reg);}/* Return true when an argument must be passed by reference. */static booliq2000_pass_by_reference (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type, bool named ATTRIBUTE_UNUSED){ int size; /* We must pass by reference if we would be both passing in registers and the stack. This is because any subsequent partial arg would be handled incorrectly in this case. */ if (cum && targetm.calls.must_pass_in_stack (mode, type)) { /* Don't pass the actual CUM to FUNCTION_ARG, because we would get double copies of any offsets generated for small structs passed in registers. */ CUMULATIVE_ARGS temp; temp = *cum; if (FUNCTION_ARG (temp, mode, type, named) != 0) return 1; } if (type == NULL_TREE || mode == DImode || mode == DFmode) return 0; size = int_size_in_bytes (type); return size == -1 || size > UNITS_PER_WORD;}/* Return the length of INSN. LENGTH is the initial length computed by attributes in the machine-description file. */intiq2000_adjust_insn_length (rtx insn, int length){ /* A unconditional jump has an unfilled delay slot if it is not part of a sequence. A conditional jump normally has a delay slot. */ if (simplejump_p (insn) || ( (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CALL_INSN))) length += 4; return length;}/* Output assembly instructions to perform a conditional branch. INSN is the branch instruction. OPERANDS[0] is the condition. OPERANDS[1] is the target of the branch. OPERANDS[2] is the target of the first operand to the condition. If TWO_OPERANDS_P is nonzero the comparison takes two operands; OPERANDS[3] will be the second operand. If INVERTED_P is nonzero we are to branch if the condition does not hold. If FLOAT_P is nonzero this is a floating-point comparison. LENGTH is the length (in bytes) of the sequence we are to generate. That tells us whether to generate a simple conditional branch, or a reversed conditional branch around a `jr' instruction. */char *iq2000_output_conditional_branch (rtx insn, rtx * operands, int two_operands_p, int float_p, int inverted_p, int length){ static char buffer[200]; /* The kind of comparison we are doing. */ enum rtx_code code = GET_CODE (operands[0]); /* Nonzero if the opcode for the comparison needs a `z' indicating that it is a comparison against zero. */ int need_z_p; /* A string to use in the assembly output to represent the first operand. */ const char *op1 = "%z2"; /* A string to use in the assembly output to represent the second operand. Use the hard-wired zero register if there's no second operand. */ const char *op2 = (two_operands_p ? ",%z3" : ",%."); /* The operand-printing string for the comparison. */ const char *comp = (float_p ? "%F0" : "%C0"); /* The operand-printing string for the inverted comparison. */ const char *inverted_comp = (float_p ? "%W0" : "%N0"); /* Likely variants of each branch instruction annul the instruction in the delay slot if the branch is not taken. */ iq2000_branch_likely = (final_sequence && INSN_ANNULLED_BRANCH_P (insn)); if (!two_operands_p) { /* To compute whether than A > B, for example, we normally subtract B from A and then look at the sign bit. But, if we are doing an unsigned comparison, and B is zero, we don't have to do the subtraction. Instead, we can just check to see if A is nonzero. Thus, we change the CODE here to reflect the simpler comparison operation. */ switch (code) { case GTU: code = NE; break; case LEU: code = EQ; break; case GEU: /* A condition which will always be true. */ code = EQ; op1 = "%."; break; case LTU: /* A condition which will always be false. */ code = NE; op1 = "%."; break; default: /* Not a special case. */ break; } } /* Relative comparisons are always done against zero. But equality comparisons are done between two operands, and therefore do not require a `z' in the assembly language output. */ need_z_p = (!float_p && code != EQ && code != NE); /* For comparisons against zero, the zero is not provided explicitly. */ if (need_z_p) op2 = ""; /* Begin by terminating the buffer. That way we can always use strcat to add to it. */ buffer[0] = '\0'; switch (length) { case 4: case 8: /* Just a simple conditional branch. */ if (float_p) sprintf (buffer, "b%s%%?\t%%Z2%%1", inverted_p ? inverted_comp : comp); else sprintf (buffer, "b%s%s%%?\t%s%s,%%1", inverted_p ? inverted_comp : comp, need_z_p ? "z" : "", op1, op2); return buffer; case 12: case 16: { /* Generate a reversed conditional branch around ` j' instruction
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -