--- gcc/config/pa/pa.c 2018/04/24 18:10:27 1.1 +++ gcc/config/pa/pa.c 2018/04/24 18:31:15 1.1.1.4 @@ -1,5 +1,5 @@ /* Subroutines for insn-output.c for HPPA. - Copyright (C) 1992 Free Software Foundation, Inc. + Copyright (C) 1992, 1993, 1994, 1995 Free Software Foundation, Inc. Contributed by Tim Moore (moore@cs.utah.edu), based on sparc.c This file is part of GNU CC. @@ -16,7 +16,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" @@ -41,13 +42,64 @@ the Free Software Foundation, 675 Mass A rtx hppa_compare_op0, hppa_compare_op1; enum cmp_type hppa_branch_type; -rtx hppa_save_pic_table_rtx; +/* Which cpu we are scheduling for. */ +enum processor_type pa_cpu; + +/* String to hold which cpu we are scheduling for. */ +char *pa_cpu_string; /* Set by the FUNCTION_PROFILER macro. */ int hp_profile_labelno; +/* Counts for the number of callee-saved general and floating point + registers which were saved by the current function's prologue. */ +static int gr_saved, fr_saved; + static rtx find_addr_reg (); +/* Keep track of the number of bytes we have output in the CODE subspaces + during this compilation so we'll know when to emit inline long-calls. */ + +unsigned int total_code_bytes; + +/* Variables to handle plabels that we discover are necessary at assembly + output time. They are output after the current function. */ + +struct defer_plab +{ + rtx internal_label; + rtx symbol; +} *deferred_plabels = 0; +int n_deferred_plabels = 0; + +void +override_options () +{ + /* Default to 700 scheduling which is reasonable for older 800 processors + correct for the 700s, and not too bad for the 7100s and 7100LCs. */ + if (pa_cpu_string == NULL + || ! strcmp (pa_cpu_string, "700")) + { + pa_cpu_string = "700"; + pa_cpu = PROCESSOR_700; + } + else if (! strcmp (pa_cpu_string, "7100")) + { + pa_cpu_string = "7100"; + pa_cpu = PROCESSOR_7100; + } + else if (! strcmp (pa_cpu_string, "7100LC")) + { + pa_cpu_string = "7100LC"; + pa_cpu = PROCESSOR_7100LC; + } + else + { + warning ("Unknown -mschedule= option (%s).\nValid options are 700, 7100 and 7100LC\n", pa_cpu_string); + } +} + + /* Return non-zero only if OP is a register of mode MODE, or CONST0_RTX. */ int @@ -58,24 +110,28 @@ reg_or_0_operand (op, mode) return (op == CONST0_RTX (mode) || register_operand (op, mode)); } +/* Return non-zero if OP is suitable for use in a call to a named + function. + + (???) For 2.5 try to eliminate either call_operand_address or + function_label_operand, they perform very similar functions. */ int call_operand_address (op, mode) rtx op; enum machine_mode mode; { - return (REG_P (op) - || (CONSTANT_P (op) && ! TARGET_LONG_CALLS)); + return (CONSTANT_P (op) && ! TARGET_PORTABLE_RUNTIME); } -/* Return 1 if X contains a symbolic expression. We know these - expressions will have one of a few well defined forms, so +/* Return 1 if X contains a symbolic expression. We know these + expressions will have one of a few well defined forms, so we need only check those forms. */ int symbolic_expression_p (x) register rtx x; { - /* Strip off any HIGH. */ + /* Strip off any HIGH. */ if (GET_CODE (x) == HIGH) x = XEXP (x, 0); @@ -136,7 +192,7 @@ reg_or_nonsymb_mem_operand (op, mode) return 0; } -/* Return 1 if the operand is either a register, zero, or a memory operand +/* Return 1 if the operand is either a register, zero, or a memory operand that is not symbolic. */ int @@ -156,11 +212,11 @@ reg_or_0_or_nonsymb_mem_operand (op, mod return 0; } -/* Accept any constant that can be moved in one instructions into a +/* Accept any constant that can be moved in one instructions into a general register. */ -int +int cint_ok_for_move (intval) - int intval; + HOST_WIDE_INT intval; { /* OK if ldo, ldil, or zdepi, can be used. */ return (VAL_14_BITS_P (intval) || (intval & 0x7ff) == 0 @@ -211,11 +267,24 @@ reg_or_cint_move_operand (op, mode) } int -pic_operand (op, mode) +pic_label_operand (op, mode) rtx op; enum machine_mode mode; { - return flag_pic && GET_CODE (op) == LABEL_REF; + if (!flag_pic) + return 0; + + switch (GET_CODE (op)) + { + case LABEL_REF: + return 1; + case CONST: + op = XEXP (op, 0); + return (GET_CODE (XEXP (op, 0)) == LABEL_REF + && GET_CODE (XEXP (op, 1)) == CONST_INT); + default: + return 0; + } } int @@ -227,32 +296,6 @@ fp_reg_operand (op, mode) } -extern int current_function_uses_pic_offset_table; -extern rtx force_reg (), validize_mem (); - -/* 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; -{ - extern rtx recog_operand[]; - 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 truth value of whether OP can be used as an operand in a three operand arithmetic insn that accepts registers of mode MODE @@ -278,7 +321,7 @@ arith11_operand (op, mode) || (GET_CODE (op) == CONST_INT && INT_11_BITS (op))); } -/* A constant integer suitable for use in a PRE_MODIFY memory +/* A constant integer suitable for use in a PRE_MODIFY memory reference. */ int pre_cint_operand (op, mode) @@ -289,7 +332,7 @@ pre_cint_operand (op, mode) && INTVAL (op) >= -0x2000 && INTVAL (op) < 0x10); } -/* A constant integer suitable for use in a POST_MODIFY memory +/* A constant integer suitable for use in a POST_MODIFY memory reference. */ int post_cint_operand (op, mode) @@ -332,13 +375,28 @@ uint5_operand (op, mode) return (GET_CODE (op) == CONST_INT && INT_U5_BITS (op)); } - int int11_operand (op, mode) rtx op; enum machine_mode mode; { - return (GET_CODE (op) == CONST_INT && INT_11_BITS (op)); + return (GET_CODE (op) == CONST_INT && INT_11_BITS (op)); +} + +int +uint32_operand (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) < 0x100000000L)); +#else + return (GET_CODE (op) == CONST_INT + || (GET_CODE (op) == CONST_DOUBLE + && CONST_DOUBLE_HIGH (op) == 0)); +#endif } int @@ -352,22 +410,26 @@ arith5_operand (op, mode) /* True iff zdepi can be used to generate this CONST_INT. */ int zdepi_cint_p (x) - unsigned x; + unsigned HOST_WIDE_INT x; { - unsigned lsb_mask, t; + unsigned HOST_WIDE_INT lsb_mask, t; /* This might not be obvious, but it's at least fast. - This function is critcal; we don't have the time loops would take. */ + This function is critical; we don't have the time loops would take. */ lsb_mask = x & -x; t = ((x >> 4) + lsb_mask) & ~(lsb_mask - 1); /* Return true iff t is a power of two. */ return ((t & (t - 1)) == 0); } -/* True iff depi or extru can be used to compute (reg & mask). */ +/* True iff depi or extru can be used to compute (reg & mask). + Accept bit pattern like these: + 0....01....1 + 1....10....0 + 1..10..01..1 */ int and_mask_p (mask) - unsigned mask; + unsigned HOST_WIDE_INT mask; { mask = ~mask; mask += mask & -mask; @@ -387,7 +449,7 @@ and_operand (op, mode) /* True iff depi can be used to compute (reg | MASK). */ int ior_mask_p (mask) - unsigned mask; + unsigned HOST_WIDE_INT mask; { mask += mask & -mask; return (mask & (mask - 1)) == 0; @@ -418,7 +480,7 @@ lhs_lshift_cint_operand (op, mode) rtx op; enum machine_mode mode; { - unsigned x; + unsigned HOST_WIDE_INT x; if (GET_CODE (op) != CONST_INT) return 0; x = INTVAL (op) >> 4; @@ -453,6 +515,13 @@ legitimize_pic_address (orig, mode, reg) { rtx pic_ref = orig; + /* Labels need special handling. */ + if (pic_label_operand (orig)) + { + emit_insn (gen_pic_load_label (reg, orig)); + current_function_uses_pic_offset_table = 1; + return reg; + } if (GET_CODE (orig) == SYMBOL_REF) { if (reg == 0) @@ -460,15 +529,14 @@ legitimize_pic_address (orig, mode, reg) if (flag_pic == 2) { - emit_insn (gen_rtx (SET, VOIDmode, reg, - gen_rtx (HIGH, Pmode, orig))); - emit_insn (gen_rtx (SET, VOIDmode, reg, - gen_rtx (LO_SUM, Pmode, reg, orig))); - orig = reg; - } - pic_ref = gen_rtx (MEM, Pmode, - gen_rtx (PLUS, Pmode, - pic_offset_table_rtx, orig)); + emit_insn (gen_pic2_highpart (reg, pic_offset_table_rtx, orig)); + pic_ref = gen_rtx (MEM, Pmode, + gen_rtx (LO_SUM, Pmode, reg, + gen_rtx (UNSPEC, SImode, gen_rtvec (1, orig), 0))); + } + else + pic_ref = gen_rtx (MEM, Pmode, + gen_rtx (PLUS, Pmode, pic_offset_table_rtx, orig)); current_function_uses_pic_offset_table = 1; RTX_UNCHANGING_P (pic_ref) = 1; emit_move_insn (reg, pic_ref); @@ -476,7 +544,7 @@ legitimize_pic_address (orig, mode, reg) } else if (GET_CODE (orig) == CONST) { - rtx base, offset; + rtx base; if (GET_CODE (XEXP (orig, 0)) == PLUS && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx) @@ -504,33 +572,6 @@ legitimize_pic_address (orig, mode, reg) return pic_ref; } -/* Set up PIC-specific rtl. This should not cause any insns - to be emitted. */ - -void -initialize_pic () -{ -} - -/* Emit special PIC prologues and epilogues. */ - -void -finalize_pic () -{ - if (hppa_save_pic_table_rtx) - { - emit_insn_after (gen_rtx (SET, VOIDmode, - hppa_save_pic_table_rtx, - gen_rtx (REG, Pmode, 19)), - get_insns ()); - /* Need to emit this whether or not we obey regdecls, - since setjmp/longjmp can cause life info to screw up. */ - hppa_save_pic_table_rtx = 0; - } - emit_insn (gen_rtx (USE, VOIDmode, pic_offset_table_rtx)); - -} - /* Try machine-dependent ways of modifying an illegitimate address to be legitimate. If we find one, return the new, valid address. This macro is used in only one place: `memory_address' in explow.c. @@ -542,7 +583,7 @@ finalize_pic () GO_IF_LEGITIMATE_ADDRESS. It is always safe for this macro to do nothing. It exists to recognize - opportunities to optimize the output. + opportunities to optimize the output. For the PA, transform: @@ -557,17 +598,17 @@ finalize_pic () Z = X + Y memory (Z + ( - Y)); - This is for CSE to find several similar references, and only use one Z. + This is for CSE to find several similar references, and only use one Z. X can either be a SYMBOL_REF or REG, but because combine can not perform a 4->2 combination we do nothing for SYMBOL_REF + D where D will not fit in 14 bits. MODE_FLOAT references allow displacements which fit in 5 bits, so use - 0x1f as the mask. + 0x1f as the mask. MODE_INT references allow displacements which fit in 14 bits, so use - 0x3fff as the mask. + 0x3fff as the mask. This relies on the fact that most mode MODE_FLOAT references will use FP registers and most mode MODE_INT references will use integer registers. @@ -577,7 +618,7 @@ finalize_pic () It is also beneficial to handle (plus (mult (X) (Y)) (Z)) in a special manner if Y is 2, 4, or 8. (allows more shadd insns and shifted indexed - adressing modes to be used). + addressing modes to be used). Put X and Z into registers. Then put the entire expression into a register. */ @@ -587,16 +628,21 @@ hppa_legitimize_address (x, oldx, mode) rtx x, oldx; enum machine_mode mode; { - rtx orig = x; + if (flag_pic) + return legitimize_pic_address (x, mode, gen_reg_rtx (Pmode)); + /* Strip off CONST. */ if (GET_CODE (x) == CONST) x = XEXP (x, 0); + /* Note we must reject symbols which represent function addresses + since the assembler/linker can't handle arithmetic on plabels. */ if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 1)) == CONST_INT - && (GET_CODE (XEXP (x, 0)) == SYMBOL_REF + && ((GET_CODE (XEXP (x, 0)) == SYMBOL_REF + && !FUNCTION_NAME_P (XSTR (XEXP (x, 0), 0))) || GET_CODE (XEXP (x, 0)) == REG)) { rtx int_part, ptr_reg; @@ -604,7 +650,7 @@ hppa_legitimize_address (x, oldx, mode) int offset = INTVAL (XEXP (x, 1)); int mask = GET_MODE_CLASS (mode) == MODE_FLOAT ? 0x1f : 0x3fff; - /* Choose which way to round the offset. Round up if we + /* Choose which way to round the offset. Round up if we are >= halfway to the next boundary. */ if ((offset & mask) >= ((mask + 1) / 2)) newoffset = (offset & ~ mask) + mask + 1; @@ -646,9 +692,21 @@ hppa_legitimize_address (x, oldx, mode) } return plus_constant (ptr_reg, offset - newoffset); } + + /* Try to arrange things so that indexing modes can be used, but + only do so if indexing is safe. + + Indexing is safe when the second operand for the outer PLUS + is a REG, SUBREG, SYMBOL_REF or the like. + + For 2.5, indexing is also safe for (plus (symbol_ref) (const_int)) + if the integer is > 0. */ if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT - && shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1)))) + && shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1))) + && (GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == 'o' + || GET_CODE (XEXP (x, 1)) == SUBREG) + && GET_CODE (XEXP (x, 1)) != CONST) { int val = INTVAL (XEXP (XEXP (x, 0), 1)); rtx reg1, reg2; @@ -661,8 +719,39 @@ hppa_legitimize_address (x, oldx, mode) GEN_INT (val)), reg1)); } - if (flag_pic) - return legitimize_pic_address (x, mode, gen_reg_rtx (Pmode)); + + /* Uh-oh. We might have an address for x[n-100000]. This needs + special handling. */ + + if (GET_CODE (x) == PLUS && GET_CODE (XEXP (x, 0)) == MULT + && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT + && shadd_constant_p (INTVAL (XEXP (XEXP (x, 0), 1)))) + { + /* Ugly. We modify things here so that the address offset specified + by the index expression is computed first, then added to x to form + the entire address. + + For 2.5, it might be profitable to set things up so that we + compute the raw (unscaled) index first, then use scaled indexing + to access memory, or better yet have the MI parts of the compiler + handle this. */ + + rtx regx1, regy1, regy2, y; + + /* Strip off any CONST. */ + y = XEXP (x, 1); + if (GET_CODE (y) == CONST) + y = XEXP (y, 0); + + if (GET_CODE (y) == PLUS || GET_CODE (y) == MINUS) + { + regx1 = force_reg (Pmode, force_operand (XEXP (x, 0), 0)); + regy1 = force_reg (Pmode, force_operand (XEXP (y, 0), 0)); + regy2 = force_reg (Pmode, force_operand (XEXP (y, 1), 0)); + regx1 = force_reg (Pmode, gen_rtx (GET_CODE (y), Pmode, regx1, regy2)); + return force_reg (Pmode, gen_rtx (PLUS, Pmode, regx1, regy1)); + } + } return orig; } @@ -703,38 +792,72 @@ emit_move_sequence (operands, mode, scra register rtx operand1 = operands[1]; /* Handle secondary reloads for loads/stores of FP registers from - REG+D addresses where D does not fit in 5 bits. */ + REG+D addresses where D does not fit in 5 bits, including + (subreg (mem (addr)) cases. */ if (fp_reg_operand (operand0, mode) - && GET_CODE (operand1) == MEM - /* Using DFmode forces only short displacements be be - recognized as valid in reg+d addressing modes. */ - && ! memory_address_p (DFmode, XEXP (operand1, 0)) + && ((GET_CODE (operand1) == MEM + && ! memory_address_p (DFmode, XEXP (operand1, 0))) + || ((GET_CODE (operand1) == SUBREG + && GET_CODE (XEXP (operand1, 0)) == MEM + && !memory_address_p (DFmode, XEXP (XEXP (operand1, 0), 0))))) && scratch_reg) { - emit_move_insn (scratch_reg, XEXP (operand1 , 0)); + if (GET_CODE (operand1) == SUBREG) + operand1 = XEXP (operand1, 0); + + scratch_reg = gen_rtx (REG, SImode, REGNO (scratch_reg)); + + /* D might not fit in 14 bits either; for such cases load D into + scratch reg. */ + if (!memory_address_p (SImode, XEXP (operand1, 0))) + { + emit_move_insn (scratch_reg, XEXP (XEXP (operand1, 0), 1)); + emit_move_insn (scratch_reg, gen_rtx (GET_CODE (XEXP (operand1, 0)), + SImode, + XEXP (XEXP (operand1, 0), 0), + scratch_reg)); + } + else + emit_move_insn (scratch_reg, XEXP (operand1, 0)); emit_insn (gen_rtx (SET, VOIDmode, operand0, gen_rtx (MEM, mode, scratch_reg))); return 1; } else if (fp_reg_operand (operand1, mode) - && GET_CODE (operand0) == MEM - /* Using DFmode forces only short displacements be be - recognized as valid in reg+d addressing modes. */ - && ! memory_address_p (DFmode, XEXP (operand0, 0)) + && ((GET_CODE (operand0) == MEM + && ! memory_address_p (DFmode, XEXP (operand0, 0))) + || ((GET_CODE (operand0) == SUBREG) + && GET_CODE (XEXP (operand0, 0)) == MEM + && !memory_address_p (DFmode, XEXP (XEXP (operand0, 0), 0)))) && scratch_reg) { - emit_move_insn (scratch_reg, XEXP (operand0 , 0)); - emit_insn (gen_rtx (SET, VOIDmode, gen_rtx (MEM, mode, scratch_reg), + if (GET_CODE (operand0) == SUBREG) + operand0 = XEXP (operand0, 0); + + scratch_reg = gen_rtx (REG, SImode, REGNO (scratch_reg)); + /* D might not fit in 14 bits either; for such cases load D into + scratch reg. */ + if (!memory_address_p (SImode, XEXP (operand0, 0))) + { + emit_move_insn (scratch_reg, XEXP (XEXP (operand0, 0), 1)); + emit_move_insn (scratch_reg, gen_rtx (GET_CODE (XEXP (operand0, 0)), + SImode, + XEXP (XEXP (operand0, 0), 0), + scratch_reg)); + } + else + emit_move_insn (scratch_reg, XEXP (operand0, 0)); + emit_insn (gen_rtx (SET, VOIDmode, gen_rtx (MEM, mode, scratch_reg), operand1)); return 1; } /* Handle secondary reloads for loads of FP registers from constant expressions by forcing the constant into memory. - use scratch_reg to hold the address of the memory location. + use scratch_reg to hold the address of the memory location. - ??? The proper fix is to change PREFERRED_RELOAD_CLASS to return - NO_REGS when presented with a const_int and an register class + ??? The proper fix is to change PREFERRED_RELOAD_CLASS to return + NO_REGS when presented with a const_int and an register class containing only FP registers. Doing so unfortunately creates more problems than it solves. Fix this for 2.5. */ else if (fp_reg_operand (operand0, mode) @@ -747,7 +870,7 @@ emit_move_sequence (operands, mode, scra memory location into scratch_reg. */ xoperands[0] = scratch_reg; xoperands[1] = XEXP (force_const_mem (mode, operand1), 0); - emit_move_sequence (xoperands, mode, 0); + emit_move_sequence (xoperands, Pmode, 0); /* Now load the destination register. */ emit_insn (gen_rtx (SET, mode, operand0, @@ -755,10 +878,11 @@ emit_move_sequence (operands, mode, scra return 1; } /* Handle secondary reloads for SAR. These occur when trying to load - the SAR from memory or from a FP register. */ + the SAR from memory a FP register, or with a constant. */ else if (GET_CODE (operand0) == REG && REGNO_REG_CLASS (REGNO (operand0)) == SHIFT_REGS && (GET_CODE (operand1) == MEM + || GET_CODE (operand1) == CONST_INT || (GET_CODE (operand1) == REG && FP_REG_CLASS_P (REGNO_REG_CLASS (REGNO (operand1))))) && scratch_reg) @@ -774,7 +898,7 @@ emit_move_sequence (operands, mode, scra || (GET_CODE (operand1) == CONST_INT && INT_14_BITS (operand1)) || (operand1 == CONST0_RTX (mode)) || (GET_CODE (operand1) == HIGH - && !symbolic_operand (XEXP (operand1, 0))) + && !symbolic_operand (XEXP (operand1, 0), VOIDmode)) /* Only `general_operands' can come here, so MEM is ok. */ || GET_CODE (operand1) == MEM) { @@ -791,7 +915,7 @@ emit_move_sequence (operands, mode, scra emit_insn (gen_rtx (SET, VOIDmode, operand0, operand1)); return 1; } - if (! reload_in_progress) + if (! (reload_in_progress || reload_completed)) { operands[0] = validize_mem (operand0); operands[1] = operand1 = force_reg (mode, operand1); @@ -799,10 +923,9 @@ emit_move_sequence (operands, mode, scra } /* Simplify the source if we need to. */ - if (GET_CODE (operand1) != HIGH && immediate_operand (operand1, mode) + if ((GET_CODE (operand1) != HIGH && immediate_operand (operand1, mode)) || (GET_CODE (operand1) == HIGH - && symbolic_operand (XEXP (operand1, 0), mode) - && TARGET_KERNEL)) + && symbolic_operand (XEXP (operand1, 0), mode))) { int ishighonly = 0; @@ -813,20 +936,74 @@ emit_move_sequence (operands, mode, scra } if (symbolic_operand (operand1, mode)) { + rtx const_part = NULL; + + /* Argh. The assembler and linker can't handle arithmetic + involving plabels. We'll have to split up operand1 here + if it's a function label involved in an arithmetic + expression. Luckily, this only happens with addition + of constants to plabels, which simplifies the test. + + We add the constant back in just before returning to + our caller. */ + if (GET_CODE (operand1) == CONST + && GET_CODE (XEXP (operand1, 0)) == PLUS + && function_label_operand (XEXP (XEXP (operand1, 0), 0), Pmode)) + { + /* Save away the constant part of the expression. */ + const_part = XEXP (XEXP (operand1, 0), 1); + if (GET_CODE (const_part) != CONST_INT) + abort (); + + /* Set operand1 to just the SYMBOL_REF. */ + operand1 = XEXP (XEXP (operand1, 0), 0); + } + if (flag_pic) { - rtx temp = reload_in_progress ? operand0 : gen_reg_rtx (Pmode); - operands[1] = legitimize_pic_address (operand1, mode, temp); - emit_insn (gen_rtx (SET, VOIDmode, operand0, operands[1])); - } - /* On the HPPA, references to data space are supposed to */ - /* use dp, register 27, but showing it in the RTL inhibits various - cse and loop optimizations. */ - else + rtx temp; + + if (reload_in_progress || reload_completed) + temp = scratch_reg ? scratch_reg : operand0; + else + temp = gen_reg_rtx (Pmode); + + /* If operand1 is a function label, then we've got to + force it to memory, then load op0 from memory. */ + if (function_label_operand (operand1, mode)) + { + operands[1] = force_const_mem (mode, operand1); + emit_move_sequence (operands, mode, temp); + } + /* Likewise for (const (plus (symbol) (const_int)) when generating + pic code during or after reload and const_int will not fit + in 14 bits. */ + else if (GET_CODE (operand1) == CONST + && GET_CODE (XEXP (operand1, 0)) == PLUS + && GET_CODE (XEXP (XEXP (operand1, 0), 1)) == CONST_INT + && !INT_14_BITS (XEXP (XEXP (operand1, 0), 1)) + && (reload_completed || reload_in_progress) + && flag_pic) + { + operands[1] = force_const_mem (mode, operand1); + operands[1] = legitimize_pic_address (XEXP (operands[1], 0), + mode, temp); + emit_move_sequence (operands, mode, temp); + } + else + { + operands[1] = legitimize_pic_address (operand1, mode, temp); + emit_insn (gen_rtx (SET, VOIDmode, operand0, operands[1])); + } + } + /* On the HPPA, references to data space are supposed to use dp, + register 27, but showing it in the RTL inhibits various cse + and loop optimizations. */ + else { rtx temp, set; - if (reload_in_progress) + if (reload_in_progress || reload_completed) temp = scratch_reg ? scratch_reg : operand0; else temp = gen_reg_rtx (mode); @@ -837,35 +1014,29 @@ emit_move_sequence (operands, mode, scra set = gen_rtx (SET, VOIDmode, operand0, gen_rtx (LO_SUM, mode, temp, operand1)); - + emit_insn (gen_rtx (SET, VOIDmode, temp, gen_rtx (HIGH, mode, operand1))); - if (TARGET_SHARED_LIBS - && function_label_operand (operand1, mode)) - { - rtx temp = reload_in_progress ? scratch_reg - : gen_reg_rtx (mode); - if (!temp) - abort (); - emit_insn (gen_rtx (PARALLEL, VOIDmode, - gen_rtvec (2, - set, - gen_rtx (CLOBBER, VOIDmode, - temp)))); - } - else - emit_insn (set); - return 1; + emit_insn (set); + } + + /* Add back in the constant part if needed. */ + if (const_part != NULL) + expand_inc (operand0, const_part); return 1; } else if (GET_CODE (operand1) != CONST_INT - || (! INT_14_BITS (operand1) - && ! ((INTVAL (operand1) & 0x7ff) == 0) - && ! zdepi_cint_p (INTVAL (operand1)))) + || ! cint_ok_for_move (INTVAL (operand1))) { - rtx temp = reload_in_progress ? operand0 : gen_reg_rtx (mode); + rtx temp; + + if (reload_in_progress || reload_completed) + temp = operand0; + else + temp = gen_reg_rtx (mode); + emit_insn (gen_rtx (SET, VOIDmode, temp, gen_rtx (HIGH, mode, operand1))); operands[1] = gen_rtx (LO_SUM, mode, temp, operand1); @@ -884,50 +1055,58 @@ read_only_operand (operand) { if (GET_CODE (operand) == CONST) operand = XEXP (XEXP (operand, 0), 0); - if (GET_CODE (operand) == SYMBOL_REF) - return SYMBOL_REF_FLAG (operand) || CONSTANT_POOL_ADDRESS_P (operand); + if (flag_pic) + { + if (GET_CODE (operand) == SYMBOL_REF) + return SYMBOL_REF_FLAG (operand) && !CONSTANT_POOL_ADDRESS_P (operand); + } + else + { + if (GET_CODE (operand) == SYMBOL_REF) + return SYMBOL_REF_FLAG (operand) || CONSTANT_POOL_ADDRESS_P (operand); + } return 1; } - + /* Return the best assembler insn template - for moving operands[1] into operands[0] as a fullword. - - For CONST_DOUBLE and CONST_INT we should also check for - other values we can load directly via zdepi, ldil, etc. - ??? Do this for 2.5. */ - + for moving operands[1] into operands[0] as a fullword. */ char * singlemove_string (operands) rtx *operands; { + HOST_WIDE_INT intval; + if (GET_CODE (operands[0]) == MEM) return "stw %r1,%0"; - else if (GET_CODE (operands[1]) == MEM) + if (GET_CODE (operands[1]) == MEM) return "ldw %1,%0"; - else if (GET_CODE (operands[1]) == CONST_DOUBLE - && GET_MODE (operands[1]) == SFmode) + if (GET_CODE (operands[1]) == CONST_DOUBLE) { - int i; - union real_extract u; - union float_extract { float f; int i; } v; - - bcopy (&CONST_DOUBLE_LOW (operands[1]), &u, sizeof u); - v.f = REAL_VALUE_TRUNCATE (SFmode, u.d); - i = v.i; - - operands[1] = gen_rtx (CONST_INT, VOIDmode, i); + long i; + REAL_VALUE_TYPE d; - if (INT_14_BITS (operands[1])) - return (INTVAL (operands[1]) == 0 ? "copy 0,%0" : "ldi %1,%0"); - else - return "ldil L'%1,%0\n\tldo R'%1(%0),%0"; - } + if (GET_MODE (operands[1]) != SFmode) + abort (); - else if (GET_CODE (operands[1]) == CONST_INT) - { - if (INT_14_BITS (operands[1])) - return (INTVAL (operands[1]) == 0 ? "copy 0,%0" : "ldi %1,%0"); + /* Translate the CONST_DOUBLE to a CONST_INT with the same target + bit pattern. */ + REAL_VALUE_FROM_CONST_DOUBLE (d, operands[1]); + REAL_VALUE_TO_TARGET_SINGLE (d, i); + + operands[1] = GEN_INT (i); + /* Fall through to CONST_INT case. */ + } + if (GET_CODE (operands[1]) == CONST_INT) + { + intval = INTVAL (operands[1]); + + if (VAL_14_BITS_P (intval)) + return "ldi %1,%0"; + else if ((intval & 0x7ff) == 0) + return "ldil L'%1,%0"; + else if (zdepi_cint_p (intval)) + return "zdepi %Z1,%0"; else return "ldil L'%1,%0\n\tldo R'%1(%0),%0"; } @@ -940,7 +1119,7 @@ singlemove_string (operands) instructions. Store the immediate value to insert in OP[0]. */ void compute_zdepi_operands (imm, op) - unsigned imm; + unsigned HOST_WIDE_INT imm; unsigned *op; { int lsb, len; @@ -1221,10 +1400,10 @@ output_fp_move_double (operands) { if (FP_REG_P (operands[0])) { - if (FP_REG_P (operands[1]) + if (FP_REG_P (operands[1]) || operands[1] == CONST0_RTX (GET_MODE (operands[0]))) output_asm_insn ("fcpy,dbl %r1,%0", operands); - else + else output_asm_insn ("fldds%F1 %1,%0", operands); } else if (FP_REG_P (operands[1])) @@ -1240,8 +1419,8 @@ output_fp_move_double (operands) xoperands[0] = operands[0]; output_asm_insn ("copy %%r0,%0\n\tcopy %%r0,%1", xoperands); } - /* This is a pain. You have to be prepared to deal with an - arbritary address here including pre/post increment/decrement. + /* This is a pain. You have to be prepared to deal with an + arbitrary address here including pre/post increment/decrement. so avoid this in the MD. */ else @@ -1306,7 +1485,6 @@ output_block_move (operands, size_is_con if (size_is_constant) { - unsigned long n_items; unsigned long offset; rtx temp; @@ -1317,7 +1495,7 @@ output_block_move (operands, size_is_con if (align >= 4) { /* Don't unroll too large blocks. */ - if (n_bytes > 64) + if (n_bytes > 32) goto copy_with_loop; /* Read and store using two registers, and hide latency @@ -1344,7 +1522,7 @@ output_block_move (operands, size_is_con else { /* Store the last, partial word. */ - operands[4] = gen_rtx (CONST_INT, VOIDmode, n_bytes % 4); + operands[4] = GEN_INT (n_bytes % 4); output_asm_insn ("stbys,e %2,%4(0,%0)", operands); } return ""; @@ -1392,14 +1570,14 @@ output_block_move (operands, size_is_con if (align != 4) abort(); - + copy_with_loop: if (size_is_constant) { /* Size is compile-time determined, and also not very small (such small cases are handled above). */ - operands[4] = gen_rtx (CONST_INT, VOIDmode, n_bytes - 4); + operands[4] = GEN_INT (n_bytes - 4); output_asm_insn ("ldo %4(0),%2", operands); } else @@ -1425,7 +1603,7 @@ output_block_move (operands, size_is_con { /* Read the entire word of the source block tail. */ output_asm_insn ("ldw 0(0,%1),%3", operands); - operands[4] = gen_rtx (CONST_INT, VOIDmode, n_bytes % 4); + operands[4] = GEN_INT (n_bytes % 4); output_asm_insn ("stbys,e %3,%4(0,%0)", operands); } } @@ -1439,7 +1617,7 @@ output_block_move (operands, size_is_con output_asm_insn ("ldw 0(0,%1),%3", operands); /* Make %0 point at the first byte after the destination block. */ - output_asm_insn ("add %2,%0,%0", operands); + output_asm_insn ("addl %2,%0,%0", operands); /* Store the leftmost bytes, up to, but not including, the address in %0. */ output_asm_insn ("stbys,e %3,0(0,%0)", operands); @@ -1480,9 +1658,7 @@ compute_movstrsi_length (insn) if (size_is_constant) { - unsigned long n_items; unsigned long offset; - rtx temp; if (n_bytes == 0) return 0; @@ -1490,7 +1666,7 @@ compute_movstrsi_length (insn) if (align >= 4) { /* Don't unroll too large blocks. */ - if (n_bytes > 64) + if (n_bytes > 32) goto copy_with_loop; /* first load */ @@ -1502,7 +1678,7 @@ compute_movstrsi_length (insn) /* Count last store or partial store. */ insn_count += 1; - return insn_count; + return insn_count * 4; } if (align >= 2 && n_bytes >= 2) @@ -1521,7 +1697,7 @@ compute_movstrsi_length (insn) /* ??? final store from loop. */ insn_count += 1; - return insn_count; + return insn_count * 4; } /* First load. */ @@ -1534,12 +1710,12 @@ compute_movstrsi_length (insn) /* Final store. */ insn_count += 1; - return insn_count; + return insn_count * 4; } if (align != 4) abort(); - + copy_with_loop: /* setup for constant and non-constant case. */ @@ -1558,7 +1734,7 @@ compute_movstrsi_length (insn) } else insn_count += 4; - return insn_count; + return insn_count * 4; } @@ -1568,7 +1744,7 @@ output_and (operands) { if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) != 0) { - unsigned mask = INTVAL (operands[2]); + unsigned HOST_WIDE_INT mask = INTVAL (operands[2]); int ls0, ls1, ms0, p, len; for (ls0 = 0; ls0 < 32; ls0++) @@ -1593,7 +1769,7 @@ output_and (operands) if (len == 0) abort (); - operands[2] = gen_rtx (CONST_INT, VOIDmode, len); + operands[2] = GEN_INT (len); return "extru %1,31,%2,%0"; } else @@ -1604,8 +1780,8 @@ output_and (operands) p = 31 - ls0; len = ls1 - ls0; - operands[2] = gen_rtx (CONST_INT, VOIDmode, p); - operands[3] = gen_rtx (CONST_INT, VOIDmode, len); + operands[2] = GEN_INT (p); + operands[3] = GEN_INT (len); return "depi 0,%2,%3,%0"; } } @@ -1617,9 +1793,9 @@ char * output_ior (operands) rtx *operands; { - unsigned mask = INTVAL (operands[2]); - int bs0, bs1, bs2, p, len; - + unsigned HOST_WIDE_INT mask = INTVAL (operands[2]); + int bs0, bs1, p, len; + if (INTVAL (operands[2]) == 0) return "copy %1,%0"; @@ -1631,18 +1807,19 @@ output_ior (operands) if ((mask & (1 << bs1)) == 0) break; - if (bs1 != 32 && ((unsigned) 1 << bs1) <= mask) + if (bs1 != 32 && ((unsigned HOST_WIDE_INT) 1 << bs1) <= mask) abort(); p = 31 - bs0; len = bs1 - bs0; - operands[2] = gen_rtx (CONST_INT, VOIDmode, p); - operands[3] = gen_rtx (CONST_INT, VOIDmode, len); + operands[2] = GEN_INT (p); + operands[3] = GEN_INT (len); return "depi -1,%2,%3,%0"; } /* Output an ascii string. */ +void output_ascii (file, p, size) FILE *file; unsigned char *p; @@ -1760,7 +1937,7 @@ output_ascii (file, p, size) . . SP + p (SP') points to next available address. - + */ /* Emit RTL to store REG at the memory location specified by BASE+DISP. @@ -1774,19 +1951,19 @@ store_reg (reg, disp, base) { if (VAL_14_BITS_P (disp)) { - emit_move_insn (gen_rtx (MEM, SImode, - gen_rtx (PLUS, SImode, + emit_move_insn (gen_rtx (MEM, SImode, + gen_rtx (PLUS, SImode, gen_rtx (REG, SImode, base), GEN_INT (disp))), gen_rtx (REG, SImode, reg)); } else { - emit_insn (gen_add_high_const (gen_rtx (REG, SImode, 1), - gen_rtx (REG, SImode, base), + emit_insn (gen_add_high_const (gen_rtx (REG, SImode, 1), + gen_rtx (REG, SImode, base), GEN_INT (disp))); emit_move_insn (gen_rtx (MEM, SImode, - gen_rtx (LO_SUM, SImode, + gen_rtx (LO_SUM, SImode, gen_rtx (REG, SImode, 1), GEN_INT (disp))), gen_rtx (REG, SImode, reg)); @@ -1805,21 +1982,20 @@ load_reg (reg, disp, base) if (VAL_14_BITS_P (disp)) { emit_move_insn (gen_rtx (REG, SImode, reg), - gen_rtx (MEM, SImode, - gen_rtx (PLUS, SImode, + gen_rtx (MEM, SImode, + gen_rtx (PLUS, SImode, gen_rtx (REG, SImode, base), GEN_INT (disp)))); - } else { - emit_insn (gen_add_high_const (gen_rtx (REG, SImode, 1), + emit_insn (gen_add_high_const (gen_rtx (REG, SImode, 1), gen_rtx (REG, SImode, base), GEN_INT (disp))); emit_move_insn (gen_rtx (REG, SImode, reg), gen_rtx (MEM, SImode, - gen_rtx (LO_SUM, SImode, - gen_rtx (REG, SImode, 1), + gen_rtx (LO_SUM, SImode, + gen_rtx (REG, SImode, 1), GEN_INT (disp)))); } } @@ -1836,18 +2012,17 @@ set_reg_plus_d(reg, base, disp) if (VAL_14_BITS_P (disp)) { emit_move_insn (gen_rtx (REG, SImode, reg), - gen_rtx (PLUS, SImode, + gen_rtx (PLUS, SImode, gen_rtx (REG, SImode, base), GEN_INT (disp))); - } else { - emit_insn (gen_add_high_const (gen_rtx (REG, SImode, 1), + emit_insn (gen_add_high_const (gen_rtx (REG, SImode, 1), gen_rtx (REG, SImode, base), GEN_INT (disp))); emit_move_insn (gen_rtx (REG, SImode, reg), - gen_rtx (LO_SUM, SImode, + gen_rtx (LO_SUM, SImode, gen_rtx (REG, SImode, 1), GEN_INT (disp))); } @@ -1867,54 +2042,38 @@ compute_frame_size (size, fregs_live) extern int current_function_outgoing_args_size; int i, fsize; - /* 8 is space for frame pointer + filler. If any frame is allocated + /* 8 is space for frame pointer + filler. If any frame is allocated we need to add this in because of STARTING_FRAME_OFFSET. */ fsize = size + (size || frame_pointer_needed ? 8 : 0); - /* fp is stored in a special place. */ - if (frame_pointer_needed) + for (i = 18; i >= 4; i--) { - for (i = 18; i >= 5; i--) - if (regs_ever_live[i]) - fsize += 4; - - if (regs_ever_live[3]) + if (regs_ever_live[i]) fsize += 4; } - else + /* If we don't have a frame pointer, the register normally used for that + purpose is saved just like other registers, not in the "frame marker". */ + if (! frame_pointer_needed) { - for (i = 18; i >= 3; i--) - if (regs_ever_live[i]) - fsize += 4; + if (regs_ever_live[FRAME_POINTER_REGNUM]) + fsize += 4; } fsize = (fsize + 7) & ~7; - if (!TARGET_SNAKE) - { - for (i = 43; i >= 40; i--) - if (regs_ever_live[i]) - { - fsize += 8; - if (fregs_live) - *fregs_live = 1; - } - } - else - { - for (i = 78; i >= 60; i -= 2) - if (regs_ever_live[i] || regs_ever_live[i + 1]) - { - fsize += 8; - if (fregs_live) - *fregs_live = 1; - } - } + for (i = 66; i >= 48; i -= 2) + if (regs_ever_live[i] || regs_ever_live[i + 1]) + { + fsize += 8; + if (fregs_live) + *fregs_live = 1; + } + fsize += current_function_outgoing_args_size; if (! leaf_function_p () || fsize) fsize += 32; - return TARGET_SNAKE ? (fsize + 63 & ~63) : fsize; + return (fsize + 63) & ~63; } - + rtx hp_profile_label_rtx; static char hp_profile_label_name[8]; void @@ -1922,33 +2081,71 @@ output_function_prologue (file, size) FILE *file; int size; { + /* The function's label and associated .PROC must never be + separated and must be output *after* any profiling declarations + to avoid changing spaces/subspaces within a procedure. */ + ASM_OUTPUT_LABEL (file, XSTR (XEXP (DECL_RTL (current_function_decl), 0), 0)); + fputs ("\t.PROC\n", file); /* hppa_expand_prologue does the dirty work now. We just need to output the assembler directives which denote the start of a function. */ - fprintf (file, "\t.PROC\n\t.CALLINFO FRAME=%d", actual_fsize); + fprintf (file, "\t.CALLINFO FRAME=%d", actual_fsize); if (regs_ever_live[2] || profile_flag) - fprintf (file, ",CALLS,SAVE_RP\n"); + fprintf (file, ",CALLS,SAVE_RP"); else - fprintf (file, ",NO_CALLS\n"); - fprintf (file, "\t.ENTRY\n"); + fprintf (file, ",NO_CALLS"); + + if (frame_pointer_needed) + fprintf (file, ",SAVE_SP"); + + /* Pass on information about the number of callee register saves + performed in the prologue. + + The compiler is supposed to pass the highest register number + saved, the assembler then has to adjust that number before + entering it into the unwind descriptor (to account for any + caller saved registers with lower register numbers than the + first callee saved register). */ + if (gr_saved) + fprintf (file, ",ENTRY_GR=%d", gr_saved + 2); + + if (fr_saved) + fprintf (file, ",ENTRY_FR=%d", fr_saved + 11); + + fprintf (file, "\n\t.ENTRY\n"); /* Horrid hack. emit_function_prologue will modify this RTL in place to get the expected results. */ if (profile_flag) - sprintf(hp_profile_label_name, "LP$%04d", hp_profile_labelno); + ASM_GENERATE_INTERNAL_LABEL (hp_profile_label_name, "LP", + hp_profile_labelno); + + if (insn_addresses) + { + unsigned int old_total = total_code_bytes; + + total_code_bytes += insn_addresses[INSN_UID (get_last_insn())]; + total_code_bytes += FUNCTION_BOUNDARY /BITS_PER_UNIT; + + /* Be prepared to handle overflows. */ + total_code_bytes = old_total > total_code_bytes ? -1 : total_code_bytes; + } + else + total_code_bytes = -1; } +void hppa_expand_prologue() { - extern char call_used_regs[]; int size = get_frame_size (); int merge_sp_adjust_with_store = 0; int i, offset; rtx tmpreg, size_rtx; - + gr_saved = 0; + fr_saved = 0; save_fregs = 0; local_fsize = size + (size || frame_pointer_needed ? 8 : 0); actual_fsize = compute_frame_size (size, &save_fregs); @@ -1957,11 +2154,11 @@ hppa_expand_prologue() tmpreg = gen_rtx (REG, SImode, 1); size_rtx = GEN_INT (actual_fsize); - /* Save RP first. The calling conventions manual states RP will + /* Save RP first. The calling conventions manual states RP will always be stored into the caller's frame at sp-20. */ if (regs_ever_live[2] || profile_flag) - store_reg (2, -20, STACK_POINTER_REGNUM); - + store_reg (2, -20, STACK_POINTER_REGNUM); + /* Allocate the local frame and set up the frame pointer if needed. */ if (actual_fsize) if (frame_pointer_needed) @@ -1980,10 +2177,18 @@ hppa_expand_prologue() size_rtx, tmpreg)); else { - store_reg (1, 0, FRAME_POINTER_REGNUM); + /* It is incorrect to store the saved frame pointer at *sp, + then increment sp (writes beyond the current stack boundary). + + So instead use stwm to store at *sp and post-increment the + stack pointer as an atomic operation. Then increment sp to + finish allocating the new frame. */ + emit_insn (gen_post_stwm (stack_pointer_rtx, + stack_pointer_rtx, + GEN_INT (64), tmpreg)); set_reg_plus_d (STACK_POINTER_REGNUM, STACK_POINTER_REGNUM, - actual_fsize); + actual_fsize - 64); } } /* no frame pointer needed. */ @@ -1993,8 +2198,8 @@ hppa_expand_prologue() and allocating the stack frame at the same time. If so, just make a note of it and defer allocating the frame until saving the callee registers. */ - if (VAL_14_BITS_P (-actual_fsize) - && local_fsize == 0 + if (VAL_14_BITS_P (-actual_fsize) + && local_fsize == 0 && ! profile_flag && ! flag_pic) merge_sp_adjust_with_store = 1; @@ -2006,17 +2211,20 @@ hppa_expand_prologue() } /* The hppa calling conventions say that that %r19, the pic offset register, is saved at sp - 32 (in this function's frame) when - generating PIC code. */ - if (flag_pic) - store_reg (19, -32, STACK_POINTER_REGNUM); + generating PIC code. FIXME: What is the correct thing to do + for functions which make no calls and allocate no frame? Do + we need to allocate a frame, or can we just omit the save? For + now we'll just omit the save. */ + if (actual_fsize != 0 && flag_pic) + store_reg (PIC_OFFSET_TABLE_REGNUM, -32, STACK_POINTER_REGNUM); /* Profiling code. Instead of taking one argument, the counter label, as most normal mcounts do, _mcount appears to behave differently on the HPPA. It - takes the return address of the caller, the address of this routine, - and the address of the label. Also, it isn't magic, so - argument registre hsave to be preserved. */ + takes the return address of the caller, the address of this routine, + and the address of the label. Also, it isn't magic, so + argument registers have to be preserved. */ if (profile_flag) { int pc_offset, i, arg_offset, basereg, offsetadj; @@ -2068,19 +2276,21 @@ hppa_expand_prologue() } - /* Normal register save. + /* Normal register save. Do not save the frame pointer in the frame_pointer_needed case. It was done earlier. */ if (frame_pointer_needed) { - for (i = 18, offset = local_fsize; i >= 3; i--) - if (regs_ever_live[i] && ! call_used_regs[i] - && i != FRAME_POINTER_REGNUM) + for (i = 18, offset = local_fsize; i >= 4; i--) + if (regs_ever_live[i] && ! call_used_regs[i]) { - store_reg (i, offset, FRAME_POINTER_REGNUM); + store_reg (i, offset, FRAME_POINTER_REGNUM); offset += 4; + gr_saved++; } + /* Account for %r4 which is saved in a special place. */ + gr_saved++; } /* No frame pointer needed. */ else @@ -2088,7 +2298,7 @@ hppa_expand_prologue() for (i = 18, offset = local_fsize - actual_fsize; i >= 3; i--) if (regs_ever_live[i] && ! call_used_regs[i]) { - /* If merge_sp_adjust_with_store is nonzero, then we can + /* If merge_sp_adjust_with_store is nonzero, then we can optimize the first GR save. */ if (merge_sp_adjust_with_store) { @@ -2101,6 +2311,7 @@ hppa_expand_prologue() else store_reg (i, offset, STACK_POINTER_REGNUM); offset += 4; + gr_saved++; } /* If we wanted to merge the SP adjustment with a GR save, but we never @@ -2110,7 +2321,7 @@ hppa_expand_prologue() STACK_POINTER_REGNUM, actual_fsize); } - + /* Align pointer properly (doubleword boundary). */ offset = (offset + 7) & ~7; @@ -2126,27 +2337,36 @@ hppa_expand_prologue() set_reg_plus_d (1, STACK_POINTER_REGNUM, offset); /* Now actually save the FP registers. */ - if (! TARGET_SNAKE) - { - for (i = 43; i >= 40; i--) - { - if (regs_ever_live[i]) - emit_move_insn (gen_rtx (MEM, DFmode, - gen_rtx (POST_INC, DFmode, tmpreg)), - gen_rtx (REG, DFmode, i)); - } - } - else - { - for (i = 78; i >= 60; i -= 2) - if (regs_ever_live[i] || regs_ever_live[i + 1]) - { - emit_move_insn (gen_rtx (MEM, DFmode, - gen_rtx (POST_INC, DFmode, tmpreg)), - gen_rtx (REG, DFmode, i)); - } - } + for (i = 66; i >= 48; i -= 2) + if (regs_ever_live[i] || regs_ever_live[i + 1]) + { + emit_move_insn (gen_rtx (MEM, DFmode, + gen_rtx (POST_INC, DFmode, tmpreg)), + gen_rtx (REG, DFmode, i)); + fr_saved++; + } } + + /* When generating PIC code it is necessary to save/restore the + PIC register around each function call. We used to do this + in the call patterns themselves, but that implementation + made incorrect assumptions about using global variables to hold + per-function rtl code generated in the backend. + + So instead, we copy the PIC register into a reserved callee saved + register in the prologue. Then after each call we reload the PIC + register from the callee saved register. We also reload the PIC + register from the callee saved register in the epilogue ensure the + PIC register is valid at function exit. + + This may (depending on the exact characteristics of the function) + even be more efficient. + + Avoid this if the callee saved register wasn't used (these are + leaf functions. */ + if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM_SAVED]) + emit_move_insn (gen_rtx (REG, SImode, PIC_OFFSET_TABLE_REGNUM_SAVED), + gen_rtx (REG, SImode, PIC_OFFSET_TABLE_REGNUM)); } @@ -2155,8 +2375,8 @@ output_function_epilogue (file, size) FILE *file; int size; { - rtx insn = get_last_insn (); + int i; /* hppa_expand_epilogue does the dirty work now. We just need to output the assembler directives which denote the end @@ -2164,7 +2384,7 @@ output_function_epilogue (file, size) To make debuggers happy, emit a nop if the epilogue was completely eliminated due to a volatile call as the last insn in the - current function. That way the return address (in %r2) will + current function. That way the return address (in %r2) will always point to a valid instruction in the current function. */ /* Get the last real insn. */ @@ -2175,18 +2395,35 @@ output_function_epilogue (file, size) if (insn && GET_CODE (insn) == INSN && GET_CODE (PATTERN (insn)) == SEQUENCE) insn = XVECEXP (PATTERN (insn), 0, 0); - /* If insn is a CALL_INSN, then it must be a call to a volatile + /* If insn is a CALL_INSN, then it must be a call to a volatile function (otherwise there would be epilogue insns). */ if (insn && GET_CODE (insn) == CALL_INSN) fprintf (file, "\tnop\n"); - + fprintf (file, "\t.EXIT\n\t.PROCEND\n"); + + /* If we have deferred plabels, then we need to switch into the data + section and align it to a 4 byte boundary before we output the + deferred plabels. */ + if (n_deferred_plabels) + { + data_section (); + ASM_OUTPUT_ALIGN (file, 2); + } + + /* Now output the deferred plabels. */ + for (i = 0; i < n_deferred_plabels; i++) + { + ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (deferred_plabels[i].internal_label)); + ASM_OUTPUT_INT (file, deferred_plabels[i].symbol); + } + n_deferred_plabels = 0; } void hppa_expand_epilogue () { - rtx tmpreg; + rtx tmpreg; int offset,i; int merge_sp_adjust_with_load = 0; @@ -2209,9 +2446,8 @@ hppa_expand_epilogue () /* General register restores. */ if (frame_pointer_needed) { - for (i = 18, offset = local_fsize; i >= 3; i--) - if (regs_ever_live[i] && ! call_used_regs[i] - && i != FRAME_POINTER_REGNUM) + for (i = 18, offset = local_fsize; i >= 4; i--) + if (regs_ever_live[i] && ! call_used_regs[i]) { load_reg (i, offset, FRAME_POINTER_REGNUM); offset += 4; @@ -2248,23 +2484,11 @@ hppa_expand_epilogue () set_reg_plus_d (1, STACK_POINTER_REGNUM, offset); /* Actually do the restores now. */ - if (! TARGET_SNAKE) - { - for (i = 43; i >= 40; i--) - if (regs_ever_live[i]) - emit_move_insn (gen_rtx (REG, DFmode, i), - gen_rtx (MEM, DFmode, - gen_rtx (POST_INC, DFmode, tmpreg))); - - } - else - { - for (i = 78; i >= 60; i -= 2) - if (regs_ever_live[i] || regs_ever_live[i + 1]) - emit_move_insn (gen_rtx (REG, DFmode, i), - gen_rtx (MEM, DFmode, - gen_rtx (POST_INC, DFmode, tmpreg))); - } + for (i = 66; i >= 48; i -= 2) + if (regs_ever_live[i] || regs_ever_live[i + 1]) + emit_move_insn (gen_rtx (REG, DFmode, i), + gen_rtx (MEM, DFmode, + gen_rtx (POST_INC, DFmode, tmpreg))); } /* No frame pointer, but we have a stack greater than 8k. We restore @@ -2277,8 +2501,14 @@ hppa_expand_epilogue () set_reg_plus_d (STACK_POINTER_REGNUM, STACK_POINTER_REGNUM, - actual_fsize); - /* Uses value left over in %r1 by set_reg_plus_d. */ - load_reg (2, - (actual_fsize + 20 + ((- actual_fsize) & ~0x7ff)), 1); + + /* This used to try and be clever by not depending on the value in + %r30 and instead use the value held in %r1 (so that the 2nd insn + which sets %r30 could be put in the delay slot of the return insn). + + That won't work since if the stack is exactly 8k set_reg_plus_d + doesn't set %r1, just %r30. */ + load_reg (2, - 20, STACK_POINTER_REGNUM); } /* Reset stack pointer (and possibly frame pointer). The stack */ @@ -2299,7 +2529,7 @@ hppa_expand_epilogue () emit_insn (gen_pre_ldwm (stack_pointer_rtx, stack_pointer_rtx, GEN_INT (- actual_fsize), - gen_rtx (REG, SImode, + gen_rtx (REG, SImode, merge_sp_adjust_with_load))); else if (actual_fsize != 0) set_reg_plus_d (STACK_POINTER_REGNUM, @@ -2331,7 +2561,7 @@ emit_bcond_fp (code, operand0) { emit_jump_insn (gen_rtx (SET, VOIDmode, pc_rtx, gen_rtx (IF_THEN_ELSE, VOIDmode, - gen_rtx (code, VOIDmode, + gen_rtx (code, VOIDmode, gen_rtx (REG, CCFPmode, 0), const0_rtx), gen_rtx (LABEL_REF, VOIDmode, operand0), @@ -2389,18 +2619,20 @@ pa_adjust_cost (insn, link, dep_insn, co switch (get_attr_type (dep_insn)) { case TYPE_FPLOAD: - /* This cost 3 cycles, not 2 as the md says. */ - return cost + 1; + /* This cost 3 cycles, not 2 as the md says for the + 700 and 7100. Note scaling of cost for 7100. */ + return cost + (pa_cpu == PROCESSOR_700) ? 1 : 2; case TYPE_FPALU: - case TYPE_FPMUL: + case TYPE_FPMULSGL: + case TYPE_FPMULDBL: case TYPE_FPDIVSGL: case TYPE_FPDIVDBL: case TYPE_FPSQRTSGL: case TYPE_FPSQRTDBL: /* In these important cases, we save one cycle compared to when flop instruction feed each other. */ - return cost - 1; + return cost - (pa_cpu == PROCESSOR_700) ? 1 : 2; default: return cost; @@ -2438,16 +2670,52 @@ pa_adjust_cost (insn, link, dep_insn, co switch (get_attr_type (dep_insn)) { case TYPE_FPALU: - case TYPE_FPMUL: + case TYPE_FPMULSGL: + case TYPE_FPMULDBL: case TYPE_FPDIVSGL: case TYPE_FPDIVDBL: case TYPE_FPSQRTSGL: case TYPE_FPSQRTDBL: /* A fpload can't be issued until one cycle before a - preceeding arithmetic operation has finished, if + preceding arithmetic operation has finished if the target of the fpload is any of the sources (or destination) of the arithmetic operation. */ - return cost - 1; + return cost - (pa_cpu == PROCESSOR_700) ? 1 : 2; + + default: + return 0; + } + } + } + else if (get_attr_type (insn) == TYPE_FPALU) + { + rtx pat = PATTERN (insn); + rtx dep_pat = PATTERN (dep_insn); + if (GET_CODE (pat) == PARALLEL) + { + /* This happens for the fldXs,mb patterns. */ + pat = XVECEXP (pat, 0, 0); + } + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) + /* If this happens, we have to extend this to schedule + optimally. Return 0 for now. */ + return 0; + + if (reg_mentioned_p (SET_DEST (pat), SET_SRC (dep_pat))) + { + if (! recog_memoized (dep_insn)) + return 0; + switch (get_attr_type (dep_insn)) + { + case TYPE_FPDIVSGL: + case TYPE_FPDIVDBL: + case TYPE_FPSQRTSGL: + case TYPE_FPSQRTDBL: + /* An ALU flop can't be issued until two cycles before a + preceding divide or sqrt operation has finished if + the target of the ALU flop is any of the sources + (or destination) of the divide or sqrt operation. */ + return cost - (pa_cpu == PROCESSOR_700) ? 2 : 4; default: return 0; @@ -2458,25 +2726,100 @@ pa_adjust_cost (insn, link, dep_insn, co /* For other anti dependencies, the cost is 0. */ return 0; } + else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT) + { + /* Output dependency; DEP_INSN writes a register that INSN writes some + cycles later. */ + if (get_attr_type (insn) == TYPE_FPLOAD) + { + rtx pat = PATTERN (insn); + rtx dep_pat = PATTERN (dep_insn); + if (GET_CODE (pat) == PARALLEL) + { + /* This happens for the fldXs,mb patterns. */ + pat = XVECEXP (pat, 0, 0); + } + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) + /* If this happens, we have to extend this to schedule + optimally. Return 0 for now. */ + return 0; + + if (reg_mentioned_p (SET_DEST (pat), SET_DEST (dep_pat))) + { + if (! recog_memoized (dep_insn)) + return 0; + switch (get_attr_type (dep_insn)) + { + case TYPE_FPALU: + case TYPE_FPMULSGL: + case TYPE_FPMULDBL: + case TYPE_FPDIVSGL: + case TYPE_FPDIVDBL: + case TYPE_FPSQRTSGL: + case TYPE_FPSQRTDBL: + /* A fpload can't be issued until one cycle before a + preceding arithmetic operation has finished if + the target of the fpload is the destination of the + arithmetic operation. */ + return cost - (pa_cpu == PROCESSOR_700) ? 1 : 2; + + default: + return 0; + } + } + } + else if (get_attr_type (insn) == TYPE_FPALU) + { + rtx pat = PATTERN (insn); + rtx dep_pat = PATTERN (dep_insn); + if (GET_CODE (pat) == PARALLEL) + { + /* This happens for the fldXs,mb patterns. */ + pat = XVECEXP (pat, 0, 0); + } + if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET) + /* If this happens, we have to extend this to schedule + optimally. Return 0 for now. */ + return 0; + + if (reg_mentioned_p (SET_DEST (pat), SET_DEST (dep_pat))) + { + if (! recog_memoized (dep_insn)) + return 0; + switch (get_attr_type (dep_insn)) + { + case TYPE_FPDIVSGL: + case TYPE_FPDIVDBL: + case TYPE_FPSQRTSGL: + case TYPE_FPSQRTDBL: + /* An ALU flop can't be issued until two cycles before a + preceding divide or sqrt operation has finished if + the target of the ALU flop is also the target of + of the divide or sqrt operation. */ + return cost - (pa_cpu == PROCESSOR_700) ? 2 : 4; + + default: + return 0; + } + } + } - /* For output dependencies, the cost is often one too high. */ - return cost - 1; + /* For other output dependencies, the cost is 0. */ + return 0; + } + else + abort (); } /* Return any length adjustment needed by INSN which already has its length - computed as LENGTH. Return zero if no adjustment is necessary. + computed as LENGTH. Return zero if no adjustment is necessary. - For the PA: function calls, millicode calls, and short conditional branches - with unfilled delay slots need an adjustment by +1 (to account for - the NOP which will be inserted into the instruction stream). + For the PA: function calls, millicode calls, and backwards short + conditional branches with unfilled delay slots need an adjustment by +1 + (to account for the NOP which will be inserted into the instruction stream). Also compute the length of an inline block move here as it is too - complicated to express as a length attribute in pa.md. - - (For 2.5) Indirect calls do not need length adjustment as their - delay slot is filled internally in the output template. - - (For 2.5) No adjustment is necessary for jump tables or casesi insns. */ + complicated to express as a length attribute in pa.md. */ int pa_adjust_insn_length (insn, length) rtx insn; @@ -2484,16 +2827,33 @@ pa_adjust_insn_length (insn, length) { rtx pat = PATTERN (insn); - /* Call insn with an unfilled delay slot. */ + /* Call insns which are *not* indirect and have unfilled delay slots. */ if (GET_CODE (insn) == CALL_INSN) - return 1; + { + + if (GET_CODE (XVECEXP (pat, 0, 0)) == CALL + && GET_CODE (XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0)) == SYMBOL_REF) + return 4; + else if (GET_CODE (XVECEXP (pat, 0, 0)) == SET + && GET_CODE (XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0)) + == SYMBOL_REF) + return 4; + else + return 0; + } + /* Jumps inside switch tables which have unfilled delay slots + also need adjustment. */ + else if (GET_CODE (insn) == JUMP_INSN + && simplejump_p (insn) + && GET_MODE (PATTERN (insn)) == DImode) + return 4; /* Millicode insn with an unfilled delay slot. */ else if (GET_CODE (insn) == INSN && GET_CODE (pat) != SEQUENCE && GET_CODE (pat) != USE && GET_CODE (pat) != CLOBBER && get_attr_type (insn) == TYPE_MILLI) - return 1; + return 4; /* Block move pattern. */ else if (GET_CODE (insn) == INSN && GET_CODE (pat) == PARALLEL @@ -2501,11 +2861,28 @@ pa_adjust_insn_length (insn, length) && GET_CODE (XEXP (XVECEXP (pat, 0, 0), 1)) == MEM && GET_MODE (XEXP (XVECEXP (pat, 0, 0), 0)) == BLKmode && GET_MODE (XEXP (XVECEXP (pat, 0, 0), 1)) == BLKmode) - return compute_movstrsi_length (insn) - 1; + return compute_movstrsi_length (insn) - 4; /* Conditional branch with an unfilled delay slot. */ - else if (GET_CODE (insn) == JUMP_INSN && ! simplejump_p (insn) - && length != 2 && length != 4) - return 1; + else if (GET_CODE (insn) == JUMP_INSN && ! simplejump_p (insn)) + { + /* Adjust a short backwards conditional with an unfilled delay slot. */ + if (GET_CODE (pat) == SET + && length == 4 + && ! forward_branch_p (insn)) + return 4; + /* Adjust dbra insn with short backwards conditional branch with + unfilled delay slot -- only for case where counter is in a + general register register. */ + else if (GET_CODE (pat) == PARALLEL + && GET_CODE (XVECEXP (pat, 0, 1)) == SET + && GET_CODE (XEXP (XVECEXP (pat, 0, 1), 0)) == REG + && ! FP_REG_P (XEXP (XVECEXP (pat, 0, 1), 0)) + && length == 4 + && ! forward_branch_p (insn)) + return 4; + else + return 0; + } else return 0; } @@ -2529,7 +2906,7 @@ print_operand (file, x, code) return; case '*': /* Output an nullification completer if there's nothing for the */ - /* delay slot or nullification is requested. */ + /* delay slot or nullification is requested. */ if (dbr_sequence_length () == 0 || (final_sequence && INSN_ANNULLED_BRANCH_P (XVECEXP (final_sequence, 0, 0)))) @@ -2554,7 +2931,7 @@ print_operand (file, x, code) case 'C': /* Plain (C)ondition */ case 'X': switch (GET_CODE (x)) - { + { case EQ: fprintf (file, "="); break; case NE: @@ -2576,8 +2953,6 @@ print_operand (file, x, code) case LTU: fprintf (file, "<<"); break; default: - printf ("Can't grok '%c' operator:\n", code); - debug_rtx (x); abort (); } return; @@ -2605,8 +2980,6 @@ print_operand (file, x, code) case LTU: fprintf (file, ">>="); break; default: - printf ("Can't grok '%c' operator:\n", code); - debug_rtx (x); abort (); } return; @@ -2628,8 +3001,6 @@ print_operand (file, x, code) case LE: fprintf (file, "!<="); break; default: - printf ("Can't grok '%c' operator:\n", code); - debug_rtx (x); abort (); } return; @@ -2657,10 +3028,8 @@ print_operand (file, x, code) case LTU: fprintf (file, ">>"); break; default: - printf ("Can't grok '%c' operator:\n", code); - debug_rtx (x); abort (); - } + } return; case 'B': /* Condition, (B)oth swapped and negate. */ switch (GET_CODE (x)) @@ -2686,10 +3055,8 @@ print_operand (file, x, code) case LTU: fprintf (file, "<<="); break; default: - printf ("Can't grok '%c' operator:\n", code); - debug_rtx (x); abort (); - } + } return; case 'k': if (GET_CODE (x) == CONST_INT) @@ -2769,7 +3136,12 @@ print_operand (file, x, code) abort (); } if (GET_CODE (x) == REG) - fprintf (file, "%s", reg_names [REGNO (x)]); + { + if (FP_REG_P (x) && GET_MODE_SIZE (GET_MODE (x)) <= 4 && (REGNO (x) & 1) == 0) + fprintf (file, "%sL", reg_names [REGNO (x)]); + else + fprintf (file, "%s", reg_names [REGNO (x)]); + } else if (GET_CODE (x) == MEM) { int size = GET_MODE_SIZE (GET_MODE (x)); @@ -2789,6 +3161,12 @@ print_operand (file, x, code) break; } } +#if 0 + /* The code here is completely wrong. It attempts to extract parts of + a CONST_DOUBLE which is wrong since REAL_ARITHMETIC is defined, and it + extracts the wrong indices (0 instead of 2 and 1 instead of 3) using + the wrong macro (XINT instead of XWINT). + Just disable it for now, since the code will never be used anyway! */ else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) == SFmode) { union { double d; int i[2]; } u; @@ -2800,12 +3178,13 @@ print_operand (file, x, code) else fprintf (file, "0x%x", u1.i); } - else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != DImode) + else if (GET_CODE (x) == CONST_DOUBLE && GET_MODE (x) != VOIDmode) { union { double d; int i[2]; } u; u.i[0] = XINT (x, 0); u.i[1] = XINT (x, 1); fprintf (file, "0r%.20g", u.d); } +#endif else output_addr_const (file, x); } @@ -2824,7 +3203,7 @@ output_global_address (file, x) if (GET_CODE (x) == SYMBOL_REF && read_only_operand (x)) assemble_name (file, XSTR (x, 0)); - else if (GET_CODE (x) == SYMBOL_REF) + else if (GET_CODE (x) == SYMBOL_REF && !flag_pic) { assemble_name (file, XSTR (x, 0)); fprintf (file, "-$global$"); @@ -2834,7 +3213,7 @@ output_global_address (file, x) char *sep = ""; int offset = 0; /* assembler wants -$global$ at end */ rtx base; - + if (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF) { base = XEXP (XEXP (x, 0), 0); @@ -2868,7 +3247,7 @@ output_global_address (file, x) sep = "-"; else abort (); - if (!read_only_operand (base)) + if (!read_only_operand (base) && !flag_pic) fprintf (file, "-$global$"); fprintf (file, "%s", sep); if (offset) fprintf (file,"%d", offset); @@ -2886,12 +3265,12 @@ static char *milli_names[] = {"remI", "r static char import_string[] = ".IMPORT $$....,MILLICODE"; #define MILLI_START 10 -static int +static void import_milli (code) enum millicodes code; { char str[sizeof (import_string)]; - + if (!imported[(int)code]) { imported[(int)code] = 1; @@ -2901,37 +3280,26 @@ import_milli (code) } } -/* The register constraints have put the operands and return value in +/* The register constraints have put the operands and return value in the proper registers. */ char * -output_mul_insn (unsignedp) +output_mul_insn (unsignedp, insn) int unsignedp; + rtx insn; { - if (unsignedp) - { - import_milli (mulU); - return "bl $$mulU,31%#"; - } - else - { - import_milli (mulI); - return "bl $$mulI,31%#"; - } + import_milli (mulI); + return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$mulI"), + gen_rtx (REG, SImode, 31)); } -/* If operands isn't NULL, then it's a CONST_INT with which we can do - something */ - - /* Emit the rtl for doing a division by a constant. */ - /* Do magic division millicodes exist for this value? */ - +/* Do magic division millicodes exist for this value? */ static int magic_milli[]= {0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1}; -/* We'll use an array to keep track of the magic millicodes and +/* We'll use an array to keep track of the magic millicodes and whether or not we've used them already. [n][0] is signed, [n][1] is unsigned. */ @@ -2977,28 +3345,39 @@ emit_hpdiv_const (operands, unsignedp) } char * -output_div_insn (operands, unsignedp) +output_div_insn (operands, unsignedp, insn) rtx *operands; int unsignedp; + rtx insn; { int divisor; - - /* If the divisor is a constant, try to use one of the special + + /* If the divisor is a constant, try to use one of the special opcodes .*/ if (GET_CODE (operands[0]) == CONST_INT) { + static char buf[100]; divisor = INTVAL (operands[0]); if (!div_milli[divisor][unsignedp]) { + div_milli[divisor][unsignedp] = 1; if (unsignedp) output_asm_insn (".IMPORT $$divU_%0,MILLICODE", operands); else output_asm_insn (".IMPORT $$divI_%0,MILLICODE", operands); - div_milli[divisor][unsignedp] = 1; } if (unsignedp) - return "bl $$divU_%0,31%#"; - return "bl $$divI_%0,31%#"; + { + sprintf (buf, "$$divU_%d", INTVAL (operands[0])); + return output_call (insn, gen_rtx (SYMBOL_REF, SImode, buf), + gen_rtx (REG, SImode, 31)); + } + else + { + sprintf (buf, "$$divI_%d", INTVAL (operands[0])); + return output_call (insn, gen_rtx (SYMBOL_REF, SImode, buf), + gen_rtx (REG, SImode, 31)); + } } /* Divisor isn't a special constant. */ else @@ -3006,12 +3385,14 @@ output_div_insn (operands, unsignedp) if (unsignedp) { import_milli (divU); - return "bl $$divU,31%#"; + return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$divU"), + gen_rtx (REG, SImode, 31)); } else { import_milli (divI); - return "bl $$divI,31%#"; + return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$divI"), + gen_rtx (REG, SImode, 31)); } } } @@ -3019,76 +3400,77 @@ output_div_insn (operands, unsignedp) /* Output a $$rem millicode to do mod. */ char * -output_mod_insn (unsignedp) +output_mod_insn (unsignedp, insn) int unsignedp; + rtx insn; { if (unsignedp) { import_milli (remU); - return "bl $$remU,31%#"; + return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$remU"), + gen_rtx (REG, SImode, 31)); } else { import_milli (remI); - return "bl $$remI,31%#"; + return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$remI"), + gen_rtx (REG, SImode, 31)); } } void -output_arg_descriptor (insn) - rtx insn; +output_arg_descriptor (call_insn) + rtx call_insn; { char *arg_regs[4]; enum machine_mode arg_mode; - rtx prev_insn; + rtx link; int i, output_flag = 0; int regno; - + for (i = 0; i < 4; i++) arg_regs[i] = 0; - for (prev_insn = PREV_INSN (insn); GET_CODE (prev_insn) == INSN; - prev_insn = PREV_INSN (prev_insn)) + /* Specify explicitly that no argument relocations should take place + if using the portable runtime calling conventions. */ + if (TARGET_PORTABLE_RUNTIME) { - if (!(GET_CODE (PATTERN (prev_insn)) == USE && - GET_CODE (XEXP (PATTERN (prev_insn), 0)) == REG && - FUNCTION_ARG_REGNO_P (REGNO (XEXP (PATTERN (prev_insn), 0))))) - break; - arg_mode = GET_MODE (XEXP (PATTERN (prev_insn), 0)); - regno = REGNO (XEXP (PATTERN (prev_insn), 0)); + fprintf (asm_out_file, + "\t.CALL ARGW0=NO,ARGW1=NO,ARGW2=NO,ARGW3=NO,RETVAL=NO\n"); + return; + } + + if (GET_CODE (call_insn) != CALL_INSN) + abort (); + for (link = CALL_INSN_FUNCTION_USAGE (call_insn); link; link = XEXP (link, 1)) + { + rtx use = XEXP (link, 0); + + if (! (GET_CODE (use) == USE + && GET_CODE (XEXP (use, 0)) == REG + && FUNCTION_ARG_REGNO_P (REGNO (XEXP (use, 0))))) + continue; + + arg_mode = GET_MODE (XEXP (use, 0)); + regno = REGNO (XEXP (use, 0)); if (regno >= 23 && regno <= 26) { arg_regs[26 - regno] = "GR"; if (arg_mode == DImode) arg_regs[25 - regno] = "GR"; } - else if (!TARGET_SNAKE) /* fp args */ - { - if (arg_mode == SFmode) - arg_regs[regno - 32] = "FR"; - else - { -#ifdef HP_FP_ARG_DESCRIPTOR_REVERSED - arg_regs[regno - 33] = "FR"; - arg_regs[regno - 32] = "FU"; -#else - arg_regs[regno - 33] = "FU"; - arg_regs[regno - 32] = "FR"; -#endif - } - } - else + else if (regno >= 32 && regno <= 39) { if (arg_mode == SFmode) - arg_regs[(regno - 44) / 2] = "FR"; + arg_regs[(regno - 32) / 2] = "FR"; else { -#ifdef HP_FP_ARG_DESCRIPTOR_REVERSED - arg_regs[(regno - 46) / 2] = "FR"; - arg_regs[(regno - 46) / 2 + 1] = "FU"; +#ifndef HP_FP_ARG_DESCRIPTOR_REVERSED + arg_regs[(regno - 34) / 2] = "FR"; + arg_regs[(regno - 34) / 2 + 1] = "FU"; #else - arg_regs[(regno - 46) / 2] = "FU"; - arg_regs[(regno - 46) / 2 + 1] = "FR"; + arg_regs[(regno - 34) / 2] = "FU"; + arg_regs[(regno - 34) / 2 + 1] = "FR"; #endif } } @@ -3117,19 +3499,40 @@ secondary_reload_class (class, mode, in) { int regno = true_regnum (in); - if ((TARGET_SHARED_LIBS && function_label_operand (in, mode)) - || ((regno >= FIRST_PSEUDO_REGISTER || regno == -1) - && GET_MODE_CLASS (mode) == MODE_INT - && FP_REG_CLASS_P (class)) + /* Trying to load a constant into a FP register during PIC code + generation will require %r1 as a scratch register. */ + if (flag_pic == 2 + && GET_MODE_CLASS (mode) == MODE_INT + && FP_REG_CLASS_P (class) + && (GET_CODE (in) == CONST_INT || GET_CODE (in) == CONST_DOUBLE)) + return R1_REGS; + + if (((regno >= FIRST_PSEUDO_REGISTER || regno == -1) + && GET_MODE_CLASS (mode) == MODE_INT + && FP_REG_CLASS_P (class)) || (class == SHIFT_REGS && (regno <= 0 || regno >= 32))) return GENERAL_REGS; if (GET_CODE (in) == HIGH) in = XEXP (in, 0); - if (TARGET_KERNEL && class != R1_REGS && symbolic_operand (in, VOIDmode)) + if (!flag_pic + && symbolic_operand (in, VOIDmode) + && read_only_operand (in)) + return NO_REGS; + + if (class != R1_REGS && symbolic_operand (in, VOIDmode)) return R1_REGS; + if (GET_CODE (in) == SUBREG) + in = SUBREG_REG (in); + + if (FP_REG_CLASS_P (class) + && GET_CODE (in) == MEM + && !memory_address_p (DFmode, XEXP (in, 0)) + && memory_address_p (SImode, XEXP (in, 0))) + return GENERAL_REGS; + return NO_REGS; } @@ -3168,7 +3571,7 @@ struct rtx_def * hppa_builtin_saveregs (arglist) tree arglist; { - rtx block, float_addr, offset, float_mem; + rtx offset; tree fntype = TREE_TYPE (current_function_decl); int argadj = ((!(TYPE_ARG_TYPES (fntype) != 0 && (TREE_VALUE (tree_last (TYPE_ARG_TYPES (fntype))) @@ -3185,17 +3588,17 @@ hppa_builtin_saveregs (arglist) gen_rtx (MEM, BLKmode, plus_constant (current_function_internal_arg_pointer, -16)), - 4); + 4, 4 * UNITS_PER_WORD); return copy_to_reg (expand_binop (Pmode, add_optab, current_function_internal_arg_pointer, offset, 0, 0, OPTAB_LIB_WIDEN)); } -/* This routine handles all the normal conditional branch sequences we - might need to generate. It handles compare immediate vs compare - register, nullification of delay slots, varying length branches, +/* This routine handles all the normal conditional branch sequences we + might need to generate. It handles compare immediate vs compare + register, nullification of delay slots, varying length branches, negated branches, and all combinations of the above. It returns the - output appropriate to emit the branch corresponding to all given + output appropriate to emit the branch corresponding to all given parameters. */ char * @@ -3203,24 +3606,43 @@ output_cbranch (operands, nullify, lengt rtx *operands; int nullify, length, negated; rtx insn; -{ +{ static char buf[100]; int useskip = 0; - /* A forward branch over a single nullified insn can be done with a + /* A conditional branch to the following instruction (eg the delay slot) is + asking for a disaster. This can happen when not optimizing. + + In such cases it is safe to emit nothing. */ + + if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn)) + return ""; + + /* If this is a long branch with its delay slot unfilled, set `nullify' + as it can nullify the delay slot and save a nop. */ + if (length == 8 && dbr_sequence_length () == 0) + nullify = 1; + + /* If this is a short forward conditional branch which did not get + its delay slot filled, the delay slot can still be nullified. */ + if (! nullify && length == 4 && dbr_sequence_length () == 0) + nullify = forward_branch_p (insn); + + /* A forward branch over a single nullified insn can be done with a comclr instruction. This avoids a single cycle penalty due to mis-predicted branch if we fall through (branch not taken). */ - - if (length == 1 - && JUMP_LABEL (insn) == next_nonnote_insn (NEXT_INSN (insn)) + if (length == 4 + && next_real_insn (insn) != 0 + && get_attr_length (next_real_insn (insn)) == 4 + && JUMP_LABEL (insn) == next_nonnote_insn (next_real_insn (insn)) && nullify) useskip = 1; switch (length) { - - /* Short conditional branch. May nullify either direction. */ - case 1: + /* All short conditional branches except backwards with an unfilled + delay slot. */ + case 4: if (useskip) strcpy (buf, "com%I2clr,"); else @@ -3233,46 +3655,63 @@ output_cbranch (operands, nullify, lengt strcat (buf, " %2,%1,0"); else if (nullify) strcat (buf, ",n %2,%1,%0"); - else - strcat (buf, " %2,%1,%0%#"); - break; - - /* Long conditional branch, possible forward nullification. Also - note all conditional branches have a length of 4 when not - optimizing! */ - case 2: - case 4: - strcpy (buf, "com%I2clr,"); - if (negated) - strcat (buf, "%S3"); - else - strcat (buf, "%B3"); - /* Nullify the delay slot if the delay slot was explicitly - nullified by the delay branch scheduler or if no insn - could be placed in the delay slot. */ - if (nullify) - strcat (buf, " %2,%1,0\n\tbl,n %0,0"); else - strcat (buf, " %2,%1,0\n\tbl%* %0,0"); + strcat (buf, " %2,%1,%0"); break; - /* Long backward conditional branch with nullification. */ - case 3: - strcpy (buf, "com%I2b,"); - if (negated) - strcat (buf, "%S3"); + /* All long conditionals. Note an short backward branch with an + unfilled delay slot is treated just like a long backward branch + with an unfilled delay slot. */ + case 8: + /* Handle weird backwards branch with a filled delay slot + with is nullified. */ + if (dbr_sequence_length () != 0 + && ! forward_branch_p (insn) + && nullify) + { + strcpy (buf, "com%I2b,"); + if (negated) + strcat (buf, "%S3"); + else + strcat (buf, "%B3"); + strcat (buf, ",n %2,%1,.+12\n\tbl %0,0"); + } + /* Handle short backwards branch with an unfilled delay slot. + Using a comb;nop rather than comiclr;bl saves 1 cycle for both + taken and untaken branches. */ + else if (dbr_sequence_length () == 0 + && ! forward_branch_p (insn) + && insn_addresses + && VAL_14_BITS_P (insn_addresses[INSN_UID (JUMP_LABEL (insn))] + - insn_addresses[INSN_UID (insn)])) + { + strcpy (buf, "com%I2b,"); + if (negated) + strcat (buf, "%B3 %2,%1,%0%#"); + else + strcat (buf, "%S3 %2,%1,%0%#"); + } else - strcat (buf, "%B3"); - strcat (buf, " %2,%1,.+16\n\tnop\n\t bl %0,0"); + { + strcpy (buf, "com%I2clr,"); + if (negated) + strcat (buf, "%S3"); + else + strcat (buf, "%B3"); + if (nullify) + strcat (buf, " %2,%1,0\n\tbl,n %0,0"); + else + strcat (buf, " %2,%1,0\n\tbl %0,0"); + } break; default: abort(); - } + } return buf; } -/* This routine handles all the branch-on-bit conditional branch sequences we +/* This routine handles all the branch-on-bit conditional branch sequences we might need to generate. It handles nullification of delay slots, varying length branches, negated branches and all combinations of the above. it returns the appropriate output template to emit the branch. */ @@ -3283,27 +3722,48 @@ output_bb (operands, nullify, length, ne int nullify, length, negated; rtx insn; int which; -{ +{ static char buf[100]; int useskip = 0; - /* A forward branch over a single nullified insn can be done with a + /* A conditional branch to the following instruction (eg the delay slot) is + asking for a disaster. I do not think this can happen as this pattern + is only used when optimizing; jump optimization should eliminate the + jump. But be prepared just in case. */ + + if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn)) + return ""; + + /* If this is a long branch with its delay slot unfilled, set `nullify' + as it can nullify the delay slot and save a nop. */ + if (length == 8 && dbr_sequence_length () == 0) + nullify = 1; + + /* If this is a short forward conditional branch which did not get + its delay slot filled, the delay slot can still be nullified. */ + if (! nullify && length == 4 && dbr_sequence_length () == 0) + nullify = forward_branch_p (insn); + + /* A forward branch over a single nullified insn can be done with a extrs instruction. This avoids a single cycle penalty due to mis-predicted branch if we fall through (branch not taken). */ - if (length == 1 - && JUMP_LABEL (insn) == next_nonnote_insn (NEXT_INSN (insn)) + if (length == 4 + && next_real_insn (insn) != 0 + && get_attr_length (next_real_insn (insn)) == 4 + && JUMP_LABEL (insn) == next_nonnote_insn (next_real_insn (insn)) && nullify) useskip = 1; switch (length) { - /* Short conditional branch. May nullify either direction. */ - case 1: + /* All short conditional branches except backwards with an unfilled + delay slot. */ + case 4: if (useskip) strcpy (buf, "extrs,"); - else + else strcpy (buf, "bb,"); if ((which == 0 && negated) || (which == 1 && ! negated)) @@ -3317,69 +3777,515 @@ output_bb (operands, nullify, length, ne else if (nullify && ! negated) strcat (buf, ",n %0,%1,%2"); else if (! nullify && negated) - strcat (buf, "%0,%1,%3%#"); + strcat (buf, "%0,%1,%3"); else if (! nullify && ! negated) - strcat (buf, " %0,%1,%2%#"); - break; - - /* Long conditional branch, possible forward nullification. Also - note all conditional branches have a length of 4 when not - optimizing! */ - case 2: - case 4: - strcpy (buf, "extrs,"); - if ((which == 0 && negated) - || (which == 1 && ! negated)) - strcat (buf, "<"); - else - strcat (buf, ">="); - /* Nullify the delay slot if the delay slot was explicitly - nullified by the delay branch scheduler or if no insn - could be placed in the delay slot. */ - if (nullify && negated) - strcat (buf, " %0,%1,1,0\n\tbl,n %3,0"); - else if (nullify && ! negated) - strcat (buf, " %0,%1,1,0\n\tbl,n %2,0"); - else if (negated) - strcat (buf, " %0,%1,1,0\n\tbl%* %3,0"); - else - strcat (buf, " %0,%1,1,0\n\tbl%* %2,0"); + strcat (buf, " %0,%1,%2"); break; - /* Long backward conditional branch with nullification. */ - case 3: - strcpy (buf, "bb,"); - if ((which == 0 && negated) - || (which == 1 && ! negated)) - strcat (buf, "<"); - else - strcat (buf, ">="); - if (negated) - strcat (buf, " %0,%1,.+16\n\tnop\n\t bl %3,0"); + /* All long conditionals. Note an short backward branch with an + unfilled delay slot is treated just like a long backward branch + with an unfilled delay slot. */ + case 8: + /* Handle weird backwards branch with a filled delay slot + with is nullified. */ + if (dbr_sequence_length () != 0 + && ! forward_branch_p (insn) + && nullify) + { + strcpy (buf, "bb,"); + if ((which == 0 && negated) + || (which == 1 && ! negated)) + strcat (buf, "<"); + else + strcat (buf, ">="); + if (negated) + strcat (buf, ",n %0,%1,.+12\n\tbl %3,0"); + else + strcat (buf, ",n %0,%1,.+12\n\tbl %2,0"); + } + /* Handle short backwards branch with an unfilled delay slot. + Using a bb;nop rather than extrs;bl saves 1 cycle for both + taken and untaken branches. */ + else if (dbr_sequence_length () == 0 + && ! forward_branch_p (insn) + && insn_addresses + && VAL_14_BITS_P (insn_addresses[INSN_UID (JUMP_LABEL (insn))] + - insn_addresses[INSN_UID (insn)])) + { + strcpy (buf, "bb,"); + if ((which == 0 && negated) + || (which == 1 && ! negated)) + strcat (buf, ">="); + else + strcat (buf, "<"); + if (negated) + strcat (buf, " %0,%1,%3%#"); + else + strcat (buf, " %0,%1,%2%#"); + } else - strcat (buf, " %0,%1,.+16\n\tnop\n\t bl %2,0"); + { + strcpy (buf, "extrs,"); + if ((which == 0 && negated) + || (which == 1 && ! negated)) + strcat (buf, "<"); + else + strcat (buf, ">="); + if (nullify && negated) + strcat (buf, " %0,%1,1,0\n\tbl,n %3,0"); + else if (nullify && ! negated) + strcat (buf, " %0,%1,1,0\n\tbl,n %2,0"); + else if (negated) + strcat (buf, " %0,%1,1,0\n\tbl %3,0"); + else + strcat (buf, " %0,%1,1,0\n\tbl %2,0"); + } break; default: abort(); - } + } return buf; } +/* Return the output template for emitting a dbra type insn. + + Note it may perform some output operations on its own before + returning the final output string. */ +char * +output_dbra (operands, insn, which_alternative) + rtx *operands; + rtx insn; + int which_alternative; +{ + + /* A conditional branch to the following instruction (eg the delay slot) is + asking for a disaster. Be prepared! */ + + if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn)) + { + if (which_alternative == 0) + return "ldo %1(%0),%0"; + else if (which_alternative == 1) + { + output_asm_insn ("fstws %0,-16(0,%%r30)",operands); + output_asm_insn ("ldw -16(0,%%r30),%4",operands); + output_asm_insn ("ldo %1(%4),%4\n\tstw %4,-16(0,%%r30)", operands); + return "fldws -16(0,%%r30),%0"; + } + else + { + output_asm_insn ("ldw %0,%4", operands); + return "ldo %1(%4),%4\n\tstw %4,%0"; + } + } + + if (which_alternative == 0) + { + int nullify = INSN_ANNULLED_BRANCH_P (insn); + int length = get_attr_length (insn); + + /* If this is a long branch with its delay slot unfilled, set `nullify' + as it can nullify the delay slot and save a nop. */ + if (length == 8 && dbr_sequence_length () == 0) + nullify = 1; + + /* If this is a short forward conditional branch which did not get + its delay slot filled, the delay slot can still be nullified. */ + if (! nullify && length == 4 && dbr_sequence_length () == 0) + nullify = forward_branch_p (insn); + + /* Handle short versions first. */ + if (length == 4 && nullify) + return "addib,%C2,n %1,%0,%3"; + else if (length == 4 && ! nullify) + return "addib,%C2 %1,%0,%3"; + else if (length == 8) + { + /* Handle weird backwards branch with a fulled delay slot + which is nullified. */ + if (dbr_sequence_length () != 0 + && ! forward_branch_p (insn) + && nullify) + return "addib,%N2,n %1,%0,.+12\n\tbl %3,0"; + /* Handle short backwards branch with an unfilled delay slot. + Using a addb;nop rather than addi;bl saves 1 cycle for both + taken and untaken branches. */ + else if (dbr_sequence_length () == 0 + && ! forward_branch_p (insn) + && insn_addresses + && VAL_14_BITS_P (insn_addresses[INSN_UID (JUMP_LABEL (insn))] + - insn_addresses[INSN_UID (insn)])) + return "addib,%C2 %1,%0,%3%#"; + + /* Handle normal cases. */ + if (nullify) + return "addi,%N2 %1,%0,%0\n\tbl,n %3,0"; + else + return "addi,%N2 %1,%0,%0\n\tbl %3,0"; + } + else + abort(); + } + /* Deal with gross reload from FP register case. */ + else if (which_alternative == 1) + { + /* Move loop counter from FP register to MEM then into a GR, + increment the GR, store the GR into MEM, and finally reload + the FP register from MEM from within the branch's delay slot. */ + output_asm_insn ("fstws %0,-16(0,%%r30)\n\tldw -16(0,%%r30),%4",operands); + output_asm_insn ("ldo %1(%4),%4\n\tstw %4,-16(0,%%r30)", operands); + if (get_attr_length (insn) == 24) + return "comb,%S2 0,%4,%3\n\tfldws -16(0,%%r30),%0"; + else + return "comclr,%B2 0,%4,0\n\tbl %3,0\n\tfldws -16(0,%%r30),%0"; + } + /* Deal with gross reload from memory case. */ + else + { + /* Reload loop counter from memory, the store back to memory + happens in the branch's delay slot. */ + output_asm_insn ("ldw %0,%4", operands); + if (get_attr_length (insn) == 12) + return "addib,%C2 %1,%4,%3\n\tstw %4,%0"; + else + return "addi,%N2 %1,%4,%4\n\tbl %3,0\n\tstw %4,%0"; + } +} + +/* Return the output template for emitting a dbra type insn. + + Note it may perform some output operations on its own before + returning the final output string. */ +char * +output_movb (operands, insn, which_alternative, reverse_comparison) + rtx *operands; + rtx insn; + int which_alternative; + int reverse_comparison; +{ + + /* A conditional branch to the following instruction (eg the delay slot) is + asking for a disaster. Be prepared! */ + + if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn)) + { + if (which_alternative == 0) + return "copy %1,%0"; + else if (which_alternative == 1) + { + output_asm_insn ("stw %1,-16(0,%%r30)",operands); + return "fldws -16(0,%%r30),%0"; + } + else + return "stw %1,%0"; + } + + /* Support the second variant. */ + if (reverse_comparison) + PUT_CODE (operands[2], reverse_condition (GET_CODE (operands[2]))); + + if (which_alternative == 0) + { + int nullify = INSN_ANNULLED_BRANCH_P (insn); + int length = get_attr_length (insn); + + /* If this is a long branch with its delay slot unfilled, set `nullify' + as it can nullify the delay slot and save a nop. */ + if (length == 8 && dbr_sequence_length () == 0) + nullify = 1; + + /* If this is a short forward conditional branch which did not get + its delay slot filled, the delay slot can still be nullified. */ + if (! nullify && length == 4 && dbr_sequence_length () == 0) + nullify = forward_branch_p (insn); + + /* Handle short versions first. */ + if (length == 4 && nullify) + return "movb,%C2,n %1,%0,%3"; + else if (length == 4 && ! nullify) + return "movb,%C2 %1,%0,%3"; + else if (length == 8) + { + /* Handle weird backwards branch with a filled delay slot + which is nullified. */ + if (dbr_sequence_length () != 0 + && ! forward_branch_p (insn) + && nullify) + return "movb,%N2,n %1,%0,.+12\n\tbl %3,0"; + + /* Handle short backwards branch with an unfilled delay slot. + Using a movb;nop rather than or;bl saves 1 cycle for both + taken and untaken branches. */ + else if (dbr_sequence_length () == 0 + && ! forward_branch_p (insn) + && insn_addresses + && VAL_14_BITS_P (insn_addresses[INSN_UID (JUMP_LABEL (insn))] + - insn_addresses[INSN_UID (insn)])) + return "movb,%C2 %1,%0,%3%#"; + /* Handle normal cases. */ + if (nullify) + return "or,%N2 %1,%%r0,%0\n\tbl,n %3,0"; + else + return "or,%N2 %1,%%r0,%0\n\tbl %3,0"; + } + else + abort(); + } + /* Deal with gross reload from FP register case. */ + else if (which_alternative == 1) + { + /* Move loop counter from FP register to MEM then into a GR, + increment the GR, store the GR into MEM, and finally reload + the FP register from MEM from within the branch's delay slot. */ + output_asm_insn ("stw %1,-16(0,%%r30)",operands); + if (get_attr_length (insn) == 12) + return "comb,%S2 0,%1,%3\n\tfldws -16(0,%%r30),%0"; + else + return "comclr,%B2 0,%1,0\n\tbl %3,0\n\tfldws -16(0,%%r30),%0"; + } + /* Deal with gross reload from memory case. */ + else + { + /* Reload loop counter from memory, the store back to memory + happens in the branch's delay slot. */ + if (get_attr_length (insn) == 8) + return "comb,%S2 0,%1,%3\n\tstw %1,%0"; + else + return "comclr,%B2 0,%1,0\n\tbl %3,0\n\tstw %1,%0"; + } +} + + +/* INSN is either a function call or a millicode call. It may have an + unconditional jump in its delay slot. + + CALL_DEST is the routine we are calling. + + RETURN_POINTER is the register which will hold the return address. + %r2 for most calls, %r31 for millicode calls. + + When TARGET_MILLICODE_LONG_CALLS is true, then we have to assume + that two instruction sequences must be used to reach the millicode + routines (including dyncall!). */ + +char * +output_call (insn, call_dest, return_pointer) + rtx insn; + rtx call_dest; + rtx return_pointer; + +{ + int distance; + rtx xoperands[4]; + rtx seq_insn; + + /* Handle long millicode calls for mod, div, and mul. */ + if (TARGET_PORTABLE_RUNTIME + || (TARGET_MILLICODE_LONG_CALLS && REGNO (return_pointer) == 31)) + { + xoperands[0] = call_dest; + xoperands[1] = return_pointer; + output_asm_insn ("ldil L%%%0,%%r29", xoperands); + output_asm_insn ("ldo R%%%0(%%r29),%%r29", xoperands); + output_asm_insn ("blr 0,%r1\n\tbv,n 0(%%r29)\n\tnop", xoperands); + return ""; + } + + /* Handle common case -- empty delay slot or no jump in the delay slot, + and we're sure that the branch will reach the beginning of the $CODE$ + subspace. */ + if ((dbr_sequence_length () == 0 + && get_attr_length (insn) == 8) + || (dbr_sequence_length () != 0 + && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN + && get_attr_length (insn) == 4)) + { + xoperands[0] = call_dest; + xoperands[1] = return_pointer; + output_asm_insn ("bl %0,%r1%#", xoperands); + return ""; + } + + /* This call may not reach the beginning of the $CODE$ subspace. */ + if (get_attr_length (insn) > 8) + { + int delay_insn_deleted = 0; + rtx xoperands[2]; + rtx link; + + /* We need to emit an inline long-call branch. Furthermore, + because we're changing a named function call into an indirect + function call well after the parameters have been set up, we + need to make sure any FP args appear in both the integer + and FP registers. Also, we need move any delay slot insn + out of the delay slot -- Yuk! */ + if (dbr_sequence_length () != 0 + && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN) + { + /* A non-jump insn in the delay slot. By definition we can + emit this insn before the call (and in fact before argument + relocating. */ + final_scan_insn (NEXT_INSN (insn), asm_out_file, optimize, 0, 0); + + /* Now delete the delay insn. */ + PUT_CODE (NEXT_INSN (insn), NOTE); + NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + delay_insn_deleted = 1; + } + + /* Now copy any FP arguments into integer registers. */ + for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1)) + { + int arg_mode, regno; + rtx use = XEXP (link, 0); + if (! (GET_CODE (use) == USE + && GET_CODE (XEXP (use, 0)) == REG + && FUNCTION_ARG_REGNO_P (REGNO (XEXP (use, 0))))) + continue; + + arg_mode = GET_MODE (XEXP (use, 0)); + regno = REGNO (XEXP (use, 0)); + /* Is it a floating point register? */ + if (regno >= 32 && regno <= 39) + { + /* Copy from the FP register into an integer register + (via memory). */ + if (arg_mode == SFmode) + { + xoperands[0] = XEXP (use, 0); + xoperands[1] = gen_rtx (REG, SImode, 26 - (regno - 32) / 2); + output_asm_insn ("fstws %0,-16(%%sr0,%%r30)", xoperands); + output_asm_insn ("ldw -16(%%sr0,%%r30),%1", xoperands); + } + else + { + xoperands[0] = XEXP (use, 0); + xoperands[1] = gen_rtx (REG, DImode, 25 - (regno - 34) / 2); + output_asm_insn ("fstds %0,-16(%%sr0,%%r30)", xoperands); + output_asm_insn ("ldw -12(%%sr0,%%r30),%R1", xoperands); + output_asm_insn ("ldw -16(%%sr0,%%r30),%1", xoperands); + } + + } + } + + if (flag_pic) + { + /* We have to load the address of the function using a procedure + label (plabel). The LP and RP relocs don't work reliably for PIC, + so we make a plain 32 bit plabel in the data segment instead. We + have to defer outputting it of course... Not pretty. */ + + xoperands[0] = gen_label_rtx (); + output_asm_insn ("addil LT%%%0,%%r19\n\tldw RT%%%0(%%r1),%%r22", + xoperands); + output_asm_insn ("ldw 0(0,%%r22),%%r22", xoperands); + + if (deferred_plabels == 0) + deferred_plabels = (struct defer_plab *) + xmalloc (1 * sizeof (struct defer_plab)); + else + deferred_plabels = (struct defer_plab *) + xrealloc (deferred_plabels, + (n_deferred_plabels + 1) * sizeof (struct defer_plab)); + deferred_plabels[n_deferred_plabels].internal_label = xoperands[0]; + deferred_plabels[n_deferred_plabels].symbol = call_dest; + n_deferred_plabels++; + } + else + { + /* Now emit the inline long-call. */ + xoperands[0] = call_dest; + output_asm_insn ("ldil LP%%%0,%%r22\n\tldo RP%%%0(%%r22),%%r22", + xoperands); + } + + /* If TARGET_MILLICODE_LONG_CALLS, then we must use a long-call sequence + to call dyncall! */ + if (TARGET_MILLICODE_LONG_CALLS) + { + output_asm_insn ("ldil L%%$$dyncall,%%r31", xoperands); + output_asm_insn ("ldo R%%$$dyncall(%%r31),%%r31", xoperands); + output_asm_insn ("blr 0,%%r2\n\tbv,n 0(%%r31)\n\tnop", xoperands); + } + else + output_asm_insn ("bl $$dyncall,%%r31\n\tcopy %%r31,%%r2", xoperands); + + /* If we had a jump in the call's delay slot, output it now. */ + if (dbr_sequence_length () != 0 + && !delay_insn_deleted) + { + xoperands[0] = XEXP (PATTERN (NEXT_INSN (insn)), 1); + output_asm_insn ("b,n %0", xoperands); + + /* Now delete the delay insn. */ + PUT_CODE (NEXT_INSN (insn), NOTE); + NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + } + return ""; + } + + /* This call has an unconditional jump in its delay slot. */ + + /* Use the containing sequence insn's address. */ + seq_insn = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0))); + + distance = insn_addresses[INSN_UID (JUMP_LABEL (NEXT_INSN (insn)))] + - insn_addresses[INSN_UID (seq_insn)] - 8; + + /* If the branch was too far away, emit a normal call followed + by a nop, followed by the unconditional branch. + + If the branch is close, then adjust %r2 from within the + call's delay slot. */ + + xoperands[0] = call_dest; + xoperands[1] = XEXP (PATTERN (NEXT_INSN (insn)), 1); + xoperands[2] = return_pointer; + if (! VAL_14_BITS_P (distance)) + output_asm_insn ("bl %0,%r2\n\tnop\n\tbl,n %1,%%r0", xoperands); + else + { + xoperands[3] = gen_label_rtx (); + output_asm_insn ("\n\tbl %0,%r2\n\tldo %1-%3(%r2),%r2", xoperands); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", + CODE_LABEL_NUMBER (xoperands[3])); + } + + /* Delete the jump. */ + PUT_CODE (NEXT_INSN (insn), NOTE); + NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED; + NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0; + return ""; +} + +extern struct obstack permanent_obstack; extern struct obstack *saveable_obstack; /* In HPUX 8.0's shared library scheme, special relocations are needed - for function labels if they might be passed to a function + for function labels if they might be passed to a function in a shared library (because shared libraries don't live in code - space), and special magic is needed to construct their address. */ + space), and special magic is needed to construct their address. + + For reasons too disgusting to describe storage for the new name + is allocated either on the saveable_obstack (released at function + exit) or on the permanent_obstack for things that can never change + (libcall names for example). */ void -hppa_encode_label (sym) +hppa_encode_label (sym, permanent) rtx sym; + int permanent; { char *str = XSTR (sym, 0); int len = strlen (str); - char *newstr = obstack_alloc (saveable_obstack, len + 2) ; + char *newstr; + + newstr = obstack_alloc ((permanent ? &permanent_obstack : saveable_obstack), + len + 2); if (str[0] == '*') *newstr++ = *str++; @@ -3387,19 +4293,35 @@ hppa_encode_label (sym) *newstr = '@'; XSTR (sym,0) = newstr; } - + int -function_label_operand (op, mode) +function_label_operand (op, mode) rtx op; enum machine_mode mode; { return GET_CODE (op) == SYMBOL_REF && FUNCTION_NAME_P (XSTR (op, 0)); } +/* Returns 1 if OP is a function label involved in a simple addition + with a constant. Used to keep certain patterns from matching + during instruction combination. */ +int +is_function_label_plus_const (op) + rtx op; +{ + /* Strip off any CONST. */ + if (GET_CODE (op) == CONST) + op = XEXP (op, 0); + + return (GET_CODE (op) == PLUS + && function_label_operand (XEXP (op, 0), Pmode) + && GET_CODE (XEXP (op, 1)) == CONST_INT); +} + /* Returns 1 if the 6 operands specified in OPERANDS are suitable for use in fmpyadd instructions. */ int -fmpyaddoperands(operands) +fmpyaddoperands (operands) rtx *operands; { enum machine_mode mode = GET_MODE (operands[0]); @@ -3441,7 +4363,7 @@ fmpyaddoperands(operands) /* Returns 1 if the 6 operands specified in OPERANDS are suitable for use in fmpysub instructions. */ int -fmpysuboperands(operands) +fmpysuboperands (operands) rtx *operands; { enum machine_mode mode = GET_MODE (operands[0]); @@ -3508,3 +4430,150 @@ shadd_operand (op, mode) { return (GET_CODE (op) == CONST_INT && shadd_constant_p (INTVAL (op))); } + +/* Return 1 if this operand is anything other than a hard register. */ + +int +non_hard_reg_operand (op, mode) + rtx op; + enum machine_mode mode; +{ + return ! (GET_CODE (op) == REG && REGNO (op) < FIRST_PSEUDO_REGISTER); +} + +/* Return 1 if INSN branches forward. Should be using insn_addresses + to avoid walking through all the insns... */ +int +forward_branch_p (insn) + rtx insn; +{ + rtx label = JUMP_LABEL (insn); + + while (insn) + { + if (insn == label) + break; + else + insn = NEXT_INSN (insn); + } + + return (insn == label); +} + +/* Return 1 if OP is an equality comparison, else return 0. */ +int +eq_neq_comparison_operator (op, mode) + rtx op; + enum machine_mode mode; +{ + return (GET_CODE (op) == EQ || GET_CODE (op) == NE); +} + +/* Return 1 if OP is an operator suitable for use in a movb instruction. */ +int +movb_comparison_operator (op, mode) + rtx op; + enum machine_mode mode; +{ + return (GET_CODE (op) == EQ || GET_CODE (op) == NE + || GET_CODE (op) == LT || GET_CODE (op) == GE); +} + +/* Return 1 if INSN is in the delay slot of a call instruction. */ +int +jump_in_call_delay (insn) + rtx insn; +{ + + if (GET_CODE (insn) != JUMP_INSN) + return 0; + + if (PREV_INSN (insn) + && PREV_INSN (PREV_INSN (insn)) + && GET_CODE (next_active_insn (PREV_INSN (PREV_INSN (insn)))) == INSN) + { + rtx test_insn = next_active_insn (PREV_INSN (PREV_INSN (insn))); + + return (GET_CODE (PATTERN (test_insn)) == SEQUENCE + && XVECEXP (PATTERN (test_insn), 0, 1) == insn); + + } + else + return 0; +} + + +/* We use this hook to perform a PA specific optimization which is difficult + to do in earlier passes. + + We want the delay slots of branches within jump tables to be filled. + None of the compiler passes at the moment even has the notion that a + PA jump table doesn't contain addresses, but instead contains actual + instructions! + + Because we actually jump into the table, the addresses of each entry + must stay constant in relation to the beginning of the table (which + itself must stay constant relative to the instruction to jump into + it). I don't believe we can guarantee earlier passes of the compiler + will adhere to those rules. + + So, late in the compilation process we find all the jump tables, and + expand them into real code -- eg each entry in the jump table vector + will get an appropriate label followed by a jump to the final target. + + Reorg and the final jump pass can then optimize these branches and + fill their delay slots. We end up with smaller, more efficient code. + + The jump instructions within the table are special; we must be able + to identify them during assembly output (if the jumps don't get filled + we need to emit a nop rather than nullifying the delay slot)). We + identify jumps in switch tables by marking the SET with DImode. */ + +pa_reorg (insns) + rtx insns; +{ + rtx insn; + + /* This is fairly cheap, so always run it if optimizing. */ + if (optimize > 0) + { + /* Find and explode all ADDR_VEC insns. */ + insns = get_insns (); + for (insn = insns; insn; insn = NEXT_INSN (insn)) + { + rtx pattern, tmp, location; + unsigned int length, i; + + /* Find an ADDR_VEC insn to explode. */ + if (GET_CODE (insn) != JUMP_INSN + || GET_CODE (PATTERN (insn)) != ADDR_VEC) + continue; + + pattern = PATTERN (insn); + location = PREV_INSN (insn); + length = XVECLEN (pattern, 0); + for (i = 0; i < length; i++) + { + /* Emit the jump itself. */ + tmp = gen_switch_jump (XEXP (XVECEXP (pattern, 0, i), 0)); + tmp = emit_jump_insn_after (tmp, location); + JUMP_LABEL (tmp) = XEXP (XVECEXP (pattern, 0, i), 0); + LABEL_NUSES (JUMP_LABEL (tmp))++; + + /* Emit a BARRIER after the jump. */ + location = NEXT_INSN (location); + emit_barrier_after (location); + + /* Put a CODE_LABEL before each so jump.c does not optimize + the jumps away. */ + location = NEXT_INSN (location); + tmp = gen_label_rtx (); + LABEL_NUSES (tmp) = 1; + emit_label_after (tmp, location); + location = NEXT_INSN (location); + } + /* Delete the ADDR_VEC. */ + delete_insn (insn); + } + } +}