OSDN Git Service

* pa.c (pa_asm_output_aligned_bss, pa_asm_output_aligned_common,
[pf3gnuchains/gcc-fork.git] / gcc / config / pa / pa.c
index bcb8329..63eee3b 100644 (file)
@@ -48,17 +48,6 @@ Boston, MA 02111-1307, USA.  */
 #include "target.h"
 #include "target-def.h"
 
-static int hppa_use_dfa_pipeline_interface (void);
-
-#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE 
-#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE hppa_use_dfa_pipeline_interface
-
-static int
-hppa_use_dfa_pipeline_interface (void)
-{
-  return 1;
-}
-
 /* Return nonzero if there is a bypass for the output of 
    OUT_INSN and the fp store IN_INSN.  */
 int
@@ -94,6 +83,7 @@ hppa_fpstore_bypass_p (rtx out_insn, rtx in_insn)
 #endif
 
 static void copy_reg_pointer (rtx, rtx);
+static void fix_range (const char *);
 static int hppa_address_cost (rtx);
 static bool hppa_rtx_costs (rtx, int, int, int *);
 static inline rtx force_mode (enum machine_mode, rtx);
@@ -103,14 +93,14 @@ static int pa_can_combine_p (rtx, rtx, rtx, int, rtx, rtx, rtx);
 static int forward_branch_p (rtx);
 static int shadd_constant_p (int);
 static void compute_zdepwi_operands (unsigned HOST_WIDE_INT, unsigned *);
-static int compute_movstr_length (rtx);
-static int compute_clrstr_length (rtx);
+static int compute_movmem_length (rtx);
+static int compute_clrmem_length (rtx);
 static bool pa_assemble_integer (rtx, unsigned int, int);
 static void remove_useless_addtr_insns (int);
-static void store_reg (int, int, int);
-static void store_reg_modify (int, int, int);
-static void load_reg (int, int, int);
-static void set_reg_plus_d (int, int, int, int);
+static void store_reg (int, HOST_WIDE_INT, int);
+static void store_reg_modify (int, int, HOST_WIDE_INT);
+static void load_reg (int, HOST_WIDE_INT, int);
+static void set_reg_plus_d (int, int, HOST_WIDE_INT, int);
 static void pa_output_function_prologue (FILE *, HOST_WIDE_INT);
 static void update_total_code_bytes (int);
 static void pa_output_function_epilogue (FILE *, HOST_WIDE_INT);
@@ -131,6 +121,8 @@ static void pa_asm_out_constructor (rtx, int);
 static void pa_asm_out_destructor (rtx, int);
 #endif
 static void pa_init_builtins (void);
+static rtx hppa_builtin_saveregs (void);
+static tree hppa_gimplify_va_arg_expr (tree, tree, tree *, tree *);
 static void copy_fp_args (rtx) ATTRIBUTE_UNUSED;
 static int length_fp_args (rtx) ATTRIBUTE_UNUSED;
 static struct deferred_plabel *get_plabel (const char *)
@@ -148,24 +140,32 @@ static void output_deferred_plabels (void);
 #ifdef HPUX_LONG_DOUBLE_LIBRARY
 static void pa_hpux_init_libfuncs (void);
 #endif
+static rtx pa_struct_value_rtx (tree, int);
+static bool pa_pass_by_reference (CUMULATIVE_ARGS *ca, enum machine_mode,
+                                 tree, bool);
+static struct machine_function * pa_init_machine_status (void);
+
 
 /* Save the operands last given to a compare for use when we
    generate a scc or bcc insn.  */
 rtx hppa_compare_op0, hppa_compare_op1;
 enum cmp_type hppa_branch_type;
 
-/* Which cpu we are scheduling for.  */
-enum processor_type pa_cpu;
-
-/* String to hold which cpu we are scheduling for.  */
-const char *pa_cpu_string;
-
 /* Which architecture we are generating code for.  */
 enum architecture_type pa_arch;
 
 /* String to hold which architecture we are generating code for.  */
 const char *pa_arch_string;
 
+/* String used with the -mfixed-range= option.  */
+const char *pa_fixed_range_string;
+
+/* Which cpu we are scheduling for.  */
+enum processor_type pa_cpu;
+
+/* String to hold which cpu we are scheduling for.  */
+const char *pa_cpu_string;
+
 /* 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;
@@ -261,8 +261,100 @@ static size_t n_deferred_plabels = 0;
 #define TARGET_INIT_LIBFUNCS pa_hpux_init_libfuncs
 #endif
 
+#undef TARGET_PROMOTE_FUNCTION_RETURN
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_tree_true
+
+#undef TARGET_STRUCT_VALUE_RTX
+#define TARGET_STRUCT_VALUE_RTX pa_struct_value_rtx
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY pa_return_in_memory
+#undef TARGET_MUST_PASS_IN_STACK
+#define TARGET_MUST_PASS_IN_STACK must_pass_in_stack_var_size
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE pa_pass_by_reference
+
+#undef TARGET_EXPAND_BUILTIN_SAVEREGS
+#define TARGET_EXPAND_BUILTIN_SAVEREGS hppa_builtin_saveregs
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR hppa_gimplify_va_arg_expr
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
+/* Parse the -mfixed-range= option string.  */
+
+static void
+fix_range (const char *const_str)
+{
+  int i, first, last;
+  char *str, *dash, *comma;
+
+  /* str must be of the form REG1'-'REG2{,REG1'-'REG} where REG1 and
+     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.  */
+
+  i = strlen (const_str);
+  str = (char *) alloca (i + 1);
+  memcpy (str, const_str, i + 1);
+
+  while (1)
+    {
+      dash = strchr (str, '-');
+      if (!dash)
+       {
+         warning ("value of -mfixed-range must have form REG1-REG2");
+         return;
+       }
+      *dash = '\0';
+
+      comma = strchr (dash + 1, ',');
+      if (comma)
+       *comma = '\0';
+
+      first = decode_reg_name (str);
+      if (first < 0)
+       {
+         warning ("unknown register name: %s", str);
+         return;
+       }
+
+      last = decode_reg_name (dash + 1);
+      if (last < 0)
+       {
+         warning ("unknown register name: %s", dash + 1);
+         return;
+       }
+
+      *dash = '-';
+
+      if (first > last)
+       {
+         warning ("%s-%s is an empty range", str, dash + 1);
+         return;
+       }
+
+      for (i = first; i <= last; ++i)
+       fixed_regs[i] = call_used_regs[i] = 1;
+
+      if (!comma)
+       break;
+
+      *comma = ',';
+      str = comma + 1;
+    }
+
+  /* Check if all floating point registers have been fixed.  */
+  for (i = FP_REG_FIRST; i <= FP_REG_LAST; i++)
+    if (!fixed_regs[i])
+      break;
+
+  if (i > FP_REG_LAST)
+    target_flags |= MASK_DISABLE_FPREGS;
+}
+
 void
 override_options (void)
 {
@@ -304,7 +396,7 @@ override_options (void)
       warning ("unknown -mschedule= option (%s).\nValid options are 700, 7100, 7100LC, 7200, 7300, and 8000\n", pa_cpu_string);
     }
 
-  /* Set the instruction set architecture.  */
+  /* Set the instruction architecture.  */
   if (pa_arch_string && ! strcmp (pa_arch_string, "1.0"))
     {
       pa_arch_string = "1.0";
@@ -329,6 +421,9 @@ override_options (void)
       warning ("unknown -march= option (%s).\nValid options are 1.0, 1.1, and 2.0\n", pa_arch_string);
     }
 
+  if (pa_fixed_range_string)
+    fix_range (pa_fixed_range_string);
+
   /* Unconditional branches in the delay slot are not compatible with dwarf2
      call frame information.  There is no benefit in using this optimization
      on PA8000 and later processors.  */
@@ -370,6 +465,8 @@ override_options (void)
       targetm.asm_out.unaligned_op.si = NULL;
       targetm.asm_out.unaligned_op.di = NULL;
     }
+
+  init_machine_status = pa_init_machine_status;
 }
 
 static void
@@ -381,6 +478,16 @@ pa_init_builtins (void)
 #endif
 }
 
+/* Function to init struct machine_function.
+   This will be called, via a pointer variable,
+   from push_function_context.  */
+
+static struct machine_function *
+pa_init_machine_status (void)
+{
+  return ggc_alloc_cleared (sizeof (machine_function));
+}
+
 /* If FROM is a probable pointer register, mark TO as a probable
    pointer register with the same pointer alignment as FROM.  */
 
@@ -577,9 +684,6 @@ move_src_operand (rtx op, enum machine_mode mode)
   if (register_operand (op, mode))
     return 1;
 
-  if (GET_CODE (op) == CONSTANT_P_RTX)
-    return 1;
-
   if (GET_CODE (op) == CONST_INT)
     return cint_ok_for_move (INTVAL (op));
 
@@ -605,6 +709,27 @@ move_src_operand (rtx op, enum machine_mode mode)
   return memory_address_p (mode, XEXP (op, 0));
 }
 
+/* Accept anything that can be used as the source operand for a prefetch
+   instruction.  */
+int
+prefetch_operand (rtx op, enum machine_mode mode)
+{
+  if (GET_CODE (op) != MEM)
+    return 0;
+
+  /* Until problems with management of the REG_POINTER flag are resolved,
+     we need to delay creating prefetch insns with unscaled indexed addresses
+     until CSE is not expected.  */
+  if (!TARGET_NO_SPACE_REGS
+      && !cse_not_expected
+      && GET_CODE (XEXP (op, 0)) == PLUS
+      && REG_P (XEXP (XEXP (op, 0), 0))
+      && REG_P (XEXP (XEXP (op, 0), 1)))
+    return 0;
+
+  return memory_address_p (mode, XEXP (op, 0));
+}
+
 /* Accept REG and any CONST_INT that can be moved in one instruction into a
    general register.  */
 int
@@ -892,7 +1017,7 @@ legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg)
                       gen_rtx_LO_SUM (Pmode, tmp_reg,
                                       gen_rtx_UNSPEC (Pmode,
                                                       gen_rtvec (1, orig),
-                                                      0)));
+                                                      UNSPEC_DLTIND14R)));
 
       current_function_uses_pic_offset_table = 1;
       MEM_NOTRAP_P (pic_ref) = 1;
@@ -1085,7 +1210,7 @@ hppa_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
   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)))
-      && (GET_RTX_CLASS (GET_CODE (XEXP (x, 1))) == 'o'
+      && (OBJECT_P (XEXP (x, 1))
          || GET_CODE (XEXP (x, 1)) == SUBREG)
       && GET_CODE (XEXP (x, 1)) != CONST)
     {
@@ -1493,7 +1618,7 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
      We have to do this because the REG_POINTER flag is not correctly
      carried through various optimization passes and CSE may substitute
      a pseudo without the pointer set for one with the pointer set.  As
-     a result, we loose various opportunites to create insns with
+     a result, we loose various opportunities to create insns with
      unscaled indexed addresses.  */
   if (!TARGET_NO_SPACE_REGS
       && !cse_not_expected
@@ -1550,15 +1675,18 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
     operand1 = gen_rtx_MEM (GET_MODE (operand1), tem);
 
   /* Handle secondary reloads for loads/stores of FP registers from
-     REG+D addresses where D does not fit in 5 bits, including
+     REG+D addresses where D does not fit in 5 or 14 bits, including
      (subreg (mem (addr))) cases.  */
-  if (fp_reg_operand (operand0, mode)
+  if (scratch_reg
+      && fp_reg_operand (operand0, mode)
       && ((GET_CODE (operand1) == MEM
-          && !memory_address_p (DFmode, XEXP (operand1, 0)))
+          && !memory_address_p ((GET_MODE_SIZE (mode) == 4 ? SFmode : DFmode),
+                                XEXP (operand1, 0)))
          || ((GET_CODE (operand1) == SUBREG
               && GET_CODE (XEXP (operand1, 0)) == MEM
-              && !memory_address_p (DFmode, XEXP (XEXP (operand1, 0), 0)))))
-      && scratch_reg)
+              && !memory_address_p ((GET_MODE_SIZE (mode) == 4
+                                     ? SFmode : DFmode),
+                                    XEXP (XEXP (operand1, 0), 0))))))
     {
       if (GET_CODE (operand1) == SUBREG)
        operand1 = XEXP (operand1, 0);
@@ -1585,14 +1713,17 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
                              gen_rtx_MEM (mode, scratch_reg)));
       return 1;
     }
-  else if (fp_reg_operand (operand1, mode)
+  else if (scratch_reg
+          && fp_reg_operand (operand1, mode)
           && ((GET_CODE (operand0) == MEM
-               && ! memory_address_p (DFmode, XEXP (operand0, 0)))
+               && !memory_address_p ((GET_MODE_SIZE (mode) == 4
+                                       ? SFmode : DFmode),
+                                      XEXP (operand0, 0)))
               || ((GET_CODE (operand0) == SUBREG)
                   && GET_CODE (XEXP (operand0, 0)) == MEM
-                  && !memory_address_p (DFmode,
-                                        XEXP (XEXP (operand0, 0), 0))))
-          && scratch_reg)
+                  && !memory_address_p ((GET_MODE_SIZE (mode) == 4
+                                         ? SFmode : DFmode),
+                                        XEXP (XEXP (operand0, 0), 0)))))
     {
       if (GET_CODE (operand0) == SUBREG)
        operand0 = XEXP (operand0, 0);
@@ -1623,15 +1754,15 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
   /* Handle secondary reloads for loads of FP registers from constant
      expressions by forcing the constant into memory.
 
-     use scratch_reg to hold the address of the memory location.
+     Use scratch_reg to hold the address of the memory location.
 
      The proper fix is to change PREFERRED_RELOAD_CLASS to return
      NO_REGS when presented with a const_int and a register class
      containing only FP registers.  Doing so unfortunately creates
      more problems than it solves.   Fix this for 2.5.  */
-  else if (fp_reg_operand (operand0, mode)
+  else if (scratch_reg
           && CONSTANT_P (operand1)
-          && scratch_reg)
+          && fp_reg_operand (operand0, mode))
     {
       rtx xoperands[2];
 
@@ -1653,14 +1784,14 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
     }
   /* Handle secondary reloads for SAR.  These occur when trying to load
      the SAR from memory, FP register, or with a constant.  */
-  else if (GET_CODE (operand0) == REG
+  else if (scratch_reg
+          && GET_CODE (operand0) == REG
           && REGNO (operand0) < FIRST_PSEUDO_REGISTER
           && REGNO_REG_CLASS (REGNO (operand0)) == SHIFT_REGS
           && (GET_CODE (operand1) == MEM
               || GET_CODE (operand1) == CONST_INT
               || (GET_CODE (operand1) == REG
-                  && FP_REG_CLASS_P (REGNO_REG_CLASS (REGNO (operand1)))))
-          && scratch_reg)
+                  && FP_REG_CLASS_P (REGNO_REG_CLASS (REGNO (operand1))))))
     {
       /* D might not fit in 14 bits either; for such cases load D into
         scratch reg.  */
@@ -1917,6 +2048,7 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
                  operands[1] = force_const_mem (mode, operand1);
                  operands[1] = legitimize_pic_address (XEXP (operands[1], 0),
                                                        mode, temp);
+                 operands[1] = gen_rtx_MEM (mode, operands[1]);
                  emit_move_sequence (operands, mode, temp);
                }
              else
@@ -1971,16 +2103,22 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
          return 1;
        }
       else if (GET_CODE (operand1) != CONST_INT
-              || ! cint_ok_for_move (INTVAL (operand1)))
+              || !cint_ok_for_move (INTVAL (operand1)))
        {
-         rtx extend = NULL_RTX;
-         rtx temp;
+         rtx insn, temp;
+         rtx op1 = operand1;
+         HOST_WIDE_INT value = 0;
+         HOST_WIDE_INT insv = 0;
+         int insert = 0;
+
+         if (GET_CODE (operand1) == CONST_INT)
+           value = INTVAL (operand1);
 
-         if (TARGET_64BIT && GET_CODE (operand1) == CONST_INT
+         if (TARGET_64BIT
+             && GET_CODE (operand1) == CONST_INT
              && HOST_BITS_PER_WIDE_INT > 32
              && GET_MODE_BITSIZE (GET_MODE (operand0)) > 32)
            {
-             HOST_WIDE_INT val = INTVAL (operand1);
              HOST_WIDE_INT nval;
 
              /* Extract the low order 32 bits of the value and sign extend.
@@ -1988,19 +2126,21 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
                 can use the original value as-is.  If the new value is
                 different, we use it and insert the most-significant 32-bits
                 of the original value into the final result.  */
-             nval = ((val & (((HOST_WIDE_INT) 2 << 31) - 1))
+             nval = ((value & (((HOST_WIDE_INT) 2 << 31) - 1))
                      ^ ((HOST_WIDE_INT) 1 << 31)) - ((HOST_WIDE_INT) 1 << 31);
-             if (val != nval)
+             if (value != nval)
                {
 #if HOST_BITS_PER_WIDE_INT > 32
-                 extend = GEN_INT (val >> 32);
+                 insv = value >= 0 ? value >> 32 : ~(~value >> 32);
 #endif
+                 insert = 1;
+                 value = nval;
                  operand1 = GEN_INT (nval);
                }
            }
 
          if (reload_in_progress || reload_completed)
-           temp = operand0;
+           temp = scratch_reg ? scratch_reg : operand0;
          else
            temp = gen_reg_rtx (mode);
 
@@ -2008,7 +2148,8 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
             because PLUS uses an 11-bit immediate and the insn sequence
             generated is not as efficient as the one using HIGH/LO_SUM.  */
          if (GET_CODE (operand1) == CONST_INT
-             && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT)
+             && GET_MODE_BITSIZE (mode) <= HOST_BITS_PER_WIDE_INT
+             && !insert)
            {
              /* Directly break constant into high and low parts.  This
                 provides better optimization opportunities because various
@@ -2016,7 +2157,6 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
                 We use a 14-bit signed low part except when the addition
                 of 0x4000 to the high part might change the sign of the
                 high part.  */
-             HOST_WIDE_INT value = INTVAL (operand1);
              HOST_WIDE_INT low = value & 0x3fff;
              HOST_WIDE_INT high = value & ~ 0x3fff;
 
@@ -2040,11 +2180,60 @@ emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
              operands[1] = gen_rtx_LO_SUM (mode, temp, operand1);
            }
 
-         emit_move_insn (operands[0], operands[1]);
+         insn = emit_move_insn (operands[0], operands[1]);
 
-         if (extend != NULL_RTX)
-           emit_insn (gen_insv (operands[0], GEN_INT (32), const0_rtx,
-                                extend));
+         /* Now insert the most significant 32 bits of the value
+            into the register.  When we don't have a second register
+            available, it could take up to nine instructions to load
+            a 64-bit integer constant.  Prior to reload, we force
+            constants that would take more than three instructions
+            to load to the constant pool.  During and after reload,
+            we have to handle all possible values.  */
+         if (insert)
+           {
+             /* Use a HIGH/LO_SUM/INSV sequence if we have a second
+                register and the value to be inserted is outside the
+                range that can be loaded with three depdi instructions.  */
+             if (temp != operand0 && (insv >= 16384 || insv < -16384))
+               {
+                 operand1 = GEN_INT (insv);
+
+                 emit_insn (gen_rtx_SET (VOIDmode, temp,
+                                         gen_rtx_HIGH (mode, operand1)));
+                 emit_move_insn (temp, gen_rtx_LO_SUM (mode, temp, operand1));
+                 emit_insn (gen_insv (operand0, GEN_INT (32),
+                                      const0_rtx, temp));
+               }
+             else
+               {
+                 int len = 5, pos = 27;
+
+                 /* Insert the bits using the depdi instruction.  */
+                 while (pos >= 0)
+                   {
+                     HOST_WIDE_INT v5 = ((insv & 31) ^ 16) - 16;
+                     HOST_WIDE_INT sign = v5 < 0;
+
+                     /* Left extend the insertion.  */
+                     insv = (insv >= 0 ? insv >> len : ~(~insv >> len));
+                     while (pos > 0 && (insv & 1) == sign)
+                       {
+                         insv = (insv >= 0 ? insv >> 1 : ~(~insv >> 1));
+                         len += 1;
+                         pos -= 1;
+                       }
+
+                     emit_insn (gen_insv (operand0, GEN_INT (len),
+                                          GEN_INT (pos), GEN_INT (v5)));
+
+                     len = pos > 0 && pos < 5 ? pos : 5;
+                     pos -= len;
+                   }
+               }
+           }
+
+         REG_NOTES (insn)
+           = gen_rtx_EXPR_LIST (REG_EQUAL, op1, REG_NOTES (insn));
 
          return 1;
        }
@@ -2120,7 +2309,7 @@ read_only_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
 
 \f
 /* Return the best assembler insn template
-   for moving operands[1] into operands[0] as a fullword.   */
+   for moving operands[1] into operands[0] as a fullword.  */
 const char *
 singlemove_string (rtx *operands)
 {
@@ -2583,7 +2772,7 @@ find_addr_reg (rtx addr)
    OPERANDS[3] is a register for temporary storage.
    OPERANDS[4] is the size as a CONST_INT
    OPERANDS[5] is the alignment safe to use, as a CONST_INT.
-   OPERANDS[6] is another temporary register.   */
+   OPERANDS[6] is another temporary register.  */
 
 const char *
 output_block_move (rtx *operands, int size_is_constant ATTRIBUTE_UNUSED)
@@ -2713,7 +2902,7 @@ output_block_move (rtx *operands, int size_is_constant ATTRIBUTE_UNUSED)
    count insns rather than emit them.  */
 
 static int
-compute_movstr_length (rtx insn)
+compute_movmem_length (rtx insn)
 {
   rtx pat = PATTERN (insn);
   unsigned int align = INTVAL (XEXP (XVECEXP (pat, 0, 7), 0));
@@ -2855,7 +3044,7 @@ output_block_clear (rtx *operands, int size_is_constant ATTRIBUTE_UNUSED)
    count insns rather than emit them.  */
 
 static int
-compute_clrstr_length (rtx insn)
+compute_clrmem_length (rtx insn)
 {
   rtx pat = PATTERN (insn);
   unsigned int align = INTVAL (XEXP (XVECEXP (pat, 0, 4), 0));
@@ -3070,7 +3259,7 @@ output_ascii (FILE *file, const char *p, int size)
 {
   int i;
   int chars_output;
-  unsigned char partial_output[16];    /* Max space 4 chars can occupy.   */
+  unsigned char partial_output[16];    /* Max space 4 chars can occupy.  */
 
   /* The HP assembler can only take strings of 256 characters at one
      time.  This is a limitation on input line length, *not* the
@@ -3334,8 +3523,8 @@ remove_useless_addtr_insns (int check_notes)
 /* Global variables set by output_function_prologue().  */
 /* Size of frame.  Need to know this to emit return insns from
    leaf procedures.  */
-static int actual_fsize;
-static int local_fsize, save_fregs;
+static HOST_WIDE_INT actual_fsize, local_fsize;
+static int save_fregs;
 
 /* Emit RTL to store REG at the memory location specified by BASE+DISP.
    Handle case where DISP > 8k by using the add_high_const patterns.
@@ -3344,7 +3533,7 @@ static int local_fsize, save_fregs;
    in %r1.  There is code in expand_hppa_{prologue,epilogue} that knows this.*/
 
 static void
-store_reg (int reg, int disp, int base)
+store_reg (int reg, HOST_WIDE_INT disp, int base)
 {
   rtx insn, dest, src, basereg;
 
@@ -3355,11 +3544,33 @@ store_reg (int reg, int disp, int base)
       dest = gen_rtx_MEM (word_mode, plus_constant (basereg, disp));
       insn = emit_move_insn (dest, src);
     }
+  else if (TARGET_64BIT && !VAL_32_BITS_P (disp))
+    {
+      rtx delta = GEN_INT (disp);
+      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);
+      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),
+                REG_NOTES (insn));
+       }
+    }
   else
     {
       rtx delta = GEN_INT (disp);
       rtx high = gen_rtx_PLUS (Pmode, basereg, gen_rtx_HIGH (Pmode, delta));
       rtx tmpreg = gen_rtx_REG (Pmode, 1);
+
       emit_move_insn (tmpreg, high);
       dest = gen_rtx_MEM (word_mode, gen_rtx_LO_SUM (Pmode, tmpreg, delta));
       insn = emit_move_insn (dest, src);
@@ -3384,11 +3595,11 @@ store_reg (int reg, int disp, int base)
    add MOD to BASE.  MOD must be <= 8k.  */
 
 static void
-store_reg_modify (int base, int reg, int mod)
+store_reg_modify (int base, int reg, HOST_WIDE_INT mod)
 {
   rtx insn, basereg, srcreg, delta;
 
-  if (! VAL_14_BITS_P (mod))
+  if (!VAL_14_BITS_P (mod))
     abort ();
 
   basereg = gen_rtx_REG (Pmode, base);
@@ -3431,7 +3642,7 @@ store_reg_modify (int base, int reg, int mod)
    There is code in expand_hppa_{prologue,epilogue} that knows about this.  */
 
 static void
-set_reg_plus_d (int reg, int base, int disp, int note)
+set_reg_plus_d (int reg, int base, HOST_WIDE_INT disp, int note)
 {
   rtx insn;
 
@@ -3440,25 +3651,35 @@ set_reg_plus_d (int reg, int base, int disp, int note)
       insn = emit_move_insn (gen_rtx_REG (Pmode, reg),
                             plus_constant (gen_rtx_REG (Pmode, base), disp));
     }
+  else if (TARGET_64BIT && !VAL_32_BITS_P (disp))
+    {
+      rtx basereg = gen_rtx_REG (Pmode, base);
+      rtx delta = GEN_INT (disp);
+      rtx tmpreg = gen_rtx_REG (Pmode, 1);
+
+      emit_move_insn (tmpreg, delta);
+      insn = emit_move_insn (gen_rtx_REG (Pmode, reg),
+                            gen_rtx_PLUS (Pmode, tmpreg, basereg));
+    }
   else
     {
       rtx basereg = gen_rtx_REG (Pmode, base);
       rtx delta = GEN_INT (disp);
+      rtx tmpreg = gen_rtx_REG (Pmode, 1);
 
-      emit_move_insn (gen_rtx_REG (Pmode, 1),
+      emit_move_insn (tmpreg,
                      gen_rtx_PLUS (Pmode, basereg,
                                    gen_rtx_HIGH (Pmode, delta)));
       insn = emit_move_insn (gen_rtx_REG (Pmode, reg),
-                            gen_rtx_LO_SUM (Pmode, gen_rtx_REG (Pmode, 1),
-                                            delta));
+                            gen_rtx_LO_SUM (Pmode, tmpreg, delta));
     }
 
   if (DO_FRAME_NOTES && note)
     RTX_FRAME_RELATED_P (insn) = 1;
 }
 
-int
-compute_frame_size (int size, int *fregs_live)
+HOST_WIDE_INT
+compute_frame_size (HOST_WIDE_INT size, int *fregs_live)
 {
   int freg_saved = 0;
   int i, j;
@@ -3562,7 +3783,7 @@ pa_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
   /* hppa_expand_prologue does the dirty work now.  We just need
      to output the assembler directives which denote the start
      of a function.  */
-  fprintf (file, "\t.CALLINFO FRAME=%d", actual_fsize);
+  fprintf (file, "\t.CALLINFO FRAME=" HOST_WIDE_INT_PRINT_DEC, actual_fsize);
   if (regs_ever_live[2])
     fputs (",CALLS,SAVE_RP", file);
   else
@@ -3603,8 +3824,9 @@ void
 hppa_expand_prologue (void)
 {
   int merge_sp_adjust_with_store = 0;
-  int size = get_frame_size ();
-  int i, offset;
+  HOST_WIDE_INT size = get_frame_size ();
+  HOST_WIDE_INT offset;
+  int i;
   rtx insn, tmpreg;
 
   gr_saved = 0;
@@ -3668,8 +3890,8 @@ hppa_expand_prologue (void)
                 So instead use stwm to store at *sp and post-increment the
                 stack pointer as an atomic operation.  Then increment sp to
                 finish allocating the new frame.  */
-             int adjust1 = 8192 - 64;
-             int adjust2 = actual_fsize - adjust1;
+             HOST_WIDE_INT adjust1 = 8192 - 64;
+             HOST_WIDE_INT adjust2 = actual_fsize - adjust1;
 
              store_reg_modify (STACK_POINTER_REGNUM, 1, adjust1);
              set_reg_plus_d (STACK_POINTER_REGNUM, STACK_POINTER_REGNUM,
@@ -3901,26 +4123,39 @@ hppa_expand_prologue (void)
    Handle case where DISP > 8k by using the add_high_const patterns.  */
 
 static void
-load_reg (int reg, int disp, int base)
+load_reg (int reg, HOST_WIDE_INT disp, int base)
 {
-  rtx src, dest, basereg;
+  rtx dest = gen_rtx_REG (word_mode, reg);
+  rtx basereg = gen_rtx_REG (Pmode, base);
+  rtx src;
 
-  dest = gen_rtx_REG (word_mode, reg);
-  basereg = gen_rtx_REG (Pmode, base);
   if (VAL_14_BITS_P (disp))
+    src = gen_rtx_MEM (word_mode, plus_constant (basereg, disp));
+  else if (TARGET_64BIT && !VAL_32_BITS_P (disp))
     {
-      src = gen_rtx_MEM (word_mode, plus_constant (basereg, disp));
-      emit_move_insn (dest, src);
+      rtx delta = GEN_INT (disp);
+      rtx tmpreg = gen_rtx_REG (Pmode, 1);
+
+      emit_move_insn (tmpreg, delta);
+      if (TARGET_DISABLE_INDEXING)
+       {
+         emit_move_insn (tmpreg, gen_rtx_PLUS (Pmode, tmpreg, basereg));
+         src = gen_rtx_MEM (word_mode, tmpreg);
+       }
+      else
+       src = gen_rtx_MEM (word_mode, gen_rtx_PLUS (Pmode, tmpreg, basereg));
     }
   else
     {
       rtx delta = GEN_INT (disp);
       rtx high = gen_rtx_PLUS (Pmode, basereg, gen_rtx_HIGH (Pmode, delta));
       rtx tmpreg = gen_rtx_REG (Pmode, 1);
+
       emit_move_insn (tmpreg, high);
       src = gen_rtx_MEM (word_mode, gen_rtx_LO_SUM (Pmode, tmpreg, delta));
-      emit_move_insn (dest, src);
     }
+
+  emit_move_insn (dest, src);
 }
 
 /* Update the total code bytes output to the text section.  */
@@ -3988,6 +4223,14 @@ pa_output_function_epilogue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 
   fputs ("\t.EXIT\n\t.PROCEND\n", file);
 
+  if (TARGET_SOM && TARGET_GAS)
+    {
+      /* We done with this subspace except possibly for some additional
+        debug information.  Forget that we are in this subspace to ensure
+        that the next function is output in its own subspace.  */
+      forget_section ();
+    }
+
   if (INSN_ADDRESSES_SET_P ())
     {
       insn = get_last_nonnote_insn ();
@@ -4006,9 +4249,10 @@ void
 hppa_expand_epilogue (void)
 {
   rtx tmpreg;
-  int offset, i;
+  HOST_WIDE_INT offset;
+  HOST_WIDE_INT ret_off = 0;
+  int i;
   int merge_sp_adjust_with_load = 0;
-  int ret_off = 0;
 
   /* We will use this often.  */
   tmpreg = gen_rtx_REG (word_mode, 1);
@@ -4669,7 +4913,7 @@ pa_adjust_insn_length (rtx insn, int 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_movstr_length (insn) - 4;
+    return compute_movmem_length (insn) - 4;
   /* Block clear pattern.  */
   else if (GET_CODE (insn) == INSN
           && GET_CODE (pat) == PARALLEL
@@ -4677,7 +4921,7 @@ pa_adjust_insn_length (rtx insn, int length)
           && GET_CODE (XEXP (XVECEXP (pat, 0, 0), 0)) == MEM
           && XEXP (XVECEXP (pat, 0, 0), 1) == const0_rtx
           && GET_MODE (XEXP (XVECEXP (pat, 0, 0), 0)) == BLKmode)
-    return compute_clrstr_length (insn) - 4;
+    return compute_clrmem_length (insn) - 4;
   /* Conditional branch with an unfilled delay slot.  */
   else if (GET_CODE (insn) == JUMP_INSN && ! simplejump_p (insn))
     {
@@ -5350,6 +5594,7 @@ pa_hpux_init_libfuncs (void)
   set_optab_libfunc (ge_optab, TFmode, "_U_Qfge");
   set_optab_libfunc (lt_optab, TFmode, "_U_Qflt");
   set_optab_libfunc (le_optab, TFmode, "_U_Qfle");
+  set_optab_libfunc (unord_optab, TFmode, "_U_Qfunord");
 
   set_conv_libfunc (sext_optab,   TFmode, SFmode, "_U_Qfcnvff_sgl_to_quad");
   set_conv_libfunc (sext_optab,   TFmode, DFmode, "_U_Qfcnvff_dbl_to_quad");
@@ -5435,8 +5680,8 @@ emit_hpdiv_const (rtx *operands, int unsignedp)
 
       emit_move_insn (gen_rtx_REG (SImode, 26), operands[1]);
       emit
-       (gen_rtx
-        (PARALLEL, VOIDmode,
+       (gen_rtx_PARALLEL
+        (VOIDmode,
          gen_rtvec (6, gen_rtx_SET (VOIDmode, gen_rtx_REG (SImode, 29),
                                     gen_rtx_fmt_ee (unsignedp ? UDIV : DIV,
                                                     SImode,
@@ -5696,6 +5941,37 @@ secondary_reload_class (enum reg_class class, enum machine_mode mode, rtx in)
   return NO_REGS;
 }
 
+/* In the 32-bit runtime, arguments larger than eight bytes are passed
+   by invisible reference.  As a GCC extension, we also pass anything
+   with a zero or variable size by reference.
+
+   The 64-bit runtime does not describe passing any types by invisible
+   reference.  The internals of GCC can't currently handle passing
+   empty structures, and zero or variable length arrays when they are
+   not passed entirely on the stack or by reference.  Thus, as a GCC
+   extension, we pass these types by reference.  The HP compiler doesn't
+   support these types, so hopefully there shouldn't be any compatibility
+   issues.  This may have to be revisited when HP releases a C99 compiler
+   or updates the ABI.  */
+
+static bool
+pa_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
+                     enum machine_mode mode, tree type,
+                     bool named ATTRIBUTE_UNUSED)
+{
+  HOST_WIDE_INT size;
+
+  if (type)
+    size = int_size_in_bytes (type);
+  else
+    size = GET_MODE_SIZE (mode);
+
+  if (TARGET_64BIT)
+    return size <= 0;
+  else
+    return size <= 0 || size > 8;
+}
+
 enum direction
 function_arg_padding (enum machine_mode mode, tree type)
 {
@@ -5735,7 +6011,7 @@ function_arg_padding (enum machine_mode mode, tree type)
    to determine if stdargs or varargs is used and fill in an initial
    va_list.  A pointer to this constructor is returned.  */
 
-struct rtx_def *
+static rtx
 hppa_builtin_saveregs (void)
 {
   rtx offset, dest;
@@ -5811,106 +6087,60 @@ hppa_va_start (tree valist, rtx nextarg)
   std_expand_builtin_va_start (valist, nextarg);
 }
 
-rtx
-hppa_va_arg (tree valist, tree type)
+static tree
+hppa_gimplify_va_arg_expr (tree valist, tree type, tree *pre_p, tree *post_p)
 {
-  HOST_WIDE_INT size = int_size_in_bytes (type);
-  HOST_WIDE_INT ofs;
-  tree t, ptr, pptr;
-
   if (TARGET_64BIT)
     {
-      /* Every argument in PA64 is supposed to be passed by value
-        (including large structs).  However, as a GCC extension, we
-        pass zero and variable sized arguments by reference.  Empty
-        structures are a GCC extension not supported by the HP
-        compilers.  Thus, passing them by reference isn't likely
-        to conflict with the ABI.  For variable sized arguments,
-        GCC doesn't have the infrastructure to allocate these to
-        registers.  */
-
-      /* Arguments with a size greater than 8 must be aligned 0 MOD 16.  */
-
-      if (size > UNITS_PER_WORD)
-        {
-          t = build (PLUS_EXPR, TREE_TYPE (valist), valist,
-                     build_int_2 (2 * UNITS_PER_WORD - 1, 0));
-          t = build (BIT_AND_EXPR, TREE_TYPE (t), t,
-                     build_int_2 (-2 * UNITS_PER_WORD, -1));
-          t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t);
-          TREE_SIDE_EFFECTS (t) = 1;
-         expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
-        }
-
-      if (size > 0)
-       return std_expand_builtin_va_arg (valist, type);
-      else
-       {
-         ptr = build_pointer_type (type);
-
-         /* Args grow upward.  */
-         t = build (POSTINCREMENT_EXPR, TREE_TYPE (valist), valist,
-                    build_int_2 (POINTER_SIZE / BITS_PER_UNIT, 0));
-         TREE_SIDE_EFFECTS (t) = 1;
-
-         pptr = build_pointer_type (ptr);
-         t = build1 (NOP_EXPR, pptr, t);
-         TREE_SIDE_EFFECTS (t) = 1;
-
-         t = build1 (INDIRECT_REF, ptr, t);
-         TREE_SIDE_EFFECTS (t) = 1;
-       }
+      /* Args grow upward.  We can use the generic routines.  */
+      return std_gimplify_va_arg_expr (valist, type, pre_p, post_p);
     }
   else /* !TARGET_64BIT */
     {
-      ptr = build_pointer_type (type);
+      tree ptr = build_pointer_type (type);
+      tree valist_type;
+      tree t, u;
+      unsigned int size, ofs;
+      bool indirect;
 
-      /* "Large" and variable sized types are passed by reference.  */
-      if (size > 8 || size <= 0)
+      indirect = pass_by_reference (NULL, TYPE_MODE (type), type, 0);
+      if (indirect)
        {
-         /* Args grow downward.  */
-         t = build (PREDECREMENT_EXPR, TREE_TYPE (valist), valist,
-                    build_int_2 (POINTER_SIZE / BITS_PER_UNIT, 0));
-         TREE_SIDE_EFFECTS (t) = 1;
-
-         pptr = build_pointer_type (ptr);
-         t = build1 (NOP_EXPR, pptr, t);
-         TREE_SIDE_EFFECTS (t) = 1;
-
-         t = build1 (INDIRECT_REF, ptr, t);
-         TREE_SIDE_EFFECTS (t) = 1;
+         type = ptr;
+         ptr = build_pointer_type (type);
        }
-      else
-       {
-         t = build (PLUS_EXPR, TREE_TYPE (valist), valist,
-                    build_int_2 (-size, -1));
+      size = int_size_in_bytes (type);
+      valist_type = TREE_TYPE (valist);
 
-         /* Copied from va-pa.h, but we probably don't need to align to
-            word size, since we generate and preserve that invariant.  */
-         t = build (BIT_AND_EXPR, TREE_TYPE (valist), t,
-                    build_int_2 ((size > 4 ? -8 : -4), -1));
+      /* Args grow down.  Not handled by generic routines.  */
 
-         t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t);
-         TREE_SIDE_EFFECTS (t) = 1;
+      u = fold_convert (valist_type, size_in_bytes (type));
+      t = build (MINUS_EXPR, valist_type, valist, u);
 
-         ofs = (8 - size) % 4;
-         if (ofs)
-           {
-             t = build (PLUS_EXPR, TREE_TYPE (valist), t,
-                        build_int_2 (ofs, 0));
-             TREE_SIDE_EFFECTS (t) = 1;
-           }
+      /* 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_2 ((size > 4 ? -8 : -4), -1);
+      u = fold_convert (valist_type, u);
+      t = build (BIT_AND_EXPR, valist_type, t, u);
+
+      t = build (MODIFY_EXPR, valist_type, valist, t);
 
-         t = build1 (NOP_EXPR, ptr, t);
-         TREE_SIDE_EFFECTS (t) = 1;
+      ofs = (8 - size) % 4;
+      if (ofs != 0)
+       {
+         u = fold_convert (valist_type, size_int (ofs));
+         t = build (PLUS_EXPR, valist_type, t, u);
        }
-    }
 
-  /* Calculate!  */
-  return expand_expr (t, NULL_RTX, VOIDmode, EXPAND_NORMAL);
-}
+      t = fold_convert (ptr, t);
+      t = build_fold_indirect_ref (t);
 
+      if (indirect)
+       t = build_fold_indirect_ref (t);
 
+      return t;
+    }
+}
 
 /* This routine handles all the normal conditional branch sequences we
    might need to generate.  It handles compare immediate vs compare
@@ -6606,7 +6836,7 @@ output_dbra (rtx *operands, rtx insn, int which_alternative)
   else
     {
       /* Reload loop counter from memory, the store back to memory
-        happens in the branch's delay slot.   */
+        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";
@@ -6709,7 +6939,7 @@ output_movb (rtx *operands, rtx insn, int which_alternative,
   else if (which_alternative == 2)
     {
       /* Reload loop counter from memory, the store back to memory
-        happens in the branch's delay slot.   */
+        happens in the branch's delay slot.  */
       if (get_attr_length (insn) == 8)
        return "{comb|cmpb},%S2 %%r0,%1,%3\n\tstw %1,%0";
       else
@@ -6721,7 +6951,7 @@ output_movb (rtx *operands, rtx insn, int which_alternative,
       if (get_attr_length (insn) == 8)
        return "{comb|cmpb},%S2 %%r0,%1,%3\n\tmtsar %r1";
       else
-       return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tbl %3\n\tmtsar %r1";
+       return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tb %3\n\tmtsar %r1";
     }
 }
 
@@ -7437,7 +7667,15 @@ output_indirect_call (rtx insn, rtx call_dest)
      No need to check target flags as the length uniquely identifies
      the remaining cases.  */
   if (attr_length_indirect_call (insn) == 8)
-    return ".CALL\tARGW0=GR\n\t{bl|b,l} $$dyncall,%%r31\n\tcopy %%r31,%%r2";
+    {
+      /* The HP linker substitutes a BLE for millicode calls using
+        the short PIC PCREL form.  Thus, we must use %r31 as the
+        link register when generating PA 1.x code.  */
+      if (TARGET_PA_20)
+       return ".CALL\tARGW0=GR\n\tb,l $$dyncall,%%r2\n\tcopy %%r2,%%r31";
+      else
+       return ".CALL\tARGW0=GR\n\tbl $$dyncall,%%r31\n\tcopy %%r31,%%r2";
+    }
 
   /* Long millicode call, but we are not generating PIC or portable runtime
      code.  */
@@ -7778,8 +8016,9 @@ pa_asm_output_mi_thunk (FILE *file, tree thunk_fndecl, HOST_WIDE_INT delta,
       fprintf (file, "\t.align 4\n");
       ASM_OUTPUT_LABEL (file, label);
       fprintf (file, "\t.word P'%s\n", fname);
-      function_section (thunk_fndecl);
     }
+  else if (TARGET_SOM && TARGET_GAS)
+    forget_section ();
 
   current_thunk_number++;
   nbytes = ((nbytes + FUNCTION_BOUNDARY / BITS_PER_UNIT - 1)
@@ -7930,6 +8169,79 @@ pa_asm_out_destructor (rtx symbol, int priority)
 }
 #endif
 
+/* This function places uninitialized global data in the bss section.
+   The ASM_OUTPUT_ALIGNED_BSS macro needs to be defined to call this
+   function on the SOM port to prevent uninitialized global data from
+   being placed in the data section.  */
+   
+void
+pa_asm_output_aligned_bss (FILE *stream,
+                          const char *name,
+                          unsigned HOST_WIDE_INT size,
+                          unsigned int align)
+{
+  bss_section ();
+  fprintf (stream, "\t.align %u\n", align / BITS_PER_UNIT);
+
+#ifdef ASM_OUTPUT_TYPE_DIRECTIVE
+  ASM_OUTPUT_TYPE_DIRECTIVE (stream, name, "object");
+#endif
+
+#ifdef ASM_OUTPUT_SIZE_DIRECTIVE
+  ASM_OUTPUT_SIZE_DIRECTIVE (stream, name, size);
+#endif
+
+  fprintf (stream, "\t.align %u\n", align / BITS_PER_UNIT);
+  ASM_OUTPUT_LABEL (stream, name);
+  fprintf (stream, "\t.block "HOST_WIDE_INT_PRINT_UNSIGNED"\n", size);
+}
+
+/* Both the HP and GNU assemblers under HP-UX provide a .comm directive
+   that doesn't allow the alignment of global common storage to be directly
+   specified.  The SOM linker aligns common storage based on the rounded
+   value of the NUM_BYTES parameter in the .comm directive.  It's not
+   possible to use the .align directive as it doesn't affect the alignment
+   of the label associated with a .comm directive.  */
+
+void
+pa_asm_output_aligned_common (FILE *stream,
+                             const char *name,
+                             unsigned HOST_WIDE_INT size,
+                             unsigned int align)
+{
+  bss_section ();
+
+  assemble_name (stream, name);
+  fprintf (stream, "\t.comm "HOST_WIDE_INT_PRINT_UNSIGNED"\n",
+           MAX (size, align / BITS_PER_UNIT));
+}
+
+/* We can't use .comm for local common storage as the SOM linker effectively
+   treats the symbol as universal and uses the same storage for local symbols
+   with the same name in different object files.  The .block directive
+   reserves an uninitialized block of storage.  However, it's not common
+   storage.  Fortunately, GCC never requests common storage with the same
+   name in any given translation unit.  */
+
+void
+pa_asm_output_aligned_local (FILE *stream,
+                            const char *name,
+                            unsigned HOST_WIDE_INT size,
+                            unsigned int align)
+{
+  bss_section ();
+  fprintf (stream, "\t.align %u\n", align / BITS_PER_UNIT);
+
+#ifdef LOCAL_ASM_OP
+  fprintf (stream, "%s", LOCAL_ASM_OP);
+  assemble_name (stream, name);
+  fprintf (stream, "\n");
+#endif
+
+  ASM_OUTPUT_LABEL (stream, name);
+  fprintf (stream, "\t.block "HOST_WIDE_INT_PRINT_UNSIGNED"\n", size);
+}
+
 /* Returns 1 if the 6 operands specified in OPERANDS are suitable for
    use in fmpysub instructions.  */
 int
@@ -8300,7 +8612,7 @@ pa_reorg (void)
         markers disables output of the branch table to readonly memory,
         and any alignment directives that might be needed.  Possibly,
         the begin_brtab insn should be output before the label for the
-        table.  This doesn matter at the moment since the tables are
+        table.  This doesn't matter at the moment since the tables are
         always output in the text section.  */
       for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
        {
@@ -8647,7 +8959,7 @@ insn_refs_are_delayed (rtx insn)
    the mode is SF or DF. Then the value is returned in fr4 (32).
 
    This must perform the same promotions as PROMOTE_MODE, else
-   PROMOTE_FUNCTION_RETURN will not work correctly.
+   TARGET_PROMOTE_FUNCTION_RETURN will not work correctly.
 
    Small structures must be returned in a PARALLEL on PA64 in order
    to match the HP Compiler ABI.  */
@@ -8807,21 +9119,9 @@ function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
          /* Structures 5 to 8 bytes in size are passed in the general
             registers in the same manner as other non floating-point
             objects.  The data is right-justified and zero-extended
-            to 64 bits.
-
-            This is magic.  Normally, using a PARALLEL results in left
-            justified data on a big-endian target.  However, using a
-            single double-word register provides the required right
-            justification for 5 to 8 byte structures.  This has nothing
-            to do with the direction of padding specified for the argument.
-            It has to do with how the data is widened and shifted into
-            and from the register.
-
-            Aside from adding load_multiple and store_multiple patterns,
-            this is the only way that I have found to obtain right
-            justification of BLKmode data when it has a size greater
-            than one word.  Splitting the operation into two SImode loads
-            or returning a DImode REG results in left justified data.  */
+            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)
            {
              rtx loc = gen_rtx_EXPR_LIST (VOIDmode,
@@ -8939,6 +9239,58 @@ cmpib_comparison_operator (rtx op, enum machine_mode mode)
              || GET_CODE (op) == LEU));
 }
 
+#ifndef ONE_ONLY_TEXT_SECTION_ASM_OP
+#define ONE_ONLY_TEXT_SECTION_ASM_OP ""
+#endif
+
+#ifndef NEW_TEXT_SECTION_ASM_OP
+#define NEW_TEXT_SECTION_ASM_OP ""
+#endif
+
+#ifndef DEFAULT_TEXT_SECTION_ASM_OP
+#define DEFAULT_TEXT_SECTION_ASM_OP ""
+#endif
+
+/* Select and return a TEXT_SECTION_ASM_OP for the current function.
+
+   This function is only used with SOM.  Because we don't support
+   named subspaces, we can only create a new subspace or switch back
+   into the default text subspace.  */
+const char *
+som_text_section_asm_op (void)
+{
+  if (TARGET_SOM && TARGET_GAS)
+    {
+      if (cfun && !cfun->machine->in_nsubspa)
+       {
+         /* We only want to emit a .nsubspa directive once at the
+            start of the function.  */
+         cfun->machine->in_nsubspa = 1;
+
+         /* Create a new subspace for the text.  This provides
+            better stub placement and one-only functions.  */
+         if (cfun->decl
+             && DECL_ONE_ONLY (cfun->decl)
+             && !DECL_WEAK (cfun->decl))
+           return ONE_ONLY_TEXT_SECTION_ASM_OP;
+
+         return NEW_TEXT_SECTION_ASM_OP;
+       }
+      else
+       {
+         /* There isn't a current function or the body of the current
+            function has been completed.  So, we are changing to the
+            text section to output debugging information.  Do this in
+            the default text section.  We need to forget that we are
+            in the text section so that text_section will call us the
+            next time around.  */
+         forget_section ();
+       }
+    }
+
+  return DEFAULT_TEXT_SECTION_ASM_OP;
+}
+
 /* On hpux10, the linker will give an error if we have a reference
    in the read-only data section to a symbol defined in a shared
    library.  Therefore, expressions that might require a reloc can
@@ -8955,11 +9307,23 @@ pa_select_section (tree exp, int reloc,
       && (DECL_INITIAL (exp) == error_mark_node
           || TREE_CONSTANT (DECL_INITIAL (exp)))
       && !reloc)
-    readonly_data_section ();
+    {
+      if (TARGET_SOM
+         && DECL_ONE_ONLY (exp)
+         && !DECL_WEAK (exp))
+       one_only_readonly_data_section ();
+      else
+       readonly_data_section ();
+    }
   else if (TREE_CODE_CLASS (TREE_CODE (exp)) == 'c'
-          && !(TREE_CODE (exp) == STRING_CST && flag_writable_strings)
           && !reloc)
     readonly_data_section ();
+  else if (TARGET_SOM
+          && TREE_CODE (exp) == VAR_DECL
+          && DECL_ONE_ONLY (exp)
+          && !DECL_WEAK (exp)
+          && DECL_INITIAL (exp))
+    one_only_data_section ();
   else
     data_section ();
 }
@@ -8976,4 +9340,31 @@ pa_globalize_label (FILE *stream, const char *name)
     fputs (",DATA\n", stream);
   }
 }
+
+/* Worker function for TARGET_STRUCT_VALUE_RTX.  */
+
+static rtx
+pa_struct_value_rtx (tree fntype ATTRIBUTE_UNUSED,
+                    int incoming ATTRIBUTE_UNUSED)
+{
+  return gen_rtx_REG (Pmode, PA_STRUCT_VALUE_REGNUM);
+}
+
+/* Worker function for TARGET_RETURN_IN_MEMORY.  */
+
+bool
+pa_return_in_memory (tree type, tree fntype ATTRIBUTE_UNUSED)
+{
+  /* SOM ABI says that objects larger than 64 bits are returned in memory.
+     PA64 ABI says that objects larger than 128 bits are returned in memory.
+     Note, int_size_in_bytes can return -1 if the size of the object is
+     variable or larger than the maximum value that can be expressed as
+     a HOST_WIDE_INT.   It can also return zero for an empty type.  The
+     simplest way to handle variable and empty types is to pass them in
+     memory.  This avoids problems in defining the boundaries of argument
+     slots, allocating registers, etc.  */
+  return (int_size_in_bytes (type) > (TARGET_64BIT ? 16 : 8)
+         || int_size_in_bytes (type) <= 0);
+}
+
 #include "gt-pa.h"