--- gcc/config/sparc/sparc.c 2018/04/24 18:10:25 1.1 +++ gcc/config/sparc/sparc.c 2018/04/24 18:28:58 1.1.1.4 @@ -1,6 +1,8 @@ /* Subroutines for insn-output.c for Sun SPARC. - Copyright (C) 1987, 1988, 1989, 1992 Free Software Foundation, Inc. + Copyright (C) 1987, 88, 89, 92, 93, 94, 1995 Free Software Foundation, Inc. Contributed by Michael Tiemann (tiemann@cygnus.com) + 64 bit SPARC V9 support by Michael Tiemann, Jim Wilson, and Doug Evans, + at Cygnus Support. This file is part of GNU CC. @@ -16,7 +18,8 @@ GNU General Public License for more deta You should have received a copy of the GNU General Public License along with GNU CC; see the file COPYING. If not, write to -the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ +the Free Software Foundation, 59 Temple Place - Suite 330, +Boston, MA 02111-1307, USA. */ #include #include "config.h" @@ -34,13 +37,42 @@ the Free Software Foundation, 675 Mass A #include "expr.h" #include "recog.h" +/* 1 if the caller has placed an "unimp" insn immediately after the call. + This is used in v8 code when calling a function that returns a structure. + v9 doesn't have this. */ + +#define SKIP_CALLERS_UNIMP_P (!TARGET_V9 && current_function_returns_struct) + /* Global variables for machine-dependent things. */ +/* Says what architecture we're compiling for. */ +enum arch_type sparc_arch_type; + +/* Size of frame. Need to know this to emit return insns from leaf procedures. + ACTUAL_FSIZE is set by compute_frame_size() which is called during the + reload pass. This is important as the value is later used in insn + scheduling (to see what can go in a delay slot). + APPARENT_FSIZE is the size of the stack less the register save area and less + the outgoing argument area. It is used when saving call preserved regs. */ +static int apparent_fsize; +static int actual_fsize; + /* Save the operands last given to a compare for use when we generate a scc or bcc insn. */ rtx sparc_compare_op0, sparc_compare_op1; +/* Count of named arguments (v9 only). + ??? INIT_CUMULATIVE_ARGS initializes these, and FUNCTION_ARG_ADVANCE + increments SPARC_ARG_COUNT. They are then used by + FUNCTION_ARG_CALLEE_COPIES to determine if the argument is really a named + argument or not. This hack is necessary because the NAMED argument to the + FUNCTION_ARG_XXX macros is not what it says it is: it does not include the + last named argument. */ + +int sparc_arg_count; +int sparc_n_named_args; + /* We may need an epilogue if we spill too many registers. If this is non-zero, then we branch here for the epilogue. */ static rtx leaf_label; @@ -60,32 +92,116 @@ char leaf_reg_remap[] = 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}; + 56, 57, 58, 59, 60, 61, 62, 63, + 64, 65, 66, 67, 68, 69, 70, 71, + 72, 73, 74, 75, 76, 77, 78, 79, + 80, 81, 82, 83, 84, 85, 86, 87, + 88, 89, 90, 91, 92, 93, 94, 95, + 96, 97, 98, 99}; -char leaf_reg_backmap[] = -{ 0, 1, 2, 3, 4, 5, 6, 7, - 24, 25, 26, 27, 28, 29, 14, 31, - -1, -1, -1, -1, -1, -1, -1, -1, - -1, -1, -1, -1, -1, -1, -1, -1, - - 32, 33, 34, 35, 36, 37, 38, 39, - 40, 41, 42, 43, 44, 45, 46, 47, - 48, 49, 50, 51, 52, 53, 54, 55, - 56, 57, 58, 59, 60, 61, 62, 63}; #endif -/* Global variables set by FUNCTION_PROLOGUE. */ -/* Size of frame. Need to know this to emit return insns from - leaf procedures. */ -int apparent_fsize; -int actual_fsize; - /* Name of where we pretend to think the frame pointer points. Normally, this is "%fp", but if we are in a leaf procedure, - this is "%sp+something". */ -char *frame_base_name; + this is "%sp+something". We record "something" separately as it may be + too big for reg+constant addressing. */ + +static char *frame_base_name; +static int frame_base_offset; static rtx find_addr_reg (); +static void sparc_init_modes (); + +/* Option handling. */ + +/* Validate and override various options, and do some machine dependent + initialization. */ + +void +sparc_override_options () +{ + /* Check for any conflicts in the choice of options. */ + /* ??? This stuff isn't really usable yet. */ + + if (! TARGET_V9) + { + if (target_flags & MASK_CODE_MODEL) + error ("code model support is only available with -mv9"); + if (TARGET_INT64) + error ("-mint64 is only available with -mv9"); + if (TARGET_LONG64) + error ("-mlong64 is only available with -mv9"); + if (TARGET_PTR64) + error ("-mptr64 is only available with -mv9"); + if (TARGET_ENV32) + error ("-menv32 is only available with -mv9"); + if (TARGET_STACK_BIAS) + error ("-mstack-bias is only available with -mv9"); + } + else + { + /* ??? Are there any options that aren't usable with v9. + -munaligned-doubles? */ + } + + /* Check for conflicts in cpu specification. + If we use -mcpu=xxx, this can be removed. */ + + if ((TARGET_V8 != 0) + (TARGET_SPARCLITE != 0) + (TARGET_V9 != 0) > 1) + error ("conflicting architectures defined"); + + /* Do various machine dependent initializations. */ + sparc_init_modes (); +} + +/* Float conversions (v9 only). + + The floating point registers cannot hold DImode values because SUBREG's + on them get the wrong register. "(subreg:SI (reg:DI M int-reg) 0)" is the + same as "(subreg:SI (reg:DI N float-reg) 1)", but gcc doesn't know how to + turn the "0" to a "1". Therefore, we must explicitly do the conversions + to/from int/fp regs. `sparc64_fpconv_stack_slot' is the address of an + 8 byte stack slot used during the transfer. + ??? I could have used [%fp-16] but I didn't want to add yet another + dependence on this. */ +/* ??? Can we use assign_stack_temp here? */ + +static rtx fpconv_stack_temp; + +/* Called once for each function. */ + +void +sparc64_init_expanders () +{ + fpconv_stack_temp = NULL_RTX; +} + +/* Assign a stack temp for fp/int DImode conversions. */ + +rtx +sparc64_fpconv_stack_temp () +{ + if (fpconv_stack_temp == NULL_RTX) + fpconv_stack_temp = + assign_stack_local (DImode, GET_MODE_SIZE (DImode), 0); + + return fpconv_stack_temp; +} + +/* Miscellaneous utilities. */ + +/* Nonzero if CODE, a comparison, is suitable for use in v9 conditional move + or branch on register contents instructions. */ + +int +v9_regcmp_p (code) + enum rtx_code code; +{ + return (code == EQ || code == NE || code == GE || code == LT + || code == LE || code == GT); +} + +/* Operand constraints. */ /* Return non-zero only if OP is a register of mode MODE, or const0_rtx. */ @@ -96,13 +212,62 @@ reg_or_0_operand (op, mode) { if (op == const0_rtx || register_operand (op, mode)) return 1; - if (GET_CODE (op) == CONST_DOUBLE + if (GET_MODE (op) == VOIDmode && GET_CODE (op) == CONST_DOUBLE && CONST_DOUBLE_HIGH (op) == 0 && CONST_DOUBLE_LOW (op) == 0) return 1; + if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT + && GET_CODE (op) == CONST_DOUBLE + && fp_zero_operand (op)) + return 1; return 0; } +/* Nonzero if OP is a floating point value with value 0.0. */ +int +fp_zero_operand (op) + rtx op; +{ + REAL_VALUE_TYPE r; + + REAL_VALUE_FROM_CONST_DOUBLE (r, op); + return REAL_VALUES_EQUAL (r, dconst0); +} + +/* Nonzero if OP is an integer register. */ + +int +intreg_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, SImode) + || (TARGET_V9 && register_operand (op, DImode))); +} + +/* Nonzero if OP is a floating point condition code register. */ + +int +ccfp_reg_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + /* This can happen when recog is called from combine. Op may be a MEM. + Fail instead of calling abort in this case. */ + if (GET_CODE (op) != REG || REGNO (op) == 0) + return 0; + if (GET_MODE (op) != mode) + return 0; + +#if 0 /* ??? ==> 1 when %fcc1-3 are pseudos first. See gen_compare_reg(). */ + if (reg_renumber == 0) + return REGNO (op) >= FIRST_PSEUDO_REGISTER; + return REGNO_OK_FOR_CCFP_P (REGNO (op)); +#else + return (unsigned) REGNO (op) - 96 < 4; +#endif +} + /* Nonzero if OP can appear as the dest of a RESTORE insn. */ int restore_operand (op, mode) @@ -124,7 +289,7 @@ call_operand (op, mode) if (GET_CODE (op) != MEM) abort (); op = XEXP (op, 0); - return (CONSTANT_P (op) || memory_address_p (Pmode, op)); + return (symbolic_operand (op, mode) || memory_address_p (Pmode, op)); } int @@ -132,7 +297,7 @@ call_operand_address (op, mode) rtx op; enum machine_mode mode; { - return (CONSTANT_P (op) || memory_address_p (Pmode, op)); + return (symbolic_operand (op, mode) || memory_address_p (Pmode, op)); } /* Returns 1 if OP is either a symbol reference or a sum of a symbol @@ -181,6 +346,52 @@ symbolic_memory_operand (op, mode) || GET_CODE (op) == HIGH || GET_CODE (op) == LABEL_REF); } +/* Return 1 if the operand is a data segment reference. This includes + the readonly data segment, or in other words anything but the text segment. + This is needed in the medium/anywhere code model on v9. These values + are accessed with MEDANY_BASE_REG. */ + +int +data_segment_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + switch (GET_CODE (op)) + { + case SYMBOL_REF : + return ! SYMBOL_REF_FLAG (op); + case PLUS : + /* Assume canonical format of symbol + constant. */ + case CONST : + return data_segment_operand (XEXP (op, 0)); + default : + return 0; + } +} + +/* Return 1 if the operand is a text segment reference. + This is needed in the medium/anywhere code model on v9. */ + +int +text_segment_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + switch (GET_CODE (op)) + { + case LABEL_REF : + return 1; + case SYMBOL_REF : + return SYMBOL_REF_FLAG (op); + case PLUS : + /* Assume canonical format of symbol + constant. */ + case CONST : + return text_segment_operand (XEXP (op, 0)); + default : + return 0; + } +} + /* Return 1 if the operand is either a register or a memory operand that is not symbolic. */ @@ -256,55 +467,29 @@ move_pic_label (op, mode) return 1; return 0; } - -/* The rtx for the global offset table which is a special form - that *is* a position independent symbolic constant. */ -rtx pic_pc_rtx; - -/* Ensure that we are not using patterns that are not OK with PIC. */ - -int -check_pic (i) - int i; -{ - switch (flag_pic) - { - case 1: - if (GET_CODE (recog_operand[i]) == SYMBOL_REF - || (GET_CODE (recog_operand[i]) == CONST - && ! rtx_equal_p (pic_pc_rtx, recog_operand[i]))) - abort (); - case 2: - default: - return 1; - } -} - -/* Return true if X is an address which needs a temporary register when - reloaded while generating PIC code. */ int -pic_address_needs_scratch (x) - rtx x; +splittable_symbolic_memory_operand (op, mode) + rtx op; + enum machine_mode mode; { - /* An address which is a symbolic plus a non SMALL_INT needs a temp reg. */ - if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS - && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF - && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && ! SMALL_INT (XEXP (XEXP (x, 0), 1))) - return 1; - - return 0; + if (GET_CODE (op) != MEM) + return 0; + if (! symbolic_operand (XEXP (op, 0), Pmode)) + return 0; + return 1; } int -memop (op, mode) +splittable_immediate_memory_operand (op, mode) rtx op; enum machine_mode mode; { - if (GET_CODE (op) == MEM) - return (mode == VOIDmode || mode == GET_MODE (op)); - return 0; + if (GET_CODE (op) != MEM) + return 0; + if (! immediate_operand (XEXP (op, 0), Pmode)) + return 0; + return 1; } /* Return truth value of whether OP is EQ or NE. */ @@ -356,6 +541,22 @@ noov_compare_op (op, mode) return 1; } +/* Nonzero if OP is a comparison operator suitable for use in v9 + conditional move or branch on register contents instructions. */ + +int +v9_regcmp_op (op, mode) + register rtx op; + enum machine_mode mode; +{ + enum rtx_code code = GET_CODE (op); + + if (GET_RTX_CLASS (code) != '<') + return 0; + + return v9_regcmp_p (code); +} + /* Return 1 if this is a SIGN_EXTEND or ZERO_EXTEND operation. */ int @@ -410,7 +611,38 @@ arith_operand (op, mode) || (GET_CODE (op) == CONST_INT && SMALL_INT (op))); } -/* Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that +/* Return true if OP is a register, or is a CONST_INT that can fit in an 11 + bit immediate field. This is an acceptable SImode operand for the movcc + instructions. */ + +int +arith11_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_INT + && ((unsigned) (INTVAL (op) + 0x400) < 0x800))); +} + +/* Return true if OP is a register, or is a CONST_INT that can fit in an 10 + bit immediate field. This is an acceptable SImode operand for the movrcc + instructions. */ + +int +arith10_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_INT + && ((unsigned) (INTVAL (op) + 0x200) < 0x400))); +} + +/* Return true if OP is a register, is a CONST_INT that fits in a 13 bit + immediate field, or is a CONST_DOUBLE whose both parts fit in a 13 bit + immediate field. + v9: Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that can fit in a 13 bit immediate field. This is an acceptable DImode operand for most 3 address instructions. */ @@ -420,16 +652,64 @@ arith_double_operand (op, mode) enum machine_mode mode; { return (register_operand (op, mode) - || (GET_CODE (op) == CONST_DOUBLE - && (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode) + || (GET_CODE (op) == CONST_INT && SMALL_INT (op)) + || (! TARGET_V9 + && GET_CODE (op) == CONST_DOUBLE + && (unsigned) (CONST_DOUBLE_LOW (op) + 0x1000) < 0x2000 + && (unsigned) (CONST_DOUBLE_HIGH (op) + 0x1000) < 0x2000) + || (TARGET_V9 + && GET_CODE (op) == CONST_DOUBLE && (unsigned) (CONST_DOUBLE_LOW (op) + 0x1000) < 0x2000 && ((CONST_DOUBLE_HIGH (op) == -1 && (CONST_DOUBLE_LOW (op) & 0x1000) == 0x1000) || (CONST_DOUBLE_HIGH (op) == 0 - && (CONST_DOUBLE_LOW (op) & 0x1000) == 0))) + && (CONST_DOUBLE_LOW (op) & 0x1000) == 0)))); +} + +/* Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that + can fit in an 11 bit immediate field. This is an acceptable DImode + operand for the movcc instructions. */ +/* ??? Replace with arith11_operand? */ + +int +arith11_double_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_DOUBLE + && (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode) + && (unsigned) (CONST_DOUBLE_LOW (op) + 0x400) < 0x800 + && ((CONST_DOUBLE_HIGH (op) == -1 + && (CONST_DOUBLE_LOW (op) & 0x400) == 0x400) + || (CONST_DOUBLE_HIGH (op) == 0 + && (CONST_DOUBLE_LOW (op) & 0x400) == 0))) + || (GET_CODE (op) == CONST_INT + && (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode) + && (unsigned) (INTVAL (op) + 0x400) < 0x800)); +} + +/* Return true if OP is a register, or is a CONST_INT or CONST_DOUBLE that + can fit in an 10 bit immediate field. This is an acceptable DImode + operand for the movrcc instructions. */ +/* ??? Replace with arith10_operand? */ + +int +arith10_double_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return (register_operand (op, mode) + || (GET_CODE (op) == CONST_DOUBLE + && (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode) + && (unsigned) (CONST_DOUBLE_LOW (op) + 0x200) < 0x400 + && ((CONST_DOUBLE_HIGH (op) == -1 + && (CONST_DOUBLE_LOW (op) & 0x200) == 0x200) + || (CONST_DOUBLE_HIGH (op) == 0 + && (CONST_DOUBLE_LOW (op) & 0x200) == 0))) || (GET_CODE (op) == CONST_INT && (GET_MODE (op) == mode || GET_MODE (op) == VOIDmode) - && (unsigned) (INTVAL (op) + 0x1000) < 0x2000)); + && (unsigned) (INTVAL (op) + 0x200) < 0x400)); } /* Return truth value of whether OP is a integer which fits the @@ -444,6 +724,36 @@ small_int (op, mode) return (GET_CODE (op) == CONST_INT && SMALL_INT (op)); } +/* Recognize operand values for the umul instruction. That instruction sign + extends immediate values just like all other sparc instructions, but + interprets the extended result as an unsigned number. */ + +int +uns_small_int (op, mode) + rtx op; + enum machine_mode mode; +{ +#if HOST_BITS_PER_WIDE_INT > 32 + /* All allowed constants will fit a CONST_INT. */ + return (GET_CODE (op) == CONST_INT + && ((INTVAL (op) >= 0 && INTVAL (op) < 0x1000) + || (INTVAL (op) >= 0xFFFFF000 && INTVAL (op) < 0x100000000L))); +#else + return ((GET_CODE (op) == CONST_INT && (unsigned) INTVAL (op) < 0x1000) + || (GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_HIGH (op) == 0 + && (unsigned) CONST_DOUBLE_LOW (op) - 0xFFFFF000 < 0x1000)); +#endif +} + +int +uns_arith_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return register_operand (op, mode) || uns_small_int (op, mode); +} + /* Return truth value of statement that OP is a call-clobbered register. */ int clobbered_register (op, mode) @@ -454,7 +764,7 @@ clobbered_register (op, mode) } /* X and Y are two things to compare using CODE. Emit the compare insn and - return the rtx for register 0 in the proper mode. */ + return the rtx for the cc reg in the proper mode. */ rtx gen_compare_reg (code, x, y) @@ -462,13 +772,183 @@ gen_compare_reg (code, x, y) rtx x, y; { enum machine_mode mode = SELECT_CC_MODE (code, x, y); - rtx cc_reg = gen_rtx (REG, mode, 0); + rtx cc_reg; + + /* ??? We don't have movcc patterns so we cannot generate pseudo regs for the + fpcc regs (cse can't tell they're really call clobbered regs and will + remove a duplicate comparison even if there is an intervening function + call - it will then try to reload the cc reg via an int reg which is why + we need the movcc patterns). It is possible to provide the movcc + patterns by using the ldxfsr/stxfsr v9 insns. I tried it: you need two + registers (say %g1,%g5) and it takes about 6 insns. A better fix would be + to tell cse that CCFPE mode registers (even pseudos) are call + clobbered. */ + + /* ??? This is an experiment. Rather than making changes to cse which may + or may not be easy/clean, we do our own cse. This is possible because + we will generate hard registers. Cse knows they're call clobbered (it + doesn't know the same thing about pseudos). If we guess wrong, no big + deal, but if we win, great! */ + + if (TARGET_V9 && GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT) +#if 1 /* experiment */ + { + int reg; + /* We cycle through the registers to ensure they're all exercised. */ + static int next_fpcc_reg = 0; + /* Previous x,y for each fpcc reg. */ + static rtx prev_args[4][2]; + + /* Scan prev_args for x,y. */ + for (reg = 0; reg < 4; reg++) + if (prev_args[reg][0] == x && prev_args[reg][1] == y) + break; + if (reg == 4) + { + reg = next_fpcc_reg; + prev_args[reg][0] = x; + prev_args[reg][1] = y; + next_fpcc_reg = (next_fpcc_reg + 1) & 3; + } + cc_reg = gen_rtx (REG, mode, reg + 96); + } +#else + cc_reg = gen_reg_rtx (mode); +#endif /* ! experiment */ + else + cc_reg = gen_rtx (REG, mode, 0); emit_insn (gen_rtx (SET, VOIDmode, cc_reg, gen_rtx (COMPARE, mode, x, y))); return cc_reg; } + +/* This function is used for v9 only. + CODE is the code for an Scc's comparison. + OPERANDS[0] is the target of the Scc insn. + OPERANDS[1] is the value we compare against const0_rtx (which hasn't + been generated yet). + + This function is needed to turn + + (set (reg:SI 110) + (gt (reg:CCX 0 %g0) + (const_int 0))) + into + (set (reg:SI 110) + (gt:DI (reg:CCX 0 %g0) + (const_int 0))) + + IE: The instruction recognizer needs to see the mode of the comparison to + find the right instruction. We could use "gt:DI" right in the + define_expand, but leaving it out allows us to handle DI, SI, etc. + + We refer to the global sparc compare operands sparc_compare_op0 and + sparc_compare_op1. + + ??? Some of this is outdated as the scc insns set the mode of the + comparison now. + + ??? We optimize for the case where op1 is 0 and the comparison allows us to + use the "movrCC" insns. This reduces the generated code from three to two + insns. This way seems too brute force though. Is there a more elegant way + to achieve the same effect? + + Currently, this function always returns 1. ??? Can it ever fail? */ + +int +gen_v9_scc (compare_code, operands) + enum rtx_code compare_code; + register rtx *operands; +{ + rtx temp; + + if (GET_MODE_CLASS (GET_MODE (sparc_compare_op0)) == MODE_INT + && sparc_compare_op1 == const0_rtx + && (compare_code == EQ || compare_code == NE + || compare_code == LT || compare_code == LE + || compare_code == GT || compare_code == GE)) + { + /* Special case for op0 != 0. This can be done with one instruction if + op0 can be clobbered. We store to a temp, and then clobber the temp, + but the combiner will remove the first insn. */ + + if (compare_code == NE + && GET_MODE (operands[0]) == DImode + && GET_MODE (sparc_compare_op0) == DImode) + { + emit_insn (gen_rtx (SET, VOIDmode, operands[0], sparc_compare_op0)); + emit_insn (gen_rtx (SET, VOIDmode, operands[0], + gen_rtx (IF_THEN_ELSE, VOIDmode, + gen_rtx (compare_code, DImode, + sparc_compare_op0, const0_rtx), + const1_rtx, + operands[0]))); + return 1; + } + + emit_insn (gen_rtx (SET, VOIDmode, operands[0], const0_rtx)); + if (GET_MODE (sparc_compare_op0) != DImode) + { + temp = gen_reg_rtx (DImode); + convert_move (temp, sparc_compare_op0, 0); + } + else + { + temp = sparc_compare_op0; + } + emit_insn (gen_rtx (SET, VOIDmode, operands[0], + gen_rtx (IF_THEN_ELSE, VOIDmode, + gen_rtx (compare_code, DImode, + temp, const0_rtx), + const1_rtx, + operands[0]))); + return 1; + } + else + { + operands[1] = gen_compare_reg (compare_code, + sparc_compare_op0, sparc_compare_op1); + + switch (GET_MODE (operands[1])) + { + case CCmode : + case CCXmode : + case CCFPEmode : + case CCFPmode : + break; + default : + abort (); + } + emit_insn (gen_rtx (SET, VOIDmode, operands[0], const0_rtx)); + emit_insn (gen_rtx (SET, VOIDmode, operands[0], + gen_rtx (IF_THEN_ELSE, VOIDmode, + gen_rtx (compare_code, + GET_MODE (operands[1]), + operands[1], const0_rtx), + const1_rtx, operands[0]))); + return 1; + } +} + +/* Emit a conditional jump insn for the v9 architecture using comparison code + CODE and jump target LABEL. + This function exists to take advantage of the v9 brxx insns. */ + +void +emit_v9_brxx_insn (code, op0, label) + enum rtx_code code; + rtx op0, label; +{ + emit_jump_insn (gen_rtx (SET, VOIDmode, + pc_rtx, + gen_rtx (IF_THEN_ELSE, VOIDmode, + gen_rtx (code, GET_MODE (op0), + op0, const0_rtx), + gen_rtx (LABEL_REF, VOIDmode, label), + pc_rtx))); +} /* Return nonzero if a return peephole merging return with setting of output register is ok. */ @@ -593,18 +1073,56 @@ reg_unused_after (reg, insn) return 1; } +/* The rtx for the global offset table which is a special form + that *is* a position independent symbolic constant. */ +static rtx pic_pc_rtx; + +/* Ensure that we are not using patterns that are not OK with PIC. */ + +int +check_pic (i) + int i; +{ + switch (flag_pic) + { + case 1: + if (GET_CODE (recog_operand[i]) == SYMBOL_REF + || (GET_CODE (recog_operand[i]) == CONST + && ! rtx_equal_p (pic_pc_rtx, recog_operand[i]))) + abort (); + case 2: + default: + return 1; + } +} + +/* Return true if X is an address which needs a temporary register when + reloaded while generating PIC code. */ + +int +pic_address_needs_scratch (x) + rtx x; +{ + /* An address which is a symbolic plus a non SMALL_INT needs a temp reg. */ + if (GET_CODE (x) == CONST && GET_CODE (XEXP (x, 0)) == PLUS + && GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && ! SMALL_INT (XEXP (XEXP (x, 0), 1))) + return 1; + + return 0; +} + /* Legitimize PIC addresses. If the address is already position-independent, we return ORIG. Newly generated position-independent addresses go into a reg. This is REG if non zero, otherwise we allocate register(s) as - necessary. If this is called during reload, and we need a second temp - register, then we use SCRATCH, which is provided via the - SECONDARY_INPUT_RELOAD_CLASS mechanism. */ + necessary. */ rtx -legitimize_pic_address (orig, mode, reg, scratch) +legitimize_pic_address (orig, mode, reg) rtx orig; enum machine_mode mode; - rtx reg, scratch; + rtx reg; { if (GET_CODE (orig) == SYMBOL_REF) { @@ -676,10 +1194,9 @@ legitimize_pic_address (orig, mode, reg, if (GET_CODE (XEXP (orig, 0)) == PLUS) { - base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, - reg, 0); + base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg); offset = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode, - base == reg ? 0 : reg, 0); + base == reg ? 0 : reg); } else abort (); @@ -690,17 +1207,8 @@ legitimize_pic_address (orig, mode, reg, return plus_constant_for_output (base, INTVAL (offset)); else if (! reload_in_progress && ! reload_completed) offset = force_reg (Pmode, offset); - /* We can't create any new registers during reload, so use the - SCRATCH reg provided by the reload_insi pattern. */ - else if (scratch) - { - emit_move_insn (scratch, offset); - offset = scratch; - } else - /* If we reach here, then the SECONDARY_INPUT_RELOAD_CLASS - macro needs to be adjusted so that a scratch reg is provided - for this address. */ + /* If we reach here, then something is seriously wrong. */ abort (); } return gen_rtx (PLUS, Pmode, base, offset); @@ -762,8 +1270,16 @@ finalize_pic () gen_rtx (LABEL_REF, VOIDmode, l1), pc_rtx)))); - emit_insn (gen_rtx (SET, VOIDmode, pic_offset_table_rtx, - gen_rtx (HIGH, Pmode, pic_pc_rtx))); + if (Pmode == DImode) + emit_insn (gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (2, + gen_rtx (SET, VOIDmode, pic_offset_table_rtx, + gen_rtx (HIGH, Pmode, pic_pc_rtx)), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, Pmode, 1))))); + else + emit_insn (gen_rtx (SET, VOIDmode, pic_offset_table_rtx, + gen_rtx (HIGH, Pmode, pic_pc_rtx))); + emit_insn (gen_rtx (SET, VOIDmode, pic_offset_table_rtx, gen_rtx (LO_SUM, Pmode, @@ -786,57 +1302,24 @@ finalize_pic () emit_insn (gen_rtx (USE, VOIDmode, pic_offset_table_rtx)); } -/* For the SPARC, REG and REG+CONST is cost 0, REG+REG is cost 1, - and addresses involving symbolic constants are cost 2. - - We make REG+REG slightly more expensive because it might keep - a register live for longer than we might like. - - PIC addresses are very expensive. - - It is no coincidence that this has the same structure - as GO_IF_LEGITIMATE_ADDRESS. */ -int -sparc_address_cost (X) - rtx X; -{ -#if 0 - /* Handled before calling here. */ - if (GET_CODE (X) == REG) - { return 1; } -#endif - if (GET_CODE (X) == PLUS) - { - if (GET_CODE (XEXP (X, 0)) == REG - && GET_CODE (XEXP (X, 1)) == REG) - return 2; - return 1; - } - else if (GET_CODE (X) == LO_SUM) - return 1; - else if (GET_CODE (X) == HIGH) - return 2; - return 4; -} - /* Emit insns to move operands[1] into operands[0]. Return 1 if we have written out everything that needs to be done to do the move. Otherwise, return 0 and the caller will emit the move - normally. - - SCRATCH_REG if non zero can be used as a scratch register for the move - operation. It is provided by a SECONDARY_RELOAD_* macro if needed. */ + normally. */ int -emit_move_sequence (operands, mode, scratch_reg) +emit_move_sequence (operands, mode) rtx *operands; enum machine_mode mode; - rtx scratch_reg; { register rtx operand0 = operands[0]; register rtx operand1 = operands[1]; + if (CONSTANT_P (operand1) && flag_pic + && pic_address_needs_scratch (operand1)) + operands[1] = operand1 = legitimize_pic_address (operand1, mode, 0); + /* Handle most common case first: storing into a register. */ if (register_operand (operand0, mode)) { @@ -877,8 +1360,7 @@ emit_move_sequence (operands, mode, scra { rtx temp_reg = reload_in_progress ? operand0 : 0; - operands[1] = legitimize_pic_address (operand1, mode, temp_reg, - scratch_reg); + operands[1] = legitimize_pic_address (operand1, mode, temp_reg); } else if (GET_CODE (operand1) == CONST_INT ? (! SMALL_INT (operand1) @@ -903,8 +1385,34 @@ emit_move_sequence (operands, mode, scra rtx temp = ((reload_in_progress || mode == DImode) ? operand0 : gen_reg_rtx (mode)); - emit_insn (gen_rtx (SET, VOIDmode, temp, - gen_rtx (HIGH, mode, operand1))); + if (TARGET_V9 && mode == DImode) + { + int high_operand = 0; + + /* If the operand is already a HIGH, then remove the HIGH so + that we won't get duplicate HIGH operators in this insn. + Also, we must store the result into the original dest, + because that is where the following LO_SUM expects it. */ + if (GET_CODE (operand1) == HIGH) + { + operand1 = XEXP (operand1, 0); + high_operand = 1; + } + + emit_insn (gen_rtx (PARALLEL, VOIDmode, + gen_rtvec (2, + gen_rtx (SET, VOIDmode, temp, + gen_rtx (HIGH, mode, operand1)), + gen_rtx (CLOBBER, VOIDmode, gen_rtx (REG, DImode, 1))))); + + /* If this was a high operand, then we are now finished. */ + if (high_operand) + return 1; + } + else + emit_insn (gen_rtx (SET, VOIDmode, temp, + gen_rtx (HIGH, mode, operand1))); + operands[1] = gen_rtx (LO_SUM, mode, temp, operand1); } } @@ -946,18 +1454,15 @@ singlemove_string (operands) return "ld %1,%0"; else if (GET_CODE (operands[1]) == CONST_DOUBLE) { - int i; - union real_extract u; - union float_extract { float f; int i; } v; + REAL_VALUE_TYPE r; + long i; /* Must be SFmode, otherwise this doesn't make sense. */ if (GET_MODE (operands[1]) != SFmode) abort (); - bcopy (&CONST_DOUBLE_LOW (operands[1]), &u, sizeof u); - v.f = REAL_VALUE_TRUNCATE (SFmode, u.d); - i = v.i; - + REAL_VALUE_FROM_CONST_DOUBLE (r, operands[1]); + REAL_VALUE_TO_TARGET_SINGLE (r, i); operands[1] = gen_rtx (CONST_INT, VOIDmode, i); if (CONST_OK_FOR_LETTER_P (i, 'I')) @@ -1029,15 +1534,18 @@ mem_aligned_8 (mem) && (REGNO (base) == FRAME_POINTER_REGNUM || REGNO (base) == STACK_POINTER_REGNUM)) { - if ((INTVAL (offset) & 0x7) == 0) + if (((INTVAL (offset) - SPARC_STACK_BIAS) & 0x7) == 0) return 1; } /* Anything else we know is properly aligned unless TARGET_UNALIGNED_DOUBLES is true, in which case we can only assume that an access is aligned if - it is to an aggregate, it is to a constant address, or the address - involves a LO_SUM. */ - else if (! TARGET_UNALIGNED_DOUBLES || MEM_IN_STRUCT_P (mem) - || CONSTANT_P (addr) || GET_CODE (addr) == LO_SUM) + it is to a constant address, or the address involves a LO_SUM. + + We used to assume an address was aligned if MEM_IN_STRUCT_P was true. + That assumption was deleted so that gcc generated code can be used with + memory allocators that only guarantee 4 byte alignment. */ + else if (! TARGET_UNALIGNED_DOUBLES || CONSTANT_P (addr) + || GET_CODE (addr) == LO_SUM) return 1; /* An obviously unaligned address. */ @@ -1061,6 +1569,8 @@ output_move_double (operands) rtx latehalf[2]; rtx addreg0 = 0; rtx addreg1 = 0; + int highest_first = 0; + int no_addreg1_decrement = 0; /* First classify both operands. */ @@ -1118,26 +1628,72 @@ output_move_double (operands) else if (optype1 == OFFSOP) latehalf[1] = adj_offsettable_operand (op1, 4); else if (optype1 == CNSTOP) - split_double (op1, &operands[1], &latehalf[1]); + { + if (TARGET_V9) + { + if (arith_double_operand (op1, DImode)) + { + operands[1] = gen_rtx (CONST_INT, VOIDmode, + CONST_DOUBLE_LOW (op1)); + return "mov %1,%0"; + } + else + { + /* The only way to handle CONST_DOUBLEs or other 64 bit + constants here is to use a temporary, such as is done + for the V9 DImode sethi insn pattern. This is not + a practical solution, so abort if we reach here. + The md file should always force such constants to + memory. */ + abort (); + } + } + else + split_double (op1, &operands[1], &latehalf[1]); + } else latehalf[1] = op1; /* Easy case: try moving both words at once. Check for moving between an even/odd register pair and a memory location. */ if ((optype0 == REGOP && optype1 != REGOP && optype1 != CNSTOP - && (REGNO (op0) & 1) == 0) + && (TARGET_V9 || (REGNO (op0) & 1) == 0)) || (optype0 != REGOP && optype0 != CNSTOP && optype1 == REGOP - && (REGNO (op1) & 1) == 0)) + && (TARGET_V9 || (REGNO (op1) & 1) == 0))) { - register rtx mem; + register rtx mem,reg; if (optype0 == REGOP) - mem = op1; + mem = op1, reg = op0; else - mem = op0; + mem = op0, reg = op1; - if (mem_aligned_8 (mem)) - return (mem == op1 ? "ldd %1,%0" : "std %1,%0"); + /* In v9, ldd can be used for word aligned addresses, so technically + some of this logic is unneeded. We still avoid ldd if the address + is obviously unaligned though. */ + + if (mem_aligned_8 (mem) + /* If this is a floating point register higher than %f31, + then we *must* use an aligned load, since `ld' will not accept + the register number. */ + || (TARGET_V9 && REGNO (reg) >= 64)) + { + if (FP_REG_P (reg) || ! TARGET_V9) + return (mem == op1 ? "ldd %1,%0" : "std %1,%0"); + else + return (mem == op1 ? "ldx %1,%0" : "stx %1,%0"); + } + } + + if (TARGET_V9) + { + if (optype0 == REGOP && optype1 == REGOP) + { + if (FP_REG_P (op0)) + return "fmovd %1,%0"; + else + return "mov %1,%0"; + } } /* If the first move would clobber the source of the second one, @@ -1156,17 +1712,40 @@ output_move_double (operands) else if (optype0 == REGOP && optype1 != REGOP && reg_overlap_mentioned_p (op0, op1)) { - /* ??? This fails if the address is a double register address, each - of which is clobbered by operand 0. */ - /* Do the late half first. */ - output_asm_insn (singlemove_string (latehalf), latehalf); - /* Then clobber. */ - return singlemove_string (operands); + /* If both halves of dest are used in the src memory address, + add the two regs and put them in the low reg (op0). + Then it works to load latehalf first. */ + if (reg_mentioned_p (op0, XEXP (op1, 0)) + && reg_mentioned_p (latehalf[0], XEXP (op1, 0))) + { + rtx xops[2]; + xops[0] = latehalf[0]; + xops[1] = op0; + output_asm_insn ("add %1,%0,%1", xops); + operands[1] = gen_rtx (MEM, DImode, op0); + latehalf[1] = adj_offsettable_operand (operands[1], 4); + addreg1 = 0; + highest_first = 1; + } + /* Only one register in the dest is used in the src memory address, + and this is the first register of the dest, so we want to do + the late half first here also. */ + else if (! reg_mentioned_p (latehalf[0], XEXP (op1, 0))) + highest_first = 1; + /* Only one register in the dest is used in the src memory address, + and this is the second register of the dest, so we want to do + the late half last. If addreg1 is set, and addreg1 is the same + register as latehalf, then we must suppress the trailing decrement, + because it would clobber the value just loaded. */ + else if (addreg1 && reg_mentioned_p (addreg1, latehalf[0])) + no_addreg1_decrement = 1; } - /* Normal case: do the two words, low-numbered first. */ + /* Normal case: do the two words, low-numbered first. + Overlap case (highest_first set): do high-numbered word first. */ - output_asm_insn (singlemove_string (operands), operands); + if (! highest_first) + output_asm_insn (singlemove_string (operands), operands); /* Make any unoffsettable addresses point at high-numbered word. */ if (addreg0) @@ -1180,9 +1759,12 @@ output_move_double (operands) /* Undo the adds we just did. */ if (addreg0) output_asm_insn ("add %0,-0x4,%0", &addreg0); - if (addreg1) + if (addreg1 && ! no_addreg1_decrement) output_asm_insn ("add %0,-0x4,%0", &addreg1); + if (highest_first) + output_asm_insn (singlemove_string (operands), operands); + return ""; } @@ -1282,15 +1864,20 @@ output_move_quad (operands) } else if (optype1 == CNSTOP) { - /* This case isn't implemented yet, because there is no internal - representation for quad-word constants, and there is no split_quad - function. */ -#if 0 - split_quad (op1, &wordpart[0][1], &wordpart[1][1], - &wordpart[2][1], &wordpart[3][1]); -#else - abort (); -#endif + REAL_VALUE_TYPE r; + long l[4]; + + /* This only works for TFmode floating point constants. */ + if (GET_CODE (op1) != CONST_DOUBLE || GET_MODE (op1) != TFmode) + abort (); + + REAL_VALUE_FROM_CONST_DOUBLE (r, op1); + REAL_VALUE_TO_TARGET_LONG_DOUBLE (r, l); + + wordpart[0][1] = GEN_INT (l[0]); + wordpart[1][1] = GEN_INT (l[1]); + wordpart[2][1] = GEN_INT (l[2]); + wordpart[3][1] = GEN_INT (l[3]); } else { @@ -1301,27 +1888,38 @@ output_move_quad (operands) } /* Easy case: try moving the quad as two pairs. Check for moving between - an even/odd register pair and a memory location. */ + an even/odd register pair and a memory location. + Also handle new v9 fp regs here. */ /* ??? Should also handle the case of non-offsettable addresses here. We can at least do the first pair as a ldd/std, and then do the third and fourth words individually. */ if ((optype0 == REGOP && optype1 == OFFSOP && (REGNO (op0) & 1) == 0) || (optype0 == OFFSOP && optype1 == REGOP && (REGNO (op1) & 1) == 0)) { - rtx mem; + rtx mem, reg; if (optype0 == REGOP) - mem = op1; + mem = op1, reg = op0; else - mem = op0; + mem = op0, reg = op1; - if (mem_aligned_8 (mem)) + if (mem_aligned_8 (mem) + /* If this is a floating point register higher than %f31, + then we *must* use an aligned load, since `ld' will not accept + the register number. */ + || (TARGET_V9 && REGNO (reg) >= 64)) { + if (TARGET_V9 && FP_REG_P (reg)) + { + if ((REGNO (reg) & 3) != 0) + abort (); + return (mem == op1 ? "ldq %1,%0" : "stq %1,%0"); + } operands[2] = adj_offsettable_operand (mem, 8); if (mem == op1) - return "ldd %1,%0;ldd %2,%S0"; + return TARGET_V9 ? "ldx %1,%0;ldx %2,%R0" : "ldd %1,%0;ldd %2,%S0"; else - return "std %1,%0;std %S1,%2"; + return TARGET_V9 ? "stx %1,%0;stx %R1,%2" : "std %1,%0;std %S1,%2"; } } @@ -1355,7 +1953,7 @@ output_move_quad (operands) abort (); } - /* Normal case: move the four words in lowest to higest address order. */ + /* Normal case: move the four words in lowest to highest address order. */ output_asm_insn (singlemove_string (wordpart[0]), wordpart[0]); @@ -1405,7 +2003,12 @@ output_fp_move_double (operands) if (FP_REG_P (operands[0])) { if (FP_REG_P (operands[1])) - return "fmovs %1,%0\n\tfmovs %R1,%R0"; + { + if (TARGET_V9) + return "fmovd %1,%0"; + else + return "fmovs %1,%0\n\tfmovs %R1,%R0"; + } else if (GET_CODE (operands[1]) == REG) abort (); else @@ -1434,7 +2037,12 @@ output_fp_move_quad (operands) if (FP_REG_P (op0)) { if (FP_REG_P (op1)) - return "fmovs %1,%0\n\tfmovs %R1,%R0\n\tfmovs %S1,%S0\n\tfmovs %T1,%T0"; + { + if (TARGET_V9) + return "fmovq %1,%0"; + else + return "fmovs %1,%0\n\tfmovs %R1,%R0\n\tfmovs %S1,%S0\n\tfmovs %T1,%T0"; + } else if (GET_CODE (op1) == REG) abort (); else @@ -1481,6 +2089,8 @@ find_addr_reg (addr) abort (); } +#if 0 /* not currently used */ + void output_sized_memop (opname, mode, signedp) char *opname; @@ -1515,6 +2125,7 @@ output_move_with_extension (operands) else abort (); } +#endif /* not currently used */ #if 0 /* ??? These are only used by the movstrsi pattern, but we get better code @@ -1686,9 +2297,23 @@ output_block_move (operands) /* This case is currently not handled. Abort instead of generating bad code. */ - if (align > 4) + if (align > UNITS_PER_WORD) abort (); + if (TARGET_V9 && align >= 8) + { + for (i = (size >> 3) - 1; i >= 0; i--) + { + INTVAL (xoperands[2]) = (i << 3) + offset; + output_asm_insn ("ldx [%a1+%2],%%g1\n\tstx %%g1,[%a0+%2]", + xoperands); + } + offset += (size & ~0x7); + size = size & 0x7; + if (size == 0) + return ""; + } + if (align >= 4) { for (i = (size >> 2) - 1; i >= 0; i--) @@ -1795,8 +2420,10 @@ output_block_move (operands) { char pattern[200]; - register char *ld_suffix = (align == 1) ? "ub" : (align == 2) ? "uh" : ""; - register char *st_suffix = (align == 1) ? "b" : (align == 2) ? "h" : ""; + register char *ld_suffix = ((align == 1) ? "ub" : (align == 2) ? "uh" + : (align == 8 && TARGET_V9) ? "x" : ""); + register char *st_suffix = ((align == 1) ? "b" : (align == 2) ? "h" + : (align == 8 && TARGET_V9) ? "x" : ""); sprintf (pattern, "ld%s [%%1+%%2],%%%%g1\n\tsubcc %%2,%%4,%%2\n\tbge %s\n\tst%s %%%%g1,[%%0+%%2]\n%s:", ld_suffix, &label3[1], st_suffix, &label5[1]); output_asm_insn (pattern, xoperands); @@ -1880,12 +2507,12 @@ output_scc_insn (operands, insn) if (final_sequence) { strcpy (string, "mov 0,%0\n\t"); - strcat (string, output_cbranch (operands[1], 2, 0, 1, 0)); + strcat (string, output_cbranch (operands[1], 0, 2, 0, 1, 0)); strcat (string, "\n\tmov 1,%0"); } else { - strcpy (string, output_cbranch (operands[1], 2, 0, 1, 0)); + strcpy (string, output_cbranch (operands[1], 0, 2, 0, 1, 0)); strcat (string, "\n\tmov 1,%0\n\tmov 0,%0"); } @@ -1895,40 +2522,60 @@ output_scc_insn (operands, insn) return string; } -/* Vectors to keep interesting information about registers where - it can easily be got. */ +/* Vectors to keep interesting information about registers where it can easily + be got. We use to use the actual mode value as the bit number, but there + are more than 32 modes now. Instead we use two tables: one indexed by + hard register number, and one indexed by mode. */ + +/* The purpose of sparc_mode_class is to shrink the range of modes so that + they all fit (as bit numbers) in a 32 bit word (again). Each real mode is + mapped into one sparc_mode_class mode. */ + +enum sparc_mode_class { + C_MODE, CCFP_MODE, + S_MODE, D_MODE, T_MODE, O_MODE, + SF_MODE, DF_MODE, TF_MODE, OF_MODE +}; /* Modes for condition codes. */ -#define C_MODES \ - ((1 << (int) CCmode) | (1 << (int) CC_NOOVmode) \ - | (1 << (int) CCFPmode) | (1 << (int) CCFPEmode)) - -/* Modes for single-word (and smaller) quantities. */ -#define S_MODES \ - ((1 << (int) QImode) | (1 << (int) HImode) | (1 << (int) SImode) \ - | (1 << (int) QFmode) | (1 << (int) HFmode) | (1 << (int) SFmode) \ - | (1 << (int) CQImode) | (1 << (int) CHImode)) - -/* Modes for double-word (and smaller) quantities. */ -#define D_MODES \ - (S_MODES | (1 << (int) DImode) | (1 << (int) DFmode) \ - | (1 << (int) CSImode) | (1 << (int) SCmode)) - -/* Modes for quad-word quantities. */ -#define T_MODES \ - (D_MODES | (1 << (int) TImode) | (1 << (int) TFmode) \ - | (1 << (int) DCmode) | (1 << (int) CDImode)) +#define C_MODES ((1 << (int) C_MODE) | (1 << (int) CCFP_MODE)) +#define CCFP_MODES (1 << (int) CCFP_MODE) + +/* Modes for single-word and smaller quantities. */ +#define S_MODES ((1 << (int) S_MODE) | (1 << (int) SF_MODE)) + +/* Modes for double-word and smaller quantities. */ +#define D_MODES (S_MODES | (1 << (int) D_MODE) | (1 << DF_MODE)) + +/* Modes for quad-word and smaller quantities. */ +#define T_MODES (D_MODES | (1 << (int) T_MODE) | (1 << (int) TF_MODE)) /* Modes for single-float quantities. We must allow any single word or smaller quantity. This is because the fix/float conversion instructions take integer inputs/outputs from the float registers. */ #define SF_MODES (S_MODES) -/* Modes for double-float quantities. */ -#define DF_MODES (SF_MODES | (1 << (int) DFmode) | (1 << (int) SCmode)) +/* Modes for double-float and smaller quantities. */ +#define DF_MODES (S_MODES | D_MODES) + +/* ??? Sparc64 fp regs cannot hold DImode values. */ +#define DF_MODES64 (SF_MODES | DF_MODE /* | D_MODE*/) + +/* Modes for double-float only quantities. */ +/* ??? Sparc64 fp regs cannot hold DImode values. */ +#define DF_ONLY_MODES ((1 << (int) DF_MODE) /*| (1 << (int) D_MODE)*/) -/* Modes for quad-float quantities. */ -#define TF_MODES (DF_MODES | (1 << (int) TFmode) | (1 << (int) DCmode)) +/* Modes for double-float and larger quantities. */ +#define DF_UP_MODES (DF_ONLY_MODES | TF_ONLY_MODES) + +/* Modes for quad-float only quantities. */ +#define TF_ONLY_MODES (1 << (int) TF_MODE) + +/* Modes for quad-float and smaller quantities. */ +#define TF_MODES (DF_MODES | TF_ONLY_MODES) + +/* ??? Sparc64 fp regs cannot hold DImode values. */ +#define TF_MODES64 (DF_MODES64 | TF_ONLY_MODES) /* Value is 1 if register/mode pair is acceptable on sparc. The funny mixture of D and T modes is because integer operations @@ -1936,7 +2583,10 @@ output_scc_insn (operands, insn) registers can hold quadword quantities (except %o4 and %i4 because they cross fixed registers. */ -int hard_regno_mode_ok[] = { +/* This points to either the 32 bit or the 64 bit version. */ +int *hard_regno_mode_classes; + +static int hard_32bit_mode_classes[] = { C_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, D_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, T_MODES, S_MODES, D_MODES, S_MODES, @@ -1945,116 +2595,299 @@ int hard_regno_mode_ok[] = { TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES, - TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES}; + TF_MODES, SF_MODES, DF_MODES, SF_MODES, TF_MODES, SF_MODES, DF_MODES, SF_MODES, +}; + +static int hard_64bit_mode_classes[] = { + C_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, + T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, + T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, + T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, T_MODES, D_MODES, + + TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, + TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, + TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, + TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, TF_MODES64, SF_MODES, DF_MODES64, SF_MODES, + + /* The remaining registers do not exist on a non-v9 sparc machine. + FP regs f32 to f63. Only the even numbered registers actually exist, + and none can hold SFmode/SImode values. */ + DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0, + DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0, + DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0, + DF_UP_MODES, 0, DF_ONLY_MODES, 0, DF_UP_MODES, 0, DF_ONLY_MODES, 0, + + /* %fcc[0123] */ + CCFP_MODE, CCFP_MODE, CCFP_MODE, CCFP_MODE +}; + +int sparc_mode_class [NUM_MACHINE_MODES]; + +static void +sparc_init_modes () +{ + int i; + + sparc_arch_type = TARGET_V9 ? ARCH_64BIT : ARCH_32BIT; + + for (i = 0; i < NUM_MACHINE_MODES; i++) + { + switch (GET_MODE_CLASS (i)) + { + case MODE_INT: + case MODE_PARTIAL_INT: + case MODE_COMPLEX_INT: + if (GET_MODE_SIZE (i) <= 4) + sparc_mode_class[i] = 1 << (int) S_MODE; + else if (GET_MODE_SIZE (i) == 8) + sparc_mode_class[i] = 1 << (int) D_MODE; + else if (GET_MODE_SIZE (i) == 16) + sparc_mode_class[i] = 1 << (int) T_MODE; + else if (GET_MODE_SIZE (i) == 32) + sparc_mode_class[i] = 1 << (int) O_MODE; + else + sparc_mode_class[i] = 0; + break; + case MODE_FLOAT: + case MODE_COMPLEX_FLOAT: + if (GET_MODE_SIZE (i) <= 4) + sparc_mode_class[i] = 1 << (int) SF_MODE; + else if (GET_MODE_SIZE (i) == 8) + sparc_mode_class[i] = 1 << (int) DF_MODE; + else if (GET_MODE_SIZE (i) == 16) + sparc_mode_class[i] = 1 << (int) TF_MODE; + else if (GET_MODE_SIZE (i) == 32) + sparc_mode_class[i] = 1 << (int) OF_MODE; + else + sparc_mode_class[i] = 0; + break; + case MODE_CC: + default: + /* mode_class hasn't been initialized yet for EXTRA_CC_MODES, so + we must explicitly check for them here. */ + if (i == (int) CCFPmode || i == (int) CCFPEmode) + sparc_mode_class[i] = 1 << (int) CCFP_MODE; + else if (i == (int) CCmode || i == (int) CC_NOOVmode +#ifdef SPARCV9 + || i == (int) CCXmode + || i == (int) CCX_NOOVmode +#endif + ) + sparc_mode_class[i] = 1 << (int) C_MODE; + else + sparc_mode_class[i] = 0; + break; + } + } + + if (TARGET_V9) + hard_regno_mode_classes = hard_64bit_mode_classes; + else + hard_regno_mode_classes = hard_32bit_mode_classes; +} +/* Save non call used registers from LOW to HIGH at BASE+OFFSET. + N_REGS is the number of 4-byte regs saved thus far. This applies even to + v9 int regs as it simplifies the code. */ + #ifdef __GNUC__ -inline +__inline__ #endif static int -save_regs (file, low, high, base, offset, n_fregs) +save_regs (file, low, high, base, offset, n_regs) FILE *file; int low, high; char *base; int offset; - int n_fregs; + int n_regs; { int i; - for (i = low; i < high; i += 2) + if (TARGET_V9 && high <= 32) { - if (regs_ever_live[i] && ! call_used_regs[i]) - if (regs_ever_live[i+1] && ! call_used_regs[i+1]) - fprintf (file, "\tstd %s,[%s+%d]\n", - reg_names[i], base, offset + 4 * n_fregs), - n_fregs += 2; - else - fprintf (file, "\tst %s,[%s+%d]\n", - reg_names[i], base, offset + 4 * n_fregs), - n_fregs += 2; - else if (regs_ever_live[i+1] && ! call_used_regs[i+1]) - fprintf (file, "\tst %s,[%s+%d]\n", - reg_names[i+1], base, offset + 4 * n_fregs), - n_fregs += 2; + for (i = low; i < high; i++) + { + if (regs_ever_live[i] && ! call_used_regs[i]) + fprintf (file, "\tstx %s,[%s+%d]\n", + reg_names[i], base, offset + 4 * n_regs), + n_regs += 2; + } } - return n_fregs; + else + { + for (i = low; i < high; i += 2) + { + if (regs_ever_live[i] && ! call_used_regs[i]) + if (regs_ever_live[i+1] && ! call_used_regs[i+1]) + fprintf (file, "\tstd %s,[%s+%d]\n", + reg_names[i], base, offset + 4 * n_regs), + n_regs += 2; + else + fprintf (file, "\tst %s,[%s+%d]\n", + reg_names[i], base, offset + 4 * n_regs), + n_regs += 2; + else if (regs_ever_live[i+1] && ! call_used_regs[i+1]) + fprintf (file, "\tst %s,[%s+%d]\n", + reg_names[i+1], base, offset + 4 * n_regs + 4), + n_regs += 2; + } + } + return n_regs; } +/* Restore non call used registers from LOW to HIGH at BASE+OFFSET. + + N_REGS is the number of 4-byte regs saved thus far. This applies even to + v9 int regs as it simplifies the code. */ + #ifdef __GNUC__ -inline +__inline__ #endif static int -restore_regs (file, low, high, base, offset, n_fregs) +restore_regs (file, low, high, base, offset, n_regs) FILE *file; int low, high; char *base; int offset; + int n_regs; { int i; - for (i = low; i < high; i += 2) + if (TARGET_V9 && high <= 32) { - if (regs_ever_live[i] && ! call_used_regs[i]) - if (regs_ever_live[i+1] && ! call_used_regs[i+1]) - fprintf (file, "\tldd [%s+%d], %s\n", - base, offset + 4 * n_fregs, reg_names[i]), - n_fregs += 2; - else - fprintf (file, "\tld [%s+%d],%s\n", - base, offset + 4 * n_fregs, reg_names[i]), - n_fregs += 2; - else if (regs_ever_live[i+1] && ! call_used_regs[i+1]) - fprintf (file, "\tld [%s+%d],%s\n", - base, offset + 4 * n_fregs, reg_names[i+1]), - n_fregs += 2; + for (i = low; i < high; i++) + { + if (regs_ever_live[i] && ! call_used_regs[i]) + fprintf (file, "\tldx [%s+%d], %s\n", + base, offset + 4 * n_regs, reg_names[i]), + n_regs += 2; + } } - return n_fregs; + else + { + for (i = low; i < high; i += 2) + { + if (regs_ever_live[i] && ! call_used_regs[i]) + if (regs_ever_live[i+1] && ! call_used_regs[i+1]) + fprintf (file, "\tldd [%s+%d], %s\n", + base, offset + 4 * n_regs, reg_names[i]), + n_regs += 2; + else + fprintf (file, "\tld [%s+%d],%s\n", + base, offset + 4 * n_regs, reg_names[i]), + n_regs += 2; + else if (regs_ever_live[i+1] && ! call_used_regs[i+1]) + fprintf (file, "\tld [%s+%d],%s\n", + base, offset + 4 * n_regs + 4, reg_names[i+1]), + n_regs += 2; + } + } + return n_regs; } /* Static variables we want to share between prologue and epilogue. */ -/* Number of live floating point registers needed to be saved. */ -static int num_fregs; +/* Number of live general or floating point registers needed to be saved + (as 4-byte quantities). This is only done if TARGET_EPILOGUE. */ +static int num_gfregs; + +/* Compute the frame size required by the function. This function is called + during the reload pass and also by output_function_prologue(). */ int compute_frame_size (size, leaf_function) int size; int leaf_function; { - int fregs_ever_live = 0; - int n_fregs = 0, i; + int n_regs = 0, i; int outgoing_args_size = (current_function_outgoing_args_size - + REG_PARM_STACK_SPACE (current_function_decl)); - - apparent_fsize = ((size) + 7 - STARTING_FRAME_OFFSET) & -8; - for (i = 32; i < FIRST_PSEUDO_REGISTER; i += 2) - fregs_ever_live |= regs_ever_live[i]|regs_ever_live[i+1]; +#ifndef SPARCV9 + + REG_PARM_STACK_SPACE (current_function_decl) +#endif + ); - if (TARGET_EPILOGUE && fregs_ever_live) + if (TARGET_EPILOGUE) { - for (i = 32; i < FIRST_PSEUDO_REGISTER; i += 2) + /* N_REGS is the number of 4-byte regs saved thus far. This applies + even to v9 int regs to be consistent with save_regs/restore_regs. */ + + if (TARGET_V9) + { + for (i = 0; i < 8; i++) + if (regs_ever_live[i] && ! call_used_regs[i]) + n_regs += 2; + } + else + { + for (i = 0; i < 8; i += 2) + if ((regs_ever_live[i] && ! call_used_regs[i]) + || (regs_ever_live[i+1] && ! call_used_regs[i+1])) + n_regs += 2; + } + + for (i = 32; i < (TARGET_V9 ? 96 : 64); i += 2) if ((regs_ever_live[i] && ! call_used_regs[i]) || (regs_ever_live[i+1] && ! call_used_regs[i+1])) - n_fregs += 2; + n_regs += 2; } /* Set up values for use in `function_epilogue'. */ - num_fregs = n_fregs; + num_gfregs = n_regs; - apparent_fsize += (outgoing_args_size+7) & -8; - if (leaf_function && n_fregs == 0 - && apparent_fsize == (REG_PARM_STACK_SPACE (current_function_decl) - - STARTING_FRAME_OFFSET)) - apparent_fsize = 0; - - actual_fsize = apparent_fsize + n_fregs*4; + if (leaf_function && n_regs == 0 + && size == 0 && current_function_outgoing_args_size == 0) + { + actual_fsize = apparent_fsize = 0; + } + else + { + /* We subtract STARTING_FRAME_OFFSET, remember it's negative. + The stack bias (if any) is taken out to undo its effects. */ + apparent_fsize = (size - STARTING_FRAME_OFFSET + SPARC_STACK_BIAS + 7) & -8; + apparent_fsize += n_regs * 4; + actual_fsize = apparent_fsize + ((outgoing_args_size + 7) & -8); + } /* Make sure nothing can clobber our register windows. If a SAVE must be done, or there is a stack-local variable, - the register window area must be allocated. */ + the register window area must be allocated. + ??? For v9 we need an additional 8 bytes of reserved space, apparently + it's needed by v8 as well. */ if (leaf_function == 0 || size > 0) - actual_fsize += (16 * UNITS_PER_WORD)+8; + actual_fsize += (16 * UNITS_PER_WORD) + 8; - return actual_fsize; + return SPARC_STACK_ALIGN (actual_fsize); +} + +/* Build a (32 bit) big number in a register. */ +/* ??? We may be able to use the set macro here too. */ + +static void +build_big_number (file, num, reg) + FILE *file; + int num; + char *reg; +{ + if (num >= 0 || ! TARGET_V9) + { + fprintf (file, "\tsethi %%hi(%d),%s\n", num, reg); + if ((num & 0x3ff) != 0) + fprintf (file, "\tor %s,%%lo(%d),%s\n", reg, num, reg); + } + else /* num < 0 && TARGET_V9 */ + { + /* Sethi does not sign extend, so we must use a little trickery + to use it for negative numbers. Invert the constant before + loading it in, then use xor immediate to invert the loaded bits + (along with the upper 32 bits) to the desired constant. This + works because the sethi and immediate fields overlap. */ + int asize = num; + int inv = ~asize; + int low = -0x400 + (asize & 0x3FF); + + fprintf (file, "\tsethi %%hi(%d),%s\n\txor %s,%d,%s\n", + inv, reg, reg, low, reg); + } } /* Output code for the function prologue. */ @@ -2065,18 +2898,24 @@ output_function_prologue (file, size, le int size; int leaf_function; { - /* ??? This should be %sp+actual_fsize for a leaf function. I think it - works only because it is never used. */ - if (leaf_function) - frame_base_name = "%sp+80"; - else - frame_base_name = "%fp"; - /* Need to use actual_fsize, since we are also allocating space for our callee (and our own register save area). */ actual_fsize = compute_frame_size (size, leaf_function); + if (leaf_function) + { + frame_base_name = "%sp"; + frame_base_offset = actual_fsize + SPARC_STACK_BIAS; + } + else + { + frame_base_name = "%fp"; + frame_base_offset = SPARC_STACK_BIAS; + } + + /* This is only for the human reader. */ fprintf (file, "\t!#PROLOGUE# 0\n"); + if (actual_fsize == 0) /* do nothing. */ ; else if (actual_fsize <= 4096) @@ -2102,44 +2941,50 @@ output_function_prologue (file, size, le } else { + build_big_number (file, -actual_fsize, "%g1"); if (! leaf_function) - { - fprintf (file, "\tsethi %%hi(-%d),%%g1\n", actual_fsize); - if ((actual_fsize & 0x3ff) != 0) - fprintf (file, "\tor %%g1,%%lo(-%d),%%g1\n", actual_fsize); - fprintf (file, "\tsave %%sp,%%g1,%%sp\n"); - } + fprintf (file, "\tsave %%sp,%%g1,%%sp\n"); else - { - fprintf (file, "\tsethi %%hi(-%d),%%g1\n", actual_fsize); - if ((actual_fsize & 0x3ff) != 0) - fprintf (file, "\tor %%g1,%%lo(-%d),%%g1\n", actual_fsize); - fprintf (file, "\tadd %%sp,%%g1,%%sp\n"); - } + fprintf (file, "\tadd %%sp,%%g1,%%sp\n"); } /* If doing anything with PIC, do it now. */ if (! flag_pic) fprintf (file, "\t!#PROLOGUE# 1\n"); - /* Figure out where to save any special registers. */ - if (num_fregs) + /* Call saved registers are saved just above the outgoing argument area. */ + if (num_gfregs) { - int offset, n_fregs = num_fregs; + int offset, n_regs; + char *base; - /* ??? This should always be -apparent_fsize. */ - if (! leaf_function) - offset = -apparent_fsize; + offset = -apparent_fsize + frame_base_offset; + if (offset < -4096 || offset + num_gfregs * 4 > 4096) + { + /* ??? This might be optimized a little as %g1 might already have a + value close enough that a single add insn will do. */ + /* ??? Although, all of this is probably only a temporary fix + because if %g1 can hold a function result, then + output_function_epilogue will lose (the result will get + clobbered). */ + build_big_number (file, offset, "%g1"); + fprintf (file, "\tadd %s,%%g1,%%g1\n", frame_base_name); + base = "%g1"; + offset = 0; + } else - offset = 0; + { + base = frame_base_name; + } if (TARGET_EPILOGUE && ! leaf_function) - n_fregs = save_regs (file, 0, 16, frame_base_name, offset, 0); + /* ??? Originally saved regs 0-15 here. */ + n_regs = save_regs (file, 0, 8, base, offset, 0); else if (leaf_function) - n_fregs = save_regs (file, 0, 32, frame_base_name, offset, 0); + /* ??? Originally saved regs 0-31 here. */ + n_regs = save_regs (file, 0, 8, base, offset, 0); if (TARGET_EPILOGUE) - save_regs (file, 32, FIRST_PSEUDO_REGISTER, - frame_base_name, offset, n_fregs); + save_regs (file, 32, TARGET_V9 ? 96 : 64, base, offset, n_regs); } leaf_label = 0; @@ -2167,30 +3012,40 @@ output_function_epilogue (file, size, le final_scan_insn (get_last_insn (), file, 0, 0, 1); } - if (num_fregs) + /* Restore any call saved registers. */ + if (num_gfregs) { - int offset, n_fregs = num_fregs; + int offset, n_regs; + char *base; - /* ??? This should always be -apparent_fsize. */ - if (! leaf_function) - offset = -apparent_fsize; + offset = -apparent_fsize + frame_base_offset; + if (offset < -4096 || offset + num_gfregs * 4 > 4096 - 8 /*double*/) + { + build_big_number (file, offset, "%g1"); + fprintf (file, "\tadd %s,%%g1,%%g1\n", frame_base_name); + base = "%g1"; + offset = 0; + } else - offset = 0; + { + base = frame_base_name; + } if (TARGET_EPILOGUE && ! leaf_function) - n_fregs = restore_regs (file, 0, 16, frame_base_name, offset, 0); + /* ??? Originally saved regs 0-15 here. */ + n_regs = restore_regs (file, 0, 8, base, offset, 0); else if (leaf_function) - n_fregs = restore_regs (file, 0, 32, frame_base_name, offset, 0); + /* ??? Originally saved regs 0-31 here. */ + n_regs = restore_regs (file, 0, 8, base, offset, 0); if (TARGET_EPILOGUE) - restore_regs (file, 32, FIRST_PSEUDO_REGISTER, - frame_base_name, offset, n_fregs); + restore_regs (file, 32, TARGET_V9 ? 96 : 64, base, offset, n_regs); } /* Work out how to skip the caller's unimp instruction if required. */ if (leaf_function) - ret = (current_function_returns_struct ? "jmp %o7+12" : "retl"); + ret = (SKIP_CALLERS_UNIMP_P ? "jmp %o7+12" : "retl"); else - ret = (current_function_returns_struct ? "jmp %i7+12" : "ret"); + ret = (SKIP_CALLERS_UNIMP_P ? "jmp %i7+12" : "ret"); if (TARGET_EPILOGUE || leaf_label) { @@ -2245,9 +3100,86 @@ output_function_epilogue (file, size, le } } -/* Do what is necessary for `va_start'. The argument is ignored; - We look at the current function to determine if stdarg or varargs - is used and return the address of the first unnamed parameter. */ +/* Do what is necessary for `va_start'. The argument is ignored. + !v9: We look at the current function to determine if stdarg or varargs + is used and return the address of the first unnamed parameter. + v9: We save the argument integer and floating point regs in a buffer, and + return the address of this buffer. The rest is handled in va-sparc.h. */ +/* ??? This is currently conditioned on #ifdef SPARCV9 because + current_function_args_info is different in each compiler. */ + +#ifdef SPARCV9 + +rtx +sparc_builtin_saveregs (arglist) + tree arglist; +{ + tree fntype = TREE_TYPE (current_function_decl); + /* First unnamed integer register. */ + int first_intreg = current_function_args_info.arg_count[(int) SPARC_ARG_INT]; + /* Number of integer registers we need to save. */ + int n_intregs = MAX (0, NPARM_REGS (SImode) - first_intreg); + /* First unnamed SFmode float reg (no, you can't pass SFmode floats as + unnamed arguments, we just number them that way). We must round up to + the next double word float reg - that is the first one to save. */ + int first_floatreg = current_function_args_info.arg_count[(int) SPARC_ARG_FLOAT] + 1 & ~1; + /* Number of SFmode float regs to save. */ + int n_floatregs = MAX (0, NPARM_REGS (SFmode) - first_floatreg); + int ptrsize = GET_MODE_SIZE (Pmode); + rtx valist, regbuf, fpregs; + int bufsize, adjust, regno; + + /* Allocate block of memory for the regs. + We only allocate as much as we need, but we must ensure quadword float + regs are stored with the appropriate alignment. */ + /* ??? If n_intregs + n_floatregs == 0, should we allocate at least 1 byte? + Or can assign_stack_local accept a 0 SIZE argument? */ + + bufsize = (n_intregs * UNITS_PER_WORD) + (n_floatregs * (UNITS_PER_WORD / 2)); + /* Add space in front of the int regs to ensure proper alignment of quadword + fp regs. We must add the space in front because va_start assumes this. */ + if (n_floatregs >= 4) + adjust = ((n_intregs + first_floatreg / 2) % 2) * UNITS_PER_WORD; + else + adjust = 0; + + regbuf = assign_stack_local (BLKmode, bufsize + adjust, + GET_MODE_BITSIZE (TFmode)); + regbuf = gen_rtx (MEM, BLKmode, plus_constant (XEXP (regbuf, 0), adjust)); + MEM_IN_STRUCT_P (regbuf) = 1; + + /* Save int args. + This is optimized to only save the regs that are necessary. Explicitly + named args need not be saved. */ + + if (n_intregs > 0) + move_block_from_reg (BASE_INCOMING_ARG_REG (SImode) + first_intreg, + regbuf, n_intregs, n_intregs * UNITS_PER_WORD); + + /* Save float args. + This is optimized to only save the regs that are necessary. Explicitly + named args need not be saved. + We explicitly build a pointer to the buffer because it halves the insn + count when not optimizing (otherwise the pointer is built for each reg + saved). */ + + fpregs = gen_reg_rtx (Pmode); + emit_move_insn (fpregs, plus_constant (XEXP (regbuf, 0), + n_intregs * UNITS_PER_WORD)); + for (regno = first_floatreg; regno < NPARM_REGS (SFmode); regno += 2) + emit_move_insn (gen_rtx (MEM, DFmode, + plus_constant (fpregs, + GET_MODE_SIZE (SFmode) + * (regno - first_floatreg))), + gen_rtx (REG, DFmode, + BASE_INCOMING_ARG_REG (DFmode) + regno)); + + /* Return the address of the regbuf. */ + + return XEXP (regbuf, 0); +} + +#else /* ! SPARCV9 */ rtx sparc_builtin_saveregs (arglist) @@ -2267,7 +3199,7 @@ sparc_builtin_saveregs (arglist) first_reg = 0; #endif - for (regno = first_reg; regno < NPARM_REGS; regno++) + for (regno = first_reg; regno < NPARM_REGS (SImode); regno++) emit_move_insn (gen_rtx (MEM, word_mode, gen_rtx (PLUS, Pmode, frame_pointer_rtx, @@ -2283,11 +3215,16 @@ sparc_builtin_saveregs (arglist) return address; } + +#endif /* ! SPARCV9 */ /* Return the string to output a conditional branch to LABEL, which is the operand number of the label. OP is the conditional expression. The mode of register 0 says what kind of comparison we made. + FP_COND_REG indicates which fp condition code register to use if this is + a floating point branch. + REVERSED is non-zero if we should reverse the sense of the comparison. ANNUL is non-zero if we should generate an annulling branch. @@ -2295,22 +3232,29 @@ sparc_builtin_saveregs (arglist) NOOP is non-zero if we have to follow this branch by a noop. */ char * -output_cbranch (op, label, reversed, annul, noop) - rtx op; +output_cbranch (op, fp_cond_reg, label, reversed, annul, noop) + rtx op, fp_cond_reg; int label; int reversed, annul, noop; { static char string[20]; enum rtx_code code = GET_CODE (op); enum machine_mode mode = GET_MODE (XEXP (op, 0)); - static char labelno[] = " %lX"; + static char v8_labelno[] = " %lX"; + static char v9_icc_labelno[] = " %%icc,%lX"; + static char v9_xcc_labelno[] = " %%xcc,%lX"; + static char v9_fcc_labelno[] = " %%fccX,%lY"; + char *labelno; + int labeloff; - /* ??? FP branches can not be preceded by another floating point insn. + /* ??? !v9: FP branches cannot be preceded by another floating point insn. Because there is currently no concept of pre-delay slots, we can fix this only by always emitting a nop before a floating point branch. */ - if (mode == CCFPmode || mode == CCFPEmode) + if ((mode == CCFPmode || mode == CCFPEmode) && ! TARGET_V9) strcpy (string, "nop\n\t"); + else + string[0] = '\0'; /* If not floating-point or if EQ or NE, we can just reverse the code. */ if (reversed @@ -2407,7 +3351,109 @@ output_cbranch (op, label, reversed, ann if (annul) strcat (string, ",a"); - labelno[3] = label + '0'; + /* ??? If v9, optional prediction bit ",pt" or ",pf" goes here. */ + + if (! TARGET_V9) + { + labeloff = 3; + labelno = v8_labelno; + } + else + { + labeloff = 9; + if (mode == CCFPmode || mode == CCFPEmode) + { + labeloff = 10; + labelno = v9_fcc_labelno; + /* Set the char indicating the number of the fcc reg to use. */ + labelno[6] = REGNO (fp_cond_reg) - 96 + '0'; + } + else if (mode == CCXmode || mode == CCX_NOOVmode) + labelno = v9_xcc_labelno; + else + labelno = v9_icc_labelno; + } + /* Set the char indicating the number of the operand containing the + label_ref. */ + labelno[labeloff] = label + '0'; + strcat (string, labelno); + + if (noop) + strcat (string, "\n\tnop"); + + return string; +} + +/* Return the string to output a conditional branch to LABEL, testing + register REG. LABEL is the operand number of the label; REG is the + operand number of the reg. OP is the conditional expression. The mode + of REG says what kind of comparison we made. + + REVERSED is non-zero if we should reverse the sense of the comparison. + + ANNUL is non-zero if we should generate an annulling branch. + + NOOP is non-zero if we have to follow this branch by a noop. */ + +char * +output_v9branch (op, reg, label, reversed, annul, noop) + rtx op; + int reg, label; + int reversed, annul, noop; +{ + static char string[20]; + enum rtx_code code = GET_CODE (op); + enum machine_mode mode = GET_MODE (XEXP (op, 0)); + static char labelno[] = " %X,%lX"; + + /* If not floating-point or if EQ or NE, we can just reverse the code. */ + if (reversed) + code = reverse_condition (code), reversed = 0; + + /* Only 64 bit versions of these instructions exist. */ + if (mode != DImode) + abort (); + + /* Start by writing the branch condition. */ + + switch (code) + { + case NE: + strcpy (string, "brnz"); + break; + + case EQ: + strcpy (string, "brz"); + break; + + case GE: + strcpy (string, "brgez"); + break; + + case LT: + strcpy (string, "brlz"); + break; + + case LE: + strcpy (string, "brlez"); + break; + + case GT: + strcpy (string, "brgz"); + break; + + default: + abort (); + } + + /* Now add the annulling, reg, label, and nop. */ + if (annul) + strcat (string, ",a"); + + /* ??? Optional prediction bit ",pt" or ",pf" goes here. */ + + labelno[2] = reg + '0'; + labelno[6] = label + '0'; strcat (string, labelno); if (noop) @@ -2418,6 +3464,9 @@ output_cbranch (op, label, reversed, ann /* Output assembler code to return from a function. */ +/* ??? v9: Update to use the new `return' instruction. Also, add patterns to + md file for the `return' instruction. */ + char * output_return (operands) rtx *operands; @@ -2440,7 +3489,7 @@ output_return (operands) if (actual_fsize <= 4096) { - if (current_function_returns_struct) + if (SKIP_CALLERS_UNIMP_P) return "jmp %%o7+12\n\tsub %%sp,-%0,%%sp"; else return "retl\n\tsub %%sp,-%0,%%sp"; @@ -2448,12 +3497,12 @@ output_return (operands) else if (actual_fsize <= 8192) { operands[0] = gen_rtx (CONST_INT, VOIDmode, actual_fsize - 4096); - if (current_function_returns_struct) + if (SKIP_CALLERS_UNIMP_P) return "sub %%sp,-4096,%%sp\n\tjmp %%o7+12\n\tsub %%sp,-%0,%%sp"; else return "sub %%sp,-4096,%%sp\n\tretl\n\tsub %%sp,-%0,%%sp"; } - else if (current_function_returns_struct) + else if (SKIP_CALLERS_UNIMP_P) { if ((actual_fsize & 0x3ff) != 0) return "sethi %%hi(%a0),%%g1\n\tor %%g1,%%lo(%a0),%%g1\n\tjmp %%o7+12\n\tadd %%sp,%%g1,%%sp"; @@ -2470,7 +3519,7 @@ output_return (operands) } else { - if (current_function_returns_struct) + if (SKIP_CALLERS_UNIMP_P) return "jmp %%i7+12\n\trestore"; else return "ret\n\trestore"; @@ -2497,8 +3546,8 @@ order_regs_for_local_alloc () if (regs_ever_live[15] != last_order_nonleaf) { last_order_nonleaf = !last_order_nonleaf; - bcopy (reg_alloc_orders[last_order_nonleaf], reg_alloc_order, - FIRST_PSEUDO_REGISTER * sizeof (int)); + bcopy ((char *) reg_alloc_orders[last_order_nonleaf], + (char *) reg_alloc_order, FIRST_PSEUDO_REGISTER * sizeof (int)); } } @@ -2512,7 +3561,6 @@ int registers_ok_for_ldd_peep (reg1, reg2) rtx reg1, reg2; { - /* We might have been passed a SUBREG. */ if (GET_CODE (reg1) != REG || GET_CODE (reg2) != REG) return 0; @@ -2521,7 +3569,6 @@ registers_ok_for_ldd_peep (reg1, reg2) return 0; return (REGNO (reg1) == REGNO (reg2) - 1); - } /* Return 1 if addr1 and addr2 are suitable for use in an ldd or @@ -2606,7 +3653,6 @@ int register_ok_for_ldd (reg) rtx reg; { - /* We might have been passed a SUBREG. */ if (GET_CODE (reg) != REG) return 0; @@ -2615,7 +3661,6 @@ register_ok_for_ldd (reg) return (REGNO (reg) % 2 == 0); else return 1; - } /* Print operand X (an rtx) in assembler syntax to file FILE. @@ -2649,6 +3694,16 @@ print_operand (file, x, code) if (dbr_sequence_length () == 0 && ! optimize) fputs ("\n\tnop", file); return; + case '_': + /* Output the Medium/Anywhere code model base register. */ + fputs (MEDANY_BASE_REG, file); + return; + case '@': + /* Print out what we are using as the frame pointer. This might + be %fp, or might be %sp+offset. */ + /* ??? What if offset is too big? Perhaps the caller knows it isn't? */ + fprintf (file, "%s+%d", frame_base_name, frame_base_offset); + return; case 'Y': /* Adjust the operand to take into account a RESTORE operation. */ if (GET_CODE (x) != REG) @@ -2691,7 +3746,7 @@ print_operand (file, x, code) else break; - case 'A': + case 'A': switch (GET_CODE (x)) { case IOR: fputs ("or", file); break; @@ -2711,6 +3766,38 @@ print_operand (file, x, code) } return; + /* This is used by the conditional move instructions. */ + case 'C': + switch (GET_CODE (x)) + { + case NE: fputs ("ne", file); break; + case EQ: fputs ("e", file); break; + case GE: fputs ("ge", file); break; + case GT: fputs ("g", file); break; + case LE: fputs ("le", file); break; + case LT: fputs ("l", file); break; + case GEU: fputs ("geu", file); break; + case GTU: fputs ("gu", file); break; + case LEU: fputs ("leu", file); break; + case LTU: fputs ("lu", file); break; + default: output_operand_lossage ("Invalid %%C operand"); + } + return; + + /* This is used by the movr instruction pattern. */ + case 'D': + switch (GET_CODE (x)) + { + case NE: fputs ("ne", file); break; + case EQ: fputs ("e", file); break; + case GE: fputs ("gez", file); break; + case LT: fputs ("lz", file); break; + case LE: fputs ("lez", file); break; + case GT: fputs ("gz", file); break; + default: output_operand_lossage ("Invalid %%D operand"); + } + return; + case 'b': { /* Print a sign-extended character. */ @@ -2721,6 +3808,13 @@ print_operand (file, x, code) return; } + case 'f': + /* Operand must be a MEM; write its address. */ + if (GET_CODE (x) != MEM) + output_operand_lossage ("Invalid %%f operand"); + output_address (XEXP (x, 0)); + return; + case 0: /* Do nothing special. */ break; @@ -2776,6 +3870,8 @@ print_operand (file, x, code) /* ??? If there is a 64 bit counterpart to .word that the assembler understands, then using that would simply this code greatly. */ +/* ??? We only output .xword's for symbols and only then in environments + where the assembler can handle them. */ void output_double_int (file, value) @@ -2799,16 +3895,31 @@ output_double_int (file, value) } else if (GET_CODE (value) == SYMBOL_REF || GET_CODE (value) == CONST - || GET_CODE (value) == PLUS) + || GET_CODE (value) == PLUS + || (TARGET_V9 && + (GET_CODE (value) == LABEL_REF + || GET_CODE (value) == MINUS))) { - /* Addresses are only 32 bits. */ - ASM_OUTPUT_INT (file, const0_rtx); - ASM_OUTPUT_INT (file, value); + if (!TARGET_V9 || TARGET_ENV32) + { + ASM_OUTPUT_INT (file, const0_rtx); + ASM_OUTPUT_INT (file, value); + } + else + { + fprintf (file, "\t%s\t", ASM_LONGLONG); + output_addr_const (file, value); + fprintf (file, "\n"); + } } else abort (); } +/* Return the value of a code used in the .proc pseudo-op that says + what kind of result this function returns. For non-C types, we pick + the closest C type. */ + #ifndef CHAR_TYPE_SIZE #define CHAR_TYPE_SIZE BITS_PER_UNIT #endif @@ -2848,6 +3959,11 @@ sparc_type_code (type) register unsigned long qualifiers = 0; register unsigned shift = 6; + /* Only the first 30 bits of the qualifier are valid. We must refrain from + setting more, since some assemblers will give an error for this. Also, + we must be careful to avoid shifts of 32 bits or more to avoid getting + unpredictable results. */ + for (;;) { switch (TREE_CODE (type)) @@ -2856,14 +3972,16 @@ sparc_type_code (type) return qualifiers; case ARRAY_TYPE: - qualifiers |= (3 << shift); + if (shift < 30) + qualifiers |= (3 << shift); shift += 2; type = TREE_TYPE (type); break; case FUNCTION_TYPE: case METHOD_TYPE: - qualifiers |= (2 << shift); + if (shift < 30) + qualifiers |= (2 << shift); shift += 2; type = TREE_TYPE (type); break; @@ -2871,7 +3989,8 @@ sparc_type_code (type) case POINTER_TYPE: case REFERENCE_TYPE: case OFFSET_TYPE: - qualifiers |= (1 << shift); + if (shift < 30) + qualifiers |= (1 << shift); shift += 2; type = TREE_TYPE (type); break; @@ -2880,6 +3999,7 @@ sparc_type_code (type) return (qualifiers | 8); case UNION_TYPE: + case QUAL_UNION_TYPE: return (qualifiers | 9); case ENUMERAL_TYPE: @@ -2889,79 +4009,46 @@ sparc_type_code (type) return (qualifiers | 16); case INTEGER_TYPE: - /* Carefully distinguish all the standard types of C, - without messing up if the language is not C. - Note that we check only for the names that contain spaces; - other names might occur by coincidence in other languages. */ - if (TYPE_NAME (type) != 0 - && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && DECL_NAME (TYPE_NAME (type)) != 0 - && TREE_CODE (DECL_NAME (TYPE_NAME (type))) == IDENTIFIER_NODE) + /* If this is a range type, consider it to be the underlying + type. */ + if (TREE_TYPE (type) != 0) { - char *name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); - - if (!strcmp (name, "unsigned char")) - return (qualifiers | 12); - if (!strcmp (name, "signed char")) - return (qualifiers | 2); - if (!strcmp (name, "unsigned int")) - return (qualifiers | 14); - if (!strcmp (name, "short int")) - return (qualifiers | 3); - if (!strcmp (name, "short unsigned int")) - return (qualifiers | 13); - if (!strcmp (name, "long int")) - return (qualifiers | 5); - if (!strcmp (name, "long unsigned int")) - return (qualifiers | 15); - if (!strcmp (name, "long long int")) - return (qualifiers | 5); /* Who knows? */ - if (!strcmp (name, "long long unsigned int")) - return (qualifiers | 15); /* Who knows? */ + type = TREE_TYPE (type); + break; } + + /* Carefully distinguish all the standard types of C, + without messing up if the language is not C. We do this by + testing TYPE_PRECISION and TREE_UNSIGNED. The old code used to + look at both the names and the above fields, but that's redundant. + Any type whose size is between two C types will be considered + to be the wider of the two types. Also, we do not have a + special code to use for "long long", so anything wider than + long is treated the same. Note that we can't distinguish + between "int" and "long" in this code if they are the same + size, but that's fine, since neither can the assembler. */ + + if (TYPE_PRECISION (type) <= CHAR_TYPE_SIZE) + return (qualifiers | (TREE_UNSIGNED (type) ? 12 : 2)); - /* Most integer types will be sorted out above, however, for the - sake of special `array index' integer types, the following code - is also provided. */ + else if (TYPE_PRECISION (type) <= SHORT_TYPE_SIZE) + return (qualifiers | (TREE_UNSIGNED (type) ? 13 : 3)); - if (TYPE_PRECISION (type) == INT_TYPE_SIZE) + else if (TYPE_PRECISION (type) <= INT_TYPE_SIZE) return (qualifiers | (TREE_UNSIGNED (type) ? 14 : 4)); - if (TYPE_PRECISION (type) == LONG_TYPE_SIZE) - return (qualifiers | (TREE_UNSIGNED (type) ? 15 : 5)); - - if (TYPE_PRECISION (type) == LONG_LONG_TYPE_SIZE) + else return (qualifiers | (TREE_UNSIGNED (type) ? 15 : 5)); - if (TYPE_PRECISION (type) == SHORT_TYPE_SIZE) - return (qualifiers | (TREE_UNSIGNED (type) ? 13 : 3)); - - if (TYPE_PRECISION (type) == CHAR_TYPE_SIZE) - return (qualifiers | (TREE_UNSIGNED (type) ? 12 : 2)); - - abort (); - case REAL_TYPE: /* Carefully distinguish all the standard types of C, without messing up if the language is not C. */ - if (TYPE_NAME (type) != 0 - && TREE_CODE (TYPE_NAME (type)) == TYPE_DECL - && DECL_NAME (TYPE_NAME (type)) != 0 - && TREE_CODE (DECL_NAME (TYPE_NAME (type))) == IDENTIFIER_NODE) - { - char *name = IDENTIFIER_POINTER (DECL_NAME (TYPE_NAME (type))); - - if (!strcmp (name, "long double")) - return (qualifiers | 7); /* Who knows? */ - } - - if (TYPE_PRECISION (type) == DOUBLE_TYPE_SIZE) - return (qualifiers | 7); + if (TYPE_PRECISION (type) == FLOAT_TYPE_SIZE) return (qualifiers | 6); - if (TYPE_PRECISION (type) == LONG_DOUBLE_TYPE_SIZE) - return (qualifiers | 7); /* Who knows? */ - abort (); + + else + return (qualifiers | 7); case COMPLEX_TYPE: /* GNU Fortran COMPLEX type. */ /* ??? We need to distinguish between double and float complex types, @@ -2972,9 +4059,9 @@ sparc_type_code (type) case CHAR_TYPE: /* GNU Pascal CHAR type. Not used in C. */ case BOOLEAN_TYPE: /* GNU Fortran BOOLEAN type. */ case FILE_TYPE: /* GNU Pascal FILE type. */ - case STRING_TYPE: /* GNU Fortran STRING type. */ + case SET_TYPE: /* GNU Pascal SET type. */ case LANG_TYPE: /* ? */ - abort (); + return qualifiers; default: abort (); /* Not a type! */ @@ -2982,6 +4069,78 @@ sparc_type_code (type) } } +/* Nested function support. */ + +/* Emit RTL insns to initialize the variable parts of a trampoline. + FNADDR is an RTX for the address of the function's pure code. + CXT is an RTX for the static chain value for the function. + + This takes 16 insns: 2 shifts & 2 ands (to split up addresses), 4 sethi + (to load in opcodes), 4 iors (to merge address and opcodes), and 4 writes + (to store insns). This is a bit excessive. Perhaps a different + mechanism would be better here. + + Emit enough FLUSH insns to synchronize the data and instruction caches. */ + +void +sparc_initialize_trampoline (tramp, fnaddr, cxt) + rtx tramp, fnaddr, cxt; +{ + rtx high_cxt = expand_shift (RSHIFT_EXPR, SImode, cxt, + size_int (10), 0, 1); + rtx high_fn = expand_shift (RSHIFT_EXPR, SImode, fnaddr, + size_int (10), 0, 1); + rtx low_cxt = expand_and (cxt, gen_rtx (CONST_INT, VOIDmode, 0x3ff), 0); + rtx low_fn = expand_and (fnaddr, gen_rtx (CONST_INT, VOIDmode, 0x3ff), 0); + rtx g1_sethi = gen_rtx (HIGH, SImode, + gen_rtx (CONST_INT, VOIDmode, 0x03000000)); + rtx g2_sethi = gen_rtx (HIGH, SImode, + gen_rtx (CONST_INT, VOIDmode, 0x05000000)); + rtx g1_ori = gen_rtx (HIGH, SImode, + gen_rtx (CONST_INT, VOIDmode, 0x82106000)); + rtx g2_ori = gen_rtx (HIGH, SImode, + gen_rtx (CONST_INT, VOIDmode, 0x8410A000)); + rtx tem = gen_reg_rtx (SImode); + emit_move_insn (tem, g1_sethi); + emit_insn (gen_iorsi3 (high_fn, high_fn, tem)); + emit_move_insn (gen_rtx (MEM, SImode, plus_constant (tramp, 0)), high_fn); + emit_move_insn (tem, g1_ori); + emit_insn (gen_iorsi3 (low_fn, low_fn, tem)); + emit_move_insn (gen_rtx (MEM, SImode, plus_constant (tramp, 4)), low_fn); + emit_move_insn (tem, g2_sethi); + emit_insn (gen_iorsi3 (high_cxt, high_cxt, tem)); + emit_move_insn (gen_rtx (MEM, SImode, plus_constant (tramp, 8)), high_cxt); + emit_move_insn (tem, g2_ori); + emit_insn (gen_iorsi3 (low_cxt, low_cxt, tem)); + emit_move_insn (gen_rtx (MEM, SImode, plus_constant (tramp, 16)), low_cxt); + emit_insn (gen_flush (validize_mem (gen_rtx (MEM, SImode, tramp)))); + emit_insn (gen_flush (validize_mem (gen_rtx (MEM, SImode, + plus_constant (tramp, 8))))); + emit_insn (gen_flush (validize_mem (gen_rtx (MEM, SImode, + plus_constant (tramp, 16))))); +} + +/* The 64 bit version is simpler because it makes more sense to load the + values as "immediate" data out of the trampoline. It's also easier since + we can read the PC without clobbering a register. */ + +void +sparc64_initialize_trampoline (tramp, fnaddr, cxt) + rtx tramp, fnaddr, cxt; +{ + emit_move_insn (gen_rtx (MEM, DImode, plus_constant (tramp, 24)), cxt); + emit_move_insn (gen_rtx (MEM, DImode, plus_constant (tramp, 32)), fnaddr); + emit_insn (gen_flush (validize_mem (gen_rtx (MEM, DImode, tramp)))); + emit_insn (gen_flush (validize_mem (gen_rtx (MEM, DImode, + plus_constant (tramp, 8))))); + emit_insn (gen_flush (validize_mem (gen_rtx (MEM, DImode, + plus_constant (tramp, 16))))); + emit_insn (gen_flush (validize_mem (gen_rtx (MEM, DImode, + plus_constant (tramp, 24))))); + emit_insn (gen_flush (validize_mem (gen_rtx (MEM, DImode, + plus_constant (tramp, 32))))); +} + /* Subroutines to support a flat (single) register window calling convention. */ @@ -2989,62 +4148,61 @@ sparc_type_code (type) Before call After call +-----------------------+ +-----------------------+ - high | | | | - mem. | | | | - | caller's temps. | | caller's temps. | + high | | | | + mem | caller's temps. | | caller's temps. | | | | | +-----------------------+ +-----------------------+ | | | | | arguments on stack. | | arguments on stack. | - | |FP+92->| | - +-----------------------+ +-----------------------+ + | | | | + +-----------------------+FP+92->+-----------------------+ | 6 words to save | | 6 words to save | | arguments passed | | arguments passed | | in registers, even | | in registers, even | - SP+68->| if not passed. |FP+68->| if not passed. | - +-----------------------+ +-----------------------+ - | 1 word struct addr |FP+64->| 1 word struct addr | - +-----------------------+ +-----------------------+ - | | | | - | 16 word reg save area | | 16 word reg save area | - SP->| | FP->| | - +-----------------------+ +-----------------------+ - | 4 word area for | - FP-16->| fp/alu reg moves | - +-----------------------+ - | | - | local variables | - | | - +-----------------------+ - | | + | if not passed. | | if not passed. | + SP+68->+-----------------------+FP+68->+-----------------------+ + | 1 word struct addr | | 1 word struct addr | + +-----------------------+FP+64->+-----------------------+ + | | | | + | 16 word reg save area | | 16 word reg save area | + | | | | + SP->+-----------------------+ FP->+-----------------------+ + | 4 word area for | + | fp/alu reg moves | + FP-16->+-----------------------+ + | | + | local variables | + | | + +-----------------------+ + | | | fp register save | - | | - +-----------------------+ - | | + | | + +-----------------------+ + | | | gp register save | | | - +-----------------------+ - | | + +-----------------------+ + | | | alloca allocations | - | | - +-----------------------+ - | | + | | + +-----------------------+ + | | | arguments on stack | - SP+92->| | - +-----------------------+ + | | + SP+92->+-----------------------+ | 6 words to save | - | arguments passed | + | arguments passed | | in registers, even | - low SP+68->| if not passed. | - memory +-----------------------+ - SP+64->| 1 word struct addr | - +-----------------------+ - | | - I 16 word reg save area | - SP->| | - +-----------------------+ */ + low | if not passed. | + memory SP+68->+-----------------------+ + | 1 word struct addr | + SP+64->+-----------------------+ + | | + I 16 word reg save area | + | | + SP->+-----------------------+ */ -/* Structure to be filled in by sparc_frw_compute_frame_size with register +/* Structure to be filled in by sparc_flat_compute_frame_size with register save masks, and offsets for the current function. */ struct sparc_frame_info @@ -3055,14 +4213,13 @@ struct sparc_frame_info unsigned long extra_size; /* # bytes of extra gunk. */ unsigned int gp_reg_size; /* # bytes needed to store gp regs. */ unsigned int fp_reg_size; /* # bytes needed to store fp regs. */ - unsigned long mask; /* Mask of saved gp registers. */ + unsigned long gmask; /* Mask of saved gp registers. */ unsigned long fmask; /* Mask of saved fp registers. */ - unsigned long gp_sp_offset; /* Offset from new sp to store gp regs. */ - unsigned long fp_sp_offset; /* Offset from new sp to store fp regs. */ + unsigned long reg_offset; /* Offset from new sp to store regs. */ int initialized; /* Nonzero if frame size already calculated. */ }; -/* Current frame information calculated by sparc_frw_compute_frame_size. */ +/* Current frame information calculated by sparc_flat_compute_frame_size. */ struct sparc_frame_info current_frame_info; /* Zero structure to initialize current_frame_info. */ @@ -3070,16 +4227,20 @@ struct sparc_frame_info zero_frame_info; /* Tell prologue and epilogue if register REGNO should be saved / restored. */ +#define RETURN_ADDR_REGNUM 15 +#define FRAME_POINTER_MASK (1 << (FRAME_POINTER_REGNUM)) +#define RETURN_ADDR_MASK (1 << (RETURN_ADDR_REGNUM)) + #define MUST_SAVE_REGISTER(regno) \ ((regs_ever_live[regno] && !call_used_regs[regno]) \ || (regno == FRAME_POINTER_REGNUM && frame_pointer_needed) \ - || (regno == 15 && regs_ever_live[15])) + || (regno == RETURN_ADDR_REGNUM && regs_ever_live[RETURN_ADDR_REGNUM])) /* Return the bytes needed to compute the frame pointer from the current stack pointer. */ unsigned long -sparc_frw_compute_frame_size (size) +sparc_flat_compute_frame_size (size) int size; /* # of var. bytes allocated. */ { int regno; @@ -3089,8 +4250,10 @@ sparc_frw_compute_frame_size (size) unsigned long extra_size; /* # extra bytes. */ unsigned int gp_reg_size; /* # bytes needed to store gp regs. */ unsigned int fp_reg_size; /* # bytes needed to store fp regs. */ - unsigned long mask; /* Mask of saved gp registers. */ + unsigned long gmask; /* Mask of saved gp registers. */ unsigned long fmask; /* Mask of saved fp registers. */ + unsigned long reg_offset; /* Offset to register save area. */ + int need_aligned_p; /* 1 if need the save area 8 byte aligned. */ /* This is the size of the 16 word reg save area, 1 word struct addr area, and 4 word fp/alu register copy area. */ @@ -3101,33 +4264,34 @@ sparc_frw_compute_frame_size (size) total_size = var_size + args_size + extra_size; gp_reg_size = 0; fp_reg_size = 0; - mask = 0; + gmask = 0; fmask = 0; + reg_offset = 0; + need_aligned_p = 0; /* Calculate space needed for gp registers. */ for (regno = 1; regno <= 31; regno++) { if (MUST_SAVE_REGISTER (regno)) { + /* If we need to save two regs in a row, ensure there's room to bump + up the address to align it to a doubleword boundary. */ if ((regno & 0x1) == 0 && MUST_SAVE_REGISTER (regno+1)) { if (gp_reg_size % 8 != 0) - gp_reg_size += UNITS_PER_WORD; + gp_reg_size += 4; gp_reg_size += 2 * UNITS_PER_WORD; - mask |= 3 << regno; + gmask |= 3 << regno; regno++; + need_aligned_p = 1; } else { gp_reg_size += UNITS_PER_WORD; - mask |= 1 << regno; + gmask |= 1 << regno; } } } - /* Add extra word in case we have to align the space to a double word - boundary. */ - if (gp_reg_size != 0) - gp_reg_size += UNITS_PER_WORD; /* Calculate space needed for fp registers. */ for (regno = 32; regno <= 63; regno++) @@ -3139,8 +4303,21 @@ sparc_frw_compute_frame_size (size) } } - total_size += gp_reg_size + fp_reg_size; + if (gmask || fmask) + { + int n; + reg_offset = FIRST_PARM_OFFSET(0) + args_size; + /* Ensure save area is 8 byte aligned if we need it. */ + n = reg_offset % 8; + if (need_aligned_p && n != 0) + { + total_size += 8 - n; + reg_offset += 8 - n; + } + total_size += gp_reg_size + fp_reg_size; + } + /* ??? This looks a little suspicious. Clarify. */ if (total_size == extra_size) total_size = extra_size = 0; @@ -3153,92 +4330,70 @@ sparc_frw_compute_frame_size (size) current_frame_info.extra_size = extra_size; current_frame_info.gp_reg_size = gp_reg_size; current_frame_info.fp_reg_size = fp_reg_size; - current_frame_info.mask = mask; + current_frame_info.gmask = gmask; current_frame_info.fmask = fmask; + current_frame_info.reg_offset = reg_offset; current_frame_info.initialized = reload_completed; - if (mask) - { - unsigned long offset = args_size; - if (extra_size) - offset += FIRST_PARM_OFFSET(0); - current_frame_info.gp_sp_offset = offset; - } - - if (fmask) - { - unsigned long offset = args_size + gp_reg_size; - if (extra_size) - offset += FIRST_PARM_OFFSET(0); - current_frame_info.fp_sp_offset = offset; - } - /* Ok, we're done. */ return total_size; } -/* Common code to save/restore registers. */ +/* Save/restore registers in GMASK and FMASK at register BASE_REG plus offset + OFFSET. + + BASE_REG must be 8 byte aligned. This allows us to test OFFSET for + appropriate alignment and use DOUBLEWORD_OP when we can. We assume + [BASE_REG+OFFSET] will always be a valid address. + + WORD_OP is either "st" for save, "ld" for restore. + DOUBLEWORD_OP is either "std" for save, "ldd" for restore. */ void -sparc_frw_save_restore (file, word_op, doubleword_op) - FILE *file; /* Stream to write to. */ - char *word_op; /* Operation to do for one word. */ - char *doubleword_op; /* Operation to do for doubleword. */ +sparc_flat_save_restore (file, base_reg, offset, gmask, fmask, word_op, doubleword_op) + FILE *file; + char *base_reg; + unsigned int offset; + unsigned long gmask; + unsigned long fmask; + char *word_op; + char *doubleword_op; { int regno; - unsigned long mask = current_frame_info.mask; - unsigned long fmask = current_frame_info.fmask; - unsigned long gp_offset; - unsigned long fp_offset; - unsigned long max_offset; - char *base_reg; - if (mask == 0 && fmask == 0) + if (gmask == 0 && fmask == 0) return; - base_reg = reg_names[STACK_POINTER_REGNUM]; - gp_offset = current_frame_info.gp_sp_offset; - fp_offset = current_frame_info.fp_sp_offset; - max_offset = (gp_offset > fp_offset) ? gp_offset : fp_offset; - - /* Deal with calling functions with a large structure. */ - if (max_offset >= 4096) - { - char *temp = "%g2"; - fprintf (file, "\tset %ld,%s\n", max_offset, temp); - fprintf (file, "\tadd %s,%s,%s\n", temp, base_reg, temp); - base_reg = temp; - gp_offset = max_offset - gp_offset; - fp_offset = max_offset - fp_offset; - } - - /* 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 registers starting from high to low. We've already saved the + previous frame pointer and previous return address for the debugger's + sake. The debugger allows us to not need a nop in the epilog if at least + one register is reloaded in addition to return address. */ - if (mask || frame_pointer_needed) + if (gmask) { for (regno = 1; regno <= 31; regno++) { - if ((mask & (1L << regno)) != 0 - || (regno == FRAME_POINTER_REGNUM && frame_pointer_needed)) + if ((gmask & (1L << regno)) != 0) { - if ((regno & 0x1) == 0 && ((mask & (1L << regno+1)) != 0)) + if ((regno & 0x1) == 0 && ((gmask & (1L << (regno+1))) != 0)) { - if (gp_offset % 8 != 0) - gp_offset += UNITS_PER_WORD; - + /* We can save two registers in a row. If we're not at a + double word boundary, move to one. + sparc_flat_compute_frame_size ensures there's room to do + this. */ + if (offset % 8 != 0) + offset += UNITS_PER_WORD; + if (word_op[0] == 's') fprintf (file, "\t%s %s,[%s+%d]\n", doubleword_op, reg_names[regno], - base_reg, gp_offset); + base_reg, offset); else fprintf (file, "\t%s [%s+%d],%s\n", - doubleword_op, base_reg, gp_offset, + doubleword_op, base_reg, offset, reg_names[regno]); - gp_offset += 2 * UNITS_PER_WORD; + offset += 2 * UNITS_PER_WORD; regno++; } else @@ -3246,12 +4401,12 @@ sparc_frw_save_restore (file, word_op, d if (word_op[0] == 's') fprintf (file, "\t%s %s,[%s+%d]\n", word_op, reg_names[regno], - base_reg, gp_offset); + base_reg, offset); else fprintf (file, "\t%s [%s+%d],%s\n", - word_op, base_reg, gp_offset, reg_names[regno]); + word_op, base_reg, offset, reg_names[regno]); - gp_offset += UNITS_PER_WORD; + offset += UNITS_PER_WORD; } } } @@ -3266,12 +4421,12 @@ sparc_frw_save_restore (file, word_op, d if (word_op[0] == 's') fprintf (file, "\t%s %s,[%s+%d]\n", word_op, reg_names[regno], - base_reg, gp_offset); + base_reg, offset); else fprintf (file, "\t%s [%s+%d],%s\n", - word_op, base_reg, gp_offset, reg_names[regno]); + word_op, base_reg, offset, reg_names[regno]); - fp_offset += UNITS_PER_WORD; + offset += UNITS_PER_WORD; } } } @@ -3280,88 +4435,174 @@ sparc_frw_save_restore (file, word_op, d /* Set up the stack and frame (if desired) for the function. */ void -sparc_frw_output_function_prologue (file, size, ignored) +sparc_flat_output_function_prologue (file, size) FILE *file; int size; { - extern char call_used_regs[]; - int tsize; char *sp_str = reg_names[STACK_POINTER_REGNUM]; + unsigned long gmask = current_frame_info.gmask; - /* ??? This should be %sp+actual_fsize for a leaf function. I think it - works only because it is never used. */ - frame_base_name - = (!frame_pointer_needed) ? "%sp+80" : reg_names[FRAME_POINTER_REGNUM]; - + /* This is only for the human reader. */ fprintf (file, "\t!#PROLOGUE# 0\n"); + fprintf (file, "\t!# vars= %d, regs= %d/%d, args= %d, extra= %d\n", + current_frame_info.var_size, + current_frame_info.gp_reg_size / 4, + current_frame_info.fp_reg_size / 4, + current_function_outgoing_args_size, + current_frame_info.extra_size); size = SPARC_STACK_ALIGN (size); - tsize = (! current_frame_info.initialized - ? sparc_frw_compute_frame_size (size) - : current_frame_info.total_size); + size = (! current_frame_info.initialized + ? sparc_flat_compute_frame_size (size) + : current_frame_info.total_size); - if (tsize > 0) - { - if (tsize <= 4095) - fprintf (file, - "\tsub %s,%d,%s\t\t!# vars= %d, regs= %d/%d, args = %d, extra= %d\n", - sp_str, tsize, sp_str, current_frame_info.var_size, - current_frame_info.gp_reg_size / 4, - current_frame_info.fp_reg_size / 8, - current_function_outgoing_args_size, - current_frame_info.extra_size); - else - fprintf (file, - "\tset %d,%s\n\tsub\t%s,%s,%s\t\t!# vars= %d, regs= %d/%d, args = %d, sfo= %d\n", - tsize, "%g1", sp_str, "%g1", - sp_str, current_frame_info.var_size, - current_frame_info.gp_reg_size / 4, - current_frame_info.fp_reg_size / 8, - current_function_outgoing_args_size, - current_frame_info.extra_size); - } - - sparc_frw_save_restore (file, "st", "std"); - - if (frame_pointer_needed) - { - if (tsize <= 4095) - fprintf (file, "\tadd %s,%d,%s\t!# set up frame pointer\n", sp_str, - tsize, frame_base_name); + /* These cases shouldn't happen. Catch them now. */ + if (size == 0 && (gmask || current_frame_info.fmask)) + abort (); + + /* Allocate our stack frame by decrementing %sp. + At present, the only algorithm gdb can use to determine if this is a + flat frame is if we always set %i7 if we set %sp. This can be optimized + in the future by putting in some sort of debugging information that says + this is a `flat' function. However, there is still the case of debugging + code without such debugging information (including cases where most fns + have such info, but there is one that doesn't). So, always do this now + so we don't get a lot of code out there that gdb can't handle. + If the frame pointer isn't needn't then that's ok - gdb won't be able to + distinguish us from a non-flat function but there won't (and shouldn't) + be any differences anyway. The return pc is saved (if necessary) right + after %i7 so gdb won't have to look too far to find it. */ + if (size > 0) + { + unsigned int reg_offset = current_frame_info.reg_offset; + char *fp_str = reg_names[FRAME_POINTER_REGNUM]; + char *t1_str = "%g1"; + + /* Things get a little tricky if local variables take up more than ~4096 + bytes and outgoing arguments take up more than ~4096 bytes. When that + happens, the register save area can't be accessed from either end of + the frame. Handle this by decrementing %sp to the start of the gp + register save area, save the regs, update %i7, and then set %sp to its + final value. Given that we only have one scratch register to play + with it is the cheapest solution, and it helps gdb out as it won't + slow down recognition of flat functions. + Don't change the order of insns emitted here without checking with + the gdb folk first. */ + + /* Is the entire register save area offsettable from %sp? */ + if (reg_offset < 4096 - 64 * UNITS_PER_WORD) + { + if (size <= 4096) + { + fprintf (file, "\tadd %s,%d,%s\n", + sp_str, -size, sp_str); + if (gmask & FRAME_POINTER_MASK) + { + fprintf (file, "\tst %s,[%s+%d]\n", + fp_str, sp_str, reg_offset); + fprintf (file, "\tsub %s,%d,%s\t!# set up frame pointer\n", + sp_str, -size, fp_str); + reg_offset += 4; + } + } + else + { + fprintf (file, "\tset %d,%s\n\tsub %s,%s,%s\n", + size, t1_str, sp_str, t1_str, sp_str); + if (gmask & FRAME_POINTER_MASK) + { + fprintf (file, "\tst %s,[%s+%d]\n", + fp_str, sp_str, reg_offset); + fprintf (file, "\tadd %s,%s,%s\t!# set up frame pointer\n", + sp_str, t1_str, fp_str); + reg_offset += 4; + } + } + if (gmask & RETURN_ADDR_MASK) + { + fprintf (file, "\tst %s,[%s+%d]\n", + reg_names[RETURN_ADDR_REGNUM], sp_str, reg_offset); + reg_offset += 4; + } + sparc_flat_save_restore (file, sp_str, reg_offset, + gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK), + current_frame_info.fmask, + "st", "std"); + } else - fprintf (file, "\tadd %s,%s,%s\t!# set up frame pointer\n", sp_str, - "%g1", frame_base_name); + { + /* Subtract %sp in two steps, but make sure there is always a + 64 byte register save area, and %sp is properly aligned. */ + /* Amount to decrement %sp by, the first time. */ + unsigned int size1 = ((size - reg_offset + 64) + 15) & -16; + /* Offset to register save area from %sp. */ + unsigned int offset = size1 - (size - reg_offset); + + if (size1 <= 4096) + { + fprintf (file, "\tadd %s,%d,%s\n", + sp_str, -size1, sp_str); + if (gmask & FRAME_POINTER_MASK) + { + fprintf (file, "\tst %s,[%s+%d]\n\tsub %s,%d,%s\t!# set up frame pointer\n", + fp_str, sp_str, offset, sp_str, -size1, fp_str); + offset += 4; + } + } + else + { + fprintf (file, "\tset %d,%s\n\tsub %s,%s,%s\n", + size1, t1_str, sp_str, t1_str, sp_str); + if (gmask & FRAME_POINTER_MASK) + { + fprintf (file, "\tst %s,[%s+%d]\n\tadd %s,%s,%s\t!# set up frame pointer\n", + fp_str, sp_str, offset, sp_str, t1_str, fp_str); + offset += 4; + } + } + if (gmask & RETURN_ADDR_MASK) + { + fprintf (file, "\tst %s,[%s+%d]\n", + reg_names[RETURN_ADDR_REGNUM], sp_str, offset); + offset += 4; + } + sparc_flat_save_restore (file, sp_str, offset, + gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK), + current_frame_info.fmask, + "st", "std"); + fprintf (file, "\tset %d,%s\n\tsub %s,%s,%s\n", + size - size1, t1_str, sp_str, t1_str, sp_str); + } } + + fprintf (file, "\t!#PROLOGUE# 1\n"); } /* Do any necessary cleanup after a function to restore stack, frame, and regs. */ void -sparc_frw_output_function_epilogue (file, size, ignored1, ignored2) +sparc_flat_output_function_epilogue (file, size) FILE *file; int size; { - extern FILE *asm_out_data_file, *asm_out_file; - extern char call_used_regs[]; - extern int frame_pointer_needed; - int tsize; - char *sp_str = reg_names[STACK_POINTER_REGNUM]; - char *t1_str = "%g1"; rtx epilogue_delay = current_function_epilogue_delay_list; int noepilogue = FALSE; + /* This is only for the human reader. */ + fprintf (file, "\t!#EPILOGUE#\n"); + /* The epilogue does not depend on any registers, but the stack registers, so we assume that if we have 1 pending nop, it can be ignored, and 2 it must be filled (2 nops occur for integer multiply and divide). */ size = SPARC_STACK_ALIGN (size); - tsize = (!current_frame_info.initialized - ? sparc_frw_compute_frame_size (size) + size = (!current_frame_info.initialized + ? sparc_flat_compute_frame_size (size) : current_frame_info.total_size); - if (tsize == 0 && epilogue_delay == 0) + if (size == 0 && epilogue_delay == 0) { rtx insn = get_last_insn (); @@ -3375,25 +4616,77 @@ sparc_frw_output_function_epilogue (file if (!noepilogue) { + unsigned int reg_offset = current_frame_info.reg_offset; + unsigned int size1; + char *sp_str = reg_names[STACK_POINTER_REGNUM]; + char *fp_str = reg_names[FRAME_POINTER_REGNUM]; + char *t1_str = "%g1"; + /* In the reload sequence, we don't need to fill the load delay slots for most of the loads, also see if we can fill the final delay slot if not otherwise filled by the reload sequence. */ - if (tsize > 4095) - fprintf (file, "\tset %d,%s\n", tsize, t1_str); + if (size > 4095) + fprintf (file, "\tset %d,%s\n", size, t1_str); if (frame_pointer_needed) { - char *fp_str = reg_names[FRAME_POINTER_REGNUM]; - if (tsize > 4095) - fprintf (file,"\tsub %s,%s,%s\t\t!# sp not trusted here\n", + if (size > 4095) + fprintf (file,"\tsub %s,%s,%s\t\t!# sp not trusted here\n", fp_str, t1_str, sp_str); else - fprintf (file,"\tsub %s,%d,%s\t\t!# sp not trusted here\n", - fp_str, tsize, sp_str); + fprintf (file,"\tsub %s,%d,%s\t\t!# sp not trusted here\n", + fp_str, size, sp_str); + } + + /* Is the entire register save area offsettable from %sp? */ + if (reg_offset < 4096 - 64 * UNITS_PER_WORD) + { + size1 = 0; } + else + { + /* Restore %sp in two steps, but make sure there is always a + 64 byte register save area, and %sp is properly aligned. */ + /* Amount to increment %sp by, the first time. */ + size1 = ((reg_offset - 64 - 16) + 15) & -16; + /* Offset to register save area from %sp. */ + reg_offset = size1 - reg_offset; - sparc_frw_save_restore (file, "ld", "ldd"); + fprintf (file, "\tset %d,%s\n\tadd %s,%s,%s\n", + size1, t1_str, sp_str, t1_str, sp_str); + } + + /* We must restore the frame pointer and return address reg first + because they are treated specially by the prologue output code. */ + if (current_frame_info.gmask & FRAME_POINTER_MASK) + { + fprintf (file, "\tld [%s+%d],%s\n", + sp_str, reg_offset, fp_str); + reg_offset += 4; + } + if (current_frame_info.gmask & RETURN_ADDR_MASK) + { + fprintf (file, "\tld [%s+%d],%s\n", + sp_str, reg_offset, reg_names[RETURN_ADDR_REGNUM]); + reg_offset += 4; + } + + /* Restore any remaining saved registers. */ + sparc_flat_save_restore (file, sp_str, reg_offset, + current_frame_info.gmask & ~(FRAME_POINTER_MASK | RETURN_ADDR_MASK), + current_frame_info.fmask, + "ld", "ldd"); + + /* If we had to increment %sp in two steps, record it so the second + restoration in the epilogue finishes up. */ + if (size1 > 0) + { + size -= size1; + if (size > 4095) + fprintf (file, "\tset %d,%s\n", + size, t1_str); + } if (current_function_returns_struct) fprintf (file, "\tjmp %%o7+12\n"); @@ -3407,16 +4700,16 @@ sparc_frw_output_function_epilogue (file if (epilogue_delay) { - if (tsize) + if (size) abort (); final_scan_insn (XEXP (epilogue_delay, 0), file, 1, -2, 1); } - else if (tsize > 4095) + else if (size > 4095) fprintf (file, "\tadd %s,%s,%s\n", sp_str, t1_str, sp_str); - else if (tsize > 0) - fprintf (file, "\tadd %s,%d,%s\n", sp_str, tsize, sp_str); + else if (size > 0) + fprintf (file, "\tadd %s,%d,%s\n", sp_str, size, sp_str); else fprintf (file, "\tnop\n"); @@ -3432,10 +4725,10 @@ sparc_frw_output_function_epilogue (file or the only register saved is the return register. */ int -sparc_frw_epilogue_delay_slots () +sparc_flat_epilogue_delay_slots () { if (!current_frame_info.initialized) - (void) sparc_frw_compute_frame_size (get_frame_size ()); + (void) sparc_flat_compute_frame_size (get_frame_size ()); if (current_frame_info.total_size == 0) return 1; @@ -3448,7 +4741,7 @@ sparc_frw_epilogue_delay_slots () pointer is OK. */ int -sparc_frw_eligible_for_epilogue_delay (trial, slot) +sparc_flat_eligible_for_epilogue_delay (trial, slot) rtx trial; int slot; { @@ -3458,3 +4751,71 @@ sparc_frw_eligible_for_epilogue_delay (t return 1; return 0; } + +/* Adjust the cost of a scheduling dependency. Return the new cost of + a dependency LINK or INSN on DEP_INSN. COST is the current cost. */ + +int +supersparc_adjust_cost (insn, link, dep_insn, cost) + rtx insn; + rtx link; + rtx dep_insn; + int cost; +{ + enum attr_type insn_type; + + if (! recog_memoized (insn)) + return 0; + + insn_type = get_attr_type (insn); + + if (REG_NOTE_KIND (link) == 0) + { + /* Data dependency; DEP_INSN writes a register that INSN reads some + cycles later. */ + + /* if a load, then the dependence must be on the memory address; + add an extra 'cycle'. Note that the cost could be two cycles + if the reg was written late in an instruction group; we can't tell + here. */ + if (insn_type == TYPE_LOAD || insn_type == TYPE_FPLOAD) + return cost + 3; + + /* Get the delay only if the address of the store is the dependence. */ + if (insn_type == TYPE_STORE || insn_type == TYPE_FPSTORE) + { + rtx pat = PATTERN(insn); + rtx dep_pat = PATTERN (dep_insn); + + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) + return cost; /* This shouldn't happen! */ + + /* The dependency between the two instructions was on the data that + is being stored. Assume that this implies that the address of the + store is not dependent. */ + if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat))) + return cost; + + return cost + 3; /* An approximation. */ + } + + /* A shift instruction cannot receive its data from an instruction + in the same cycle; add a one cycle penalty. */ + if (insn_type == TYPE_SHIFT) + return cost + 3; /* Split before cascade into shift. */ + } + else + { + /* Anti- or output- dependency; DEP_INSN reads/writes a register that + INSN writes some cycles later. */ + + /* These are only significant for the fpu unit; writing a fp reg before + the fpu has finished with it stalls the processor. */ + + /* Reusing an integer register causes no problems. */ + if (insn_type == TYPE_IALU || insn_type == TYPE_SHIFT) + return 0; + } + + return cost; +}