📄 mmix.c
字号:
value = mmix_intval (x); /* We used to map Q->J, R->K, S->L, T->N, U->O, but we don't have to any more ('U' taken for address_operand, 'R' similarly). Some letters map outside of CONST_INT, though; we still use 'S' and 'T'. */ if (c == 'S') return mmix_shiftable_wyde_value (value); else if (c == 'T') return mmix_shiftable_wyde_value (~value); return 0;}/* DYNAMIC_CHAIN_ADDRESS. */rtxmmix_dynamic_chain_address (rtx frame){ /* FIXME: the frame-pointer is stored at offset -8 from the current frame-pointer. Unfortunately, the caller assumes that a frame-pointer is present for *all* previous frames. There should be a way to say that that cannot be done, like for RETURN_ADDR_RTX. */ return plus_constant (frame, -8);}/* STARTING_FRAME_OFFSET. */intmmix_starting_frame_offset (void){ /* The old frame pointer is in the slot below the new one, so FIRST_PARM_OFFSET does not need to depend on whether the frame-pointer is needed or not. We have to adjust for the register stack pointer being located below the saved frame pointer. Similarly, we store the return address on the stack too, for exception handling, and always if we save the register stack pointer. */ return (-8 + (MMIX_CFUN_HAS_LANDING_PAD ? -16 : (MMIX_CFUN_NEEDS_SAVED_EH_RETURN_ADDRESS ? -8 : 0)));}/* RETURN_ADDR_RTX. */rtxmmix_return_addr_rtx (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 (void){ /* 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 (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 (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 && !targetm.calls.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. */static boolmmix_pass_by_reference (const CUMULATIVE_ARGS *argsp, enum machine_mode mode, tree type, bool named ATTRIBUTE_UNUSED){ /* FIXME: Check: I'm not sure the must_pass_in_stack check is necessary. */ if (targetm.calls.must_pass_in_stack (mode, type)) return true; if (MMIX_FUNCTION_ARG_SIZE (mode, type) > 8 && !TARGET_LIBFUNC && (!argsp || !argsp->lib)) return true; return false;}/* Return nonzero if regno is a register number where a parameter is passed, and 0 otherwise. */intmmix_function_arg_regno_p (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 (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); if (COMPLEX_MODE_P (mode)) /* A complex type, made up of components. */ cmode = TYPE_MODE (TREE_TYPE (valtype)); else { /* Of the other larger-than-register modes, we only support scalar mode TImode. (At least, that's the only one that's been rudimentally tested.) Make sure we're alerted for unexpected cases. */ if (mode != TImode) sorry ("support for mode %qs", GET_MODE_NAME (mode)); /* In any case, we will fill registers to the natural size. */ cmode = DImode; } 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), const0_rtx); return gen_rtx_PARALLEL (VOIDmode, gen_rtvec_v (nregs, vec));}/* FUNCTION_VALUE_REGNO_P. */intmmix_function_value_regno_p (int regno){ return regno == MMIX_RETURN_VALUE_REGNUM;}/* EH_RETURN_DATA_REGNO. */intmmix_eh_return_data_regno (int n){ 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 (void){ return gen_rtx_REG (Pmode, MMIX_EH_RETURN_STACKADJ_REGNUM);}/* EH_RETURN_HANDLER_RTX. */rtxmmix_eh_return_handler_rtx (void){ return gen_rtx_REG (Pmode, MMIX_INCOMING_RETURN_ADDRESS_REGNUM);}/* ASM_PREFERRED_EH_DATA_FORMAT. */intmmix_asm_preferred_eh_data_format (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_reorg. */static voidmmix_target_asm_function_prologue (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 (FILE *stream ATTRIBUTE_UNUSED){ cfun->machine->in_prologue = 0;}/* Implement TARGET_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. */static voidmmix_reorg (void){ 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 (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 (FILE *stream, tree fndecl ATTRIBUTE_UNUSED, HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED, tree func){ /* If you define TARGET_STRUCT_VALUE_RTX that returns 0 (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 (FILE *stream ATTRIBUTE_UNUSED, int labelno ATTRIBUTE_UNUSED){ sorry ("function_profiler support for MMIX");}/* Worker function for TARGET_SETUP_INCOMING_VARARGS. For the moment, let's stick to pushing argument registers on the stack. Later, we can parse all arguments in registers, to improve performance. */static voidmmix_setup_incoming_varargs (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");}/* TRAMPOLINE_SIZE. *//* Four 4-byte insns plus two 8-byte values. */int mmix_trampoline_size = 32;/* TRAMPOLINE_TEMPLATE. */voidmmix_trampoline_template (FILE *stream){ /* Read a value into the static-chain register and jump somewhere. The static chain is stored at offset 16, and the function address is stored at offset 24. */ /* FIXME: GCC copies this using *intsize* (tetra), when it should use register size (octa). */ fprintf (stream, "\tGETA $255,1F\n\t"); fprintf (stream, "LDOU %s,$255,0\n\t", reg_names[MMIX_STATIC_CHAIN_REGNUM]); fprintf (stream, "LDOU $255,$255,8\n\t");
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -