OSDN Git Service

2005-12-02 Richard Guenther <rguenther@suse.de>
[pf3gnuchains/gcc-fork.git] / gcc / config / pa / pa.c
index 3ffb7fb..fee8d1c 100644 (file)
@@ -17,8 +17,8 @@ GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+the Free Software Foundation, 51 Franklin Street, Fifth Floor,
+Boston, MA 02110-1301, USA.  */
 
 #include "config.h"
 #include "system.h"
@@ -124,9 +124,9 @@ static void pa_init_builtins (void);
 static rtx hppa_builtin_saveregs (void);
 static tree hppa_gimplify_va_arg_expr (tree, tree, tree *, tree *);
 static bool pa_scalar_mode_supported_p (enum machine_mode);
+static bool pa_commutative_p (rtx x, int outer_code);
 static void copy_fp_args (rtx) ATTRIBUTE_UNUSED;
 static int length_fp_args (rtx) ATTRIBUTE_UNUSED;
-static struct deferred_plabel *get_plabel (rtx) ATTRIBUTE_UNUSED;
 static inline void pa_file_start_level (void) ATTRIBUTE_UNUSED;
 static inline void pa_file_start_space (int) ATTRIBUTE_UNUSED;
 static inline void pa_file_start_file (int) ATTRIBUTE_UNUSED;
@@ -137,6 +137,7 @@ static void pa_linux_file_start (void) ATTRIBUTE_UNUSED;
 static void pa_hpux64_gas_file_start (void) ATTRIBUTE_UNUSED;
 static void pa_hpux64_hpas_file_start (void) ATTRIBUTE_UNUSED;
 static void output_deferred_plabels (void);
+static void output_deferred_profile_counters (void) ATTRIBUTE_UNUSED;
 #ifdef ASM_OUTPUT_EXTERNAL_REAL
 static void pa_hpux_file_end (void);
 #endif
@@ -149,6 +150,9 @@ static bool pa_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode,
 static int pa_arg_partial_bytes (CUMULATIVE_ARGS *, enum machine_mode,
                                 tree, bool);
 static struct machine_function * pa_init_machine_status (void);
+static enum reg_class pa_secondary_reload (bool, rtx, enum reg_class,
+                                          enum machine_mode,
+                                          secondary_reload_info *);
 
 
 /* Save the operands last given to a compare for use when we
@@ -226,6 +230,9 @@ static size_t n_deferred_plabels = 0;
 #undef TARGET_FUNCTION_OK_FOR_SIBCALL
 #define TARGET_FUNCTION_OK_FOR_SIBCALL pa_function_ok_for_sibcall
 
+#undef TARGET_COMMUTATIVE_P
+#define TARGET_COMMUTATIVE_P pa_commutative_p
+
 #undef TARGET_ASM_OUTPUT_MI_THUNK
 #define TARGET_ASM_OUTPUT_MI_THUNK pa_asm_output_mi_thunk
 #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
@@ -292,6 +299,12 @@ static size_t n_deferred_plabels = 0;
 #undef TARGET_SCALAR_MODE_SUPPORTED_P
 #define TARGET_SCALAR_MODE_SUPPORTED_P pa_scalar_mode_supported_p
 
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM pa_tls_referenced_p
+
+#undef TARGET_SECONDARY_RELOAD
+#define TARGET_SECONDARY_RELOAD pa_secondary_reload
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
 /* Parse the -mfixed-range= option string.  */
@@ -306,7 +319,7 @@ fix_range (const char *const_str)
      REG2 are either register names or register numbers.  The effect
      of this option is to mark the registers in the range from REG1 to
      REG2 as ``fixed'' so they won't be used by the compiler.  This is
-     used, e.g., to ensure that kernel mode code doesn't use f32-f127.  */
+     used, e.g., to ensure that kernel mode code doesn't use fr4-fr31.  */
 
   i = strlen (const_str);
   str = (char *) alloca (i + 1);
@@ -317,7 +330,7 @@ fix_range (const char *const_str)
       dash = strchr (str, '-');
       if (!dash)
        {
-         warning ("value of -mfixed-range must have form REG1-REG2");
+         warning (0, "value of -mfixed-range must have form REG1-REG2");
          return;
        }
       *dash = '\0';
@@ -329,14 +342,14 @@ fix_range (const char *const_str)
       first = decode_reg_name (str);
       if (first < 0)
        {
-         warning ("unknown register name: %s", str);
+         warning (0, "unknown register name: %s", str);
          return;
        }
 
       last = decode_reg_name (dash + 1);
       if (last < 0)
        {
-         warning ("unknown register name: %s", dash + 1);
+         warning (0, "unknown register name: %s", dash + 1);
          return;
        }
 
@@ -344,7 +357,7 @@ fix_range (const char *const_str)
 
       if (first > last)
        {
-         warning ("%s-%s is an empty range", str, dash + 1);
+         warning (0, "%s-%s is an empty range", str, dash + 1);
          return;
        }
 
@@ -449,18 +462,18 @@ override_options (void)
 
   if (flag_pic && TARGET_PORTABLE_RUNTIME)
     {
-      warning ("PIC code generation is not supported in the portable runtime model\n");
+      warning (0, "PIC code generation is not supported in the portable runtime model");
     }
 
   if (flag_pic && TARGET_FAST_INDIRECT_CALLS)
    {
-      warning ("PIC code generation is not compatible with fast indirect calls\n");
+      warning (0, "PIC code generation is not compatible with fast indirect calls");
    }
 
   if (! TARGET_GAS && write_symbols != NO_DEBUG)
     {
-      warning ("-g is only supported when using GAS on this processor,");
-      warning ("-g option disabled");
+      warning (0, "-g is only supported when using GAS on this processor,");
+      warning (0, "-g option disabled");
       write_symbols = NO_DEBUG;
     }
 
@@ -488,8 +501,10 @@ static void
 pa_init_builtins (void)
 {
 #ifdef DONT_HAVE_FPUTC_UNLOCKED
-  built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED] = NULL_TREE;
-  implicit_built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED] = NULL_TREE;
+  built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED] =
+    built_in_decls[(int) BUILT_IN_PUTC_UNLOCKED];
+  implicit_built_in_decls[(int) BUILT_IN_FPUTC_UNLOCKED]
+    = implicit_built_in_decls[(int) BUILT_IN_PUTC_UNLOCKED];
 #endif
 }
 
@@ -595,6 +610,8 @@ legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg)
 {
   rtx pic_ref = orig;
 
+  gcc_assert (!PA_SYMBOL_REF_TLS_P (orig));
+
   /* Labels need special handling.  */
   if (pic_label_operand (orig, mode))
     {
@@ -671,6 +688,80 @@ legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg)
   return pic_ref;
 }
 
+static GTY(()) rtx gen_tls_tga;
+
+static rtx
+gen_tls_get_addr (void)
+{
+  if (!gen_tls_tga)
+    gen_tls_tga = init_one_libfunc ("__tls_get_addr");
+  return gen_tls_tga;
+}
+
+static rtx
+hppa_tls_call (rtx arg)
+{
+  rtx ret;
+
+  ret = gen_reg_rtx (Pmode);
+  emit_library_call_value (gen_tls_get_addr (), ret,
+                          LCT_CONST, Pmode, 1, arg, Pmode);
+
+  return ret;
+}
+
+static rtx
+legitimize_tls_address (rtx addr)
+{
+  rtx ret, insn, tmp, t1, t2, tp;
+  enum tls_model model = SYMBOL_REF_TLS_MODEL (addr);
+
+  switch (model) 
+    {
+      case TLS_MODEL_GLOBAL_DYNAMIC:
+       tmp = gen_reg_rtx (Pmode);
+       emit_insn (gen_tgd_load (tmp, addr));
+       ret = hppa_tls_call (tmp);
+       break;
+
+      case TLS_MODEL_LOCAL_DYNAMIC:
+       ret = gen_reg_rtx (Pmode);
+       tmp = gen_reg_rtx (Pmode);
+       start_sequence ();
+       emit_insn (gen_tld_load (tmp, addr));
+       t1 = hppa_tls_call (tmp);
+       insn = get_insns ();
+       end_sequence ();
+       t2 = gen_reg_rtx (Pmode);
+       emit_libcall_block (insn, t2, t1, 
+                           gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx),
+                                           UNSPEC_TLSLDBASE));
+       emit_insn (gen_tld_offset_load (ret, addr, t2));
+       break;
+
+      case TLS_MODEL_INITIAL_EXEC:
+       tp = gen_reg_rtx (Pmode);
+       tmp = gen_reg_rtx (Pmode);
+       ret = gen_reg_rtx (Pmode);
+       emit_insn (gen_tp_load (tp));
+       emit_insn (gen_tie_load (tmp, addr));
+       emit_move_insn (ret, gen_rtx_PLUS (Pmode, tp, tmp));
+       break;
+
+      case TLS_MODEL_LOCAL_EXEC:
+       tp = gen_reg_rtx (Pmode);
+       ret = gen_reg_rtx (Pmode);
+       emit_insn (gen_tp_load (tp));
+       emit_insn (gen_tle_load (ret, addr, tp));
+       break;
+
+      default:
+       gcc_unreachable ();
+    }
+
+  return ret;
+}
+
 /* 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.
@@ -740,7 +831,9 @@ hppa_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
       && !REG_POINTER (XEXP (x, 1)))
     return gen_rtx_PLUS (Pmode, XEXP (x, 1), XEXP (x, 0));
 
-  if (flag_pic)
+  if (PA_SYMBOL_REF_TLS_P (x))
+    return legitimize_tls_address (x);
+  else if (flag_pic)
     return legitimize_pic_address (x, mode, gen_reg_rtx (Pmode));
 
   /* Strip off CONST.  */
@@ -1190,6 +1283,25 @@ force_mode (enum machine_mode mode, rtx orig)
   return gen_rtx_REG (mode, REGNO (orig));
 }
 
+/* Return 1 if *X is a thread-local symbol.  */
+
+static int
+pa_tls_symbol_ref_1 (rtx *x, void *data ATTRIBUTE_UNUSED)
+{
+  return PA_SYMBOL_REF_TLS_P (*x);
+}
+
+/* Return 1 if X contains a thread-local symbol.  */
+
+bool
+pa_tls_referenced_p (rtx x)
+{
+  if (!TARGET_HAVE_TLS)
+    return false;
+
+  return for_each_rtx (&x, &pa_tls_symbol_ref_1, 0);
+}
+
 /* Emit insns to move operands[1] into operands[0].
 
    Return 1 if we have written out everything that needs to be done to
@@ -1273,12 +1385,12 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
   if (scratch_reg && reload_in_progress && GET_CODE (operand0) == MEM
       && ((tem = find_replacement (&XEXP (operand0, 0)))
          != XEXP (operand0, 0)))
-    operand0 = gen_rtx_MEM (GET_MODE (operand0), tem);
+    operand0 = replace_equiv_address (operand0, tem);
 
   if (scratch_reg && reload_in_progress && GET_CODE (operand1) == MEM
       && ((tem = find_replacement (&XEXP (operand1, 0)))
          != XEXP (operand1, 0)))
-    operand1 = gen_rtx_MEM (GET_MODE (operand1), tem);
+    operand1 = replace_equiv_address (operand1, tem);
 
   /* Handle secondary reloads for loads/stores of FP registers from
      REG+D addresses where D does not fit in 5 or 14 bits, including
@@ -1316,7 +1428,7 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
       else
        emit_move_insn (scratch_reg, XEXP (operand1, 0));
       emit_insn (gen_rtx_SET (VOIDmode, operand0,
-                             gen_rtx_MEM (mode, scratch_reg)));
+                             replace_equiv_address (operand1, scratch_reg)));
       return 1;
     }
   else if (scratch_reg
@@ -1353,7 +1465,8 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
        }
       else
        emit_move_insn (scratch_reg, XEXP (operand0, 0));
-      emit_insn (gen_rtx_SET (VOIDmode, gen_rtx_MEM (mode, scratch_reg),
+      emit_insn (gen_rtx_SET (VOIDmode,
+                             replace_equiv_address (operand0, scratch_reg),
                              operand1));
       return 1;
     }
@@ -1370,7 +1483,7 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
           && CONSTANT_P (operand1)
           && fp_reg_operand (operand0, mode))
     {
-      rtx xoperands[2];
+      rtx const_mem, xoperands[2];
 
       /* SCRATCH_REG will hold an address and maybe the actual data.  We want
         it in WORD_MODE regardless of what mode it was originally given
@@ -1379,13 +1492,14 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
 
       /* Force the constant into memory and put the address of the
         memory location into scratch_reg.  */
+      const_mem = force_const_mem (mode, operand1);
       xoperands[0] = scratch_reg;
-      xoperands[1] = XEXP (force_const_mem (mode, operand1), 0);
+      xoperands[1] = XEXP (const_mem, 0);
       emit_move_sequence (xoperands, Pmode, 0);
 
       /* Now load the destination register.  */
       emit_insn (gen_rtx_SET (mode, operand0,
-                             gen_rtx_MEM (mode, scratch_reg)));
+                             replace_equiv_address (const_mem, scratch_reg)));
       return 1;
     }
   /* Handle secondary reloads for SAR.  These occur when trying to load
@@ -1422,8 +1536,8 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
             OPERAND0.  */
          scratch_reg = force_mode (GET_MODE (operand0), scratch_reg);
 
-         emit_move_insn (scratch_reg, gen_rtx_MEM (GET_MODE (operand0),
-                                                   scratch_reg));
+         emit_move_insn (scratch_reg,
+                         replace_equiv_address (operand1, scratch_reg));
        }
       else
        {
@@ -1650,10 +1764,10 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
                       && (reload_completed || reload_in_progress)
                       && flag_pic)
                {
-                 operands[1] = force_const_mem (mode, operand1);
-                 operands[1] = legitimize_pic_address (XEXP (operands[1], 0),
+                 rtx const_mem = force_const_mem (mode, operand1);
+                 operands[1] = legitimize_pic_address (XEXP (const_mem, 0),
                                                        mode, temp);
-                 operands[1] = gen_rtx_MEM (mode, operands[1]);
+                 operands[1] = replace_equiv_address (const_mem, operands[1]);
                  emit_move_sequence (operands, mode, temp);
                }
              else
@@ -1707,6 +1821,26 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
            }
          return 1;
        }
+      else if (pa_tls_referenced_p (operand1))
+       {
+         rtx tmp = operand1;
+         rtx addend = NULL;
+
+         if (GET_CODE (tmp) == CONST && GET_CODE (XEXP (tmp, 0)) == PLUS)
+           {
+             addend = XEXP (XEXP (tmp, 0), 1);
+             tmp = XEXP (XEXP (tmp, 0), 0);
+           }
+
+         gcc_assert (GET_CODE (tmp) == SYMBOL_REF);
+         tmp = legitimize_tls_address (tmp);
+         if (addend)
+           {
+             tmp = gen_rtx_PLUS (mode, tmp, addend);
+             tmp = force_operand (tmp, operands[0]);
+           }
+         operands[1] = tmp;
+       }
       else if (GET_CODE (operand1) != CONST_INT
               || !cint_ok_for_move (INTVAL (operand1)))
        {
@@ -1874,10 +2008,12 @@ reloc_needed (tree exp)
 
     case CONSTRUCTOR:
       {
-       register tree link;
-       for (link = CONSTRUCTOR_ELTS (exp); link; link = TREE_CHAIN (link))
-         if (TREE_VALUE (link) != 0)
-           reloc |= reloc_needed (TREE_VALUE (link));
+       tree value;
+       unsigned HOST_WIDE_INT ix;
+
+       FOR_EACH_CONSTRUCTOR_VALUE (CONSTRUCTOR_ELTS (exp), ix, value)
+         if (value)
+           reloc |= reloc_needed (value);
       }
       break;
 
@@ -3137,20 +3273,18 @@ store_reg (int reg, HOST_WIDE_INT disp, int base)
       rtx tmpreg = gen_rtx_REG (Pmode, 1);
 
       emit_move_insn (tmpreg, delta);
-      emit_move_insn (tmpreg, gen_rtx_PLUS (Pmode, tmpreg, basereg));
-      dest = gen_rtx_MEM (word_mode, tmpreg);
-      insn = emit_move_insn (dest, src);
+      insn = emit_move_insn (tmpreg, gen_rtx_PLUS (Pmode, tmpreg, basereg));
       if (DO_FRAME_NOTES)
        {
          REG_NOTES (insn)
            = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-               gen_rtx_SET (VOIDmode,
-                            gen_rtx_MEM (word_mode,
-                                         gen_rtx_PLUS (word_mode, basereg,
-                                                       delta)),
-                             src),
+               gen_rtx_SET (VOIDmode, tmpreg,
+                            gen_rtx_PLUS (Pmode, basereg, delta)),
                 REG_NOTES (insn));
+         RTX_FRAME_RELATED_P (insn) = 1;
        }
+      dest = gen_rtx_MEM (word_mode, tmpreg);
+      insn = emit_move_insn (dest, src);
     }
   else
     {
@@ -3198,25 +3332,9 @@ store_reg_modify (int base, int reg, HOST_WIDE_INT mod)
       RTX_FRAME_RELATED_P (insn) = 1;
 
       /* RTX_FRAME_RELATED_P must be set on each frame related set
-        in a parallel with more than one element.  Don't set
-        RTX_FRAME_RELATED_P in the first set if reg is temporary
-        register 1. The effect of this operation is recorded in
-        the initial copy.  */
-      if (reg != 1)
-       {
-         RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 0)) = 1;
-         RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 1)) = 1;
-       }
-      else
-       {
-         /* The first element of a PARALLEL is always processed if it is
-            a SET.  Thus, we need an expression list for this case.  */
-         REG_NOTES (insn)
-           = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-               gen_rtx_SET (VOIDmode, basereg,
-                            gen_rtx_PLUS (word_mode, basereg, delta)),
-                REG_NOTES (insn));
-       }
+        in a parallel with more than one element.  */
+      RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 0)) = 1;
+      RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 1)) = 1;
     }
 }
 
@@ -3246,6 +3364,12 @@ set_reg_plus_d (int reg, int base, HOST_WIDE_INT disp, int note)
       emit_move_insn (tmpreg, delta);
       insn = emit_move_insn (gen_rtx_REG (Pmode, reg),
                             gen_rtx_PLUS (Pmode, tmpreg, basereg));
+      if (DO_FRAME_NOTES)
+       REG_NOTES (insn)
+         = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+             gen_rtx_SET (VOIDmode, tmpreg,
+                          gen_rtx_PLUS (Pmode, basereg, delta)),
+             REG_NOTES (insn));
     }
   else
     {
@@ -3378,7 +3502,7 @@ pa_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
   /* The SAVE_SP flag is used to indicate that register %r3 is stored
      at the beginning of the frame and that it is used as the frame
      pointer for the frame.  We do this because our current frame
-     layout doesn't conform to that specified in the the HP runtime
+     layout doesn't conform to that specified in the HP runtime
      documentation and we need a way to indicate to programs such as
      GDB where %r3 is saved.  The SAVE_SP flag was chosen because it
      isn't used by HP compilers but is supported by the assembler.
@@ -3450,17 +3574,7 @@ hppa_expand_prologue (void)
             frames.  */
          insn = emit_move_insn (tmpreg, frame_pointer_rtx);
          if (DO_FRAME_NOTES)
-           {
-             /* We need to record the frame pointer save here since the
-                new frame pointer is set in the following insn.  */
-             RTX_FRAME_RELATED_P (insn) = 1;
-             REG_NOTES (insn)
-               = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                   gen_rtx_SET (VOIDmode,
-                                gen_rtx_MEM (word_mode, stack_pointer_rtx),
-                                frame_pointer_rtx),
-                   REG_NOTES (insn));
-           }
+           RTX_FRAME_RELATED_P (insn) = 1;
 
          insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
          if (DO_FRAME_NOTES)
@@ -4014,6 +4128,40 @@ hppa_pic_save_rtx (void)
   return get_hard_reg_initial_val (word_mode, PIC_OFFSET_TABLE_REGNUM);
 }
 
+#ifndef NO_DEFERRED_PROFILE_COUNTERS
+#define NO_DEFERRED_PROFILE_COUNTERS 0
+#endif
+
+/* Define heap vector type for funcdef numbers.  */
+DEF_VEC_I(int);
+DEF_VEC_ALLOC_I(int,heap);
+
+/* Vector of funcdef numbers.  */
+static VEC(int,heap) *funcdef_nos;
+
+/* Output deferred profile counters.  */
+static void
+output_deferred_profile_counters (void)
+{
+  unsigned int i;
+  int align, n;
+
+  if (VEC_empty (int, funcdef_nos))
+   return;
+
+  data_section ();
+  align = MIN (BIGGEST_ALIGNMENT, LONG_TYPE_SIZE);
+  ASM_OUTPUT_ALIGN (asm_out_file, floor_log2 (align / BITS_PER_UNIT));
+
+  for (i = 0; VEC_iterate (int, funcdef_nos, i, n); i++)
+    {
+      targetm.asm_out.internal_label (asm_out_file, "LP", n);
+      assemble_integer (const0_rtx, LONG_TYPE_SIZE / BITS_PER_UNIT, align, 1);
+    }
+
+  VEC_free (int, heap, funcdef_nos);
+}
+
 void
 hppa_profile_hook (int label_no)
 {
@@ -4048,11 +4196,12 @@ hppa_profile_hook (int label_no)
   emit_insn (gen_load_offset_label_address (gen_rtx_REG (SImode, 25), 
                                            reg, begin_label_rtx, label_rtx));
 
-#ifndef NO_PROFILE_COUNTERS
+#if !NO_DEFERRED_PROFILE_COUNTERS
   {
     rtx count_label_rtx, addr, r24;
     char count_label_name[16];
 
+    VEC_safe_push (int, heap, funcdef_nos, label_no);
     ASM_GENERATE_INTERNAL_LABEL (count_label_name, "LP", label_no);
     count_label_rtx = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (count_label_name));
 
@@ -5096,8 +5245,11 @@ pa_hpux64_hpas_file_start (void)
 }
 #undef aputs
 
-static struct deferred_plabel *
-get_plabel (rtx symbol)
+/* Search the deferred plabel list for SYMBOL and return its internal
+   label.  If an entry for SYMBOL is not found, a new entry is created.  */
+
+rtx
+get_deferred_plabel (rtx symbol)
 {
   const char *fname = XSTR (symbol, 0);
   size_t i;
@@ -5135,7 +5287,7 @@ get_plabel (rtx symbol)
        mark_referenced (id);
     }
 
-  return &deferred_plabels[i];
+  return deferred_plabels[i].internal_label;
 }
 
 static void
@@ -5421,100 +5573,108 @@ output_arg_descriptor (rtx call_insn)
   fputc ('\n', asm_out_file);
 }
 \f
-/* Return the class of any secondary reload register that is needed to
-   move IN into a register in class CLASS using mode MODE.
-
-   Profiling has showed this routine and its descendants account for
-   a significant amount of compile time (~7%).  So it has been
-   optimized to reduce redundant computations and eliminate useless
-   function calls.
-
-   It might be worthwhile to try and make this a leaf function too.  */
-
-enum reg_class
-secondary_reload_class (enum reg_class class, enum machine_mode mode, rtx in)
+static enum reg_class
+pa_secondary_reload (bool in_p, rtx x, enum reg_class class,
+                    enum machine_mode mode, secondary_reload_info *sri)
 {
-  int regno, is_symbolic;
-
-  /* Trying to load a constant into a FP register during PIC code
-     generation will require %r1 as a scratch register.  */
-  if (flag_pic
-      && GET_MODE_CLASS (mode) == MODE_INT
-      && FP_REG_CLASS_P (class)
-      && (GET_CODE (in) == CONST_INT || GET_CODE (in) == CONST_DOUBLE))
-    return R1_REGS;
+  int is_symbolic, regno;
 
-  /* Profiling showed the PA port spends about 1.3% of its compilation
-     time in true_regnum from calls inside secondary_reload_class.  */
+  /* Handle the easy stuff first.  */
+  if (class == R1_REGS)
+    return NO_REGS;
 
-  if (GET_CODE (in) == REG)
+  if (REG_P (x))
     {
-      regno = REGNO (in);
-      if (regno >= FIRST_PSEUDO_REGISTER)
-       regno = true_regnum (in);
+      regno = REGNO (x);
+      if (class == BASE_REG_CLASS && regno < FIRST_PSEUDO_REGISTER)
+       return NO_REGS;
     }
-  else if (GET_CODE (in) == SUBREG)
-    regno = true_regnum (in);
   else
     regno = -1;
 
   /* If we have something like (mem (mem (...)), we can safely assume the
      inner MEM will end up in a general register after reloading, so there's
      no need for a secondary reload.  */
-  if (GET_CODE (in) == MEM
-      && GET_CODE (XEXP (in, 0)) == MEM)
+  if (GET_CODE (x) == MEM && GET_CODE (XEXP (x, 0)) == MEM)
     return NO_REGS;
 
+  /* Trying to load a constant into a FP register during PIC code
+     generation requires %r1 as a scratch register.  */
+  if (flag_pic
+      && GET_MODE_CLASS (mode) == MODE_INT
+      && FP_REG_CLASS_P (class)
+      && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE))
+    {
+      gcc_assert (mode == SImode || mode == DImode);
+      sri->icode = (mode == SImode ? CODE_FOR_reload_insi_r1
+                   : CODE_FOR_reload_indi_r1);
+      return NO_REGS;
+    }
+
+  /* Profiling showed the PA port spends about 1.3% of its compilation
+     time in true_regnum from calls inside pa_secondary_reload_class.  */
+  if (regno >= FIRST_PSEUDO_REGISTER || GET_CODE (x) == SUBREG)
+    regno = true_regnum (x);
+
   /* Handle out of range displacement for integer mode loads/stores of
      FP registers.  */
   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;
+    {
+      sri->icode = in_p ? reload_in_optab[mode] : reload_out_optab[mode];
+      return NO_REGS;
+    }
 
   /* A SAR<->FP register copy requires a secondary register (GPR) as
      well as secondary memory.  */
   if (regno >= 0 && regno < FIRST_PSEUDO_REGISTER
       && ((REGNO_REG_CLASS (regno) == SHIFT_REGS && FP_REG_CLASS_P (class))
-         || (class == SHIFT_REGS && FP_REG_CLASS_P (REGNO_REG_CLASS (regno)))))
-    return GENERAL_REGS;
+         || (class == SHIFT_REGS
+             && FP_REG_CLASS_P (REGNO_REG_CLASS (regno)))))
+    {
+      sri->icode = in_p ? reload_in_optab[mode] : reload_out_optab[mode];
+      return NO_REGS;
+    }
 
-  if (GET_CODE (in) == HIGH)
-    in = XEXP (in, 0);
+  /* Secondary reloads of symbolic operands require %r1 as a scratch
+     register when we're generating PIC code and the operand isn't
+     readonly.  */
+  if (GET_CODE (x) == HIGH)
+    x = XEXP (x, 0);
 
   /* Profiling has showed GCC spends about 2.6% of its compilation
-     time in symbolic_operand from calls inside secondary_reload_class.
-
-     We use an inline copy and only compute its return value once to avoid
-     useless work.  */
-  switch (GET_CODE (in))
+     time in symbolic_operand from calls inside pa_secondary_reload_class.
+     So, we use an inline copy to avoid useless work.  */
+  switch (GET_CODE (x))
     {
-      rtx tmp;
+      rtx op;
 
       case SYMBOL_REF:
+        is_symbolic = !SYMBOL_REF_TLS_MODEL (x);
+        break;
       case LABEL_REF:
         is_symbolic = 1;
         break;
       case CONST:
-       tmp = XEXP (in, 0);
-       is_symbolic = ((GET_CODE (XEXP (tmp, 0)) == SYMBOL_REF
-                       || GET_CODE (XEXP (tmp, 0)) == LABEL_REF)
-                      && GET_CODE (XEXP (tmp, 1)) == CONST_INT);
+       op = XEXP (x, 0);
+       is_symbolic = (((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
+                        && !SYMBOL_REF_TLS_MODEL (XEXP (op, 0)))
+                       || GET_CODE (XEXP (op, 0)) == LABEL_REF)
+                      && GET_CODE (XEXP (op, 1)) == CONST_INT);
         break;
-
       default:
         is_symbolic = 0;
         break;
     }
 
-  if (!flag_pic
-      && is_symbolic
-      && read_only_operand (in, VOIDmode))
-    return NO_REGS;
-
-  if (class != R1_REGS && is_symbolic)
-    return R1_REGS;
+  if (is_symbolic && (flag_pic || !read_only_operand (x, VOIDmode)))
+    {
+      gcc_assert (mode == SImode || mode == DImode);
+      sri->icode = (mode == SImode ? CODE_FOR_reload_insi_r1
+                   : CODE_FOR_reload_indi_r1);
+    }
 
   return NO_REGS;
 }
@@ -5693,27 +5853,27 @@ hppa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p)
       /* Args grow down.  Not handled by generic routines.  */
 
       u = fold_convert (valist_type, size_in_bytes (type));
-      t = build (MINUS_EXPR, valist_type, valist, u);
+      t = build2 (MINUS_EXPR, valist_type, valist, u);
 
       /* Copied from va-pa.h, but we probably don't need to align to
         word size, since we generate and preserve that invariant.  */
       u = build_int_cst (valist_type, (size > 4 ? -8 : -4));
-      t = build (BIT_AND_EXPR, valist_type, t, u);
+      t = build2 (BIT_AND_EXPR, valist_type, t, u);
 
-      t = build (MODIFY_EXPR, valist_type, valist, t);
+      t = build2 (MODIFY_EXPR, valist_type, valist, t);
 
       ofs = (8 - size) % 4;
       if (ofs != 0)
        {
          u = fold_convert (valist_type, size_int (ofs));
-         t = build (PLUS_EXPR, valist_type, t, u);
+         t = build2 (PLUS_EXPR, valist_type, t, u);
        }
 
       t = fold_convert (ptr, t);
-      t = build_fold_indirect_ref (t);
+      t = build_va_arg_indirect_ref (t);
 
       if (indirect)
-       t = build_fold_indirect_ref (t);
+       t = build_va_arg_indirect_ref (t);
 
       return t;
     }
@@ -5794,6 +5954,8 @@ output_cbranch (rtx *operands, int nullify, int length, int negated, rtx insn)
      zero for cmpb, we must ensure that we use cmpb for the comparison.  */
   if (GET_MODE (operands[1]) == DImode && operands[2] == const0_rtx)
     operands[2] = gen_rtx_REG (DImode, 0);
+  if (GET_MODE (operands[2]) == DImode && operands[1] == const0_rtx)
+    operands[1] = gen_rtx_REG (DImode, 0);
 
   /* If this is a long branch with its delay slot unfilled, set `nullify'
      as it can nullify the delay slot and save a nop.  */
@@ -6973,9 +7135,7 @@ output_call (rtx insn, rtx call_dest, int sibcall)
          /* ??? As far as I can tell, the HP linker doesn't support the
             long pc-relative sequence described in the 64-bit runtime
             architecture.  So, we use a slightly longer indirect call.  */
-         struct deferred_plabel *p = get_plabel (call_dest);
-
-         xoperands[0] = p->internal_label;
+         xoperands[0] = get_deferred_plabel (call_dest);
          xoperands[1] = gen_label_rtx ();
 
          /* If this isn't a sibcall, we put the load of %r27 into the
@@ -7102,9 +7262,7 @@ output_call (rtx insn, rtx call_dest, int sibcall)
                     essentially an inline implementation of $$dyncall.
                     We don't actually try to call $$dyncall as this is
                     as difficult as calling the function itself.  */
-                 struct deferred_plabel *p = get_plabel (call_dest);
-
-                 xoperands[0] = p->internal_label;
+                 xoperands[0] = get_deferred_plabel (call_dest);
                  xoperands[1] = gen_label_rtx ();
 
                  /* Since the call is indirect, FP arguments in registers
@@ -7371,6 +7529,8 @@ hppa_encode_label (rtx sym)
 static void
 pa_encode_section_info (tree decl, rtx rtl, int first)
 {
+  default_encode_section_info (decl, rtl, first);
+
   if (first && TEXT_SPACE_P (decl))
     {
       SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1;
@@ -7698,6 +7858,18 @@ pa_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
   return (decl && !TREE_PUBLIC (decl));
 }
 
+/* ??? Addition is not commutative on the PA due to the weird implicit
+   space register selection rules for memory addresses.  Therefore, we
+   don't consider a + b == b + a, as this might be inside a MEM.  */
+static bool
+pa_commutative_p (rtx x, int outer_code)
+{
+  return (COMMUTATIVE_P (x)
+         && (TARGET_NO_SPACE_REGS
+             || (outer_code != UNKNOWN && outer_code != MEM)
+             || GET_CODE (x) != PLUS));
+}
+
 /* Returns 1 if the 6 operands specified in OPERANDS are suitable for
    use in fmpyadd instructions.  */
 int
@@ -7837,7 +8009,7 @@ pa_asm_output_aligned_common (FILE *stream,
   max_common_align = TARGET_64BIT ? 128 : (size >= 4096 ? 256 : 64);
   if (align > max_common_align)
     {
-      warning ("alignment (%u) for %s exceeds maximum alignment "
+      warning (0, "alignment (%u) for %s exceeds maximum alignment "
               "for global common data.  Using %u",
               align / BITS_PER_UNIT, name, max_common_align / BITS_PER_UNIT);
       align = max_common_align;
@@ -8580,24 +8752,40 @@ function_value (tree valtype, tree func ATTRIBUTE_UNUSED)
 {
   enum machine_mode valmode;
 
-  /* Aggregates with a size less than or equal to 128 bits are returned
-     in GR 28(-29).  They are left justified.  The pad bits are undefined.
-     Larger aggregates are returned in memory.  */
-  if (TARGET_64BIT && AGGREGATE_TYPE_P (valtype))
+  if (AGGREGATE_TYPE_P (valtype))
     {
-      rtx loc[2];
-      int i, offset = 0;
-      int ub = int_size_in_bytes (valtype) <= UNITS_PER_WORD ? 1 : 2;
+      if (TARGET_64BIT)
+       {
+          /* Aggregates with a size less than or equal to 128 bits are
+            returned in GR 28(-29).  They are left justified.  The pad
+            bits are undefined.  Larger aggregates are returned in
+            memory.  */
+         rtx loc[2];
+         int i, offset = 0;
+         int ub = int_size_in_bytes (valtype) <= UNITS_PER_WORD ? 1 : 2;
+
+         for (i = 0; i < ub; i++)
+           {
+             loc[i] = gen_rtx_EXPR_LIST (VOIDmode,
+                                         gen_rtx_REG (DImode, 28 + i),
+                                         GEN_INT (offset));
+             offset += 8;
+           }
 
-      for (i = 0; i < ub; i++)
+         return gen_rtx_PARALLEL (BLKmode, gen_rtvec_v (ub, loc));
+       }
+      else if (int_size_in_bytes (valtype) > UNITS_PER_WORD)
        {
-         loc[i] = gen_rtx_EXPR_LIST (VOIDmode,
-                                     gen_rtx_REG (DImode, 28 + i),
-                                     GEN_INT (offset));
-         offset += 8;
+         /* Aggregates 5 to 8 bytes in size are returned in general
+            registers r28-r29 in the same manner as other non
+            floating-point objects.  The data is right-justified and
+            zero-extended to 64 bits.  This is opposite to the normal
+            justification used on big endian targets and requires
+            special treatment.  */
+         rtx loc = gen_rtx_EXPR_LIST (VOIDmode,
+                                      gen_rtx_REG (DImode, 28), const0_rtx);
+         return gen_rtx_PARALLEL (BLKmode, gen_rtvec (1, loc));
        }
-
-      return gen_rtx_PARALLEL (BLKmode, gen_rtvec_v (ub, loc));
     }
 
   if ((INTEGRAL_TYPE_P (valtype)
@@ -8608,6 +8796,7 @@ function_value (tree valtype, tree func ATTRIBUTE_UNUSED)
     valmode = TYPE_MODE (valtype);
 
   if (TREE_CODE (valtype) == REAL_TYPE
+      && !AGGREGATE_TYPE_P (valtype)
       && TYPE_MODE (valtype) != TFmode
       && !TARGET_SOFT_FLOAT)
     return gen_rtx_REG (valmode, 32);
@@ -8733,12 +8922,12 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
             to 64 bits.  This is opposite to the normal justification
             used on big endian targets and requires special treatment.
             We now define BLOCK_REG_PADDING to pad these objects.  */
-         if (mode == BLKmode)
+         if (mode == BLKmode || (type && AGGREGATE_TYPE_P (type)))
            {
              rtx loc = gen_rtx_EXPR_LIST (VOIDmode,
                                           gen_rtx_REG (DImode, gpr_reg_base),
                                           const0_rtx);
-             return gen_rtx_PARALLEL (mode, gen_rtvec (1, loc));
+             return gen_rtx_PARALLEL (BLKmode, gen_rtvec (1, loc));
            }
        }
       else
@@ -8799,7 +8988,9 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
              && cum->indirect)
          /* If the parameter is not a floating point parameter, then
             it belongs in GPRs.  */
-         || !FLOAT_MODE_P (mode))
+         || !FLOAT_MODE_P (mode)
+         /* Structure with single SFmode field belongs in GPR.  */
+         || (type && AGGREGATE_TYPE_P (type)))
        retval = gen_rtx_REG (mode, gpr_reg_base);
       else
        retval = gen_rtx_REG (mode, fpr_reg_base);
@@ -8997,6 +9188,9 @@ pa_hpux_file_end (void)
   unsigned int i;
   extern_symbol *p;
 
+  if (!NO_DEFERRED_PROFILE_COUNTERS)
+    output_deferred_profile_counters ();
+
   output_deferred_plabels ();
 
   for (i = 0; VEC_iterate (extern_symbol, extern_symbols, i, p); i++)