--- gcc/config/pa/pa.c 2018/04/24 18:10:27 1.1 +++ gcc/config/pa/pa.c 2018/04/24 18:17:23 1.1.1.2 @@ -1,5 +1,5 @@ /* Subroutines for insn-output.c for HPPA. - Copyright (C) 1992 Free Software Foundation, Inc. + Copyright (C) 1992, 1993 Free Software Foundation, Inc. Contributed by Tim Moore (moore@cs.utah.edu), based on sparc.c This file is part of GNU CC. @@ -46,6 +46,10 @@ rtx hppa_save_pic_table_rtx; /* 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 (); /* Return non-zero only if OP is a register of mode MODE, @@ -58,13 +62,17 @@ 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_LONG_CALLS); } /* Return 1 if X contains a symbolic expression. We know these @@ -476,7 +484,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) @@ -646,9 +654,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,6 +681,40 @@ hppa_legitimize_address (x, oldx, mode) GEN_INT (val)), reg1)); } + + /* 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)); + } + } + if (flag_pic) return legitimize_pic_address (x, mode, gen_reg_rtx (Pmode)); @@ -747,7 +801,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, @@ -791,7 +845,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 +853,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; @@ -815,7 +868,13 @@ emit_move_sequence (operands, mode, scra { if (flag_pic) { - rtx temp = reload_in_progress ? operand0 : gen_reg_rtx (Pmode); + rtx temp; + + if (reload_in_progress || reload_completed) + temp = operand0; + else + temp = gen_reg_rtx (Pmode); + operands[1] = legitimize_pic_address (operand1, mode, temp); emit_insn (gen_rtx (SET, VOIDmode, operand0, operands[1])); } @@ -826,7 +885,7 @@ emit_move_sequence (operands, mode, scra { 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); @@ -841,11 +900,15 @@ emit_move_sequence (operands, mode, scra emit_insn (gen_rtx (SET, VOIDmode, temp, gen_rtx (HIGH, mode, operand1))); - if (TARGET_SHARED_LIBS - && function_label_operand (operand1, mode)) + if (function_label_operand (operand1, mode)) { - rtx temp = reload_in_progress ? scratch_reg - : gen_reg_rtx (mode); + rtx temp; + + if (reload_in_progress || reload_completed) + temp = scratch_reg; + else + temp = gen_reg_rtx (mode); + if (!temp) abort (); emit_insn (gen_rtx (PARALLEL, VOIDmode, @@ -861,11 +924,15 @@ emit_move_sequence (operands, mode, scra 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); @@ -891,12 +958,7 @@ read_only_operand (operand) /* 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; @@ -918,16 +980,40 @@ singlemove_string (operands) operands[1] = gen_rtx (CONST_INT, VOIDmode, i); - if (INT_14_BITS (operands[1])) - return (INTVAL (operands[1]) == 0 ? "copy 0,%0" : "ldi %1,%0"); + /* See if we can handle this constant in a single instruction. */ + if (cint_ok_for_move (INTVAL (operands[1]))) + { + 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"; } 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"); + /* See if we can handle this in a single instruction. */ + if (cint_ok_for_move (INTVAL (operands[1]))) + { + 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"; } @@ -1306,7 +1392,6 @@ output_block_move (operands, size_is_con if (size_is_constant) { - unsigned long n_items; unsigned long offset; rtx temp; @@ -1480,9 +1565,7 @@ compute_movstrsi_length (insn) if (size_is_constant) { - unsigned long n_items; unsigned long offset; - rtx temp; if (n_bytes == 0) return 0; @@ -1502,7 +1585,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 +1604,7 @@ compute_movstrsi_length (insn) /* ??? final store from loop. */ insn_count += 1; - return insn_count; + return insn_count * 4; } /* First load. */ @@ -1534,7 +1617,7 @@ compute_movstrsi_length (insn) /* Final store. */ insn_count += 1; - return insn_count; + return insn_count * 4; } if (align != 4) @@ -1558,7 +1641,7 @@ compute_movstrsi_length (insn) } else insn_count += 4; - return insn_count; + return insn_count * 4; } @@ -1618,7 +1701,7 @@ output_ior (operands) rtx *operands; { unsigned mask = INTVAL (operands[2]); - int bs0, bs1, bs2, p, len; + int bs0, bs1, p, len; if (INTVAL (operands[2]) == 0) return "copy %1,%0"; @@ -1643,6 +1726,7 @@ output_ior (operands) } /* Output an ascii string. */ +void output_ascii (file, p, size) FILE *file; unsigned char *p; @@ -1912,7 +1996,7 @@ compute_frame_size (size, fregs_live) 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; @@ -1928,10 +2012,28 @@ output_function_prologue (file, size) of a function. */ fprintf (file, "\t.PROC\n\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. */ @@ -1939,6 +2041,7 @@ output_function_prologue (file, size) sprintf(hp_profile_label_name, "LP$%04d", hp_profile_labelno); } +void hppa_expand_prologue() { @@ -1949,6 +2052,8 @@ hppa_expand_prologue() 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); @@ -2080,7 +2185,10 @@ hppa_expand_prologue() { 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 @@ -2101,6 +2209,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 @@ -2129,12 +2238,13 @@ hppa_expand_prologue() if (! TARGET_SNAKE) { for (i = 43; i >= 40; i--) - { - if (regs_ever_live[i]) + if (regs_ever_live[i]) + { emit_move_insn (gen_rtx (MEM, DFmode, gen_rtx (POST_INC, DFmode, tmpreg)), gen_rtx (REG, DFmode, i)); - } + fr_saved++; + } } else { @@ -2144,6 +2254,7 @@ hppa_expand_prologue() emit_move_insn (gen_rtx (MEM, DFmode, gen_rtx (POST_INC, DFmode, tmpreg)), gen_rtx (REG, DFmode, i)); + fr_saved++; } } } @@ -2466,17 +2577,12 @@ pa_adjust_cost (insn, link, dep_insn, co /* Return any length adjustment needed by INSN which already has its length 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 +2590,27 @@ 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; + } /* 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 +2618,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; } @@ -2886,7 +3020,7 @@ 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; { @@ -2905,18 +3039,22 @@ import_milli (code) 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%#"; + return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$mulU"), + gen_rtx (REG, SImode, 31)); } else { import_milli (mulI); - return "bl $$mulI,31%#"; + return output_call (insn, gen_rtx (SYMBOL_REF, SImode, "$$mulI"), + gen_rtx (REG, SImode, 31)); } } @@ -2977,9 +3115,10 @@ 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; @@ -2987,18 +3126,28 @@ output_div_insn (operands, unsignedp) 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 +3155,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,18 +3170,21 @@ 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)); } } @@ -3050,10 +3204,24 @@ output_arg_descriptor (insn) for (prev_insn = PREV_INSN (insn); GET_CODE (prev_insn) == INSN; prev_insn = PREV_INSN (prev_insn)) { - 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))))) + /* Terminate search for arguments if a non-USE insn is encountered + or a USE insn which does not specify an argument, STATIC_CHAIN, + or STRUCT_VALUE register. */ + 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))) + || REGNO (XEXP (PATTERN (prev_insn), 0)) == STATIC_CHAIN_REGNUM + || REGNO (XEXP (PATTERN (prev_insn), 0)) + == STRUCT_VALUE_REGNUM))) break; + + /* If this is a USE for the STATIC_CHAIN or STRUCT_VALUE register, + then skip it and continue the loop since those are not encoded + in the argument relocation bits. */ + if (REGNO (XEXP (PATTERN (prev_insn), 0)) == STATIC_CHAIN_REGNUM + || REGNO (XEXP (PATTERN (prev_insn), 0)) == STRUCT_VALUE_REGNUM) + continue; + arg_mode = GET_MODE (XEXP (PATTERN (prev_insn), 0)); regno = REGNO (XEXP (PATTERN (prev_insn), 0)); if (regno >= 23 && regno <= 26) @@ -3068,7 +3236,7 @@ output_arg_descriptor (insn) arg_regs[regno - 32] = "FR"; else { -#ifdef HP_FP_ARG_DESCRIPTOR_REVERSED +#ifndef HP_FP_ARG_DESCRIPTOR_REVERSED arg_regs[regno - 33] = "FR"; arg_regs[regno - 32] = "FU"; #else @@ -3083,7 +3251,7 @@ output_arg_descriptor (insn) arg_regs[(regno - 44) / 2] = "FR"; else { -#ifdef HP_FP_ARG_DESCRIPTOR_REVERSED +#ifndef HP_FP_ARG_DESCRIPTOR_REVERSED arg_regs[(regno - 46) / 2] = "FR"; arg_regs[(regno - 46) / 2 + 1] = "FU"; #else @@ -3117,7 +3285,7 @@ secondary_reload_class (class, mode, in) { int regno = true_regnum (in); - if ((TARGET_SHARED_LIBS && function_label_operand (in, mode)) + if (function_label_operand (in, mode) || ((regno >= FIRST_PSEUDO_REGISTER || regno == -1) && GET_MODE_CLASS (mode) == MODE_INT && FP_REG_CLASS_P (class)) @@ -3127,7 +3295,7 @@ secondary_reload_class (class, mode, in) if (GET_CODE (in) == HIGH) in = XEXP (in, 0); - if (TARGET_KERNEL && class != R1_REGS && symbolic_operand (in, VOIDmode)) + if (class != R1_REGS && symbolic_operand (in, VOIDmode)) return R1_REGS; return NO_REGS; @@ -3168,7 +3336,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,7 +3353,7 @@ 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)); @@ -3203,24 +3371,43 @@ output_cbranch (operands, nullify, lengt rtx *operands; int nullify, length, negated; rtx insn; -{ +{ static char buf[100]; int useskip = 0; + /* 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 (JUMP_LABEL (insn) == next_nonnote_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 @@ -3234,41 +3421,43 @@ output_cbranch (operands, nullify, lengt else if (nullify) strcat (buf, ",n %2,%1,%0"); else - strcat (buf, " %2,%1,%0%#"); + 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"); - 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"); + } 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; } @@ -3283,24 +3472,45 @@ output_bb (operands, nullify, length, ne int nullify, length, negated; rtx insn; int which; -{ +{ static char buf[100]; int useskip = 0; + /* 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 (JUMP_LABEL (insn) == next_nonnote_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 @@ -3317,55 +3527,311 @@ 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%#"); + 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"); - 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, " %0,%1,.+12\n\tbl %3,0"); + else + strcat (buf, " %0,%1,.+12\n\tbl %2,0"); + } 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 (JUMP_LABEL (insn) == next_nonnote_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 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 (JUMP_LABEL (insn) == next_nonnote_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 fulled delay slot + which is nullified. */ + if (dbr_sequence_length () != 0 + && ! forward_branch_p (insn) + && nullify) + return "movb,%N2,n %1,%0,.+12\n\ttbl %3,0"; + + /* 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. */ +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 common case -- empty delay slot or no jump in the delay slot. */ + if (dbr_sequence_length () == 0 + || (dbr_sequence_length () != 0 + && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN)) + { + xoperands[0] = call_dest; + xoperands[1] = return_pointer; + output_asm_insn ("bl %0,%r1%#", xoperands); + 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 (); + ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L", + CODE_LABEL_NUMBER (xoperands[3])); + output_asm_insn ("\n\tbl %0,%r2\n\tldo %1-%3-8(%r2),%r2", xoperands); + } + + /* 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 *saveable_obstack; /* In HPUX 8.0's shared library scheme, special relocations are needed @@ -3508,3 +3974,65 @@ shadd_operand (op, mode) { return (GET_CODE (op) == CONST_INT && shadd_constant_p (INTVAL (op))); } + +/* 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; +} +