📄 stormy16.c
字号:
If a scratch register is required (either with or without an intermediate register), you should define patterns for `reload_inM' or `reload_outM', as required.. These patterns, which will normally be implemented with a `define_expand', should be similar to the `movM' patterns, except that operand 2 is the scratch register. Define constraints for the reload register and scratch register that contain a single register class. If the original reload register (whose class is CLASS) can meet the constraint given in the pattern, the value returned by these macros is used for the class of the scratch register. Otherwise, two additional reload registers are required. Their classes are obtained from the constraints in the insn pattern. X might be a pseudo-register or a `subreg' of a pseudo-register, which could either be in a hard register or in memory. Use `true_regnum' to find out; it will return -1 if the pseudo is in memory and the hard register number if it is in a register. These macros should not be used in the case where a particular class of registers can only be copied to memory and not to another class of registers. In that case, secondary reload registers are not needed and would not be helpful. Instead, a stack location must be used to perform the copy and the `movM' pattern should use memory as an intermediate storage. This case often occurs between floating-point and general registers. */enum reg_classxstormy16_secondary_reload_class (class, mode, x) enum reg_class class; enum machine_mode mode; rtx x;{ /* This chip has the interesting property that only the first eight registers can be moved to/from memory. */ if ((GET_CODE (x) == MEM || ((GET_CODE (x) == SUBREG || GET_CODE (x) == REG) && (true_regnum (x) == -1 || true_regnum (x) >= FIRST_PSEUDO_REGISTER))) && ! reg_class_subset_p (class, EIGHT_REGS)) return EIGHT_REGS; /* When reloading a PLUS, the carry register will be required unless the inc or dec instructions can be used. */ if (xstormy16_carry_plus_operand (x, mode)) return CARRY_REGS; return NO_REGS;}/* Recognize a PLUS that needs the carry register. */intxstormy16_carry_plus_operand (x, mode) rtx x; enum machine_mode mode ATTRIBUTE_UNUSED;{ return (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT && (INTVAL (XEXP (x, 1)) < -4 || INTVAL (XEXP (x, 1)) > 4));}enum reg_classxstormy16_preferred_reload_class (x, class) enum reg_class class; rtx x;{ if (class == GENERAL_REGS && GET_CODE (x) == MEM) return EIGHT_REGS; return class;}#define LEGITIMATE_ADDRESS_INTEGER_P(X, OFFSET) \ (GET_CODE (X) == CONST_INT \ && (unsigned HOST_WIDE_INT) (INTVAL (X) + (OFFSET) + 2048) < 4096)#define LEGITIMATE_ADDRESS_CONST_INT_P(X, OFFSET) \ (GET_CODE (X) == CONST_INT \ && INTVAL (X) + (OFFSET) >= 0 \ && INTVAL (X) + (OFFSET) < 0x8000 \ && (INTVAL (X) + (OFFSET) < 0x100 || INTVAL (X) + (OFFSET) >= 0x7F00))intxstormy16_legitimate_address_p (mode, x, strict) enum machine_mode mode ATTRIBUTE_UNUSED; rtx x; int strict;{ if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0)) return 1; if (GET_CODE (x) == PLUS && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0)) x = XEXP (x, 0); if (GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC) x = XEXP (x, 0); if (GET_CODE (x) == REG && REGNO_OK_FOR_BASE_P (REGNO (x)) && (! strict || REGNO (x) < FIRST_PSEUDO_REGISTER)) return 1; return 0;}/* Return nonzero if memory address X (an RTX) can have different meanings depending on the machine mode of the memory reference it is used for or if the address is valid for some modes but not others. Autoincrement and autodecrement addresses typically have mode-dependent effects because the amount of the increment or decrement is the size of the operand being addressed. Some machines have other mode-dependent addresses. Many RISC machines have no mode-dependent addresses. You may assume that ADDR is a valid address for the machine. On this chip, this is true if the address is valid with an offset of 0 but not of 6, because in that case it cannot be used as an address for DImode or DFmode, or if the address is a post-increment or pre-decrement address. */intxstormy16_mode_dependent_address_p (x) rtx x;{ if (LEGITIMATE_ADDRESS_CONST_INT_P (x, 0) && ! LEGITIMATE_ADDRESS_CONST_INT_P (x, 6)) return 1; if (GET_CODE (x) == PLUS && LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 0) && ! LEGITIMATE_ADDRESS_INTEGER_P (XEXP (x, 1), 6)) return 1; if (GET_CODE (x) == PLUS) x = XEXP (x, 0); if (GET_CODE (x) == POST_INC || GET_CODE (x) == PRE_DEC) return 1; return 0;}/* A C expression that defines the optional machine-dependent constraint letters (`Q', `R', `S', `T', `U') that can be used to segregate specific types of operands, usually memory references, for the target machine. Normally this macro will not be defined. If it is required for a particular target machine, it should return 1 if VALUE corresponds to the operand type represented by the constraint letter C. If C is not defined as an extra constraint, the value returned should be 0 regardless of VALUE. */intxstormy16_extra_constraint_p (x, c) rtx x; int c;{ switch (c) { /* 'Q' is for pushes. */ case 'Q': return (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == POST_INC && XEXP (XEXP (x, 0), 0) == stack_pointer_rtx); /* 'R' is for pops. */ case 'R': return (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == PRE_DEC && XEXP (XEXP (x, 0), 0) == stack_pointer_rtx); /* 'S' is for immediate memory addresses. */ case 'S': return (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == CONST_INT && xstormy16_legitimate_address_p (VOIDmode, XEXP (x, 0), 0)); /* 'T' is for Rx. */ case 'T': /* Not implemented yet. */ return 0; /* 'U' is for CONST_INT values not between 2 and 15 inclusive, for allocating a scratch register for 32-bit shifts. */ case 'U': return (GET_CODE (x) == CONST_INT && (INTVAL (x) < 2 || INTVAL (x) > 15)); default: return 0; }}intshort_memory_operand (x, mode) rtx x; enum machine_mode mode;{ if (! memory_operand (x, mode)) return 0; return (GET_CODE (XEXP (x, 0)) != PLUS);}intnonimmediate_nonstack_operand (op, mode) rtx op; enum machine_mode mode;{ /* 'Q' is for pushes, 'R' for pops. */ return (nonimmediate_operand (op, mode) && ! xstormy16_extra_constraint_p (op, 'Q') && ! xstormy16_extra_constraint_p (op, 'R'));}/* Splitter for the 'move' patterns, for modes not directly implemeted by hardware. Emit insns to copy a value of mode MODE from SRC to DEST. This function is only called when reload_completed. */void xstormy16_split_move (mode, dest, src) enum machine_mode mode; rtx dest; rtx src;{ int num_words = GET_MODE_BITSIZE (mode) / BITS_PER_WORD; int direction, end, i; int src_modifies = 0; int dest_modifies = 0; int src_volatile = 0; int dest_volatile = 0; rtx mem_operand; rtx auto_inc_reg_rtx = NULL_RTX; /* Check initial conditions. */ if (! reload_completed || mode == QImode || mode == HImode || ! nonimmediate_operand (dest, mode) || ! general_operand (src, mode)) abort (); /* This case is not supported below, and shouldn't be generated. */ if (GET_CODE (dest) == MEM && GET_CODE (src) == MEM) abort (); /* This case is very very bad after reload, so trap it now. */ if (GET_CODE (dest) == SUBREG || GET_CODE (src) == SUBREG) abort (); /* The general idea is to copy by words, offsetting the source and destination. Normally the least-significant word will be copied first, but for pre-dec operations it's better to copy the most-significant word first. Only one operand can be a pre-dec or post-inc operand. It's also possible that the copy overlaps so that the direction must be reversed. */ direction = 1; if (GET_CODE (dest) == MEM) { mem_operand = XEXP (dest, 0); dest_modifies = side_effects_p (mem_operand); if (auto_inc_p (mem_operand)) auto_inc_reg_rtx = XEXP (mem_operand, 0); dest_volatile = MEM_VOLATILE_P (dest); if (dest_volatile) { dest = copy_rtx (dest); MEM_VOLATILE_P (dest) = 0; } } else if (GET_CODE (src) == MEM) { mem_operand = XEXP (src, 0); src_modifies = side_effects_p (mem_operand); if (auto_inc_p (mem_operand)) auto_inc_reg_rtx = XEXP (mem_operand, 0); src_volatile = MEM_VOLATILE_P (src); if (src_volatile) { src = copy_rtx (src); MEM_VOLATILE_P (src) = 0; } } else mem_operand = NULL_RTX; if (mem_operand == NULL_RTX) { if (GET_CODE (src) == REG && GET_CODE (dest) == REG && reg_overlap_mentioned_p (dest, src) && REGNO (dest) > REGNO (src)) direction = -1; } else if (GET_CODE (mem_operand) == PRE_DEC || (GET_CODE (mem_operand) == PLUS && GET_CODE (XEXP (mem_operand, 0)) == PRE_DEC)) direction = -1; else if (GET_CODE (src) == MEM && reg_overlap_mentioned_p (dest, src)) { int regno; if (GET_CODE (dest) != REG) abort (); regno = REGNO (dest); if (! refers_to_regno_p (regno, regno + num_words, mem_operand, 0)) abort (); if (refers_to_regno_p (regno, regno + 1, mem_operand, 0)) direction = -1; else if (refers_to_regno_p (regno + num_words - 1, regno + num_words, mem_operand, 0)) direction = 1; else /* This means something like (set (reg:DI r0) (mem:DI (reg:HI r1))) which we'd need to support by doing the set of the second word last. */ abort (); } end = direction < 0 ? -1 : num_words; for (i = direction < 0 ? num_words - 1 : 0; i != end; i += direction) { rtx w_src, w_dest, insn; if (src_modifies) w_src = gen_rtx_MEM (word_mode, mem_operand); else w_src = simplify_gen_subreg (word_mode, src, mode, i * UNITS_PER_WORD); if (src_volatile) MEM_VOLATILE_P (w_src) = 1; if (dest_modifies) w_dest = gen_rtx_MEM (word_mode, mem_operand); else w_dest = simplify_gen_subreg (word_mode, dest, mode, i * UNITS_PER_WORD); if (dest_volatile) MEM_VOLATILE_P (w_dest) = 1; /* The simplify_subreg calls must always be able to simplify. */ if (GET_CODE (w_src) == SUBREG || GET_CODE (w_dest) == SUBREG) abort (); insn = emit_insn (gen_rtx_SET (VOIDmode, w_dest, w_src)); if (auto_inc_reg_rtx) REG_NOTES (insn) = alloc_EXPR_LIST (REG_INC, auto_inc_reg_rtx, REG_NOTES (insn)); }}/* Expander for the 'move' patterns. Emit insns to copy a value of mode MODE from SRC to DEST. */void xstormy16_expand_move (mode, dest, src) enum machine_mode mode; rtx dest; rtx src;{ /* There are only limited immediate-to-memory move instructions. */ if (! reload_in_progress && ! reload_completed && GET_CODE (dest) == MEM && (GET_CODE (XEXP (dest, 0)) != CONST_INT || ! xstormy16_legitimate_address_p (mode, XEXP (dest, 0), 0)) && GET_CODE (src) != REG && GET_CODE (src) != SUBREG) src = copy_to_mode_reg (mode, src); /* Don't emit something we would immediately split. */ if (reload_completed && mode != HImode && mode != QImode) { xstormy16_split_move (mode, dest, src); return; } emit_insn (gen_rtx_SET (VOIDmode, dest, src));}/* Stack Layout: The stack is laid out as follows:SP->FP-> Local variables Register save area (up to 4 words) Argument register save area for stdarg (NUM_ARGUMENT_REGISTERS words)AP-> Return address (two words) 9th procedure parameter word 10th procedure parameter word ... last procedure parameter word The frame pointer location is tuned to make it most likely that all parameters and local variables can be accessed using a load-indexed instruction. *//* A structure to describe the layout. */struct xstormy16_stack_layout{ /* Size of the topmost three items on the stack. */ int locals_size; int register_save_size; int stdarg_save_size; /* Sum of the above items. */ int frame_size; /* Various offsets. */ int first_local_minus_ap; int sp_minus_fp; int fp_minus_ap;};/* Does REGNO need to be saved? */#define REG_NEEDS_SAVE(REGNUM, IFUN) \ ((regs_ever_live[REGNUM] && ! call_used_regs[REGNUM]) \ || (IFUN && ! fixed_regs[REGNUM] && call_used_regs[REGNUM] \ && (regs_ever_live[REGNUM] || ! current_function_is_leaf)))/* Compute the stack layout. */struct xstormy16_stack_layout
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -