📄 mmix.c
字号:
(-8 + (MMIX_CFUN_HAS_LANDING_PAD ? -16 : (MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS ? -8 : 0)));}/* RETURN_ADDR_RTX. */rtxmmix_return_addr_rtx (count, frame) int count; rtx frame ATTRIBUTE_UNUSED;{ return count == 0 ? (MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS /* FIXME: Set frame_alias_set on the following. (Why?) See mmix_initial_elimination_offset for the reason we can't use get_hard_reg_initial_val for both. Always using a stack slot and not a register would be suboptimal. */ ? validize_mem (gen_rtx_MEM (Pmode, plus_constant (frame_pointer_rtx, -16))) : get_hard_reg_initial_val (Pmode, MMIX_INCOMING_RETURN_ADDRESS_REGNUM)) : NULL_RTX;}/* SETUP_FRAME_ADDRESSES. */voidmmix_setup_frame_addresses (){ /* Nothing needed at the moment. */}/* The difference between the (imaginary) frame pointer and the stack pointer. Used to eliminate the frame pointer. */intmmix_initial_elimination_offset (fromreg, toreg) int fromreg; int toreg;{ int regno; int fp_sp_offset = (get_frame_size () + current_function_outgoing_args_size + 7) & ~7; /* There is no actual offset between these two virtual values, but for the frame-pointer, we have the old one in the stack position below it, so the offset for the frame-pointer to the stack-pointer is one octabyte larger. */ if (fromreg == MMIX_ARG_POINTER_REGNUM && toreg == MMIX_FRAME_POINTER_REGNUM) return 0; /* The difference is the size of local variables plus the size of outgoing function arguments that would normally be passed as registers but must be passed on stack because we're out of function-argument registers. Only global saved registers are counted; the others go on the register stack. The frame-pointer is counted too if it is what is eliminated, as we need to balance the offset for it from STARTING_FRAME_OFFSET. Also add in the slot for the register stack pointer we save if we have a landing pad. Unfortunately, we can't access $0..$14, from unwinder code easily, so store the return address in a frame slot too. FIXME: Only for non-leaf functions. FIXME: Always with a landing pad, because it's hard to know whether we need the other at the time we know we need the offset for one (and have to state it). It's a kludge until we can express the register stack in the EH frame info. We have to do alignment here; get_frame_size will not return a multiple of STACK_BOUNDARY. FIXME: Add note in manual. */ for (regno = MMIX_FIRST_GLOBAL_REGNUM; regno <= 255; regno++) if ((regs_ever_live[regno] && ! call_used_regs[regno]) || IS_MMIX_EH_RETURN_DATA_REG (regno)) fp_sp_offset += 8; return fp_sp_offset + (MMIX_CFUN_HAS_LANDING_PAD ? 16 : (MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS ? 8 : 0)) + (fromreg == MMIX_ARG_POINTER_REGNUM ? 0 : 8);}/* Return an rtx for a function argument to go in a register, and 0 for one that must go on stack. */rtxmmix_function_arg (argsp, mode, type, named, incoming) const CUMULATIVE_ARGS * argsp; enum machine_mode mode; tree type; int named ATTRIBUTE_UNUSED; int incoming;{ /* Last-argument marker. */ if (type == void_type_node) return (argsp->regs < MMIX_MAX_ARGS_IN_REGS) ? gen_rtx_REG (mode, (incoming ? MMIX_FIRST_INCOMING_ARG_REGNUM : MMIX_FIRST_ARG_REGNUM) + argsp->regs) : NULL_RTX; return (argsp->regs < MMIX_MAX_ARGS_IN_REGS && !MUST_PASS_IN_STACK (mode, type) && (GET_MODE_BITSIZE (mode) <= 64 || argsp->lib || TARGET_LIBFUNC)) ? gen_rtx_REG (mode, (incoming ? MMIX_FIRST_INCOMING_ARG_REGNUM : MMIX_FIRST_ARG_REGNUM) + argsp->regs) : NULL_RTX;}/* Returns nonzero for everything that goes by reference, 0 for everything that goes by value. */intmmix_function_arg_pass_by_reference (argsp, mode, type, named) const CUMULATIVE_ARGS * argsp; enum machine_mode mode; tree type; int named ATTRIBUTE_UNUSED;{ /* FIXME: Check: I'm not sure the MUST_PASS_IN_STACK check is necessary. */ return MUST_PASS_IN_STACK (mode, type) || (MMIX_FUNCTION_ARG_SIZE (mode, type) > 8 && !TARGET_LIBFUNC && !argsp->lib);}/* Return nonzero if regno is a register number where a parameter is passed, and 0 otherwise. */intmmix_function_arg_regno_p (regno, incoming) int regno; int incoming;{ int first_arg_regnum = incoming ? MMIX_FIRST_INCOMING_ARG_REGNUM : MMIX_FIRST_ARG_REGNUM; return regno >= first_arg_regnum && regno < first_arg_regnum + MMIX_MAX_ARGS_IN_REGS;}/* FUNCTION_OUTGOING_VALUE. */rtxmmix_function_outgoing_value (valtype, func) tree valtype; tree func ATTRIBUTE_UNUSED;{ enum machine_mode mode = TYPE_MODE (valtype); enum machine_mode cmode; int first_val_regnum = MMIX_OUTGOING_RETURN_VALUE_REGNUM; rtx vec[MMIX_MAX_REGS_FOR_VALUE]; int i; int nregs; /* Return values that fit in a register need no special handling. There's no register hole when parameters are passed in global registers. */ if (TARGET_ABI_GNU || GET_MODE_BITSIZE (mode) <= BITS_PER_WORD) return gen_rtx_REG (mode, MMIX_OUTGOING_RETURN_VALUE_REGNUM); /* A complex type, made up of components. */ cmode = TYPE_MODE (TREE_TYPE (valtype)); nregs = ((GET_MODE_BITSIZE (mode) + BITS_PER_WORD - 1) / BITS_PER_WORD); /* We need to take care of the effect of the register hole on return values of large sizes; the last register will appear as the first register, with the rest shifted. (For complex modes, this is just swapped registers.) */ if (nregs > MMIX_MAX_REGS_FOR_VALUE) internal_error ("too large function value type, needs %d registers,\ have only %d registers for this", nregs, MMIX_MAX_REGS_FOR_VALUE); /* FIXME: Maybe we should handle structure values like this too (adjusted for BLKmode), perhaps for both ABI:s. */ for (i = 0; i < nregs - 1; i++) vec[i] = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (cmode, first_val_regnum + i), GEN_INT ((i + 1) * BITS_PER_UNIT)); vec[nregs - 1] = gen_rtx_EXPR_LIST (VOIDmode, gen_rtx_REG (cmode, first_val_regnum + nregs - 1), GEN_INT (0)); return gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (nregs, vec));}/* FUNCTION_VALUE_REGNO_P. */intmmix_function_value_regno_p (regno) int regno;{ return regno == MMIX_RETURN_VALUE_REGNUM;}/* EH_RETURN_DATA_REGNO. */intmmix_eh_return_data_regno (n) int n ATTRIBUTE_UNUSED;{ if (n >= 0 && n < 4) return MMIX_EH_RETURN_DATA_REGNO_START + n; return INVALID_REGNUM;}/* EH_RETURN_STACKADJ_RTX. */rtxmmix_eh_return_stackadj_rtx (){ return gen_rtx_REG (Pmode, MMIX_EH_RETURN_STACKADJ_REGNUM);}/* EH_RETURN_HANDLER_RTX. */rtxmmix_eh_return_handler_rtx (){ return gen_rtx_REG (Pmode, MMIX_INCOMING_RETURN_ADDRESS_REGNUM);}/* ASM_PREFERRED_EH_DATA_FORMAT. */intmmix_asm_preferred_eh_data_format (code, global) int code ATTRIBUTE_UNUSED; int global ATTRIBUTE_UNUSED;{ /* This is the default (was at 2001-07-20). Revisit when needed. */ return DW_EH_PE_absptr;}/* Make a note that we've seen the beginning of the prologue. This matters to whether we'll translate register numbers as calculated by mmix_machine_dependent_reorg. */static voidmmix_target_asm_function_prologue (stream, framesize) FILE *stream ATTRIBUTE_UNUSED; HOST_WIDE_INT framesize ATTRIBUTE_UNUSED;{ cfun->machine->in_prologue = 1;}/* Make a note that we've seen the end of the prologue. */static voidmmix_target_asm_function_end_prologue (stream) FILE *stream ATTRIBUTE_UNUSED;{ cfun->machine->in_prologue = 0;}/* MACHINE_DEPENDENT_REORG. No actual rearrangements done here; just virtually by calculating the highest saved stack register number used to modify the register numbers at output time. */voidmmix_machine_dependent_reorg (first) rtx first ATTRIBUTE_UNUSED;{ int regno; /* We put the number of the highest saved register-file register in a location convenient for the call-patterns to output. Note that we don't tell dwarf2 about these registers, since it can't restore them anyway. */ for (regno = MMIX_LAST_STACK_REGISTER_REGNUM; regno >= 0; regno--) if ((regs_ever_live[regno] && !call_used_regs[regno]) || (regno == MMIX_FRAME_POINTER_REGNUM && frame_pointer_needed)) break; /* Regardless of whether they're saved (they might be just read), we mustn't include registers that carry parameters. We could scan the insns to see whether they're actually used (and indeed do other less trivial register usage analysis and transformations), but it seems wasteful to optimize for unused parameter registers. As of 2002-04-30, regs_ever_live[n] seems to be set for only-reads too, but that might change. */ if (!TARGET_ABI_GNU && regno < current_function_args_info.regs - 1) { regno = current_function_args_info.regs - 1; /* We don't want to let this cause us to go over the limit and make incoming parameter registers be misnumbered and treating the last parameter register and incoming return value register call-saved. Stop things at the unmodified scheme. */ if (regno > MMIX_RETURN_VALUE_REGNUM - 1) regno = MMIX_RETURN_VALUE_REGNUM - 1; } cfun->machine->highest_saved_stack_register = regno;}/* TARGET_ASM_FUNCTION_EPILOGUE. */static voidmmix_target_asm_function_epilogue (stream, locals_size) FILE *stream; HOST_WIDE_INT locals_size ATTRIBUTE_UNUSED;{ /* Emit an \n for readability of the generated assembly. */ fputc ('\n', stream);}/* TARGET_ASM_OUTPUT_MI_THUNK. */static voidmmix_asm_output_mi_thunk (stream, fndecl, delta, vcall_offset, func) FILE * stream; tree fndecl ATTRIBUTE_UNUSED; HOST_WIDE_INT delta; HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED; tree func;{ /* If you define STRUCT_VALUE to 0, rather than use STRUCT_VALUE_REGNUM, (i.e. pass location of structure to return as invisible first argument) you need to tweak this code too. */ const char *regname = reg_names[MMIX_FIRST_INCOMING_ARG_REGNUM]; if (delta >= 0 && delta < 65536) fprintf (stream, "\tINCL %s,%d\n", regname, (int)delta); else if (delta < 0 && delta >= -255) fprintf (stream, "\tSUBU %s,%s,%d\n", regname, regname, (int)-delta); else { mmix_output_register_setting (stream, 255, delta, 1); fprintf (stream, "\tADDU %s,%s,$255\n", regname, regname); } fprintf (stream, "\tJMP "); assemble_name (stream, XSTR (XEXP (DECL_RTL (func), 0), 0)); fprintf (stream, "\n");}/* FUNCTION_PROFILER. */voidmmix_function_profiler (stream, labelno) FILE *stream ATTRIBUTE_UNUSED; int labelno ATTRIBUTE_UNUSED;{ sorry ("function_profiler support for MMIX");}/* SETUP_INCOMING_VARARGS. */voidmmix_setup_incoming_varargs (args_so_farp, mode, vartype, pretend_sizep, second_time) CUMULATIVE_ARGS * args_so_farp; enum machine_mode mode; tree vartype; int * pretend_sizep; int second_time ATTRIBUTE_UNUSED;{ /* The last named variable has been handled, but args_so_farp has not been advanced for it. */ if (args_so_farp->regs + 1 < MMIX_MAX_ARGS_IN_REGS) *pretend_sizep = (MMIX_MAX_ARGS_IN_REGS - (args_so_farp->regs + 1)) * 8; /* We assume that one argument takes up one register here. That should be true until we start messing with multi-reg parameters. */ if ((7 + (MMIX_FUNCTION_ARG_SIZE (mode, vartype))) / 8 != 1) internal_error ("MMIX Internal: Last named vararg would not fit in a register");}/* EXPAND_BUILTIN_VA_ARG. *//* This is modified from the "standard" implementation of va_arg: read the value from the current (padded) address and increment by the (padded) size. The difference for MMIX is that if the type is pass-by-reference, then perform an indirection. */rtxmmix_expand_builtin_va_arg (valist, type) tree valist; tree type;{ tree ptr_size = size_int (BITS_PER_WORD / BITS_PER_UNIT); tree addr_tree, type_size = NULL; tree align, alignm1; tree rounded_size; rtx addr; /* Compute the rounded size of the type. */ /* Get AP. */ addr_tree = valist; align = size_int (PARM_BOUNDARY / BITS_PER_UNIT); alignm1 = size_int (PARM_BOUNDARY / BITS_PER_UNIT - 1); if (type == error_mark_node || (type_size = TYPE_SIZE_UNIT (TYPE_MAIN_VARIANT (type))) == NULL || TREE_OVERFLOW (type_size)) /* Presumably an error; the size isn't computable. A message has supposedly been emitted elsewhere. */ rounded_size = size_zero_node; else rounded_size = fold (build (MULT_EXPR, sizetype, fold (build (TRUNC_DIV_EXPR, sizetype, fold (build (PLUS_EXPR, sizetype, type_size, alignm1)), align)), align)); if (AGGREGATE_TYPE_P (type) && GET_MODE_UNIT_SIZE (TYPE_MODE (type)) < 8 && GET_MODE_UNIT_SIZE (TYPE_MODE (type)) != 0) { /* Adjust for big-endian the location of aggregates passed in a register, but where the aggregate is accessed in a shorter mode than the natural register mode (i.e. it is accessed as SFmode(?), SImode, HImode or QImode rather than DImode or DFmode(?)). FIXME: Or should we adjust the mode in which the aggregate is read, to be a register size mode? (Hum, nah, a small offset is generally cheaper than a wider memory access on MMIX.) */ addr_tree = build (PLUS_EXPR, TREE_TYPE (addr_tree), addr_tree, size_int ((BITS_PER_WORD / BITS_PER_UNIT) - GET_MODE_UNIT_SIZE (TYPE_MODE (type)))); } else if (!integer_zerop (rounded_size)) { if (!really_constant_p (type_size)) /* Varying-size types come in by reference. */ addr_tree = build1 (INDIRECT_REF, build_pointer_type (type), addr_tree); else { /* If the size is less than a register, then we need to pad the address by adding the difference. */ tree addend = fold (build (COND_EXPR, sizetype,
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -