--- gcc/config/pa/pa.c 2018/04/24 18:23:33 1.1.1.3 +++ 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, 1993, 1994 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,7 +42,11 @@ 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; @@ -52,6 +57,49 @@ 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 @@ -72,7 +120,7 @@ call_operand_address (op, mode) rtx op; enum machine_mode mode; { - return (CONSTANT_P (op) && ! TARGET_LONG_CALLS); + return (CONSTANT_P (op) && ! TARGET_PORTABLE_RUNTIME); } /* Return 1 if X contains a symbolic expression. We know these @@ -219,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 @@ -235,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 @@ -377,10 +412,10 @@ int zdepi_cint_p (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. */ @@ -445,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; @@ -480,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) @@ -487,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); @@ -531,24 +572,6 @@ legitimize_pic_address (orig, mode, reg) return pic_ref; } -/* 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, PIC_OFFSET_TABLE_REGNUM)), - 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. @@ -595,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. */ @@ -607,6 +630,9 @@ hppa_legitimize_address (x, oldx, 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); @@ -727,9 +753,6 @@ hppa_legitimize_address (x, oldx, mode) } } - if (flag_pic) - return legitimize_pic_address (x, mode, gen_reg_rtx (Pmode)); - return orig; } @@ -783,7 +806,19 @@ emit_move_sequence (operands, mode, scra operand1 = XEXP (operand1, 0); scratch_reg = gen_rtx (REG, SImode, REGNO (scratch_reg)); - emit_move_insn (scratch_reg, XEXP (operand1, 0)); + + /* 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; @@ -800,7 +835,18 @@ emit_move_sequence (operands, mode, scra operand0 = XEXP (operand0, 0); scratch_reg = gen_rtx (REG, SImode, REGNO (scratch_reg)); - emit_move_insn (scratch_reg, XEXP (operand0, 0)); + /* 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; @@ -890,49 +936,78 @@ 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; if (reload_in_progress || reload_completed) - temp = operand0; + temp = scratch_reg ? scratch_reg : operand0; else temp = gen_reg_rtx (Pmode); - operands[1] = legitimize_pic_address (operand1, mode, temp); - emit_insn (gen_rtx (SET, VOIDmode, operand0, operands[1])); + /* 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. */ + /* 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, const_part = NULL; + rtx temp, set; if (reload_in_progress || reload_completed) temp = scratch_reg ? scratch_reg : operand0; else temp = gen_reg_rtx (mode); - /* 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. */ - 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 (ishighonly) set = gen_rtx (SET, mode, operand0, temp); else @@ -945,13 +1020,11 @@ emit_move_sequence (operands, mode, scra gen_rtx (HIGH, mode, operand1))); emit_insn (set); - /* Add back in the constant part if needed. */ - if (const_part != NULL) - emit_insn (gen_rtx (SET, mode, operand0, - plus_constant (operand0, - XEXP (const_part, 0)))); - return 1; } + + /* 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 @@ -982,8 +1055,16 @@ 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; } @@ -994,57 +1075,38 @@ 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); - - /* See if we can handle this constant in a single instruction. */ - if (cint_ok_for_move (INTVAL (operands[1]))) - { - HOST_WIDE_INT intval = INTVAL (operands[1]); - - if (intval == 0) - return "copy 0,%0"; - else 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"; - } + long i; + REAL_VALUE_TYPE d; - else if (GET_CODE (operands[1]) == CONST_INT) - { - /* See if we can handle this in a single instruction. */ - if (cint_ok_for_move (INTVAL (operands[1]))) - { - int intval = INTVAL (operands[1]); + if (GET_MODE (operands[1]) != SFmode) + abort (); - if (intval == 0) - return "copy 0,%0"; - else 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"; - } + /* 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"; } @@ -1358,7 +1420,7 @@ output_fp_move_double (operands) 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. + arbitrary address here including pre/post increment/decrement. so avoid this in the MD. */ else @@ -1460,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 ""; @@ -1515,7 +1577,7 @@ output_block_move (operands, size_is_con { /* 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 @@ -1541,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); } } @@ -1682,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++) @@ -1707,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 @@ -1718,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"; } } @@ -1731,7 +1793,7 @@ char * output_ior (operands) rtx *operands; { - unsigned mask = INTVAL (operands[2]); + unsigned HOST_WIDE_INT mask = INTVAL (operands[2]); int bs0, bs1, p, len; if (INTVAL (operands[2]) == 0) @@ -1745,14 +1807,14 @@ 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"; } @@ -2058,6 +2120,19 @@ output_function_prologue (file, size) if (profile_flag) 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 @@ -2136,8 +2211,11 @@ 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) + 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. @@ -2146,7 +2224,7 @@ hppa_expand_prologue() 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. */ + argument registers have to be preserved. */ if (profile_flag) { int pc_offset, i, arg_offset, basereg, offsetadj; @@ -2268,6 +2346,27 @@ hppa_expand_prologue() 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)); } @@ -2276,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 @@ -2302,6 +2401,23 @@ output_function_epilogue (file, size) 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 @@ -2385,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 */ @@ -2497,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; @@ -2546,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; @@ -2566,9 +2726,89 @@ 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 @@ -2601,6 +2841,12 @@ pa_adjust_insn_length (insn, length) 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 @@ -2915,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; @@ -2932,6 +3184,7 @@ print_operand (file, x, code) 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); } @@ -2950,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$"); @@ -2994,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); @@ -3035,29 +3288,14 @@ output_mul_insn (unsignedp, insn) int unsignedp; rtx insn; { - - if (unsignedp) - { - import_milli (mulU); - return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$mulU"), - gen_rtx (REG, SImode, 31)); - } - else - { - import_milli (mulI); - return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$mulI"), - gen_rtx (REG, SImode, 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}; @@ -3261,6 +3499,14 @@ secondary_reload_class (class, mode, in) { int regno = true_regnum (in); + /* 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)) @@ -3270,6 +3516,11 @@ secondary_reload_class (class, mode, in) if (GET_CODE (in) == HIGH) in = XEXP (in, 0); + 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; @@ -3364,7 +3615,7 @@ output_cbranch (operands, nullify, lengt In such cases it is safe to emit nothing. */ - if (JUMP_LABEL (insn) == next_nonnote_insn (insn)) + 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' @@ -3425,6 +3676,21 @@ output_cbranch (operands, nullify, lengt 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 { strcpy (buf, "com%I2clr,"); @@ -3465,7 +3731,7 @@ output_bb (operands, nullify, length, ne is only used when optimizing; jump optimization should eliminate the jump. But be prepared just in case. */ - if (JUMP_LABEL (insn) == next_nonnote_insn (insn)) + 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' @@ -3533,9 +3799,29 @@ output_bb (operands, nullify, length, ne else strcat (buf, ">="); if (negated) - strcat (buf, " %0,%1,.+12\n\tbl %3,0"); + strcat (buf, ",n %0,%1,.+12\n\tbl %3,0"); else - strcat (buf, " %0,%1,.+12\n\tbl %2,0"); + 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 { @@ -3576,7 +3862,7 @@ output_dbra (operands, insn, which_alter /* A conditional branch to the following instruction (eg the delay slot) is asking for a disaster. Be prepared! */ - if (JUMP_LABEL (insn) == next_nonnote_insn (insn)) + if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn)) { if (which_alternative == 0) return "ldo %1(%0),%0"; @@ -3622,6 +3908,15 @@ output_dbra (operands, insn, which_alter && ! 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) @@ -3673,7 +3968,7 @@ output_movb (operands, insn, which_alter /* A conditional branch to the following instruction (eg the delay slot) is asking for a disaster. Be prepared! */ - if (JUMP_LABEL (insn) == next_nonnote_insn (insn)) + if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn)) { if (which_alternative == 0) return "copy %1,%0"; @@ -3717,8 +4012,17 @@ output_movb (operands, insn, which_alter if (dbr_sequence_length () != 0 && ! forward_branch_p (insn) && nullify) - return "movb,%N2,n %1,%0,.+12\n\ttbl %3,0"; + 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"; @@ -3761,9 +4065,9 @@ output_movb (operands, insn, which_alter RETURN_POINTER is the register which will hold the return address. %r2 for most calls, %r31 for millicode calls. - When TARGET_LONG_CALLS is true, output_call is only called for - millicode calls. In addition, no delay slots are available when - TARGET_LONG_CALLS is true. */ + 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) @@ -3776,21 +4080,151 @@ output_call (insn, call_dest, return_poi rtx xoperands[4]; rtx seq_insn; - /* Handle common case -- empty delay slot or no jump in the delay slot. */ - if (dbr_sequence_length () == 0 + /* 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_CODE (NEXT_INSN (insn)) != JUMP_INSN + && get_attr_length (insn) == 4)) { xoperands[0] = call_dest; xoperands[1] = return_pointer; - if (TARGET_LONG_CALLS) + 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) { - 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); + /* 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 %0,%r1%#", xoperands); + 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 ""; } @@ -3828,20 +4262,30 @@ output_call (insn, call_dest, return_poi 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 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++; @@ -4057,3 +4501,79 @@ jump_in_call_delay (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); + } + } +}