]> oss.titaniummirror.com Git - msp430-gcc.git/blobdiff - gcc/config/pa/pa.c
Imported gcc-4.4.3
[msp430-gcc.git] / gcc / config / pa / pa.c
index 545768be8a08a682c964e894b7f6e04e6f8917a7..78fbb9b420cfcf5e9241ed2fcf2ff175840b7e9a 100644 (file)
@@ -1,51 +1,80 @@
 /* Subroutines for insn-output.c for HPPA.
    Copyright (C) 1992, 1993, 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001,
-   2002, 2003 Free Software Foundation, Inc.
+   2002, 2003, 2004, 2005, 2006, 2007, 2008, 2009
+   Free Software Foundation, Inc.
    Contributed by Tim Moore (moore@cs.utah.edu), based on sparc.c
 
-This file is part of GNU CC.
+This file is part of GCC.
 
-GNU CC is free software; you can redistribute it and/or modify
+GCC is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
+the Free Software Foundation; either version 3, or (at your option)
 any later version.
 
-GNU CC is distributed in the hope that it will be useful,
+GCC is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.
 
 You should have received a copy of the GNU General Public License
-along with GNU CC; see the file COPYING.  If not, write to
-the Free Software Foundation, 59 Temple Place - Suite 330,
-Boston, MA 02111-1307, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
+#include "coretypes.h"
+#include "tm.h"
 #include "rtl.h"
 #include "regs.h"
 #include "hard-reg-set.h"
 #include "real.h"
 #include "insn-config.h"
 #include "conditions.h"
-#include "output.h"
 #include "insn-attr.h"
 #include "flags.h"
 #include "tree.h"
+#include "output.h"
+#include "except.h"
 #include "expr.h"
 #include "optabs.h"
-#include "libfuncs.h"
 #include "reload.h"
-#include "c-tree.h"
 #include "integrate.h"
 #include "function.h"
-#include "obstack.h"
 #include "toplev.h"
 #include "ggc.h"
 #include "recog.h"
+#include "predict.h"
 #include "tm_p.h"
 #include "target.h"
 #include "target-def.h"
+#include "df.h"
+
+/* Return nonzero if there is a bypass for the output of 
+   OUT_INSN and the fp store IN_INSN.  */
+int
+hppa_fpstore_bypass_p (rtx out_insn, rtx in_insn)
+{
+  enum machine_mode store_mode;
+  enum machine_mode other_mode;
+  rtx set;
+
+  if (recog_memoized (in_insn) < 0
+      || (get_attr_type (in_insn) != TYPE_FPSTORE
+         && get_attr_type (in_insn) != TYPE_FPSTORE_LOAD)
+      || recog_memoized (out_insn) < 0)
+    return 0;
+
+  store_mode = GET_MODE (SET_SRC (PATTERN (in_insn)));
+
+  set = single_set (out_insn);
+  if (!set)
+    return 0;
+
+  other_mode = GET_MODE (SET_SRC (set));
+
+  return (GET_MODE_SIZE (store_mode) == GET_MODE_SIZE (other_mode));
+}
+  
 
 #ifndef DO_FRAME_NOTES
 #ifdef INCOMING_RETURN_ADDR_RTX
@@ -55,76 +84,127 @@ Boston, MA 02111-1307, USA.  */
 #endif
 #endif
 
-#if DO_FRAME_NOTES
-#define FRP(INSN) \
-  do                                   \
-    {                                  \
-      rtx insn = INSN;                 \
-      RTX_FRAME_RELATED_P (insn) = 1;  \
-    }                                  \
-  while (0)
-#else
-#define FRP(INSN) INSN
+static void copy_reg_pointer (rtx, rtx);
+static void fix_range (const char *);
+static bool pa_handle_option (size_t, const char *, int);
+static int hppa_address_cost (rtx, bool);
+static bool hppa_rtx_costs (rtx, int, int, int *, bool);
+static inline rtx force_mode (enum machine_mode, rtx);
+static void pa_reorg (void);
+static void pa_combine_instructions (void);
+static int pa_can_combine_p (rtx, rtx, rtx, int, rtx, rtx, rtx);
+static int forward_branch_p (rtx);
+static void compute_zdepwi_operands (unsigned HOST_WIDE_INT, unsigned *);
+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, 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 (unsigned int);
+static void pa_output_function_epilogue (FILE *, HOST_WIDE_INT);
+static int pa_adjust_cost (rtx, rtx, rtx, int);
+static int pa_adjust_priority (rtx, int);
+static int pa_issue_rate (void);
+static void pa_som_asm_init_sections (void) ATTRIBUTE_UNUSED;
+static section *pa_select_section (tree, int, unsigned HOST_WIDE_INT)
+     ATTRIBUTE_UNUSED;
+static void pa_encode_section_info (tree, rtx, int);
+static const char *pa_strip_name_encoding (const char *);
+static bool pa_function_ok_for_sibcall (tree, tree);
+static void pa_globalize_label (FILE *, const char *)
+     ATTRIBUTE_UNUSED;
+static void pa_asm_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
+                                   HOST_WIDE_INT, tree);
+#if !defined(USE_COLLECT2)
+static void pa_asm_out_constructor (rtx, int);
+static void pa_asm_out_destructor (rtx, int);
 #endif
-
-static inline rtx force_mode PARAMS ((enum machine_mode, rtx));
-static void pa_combine_instructions PARAMS ((rtx));
-static int pa_can_combine_p PARAMS ((rtx, rtx, rtx, int, rtx, rtx, rtx));
-static int forward_branch_p PARAMS ((rtx));
-static int shadd_constant_p PARAMS ((int));
-static void pa_add_gc_roots PARAMS ((void));
-static void mark_deferred_plabels PARAMS ((void *));
-static void compute_zdepwi_operands PARAMS ((unsigned HOST_WIDE_INT, unsigned *));
-static int compute_movstrsi_length PARAMS ((rtx));
-static bool pa_assemble_integer PARAMS ((rtx, unsigned int, int));
-static void remove_useless_addtr_insns PARAMS ((rtx, int));
-static void store_reg PARAMS ((int, int, int));
-static void load_reg PARAMS ((int, int, int));
-static void set_reg_plus_d PARAMS ((int, int, int));
-static rtx get_last_nonnote_insn PARAMS ((void));
-static void pa_output_function_epilogue PARAMS ((FILE *, HOST_WIDE_INT));
-static int pa_adjust_cost PARAMS ((rtx, rtx, rtx, int));
-static int pa_adjust_priority PARAMS ((rtx, int));
-static int pa_issue_rate PARAMS ((void));
+static void pa_init_builtins (void);
+static rtx hppa_builtin_saveregs (void);
+static void hppa_va_start (tree, rtx);
+static tree hppa_gimplify_va_arg_expr (tree, tree, gimple_seq *, gimple_seq *);
+static bool pa_scalar_mode_supported_p (enum machine_mode);
+static bool pa_commutative_p (const_rtx x, int outer_code);
+static void copy_fp_args (rtx) ATTRIBUTE_UNUSED;
+static int length_fp_args (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;
+static inline void pa_file_start_mcount (const char*) ATTRIBUTE_UNUSED;
+static void pa_elf_file_start (void) ATTRIBUTE_UNUSED;
+static void pa_som_file_start (void) ATTRIBUTE_UNUSED;
+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
+#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 *, enum machine_mode,
+                                 const_tree, bool);
+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 *);
+static void pa_extra_live_on_entry (bitmap);
+
+/* The following extra sections are only used for SOM.  */
+static GTY(()) section *som_readonly_data_section;
+static GTY(()) section *som_one_only_readonly_data_section;
+static GTY(()) section *som_one_only_data_section;
 
 /* 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;
+enum processor_type pa_cpu = TARGET_SCHED_DEFAULT;
 
-/* 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;
+/* The UNIX standard to use for predefines and linking.  */
+int flag_pa_unix = TARGET_HPUX_11_11 ? 1998 : TARGET_HPUX_10_10 ? 1995 : 1993;
 
 /* 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 PARAMS ((rtx));
+/* Boolean indicating whether the return pointer was saved by the
+   current function's prologue.  */
+static bool rp_saved;
+
+static rtx find_addr_reg (rtx);
 
-/* Keep track of the number of bytes we have output in the CODE subspaces
+/* Keep track of the number of bytes we have output in the CODE subspace
    during this compilation so we'll know when to emit inline long-calls.  */
+unsigned long total_code_bytes;
 
-unsigned int total_code_bytes;
+/* The last address of the previous function plus the number of bytes in
+   associated thunks that have been output.  This is used to determine if
+   a thunk can use an IA-relative branch to reach its target function.  */
+static unsigned int last_address;
 
 /* Variables to handle plabels that we discover are necessary at assembly
    output time.  They are output after the current function.  */
-
-struct deferred_plabel
+struct deferred_plabel GTY(())
 {
   rtx internal_label;
-  char *name;
-} *deferred_plabels = 0;
-int n_deferred_plabels = 0;
+  rtx symbol;
+};
+static GTY((length ("n_deferred_plabels"))) struct deferred_plabel *
+  deferred_plabels;
+static size_t n_deferred_plabels = 0;
+
 \f
 /* Initialize the GCC target structure.  */
 
@@ -155,82 +235,263 @@ int n_deferred_plabels = 0;
 #undef TARGET_SCHED_ISSUE_RATE
 #define TARGET_SCHED_ISSUE_RATE pa_issue_rate
 
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO pa_encode_section_info
+#undef TARGET_STRIP_NAME_ENCODING
+#define TARGET_STRIP_NAME_ENCODING pa_strip_name_encoding
+
+#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
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK default_can_output_mi_thunk_no_vcall
+
+#undef TARGET_ASM_FILE_END
+#ifdef ASM_OUTPUT_EXTERNAL_REAL
+#define TARGET_ASM_FILE_END pa_hpux_file_end
+#else
+#define TARGET_ASM_FILE_END output_deferred_plabels
+#endif
+
+#if !defined(USE_COLLECT2)
+#undef TARGET_ASM_CONSTRUCTOR
+#define TARGET_ASM_CONSTRUCTOR pa_asm_out_constructor
+#undef TARGET_ASM_DESTRUCTOR
+#define TARGET_ASM_DESTRUCTOR pa_asm_out_destructor
+#endif
+
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | TARGET_CPU_DEFAULT)
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION pa_handle_option
+
+#undef TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS pa_init_builtins
+
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS hppa_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST hppa_address_cost
+
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG pa_reorg
+
+#ifdef HPUX_LONG_DOUBLE_LIBRARY
+#undef TARGET_INIT_LIBFUNCS
+#define TARGET_INIT_LIBFUNCS pa_hpux_init_libfuncs
+#endif
+
+#undef TARGET_PROMOTE_FUNCTION_RETURN
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_const_tree_true
+#undef TARGET_PROMOTE_PROTOTYPES
+#define TARGET_PROMOTE_PROTOTYPES hook_bool_const_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_CALLEE_COPIES
+#define TARGET_CALLEE_COPIES hook_bool_CUMULATIVE_ARGS_mode_tree_bool_true
+#undef TARGET_ARG_PARTIAL_BYTES
+#define TARGET_ARG_PARTIAL_BYTES pa_arg_partial_bytes
+
+#undef TARGET_EXPAND_BUILTIN_SAVEREGS
+#define TARGET_EXPAND_BUILTIN_SAVEREGS hppa_builtin_saveregs
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START hppa_va_start
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR hppa_gimplify_va_arg_expr
+
+#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
+
+#undef TARGET_EXTRA_LIVE_ON_ENTRY
+#define TARGET_EXTRA_LIVE_ON_ENTRY pa_extra_live_on_entry
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 \f
-void
-override_options ()
+/* Parse the -mfixed-range= option string.  */
+
+static void
+fix_range (const char *const_str)
 {
-  /* Default to 7100LC scheduling.  */
-  if (pa_cpu_string && ! strcmp (pa_cpu_string, "7100"))
-    {
-      pa_cpu_string = "7100";
-      pa_cpu = PROCESSOR_7100;
-    }
-  else if (pa_cpu_string && ! strcmp (pa_cpu_string, "700"))
-    {
-      pa_cpu_string = "700";
-      pa_cpu = PROCESSOR_700;
-    }
-  else if (pa_cpu_string == NULL
-         || ! strcmp (pa_cpu_string, "7100LC"))
-    {
-      pa_cpu_string = "7100LC";
-      pa_cpu = PROCESSOR_7100LC;
-    }
-  else if (pa_cpu_string && ! strcmp (pa_cpu_string, "7200"))
-    {
-      pa_cpu_string = "7200";
-      pa_cpu = PROCESSOR_7200;
-    }
-  else if (pa_cpu_string && ! strcmp (pa_cpu_string, "8000"))
-    {
-      pa_cpu_string = "8000";
-      pa_cpu = PROCESSOR_8000;
-    }
-  else
+  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 fr4-fr31.  */
+
+  i = strlen (const_str);
+  str = (char *) alloca (i + 1);
+  memcpy (str, const_str, i + 1);
+
+  while (1)
     {
-      warning ("unknown -mschedule= option (%s).\nValid options are 700, 7100, 7100LC, 7200, and 8000\n", pa_cpu_string);
+      dash = strchr (str, '-');
+      if (!dash)
+       {
+         warning (0, "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 (0, "unknown register name: %s", str);
+         return;
+       }
+
+      last = decode_reg_name (dash + 1);
+      if (last < 0)
+       {
+         warning (0, "unknown register name: %s", dash + 1);
+         return;
+       }
+
+      *dash = '-';
+
+      if (first > last)
+       {
+         warning (0, "%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;
     }
 
-  /* Set the instruction set architecture.  */
-  if (pa_arch_string && ! strcmp (pa_arch_string, "1.0"))
+  /* 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;
+}
+
+/* Implement TARGET_HANDLE_OPTION.  */
+
+static bool
+pa_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
+{
+  switch (code)
     {
-      pa_arch_string = "1.0";
-      pa_arch = ARCHITECTURE_10;
+    case OPT_mnosnake:
+    case OPT_mpa_risc_1_0:
+    case OPT_march_1_0:
       target_flags &= ~(MASK_PA_11 | MASK_PA_20);
-    }
-  else if (pa_arch_string && ! strcmp (pa_arch_string, "1.1"))
-    {
-      pa_arch_string = "1.1";
-      pa_arch = ARCHITECTURE_11;
+      return true;
+
+    case OPT_msnake:
+    case OPT_mpa_risc_1_1:
+    case OPT_march_1_1:
       target_flags &= ~MASK_PA_20;
       target_flags |= MASK_PA_11;
-    }
-  else if (pa_arch_string && ! strcmp (pa_arch_string, "2.0"))
-    {
-      pa_arch_string = "2.0";
-      pa_arch = ARCHITECTURE_20;
+      return true;
+
+    case OPT_mpa_risc_2_0:
+    case OPT_march_2_0:
       target_flags |= MASK_PA_11 | MASK_PA_20;
+      return true;
+
+    case OPT_mschedule_:
+      if (strcmp (arg, "8000") == 0)
+       pa_cpu = PROCESSOR_8000;
+      else if (strcmp (arg, "7100") == 0)
+       pa_cpu = PROCESSOR_7100;
+      else if (strcmp (arg, "700") == 0)
+       pa_cpu = PROCESSOR_700;
+      else if (strcmp (arg, "7100LC") == 0)
+       pa_cpu = PROCESSOR_7100LC;
+      else if (strcmp (arg, "7200") == 0)
+       pa_cpu = PROCESSOR_7200;
+      else if (strcmp (arg, "7300") == 0)
+       pa_cpu = PROCESSOR_7300;
+      else
+       return false;
+      return true;
+
+    case OPT_mfixed_range_:
+      fix_range (arg);
+      return true;
+
+#if TARGET_HPUX
+    case OPT_munix_93:
+      flag_pa_unix = 1993;
+      return true;
+#endif
+
+#if TARGET_HPUX_10_10
+    case OPT_munix_95:
+      flag_pa_unix = 1995;
+      return true;
+#endif
+
+#if TARGET_HPUX_11_11
+    case OPT_munix_98:
+      flag_pa_unix = 1998;
+      return true;
+#endif
+
+    default:
+      return true;
     }
-  else if (pa_arch_string)
-    {
-      warning ("unknown -march= option (%s).\nValid options are 1.0, 1.1, and 2.0\n", pa_arch_string);
-    }
+}
+
+void
+override_options (void)
+{
+  /* 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.  */
+  if (pa_cpu >= PROCESSOR_8000
+      || (! USING_SJLJ_EXCEPTIONS && flag_exceptions)
+      || flag_unwind_tables)
+    target_flags &= ~MASK_JUMP_IN_DELAY;
 
   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;
     }
 
@@ -251,40 +512,51 @@ override_options ()
       targetm.asm_out.unaligned_op.di = NULL;
     }
 
-  /* Register global variables with the garbage collector.  */
-  pa_add_gc_roots ();
+  init_machine_status = pa_init_machine_status;
 }
 
-/* Return non-zero only if OP is a register of mode MODE,
-   or CONST0_RTX.  */
-int
-reg_or_0_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+static void
+pa_init_builtins (void)
+{
+#ifdef DONT_HAVE_FPUTC_UNLOCKED
+  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
+#if TARGET_HPUX_11
+  if (built_in_decls [BUILT_IN_FINITE])
+    set_user_assembler_name (built_in_decls [BUILT_IN_FINITE], "_Isfinite");
+  if (built_in_decls [BUILT_IN_FINITEF])
+    set_user_assembler_name (built_in_decls [BUILT_IN_FINITEF], "_Isfinitef");
+#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 (op == CONST0_RTX (mode) || register_operand (op, mode));
+  return GGC_CNEW (machine_function);
 }
 
-/* Return non-zero if OP is suitable for use in a call to a named
-   function.
+/* If FROM is a probable pointer register, mark TO as a probable
+   pointer register with the same pointer alignment as FROM.  */
 
-   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 ATTRIBUTE_UNUSED;
+static void
+copy_reg_pointer (rtx to, rtx from)
 {
-  return (GET_MODE (op) == word_mode
-         && CONSTANT_P (op) && ! TARGET_PORTABLE_RUNTIME);
+  if (REG_POINTER (from))
+    mark_reg_pointer (to, REGNO_POINTER_ALIGN (REGNO (from)));
 }
 
 /* Return 1 if X contains a symbolic expression.  We know these
    expressions will have one of a few well defined forms, so
    we need only check those forms.  */
 int
-symbolic_expression_p (x)
-     register rtx x;
+symbolic_expression_p (rtx x)
 {
 
   /* Strip off any HIGH.  */
@@ -294,536 +566,303 @@ symbolic_expression_p (x)
   return (symbolic_operand (x, VOIDmode));
 }
 
+/* Accept any constant that can be moved in one instruction into a
+   general register.  */
 int
-symbolic_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+cint_ok_for_move (HOST_WIDE_INT ival)
 {
-  switch (GET_CODE (op))
-    {
-    case SYMBOL_REF:
-    case LABEL_REF:
-      return 1;
-    case CONST:
-      op = XEXP (op, 0);
-      return ((GET_CODE (XEXP (op, 0)) == SYMBOL_REF
-              || GET_CODE (XEXP (op, 0)) == LABEL_REF)
-             && GET_CODE (XEXP (op, 1)) == CONST_INT);
-    default:
-      return 0;
-    }
+  /* OK if ldo, ldil, or zdepi, can be used.  */
+  return (VAL_14_BITS_P (ival)
+         || ldil_cint_p (ival)
+         || zdepi_cint_p (ival));
 }
-
-/* Return truth value of statement that OP is a symbolic memory
-   operand of mode MODE.  */
-
+\f
+/* Return truth value of whether OP can be used as an operand in a
+   adddi3 insn.  */
 int
-symbolic_memory_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+adddi3_operand (rtx op, enum machine_mode mode)
 {
-  if (GET_CODE (op) == SUBREG)
-    op = SUBREG_REG (op);
-  if (GET_CODE (op) != MEM)
-    return 0;
-  op = XEXP (op, 0);
-  return (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == CONST
-         || GET_CODE (op) == HIGH || GET_CODE (op) == LABEL_REF);
+  return (register_operand (op, mode)
+         || (GET_CODE (op) == CONST_INT
+             && (TARGET_64BIT ? INT_14_BITS (op) : INT_11_BITS (op))));
 }
 
-/* Return 1 if the operand is either a register or a memory operand that is
-   not symbolic.  */
-
+/* True iff the operand OP can be used as the destination operand of
+   an integer store.  This also implies the operand could be used as
+   the source operand of an integer load.  Symbolic, lo_sum and indexed
+   memory operands are not allowed.  We accept reloading pseudos and
+   other memory operands.  */
 int
-reg_or_nonsymb_mem_operand (op, mode)
-    register rtx op;
-    enum machine_mode mode;
+integer_store_memory_operand (rtx op, enum machine_mode mode)
 {
-  if (register_operand (op, mode))
-    return 1;
-
-  if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode))
-    return 1;
-
-  return 0;
+  return ((reload_in_progress
+          && REG_P (op)
+          && REGNO (op) >= FIRST_PSEUDO_REGISTER
+          && reg_renumber [REGNO (op)] < 0)
+         || (GET_CODE (op) == MEM
+             && (reload_in_progress || memory_address_p (mode, XEXP (op, 0)))
+             && !symbolic_memory_operand (op, VOIDmode)
+             && !IS_LO_SUM_DLT_ADDR_P (XEXP (op, 0))
+             && !IS_INDEX_ADDR_P (XEXP (op, 0))));
 }
 
-/* Return 1 if the operand is either a register, zero, or a memory operand
-   that is not symbolic.  */
-
+/* True iff ldil can be used to load this CONST_INT.  The least
+   significant 11 bits of the value must be zero and the value must
+   not change sign when extended from 32 to 64 bits.  */
 int
-reg_or_0_or_nonsymb_mem_operand (op, mode)
-    register rtx op;
-    enum machine_mode mode;
+ldil_cint_p (HOST_WIDE_INT ival)
 {
-  if (register_operand (op, mode))
-    return 1;
-
-  if (op == CONST0_RTX (mode))
-    return 1;
+  HOST_WIDE_INT x = ival & (((HOST_WIDE_INT) -1 << 31) | 0x7ff);
 
-  if (memory_operand (op, mode) && ! symbolic_memory_operand (op, mode))
-    return 1;
-
-  return 0;
+  return x == 0 || x == ((HOST_WIDE_INT) -1 << 31);
 }
 
-/* Return 1 if the operand is a register operand or a non-symbolic memory
-   operand after reload.  This predicate is used for branch patterns that
-   internally handle register reloading.  We need to accept non-symbolic
-   memory operands after reload to ensure that the pattern is still valid
-   if reload didn't find a hard register for the operand.  */
-
+/* True iff zdepi can be used to generate this CONST_INT.
+   zdepi first sign extends a 5-bit signed number to a given field
+   length, then places this field anywhere in a zero.  */
 int
-reg_before_reload_operand (op, mode)
-    register rtx op;
-    enum machine_mode mode;
+zdepi_cint_p (unsigned HOST_WIDE_INT x)
 {
-  /* Don't accept a SUBREG since it will need a reload.  */
-  if (GET_CODE (op) == SUBREG)
-    return 0;
-
-  if (register_operand (op, mode))
-    return 1;
-
-  if (reload_completed
-      && memory_operand (op, mode)
-      && ! symbolic_memory_operand (op, mode))
-    return 1;
+  unsigned HOST_WIDE_INT lsb_mask, t;
 
-  return 0;
+  /* This might not be obvious, but it's at least fast.
+     This function is critical; we don't have the time loops would take.  */
+  lsb_mask = x & -x;
+  t = ((x >> 4) + lsb_mask) & ~(lsb_mask - 1);
+  /* Return true iff t is a power of two.  */
+  return ((t & (t - 1)) == 0);
 }
 
-/* Accept any constant that can be moved in one instruction into a
-   general register.  */
+/* True iff depi or extru can be used to compute (reg & mask).
+   Accept bit pattern like these:
+   0....01....1
+   1....10....0
+   1..10..01..1  */
 int
-cint_ok_for_move (intval)
-     HOST_WIDE_INT intval;
+and_mask_p (unsigned HOST_WIDE_INT mask)
 {
-  /* OK if ldo, ldil, or zdepi, can be used.  */
-  return (CONST_OK_FOR_LETTER_P (intval, 'J')
-         || CONST_OK_FOR_LETTER_P (intval, 'N')
-         || CONST_OK_FOR_LETTER_P (intval, 'K'));
+  mask = ~mask;
+  mask += mask & -mask;
+  return (mask & (mask - 1)) == 0;
 }
 
-/* Accept anything that can be moved in one instruction into a general
-   register.  */
+/* True iff depi can be used to compute (reg | MASK).  */
 int
-move_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+ior_mask_p (unsigned HOST_WIDE_INT mask)
 {
-  if (register_operand (op, mode))
-    return 1;
+  mask += mask & -mask;
+  return (mask & (mask - 1)) == 0;
+}
+\f
+/* Legitimize PIC addresses.  If the address is already
+   position-independent, we return ORIG.  Newly generated
+   position-independent addresses go to REG.  If we need more
+   than one register, we lose.  */
 
-  if (GET_CODE (op) == CONSTANT_P_RTX)
-    return 1;
+rtx
+legitimize_pic_address (rtx orig, enum machine_mode mode, rtx reg)
+{
+  rtx pic_ref = orig;
 
-  if (GET_CODE (op) == CONST_INT)
-    return cint_ok_for_move (INTVAL (op));
+  gcc_assert (!PA_SYMBOL_REF_TLS_P (orig));
 
-  if (GET_CODE (op) == SUBREG)
-    op = SUBREG_REG (op);
-  if (GET_CODE (op) != MEM)
-    return 0;
+  /* Labels need special handling.  */
+  if (pic_label_operand (orig, mode))
+    {
+      rtx insn;
 
-  op = XEXP (op, 0);
+      /* We do not want to go through the movXX expanders here since that
+        would create recursion.
 
-  /* We consider a LO_SUM DLT reference a move_operand now since it has
-     been merged into the normal movsi/movdi patterns.  */
-  if (GET_CODE (op) == LO_SUM
-      && GET_CODE (XEXP (op, 0)) == REG
-      && REG_OK_FOR_BASE_P (XEXP (op, 0))
-      && GET_CODE (XEXP (op, 1)) == UNSPEC
-      && GET_MODE (op) == Pmode)
-    return 1;
+        Nor do we really want to call a generator for a named pattern
+        since that requires multiple patterns if we want to support
+        multiple word sizes.
 
-  /* Since move_operand is only used for source operands, we can always
-     allow scaled indexing!  */
-  if (! TARGET_DISABLE_INDEXING
-      && GET_CODE (op) == PLUS
-      && ((GET_CODE (XEXP (op, 0)) == MULT
-          && GET_CODE (XEXP (XEXP (op, 0), 0)) == REG
-          && GET_CODE (XEXP (XEXP (op, 0), 1)) == CONST_INT
-          && INTVAL (XEXP (XEXP (op, 0), 1))
-             == (HOST_WIDE_INT) GET_MODE_SIZE (mode)
-          && GET_CODE (XEXP (op, 1)) == REG)
-         || (GET_CODE (XEXP (op, 1)) == MULT
-             &&GET_CODE (XEXP (XEXP (op, 1), 0)) == REG
-             && GET_CODE (XEXP (XEXP (op, 1), 1)) == CONST_INT
-             && INTVAL (XEXP (XEXP (op, 1), 1))
-                == (HOST_WIDE_INT) GET_MODE_SIZE (mode)
-             && GET_CODE (XEXP (op, 0)) == REG)))
-    return 1;
+        So instead we just emit the raw set, which avoids the movXX
+        expanders completely.  */
+      mark_reg_pointer (reg, BITS_PER_UNIT);
+      insn = emit_insn (gen_rtx_SET (VOIDmode, reg, orig));
 
-  return memory_address_p (mode, op);
-}
+      /* Put a REG_EQUAL note on this insn, so that it can be optimized.  */
+      REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_EQUAL, orig, REG_NOTES (insn));
 
-/* Accept REG and any CONST_INT that can be moved in one instruction into a
-   general register.  */
-int
-reg_or_cint_move_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  if (register_operand (op, mode))
-    return 1;
+      /* During and after reload, we need to generate a REG_LABEL_OPERAND note
+        and update LABEL_NUSES because this is not done automatically.  */
+      if (reload_in_progress || reload_completed)
+       {
+         /* Extract LABEL_REF.  */
+         if (GET_CODE (orig) == CONST)
+           orig = XEXP (XEXP (orig, 0), 0);
+         /* Extract CODE_LABEL.  */
+         orig = XEXP (orig, 0);
+         add_reg_note (insn, REG_LABEL_OPERAND, orig);
+         LABEL_NUSES (orig)++;
+       }
+      crtl->uses_pic_offset_table = 1;
+      return reg;
+    }
+  if (GET_CODE (orig) == SYMBOL_REF)
+    {
+      rtx insn, tmp_reg;
 
-  if (GET_CODE (op) == CONST_INT)
-    return cint_ok_for_move (INTVAL (op));
+      gcc_assert (reg);
 
-  return 0;
-}
+      /* Before reload, allocate a temporary register for the intermediate
+        result.  This allows the sequence to be deleted when the final
+        result is unused and the insns are trivially dead.  */
+      tmp_reg = ((reload_in_progress || reload_completed)
+                ? reg : gen_reg_rtx (Pmode));
 
-int
-pic_label_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  if (!flag_pic)
-    return 0;
+      if (function_label_operand (orig, mode))
+       {
+         /* Force function label into memory in word mode.  */
+         orig = XEXP (force_const_mem (word_mode, orig), 0);
+         /* Load plabel address from DLT.  */
+         emit_move_insn (tmp_reg,
+                         gen_rtx_PLUS (word_mode, pic_offset_table_rtx,
+                                       gen_rtx_HIGH (word_mode, orig)));
+         pic_ref
+           = gen_const_mem (Pmode,
+                            gen_rtx_LO_SUM (Pmode, tmp_reg,
+                                            gen_rtx_UNSPEC (Pmode,
+                                                        gen_rtvec (1, orig),
+                                                        UNSPEC_DLTIND14R)));
+         emit_move_insn (reg, pic_ref);
+         /* Now load address of function descriptor.  */
+         pic_ref = gen_rtx_MEM (Pmode, reg);
+       }
+      else
+       {
+         /* Load symbol reference from DLT.  */
+         emit_move_insn (tmp_reg,
+                         gen_rtx_PLUS (word_mode, pic_offset_table_rtx,
+                                       gen_rtx_HIGH (word_mode, orig)));
+         pic_ref
+           = gen_const_mem (Pmode,
+                            gen_rtx_LO_SUM (Pmode, tmp_reg,
+                                            gen_rtx_UNSPEC (Pmode,
+                                                        gen_rtvec (1, orig),
+                                                        UNSPEC_DLTIND14R)));
+       }
 
-  switch (GET_CODE (op))
-    {
-    case LABEL_REF:
-      return 1;
-    case CONST:
-      op = XEXP (op, 0);
-      return (GET_CODE (XEXP (op, 0)) == LABEL_REF
-             && GET_CODE (XEXP (op, 1)) == CONST_INT);
-    default:
-      return 0;
+      crtl->uses_pic_offset_table = 1;
+      mark_reg_pointer (reg, BITS_PER_UNIT);
+      insn = emit_move_insn (reg, pic_ref);
+
+      /* Put a REG_EQUAL note on this insn, so that it can be optimized.  */
+      set_unique_reg_note (insn, REG_EQUAL, orig);
+
+      return reg;
     }
-}
+  else if (GET_CODE (orig) == CONST)
+    {
+      rtx base;
 
-int
-fp_reg_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return reg_renumber && FP_REG_P (op);
-}
+      if (GET_CODE (XEXP (orig, 0)) == PLUS
+         && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
+       return orig;
 
-\f
+      gcc_assert (reg);
+      gcc_assert (GET_CODE (XEXP (orig, 0)) == PLUS);
+      
+      base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg);
+      orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode,
+                                    base == reg ? 0 : reg);
 
-/* Return truth value of whether OP can be used as an operand in a
-   three operand arithmetic insn that accepts registers of mode MODE
-   or 14-bit signed integers.  */
-int
-arith_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  return (register_operand (op, mode)
-         || (GET_CODE (op) == CONST_INT && INT_14_BITS (op)));
-}
-
-/* Return truth value of whether OP can be used as an operand in a
-   three operand arithmetic insn that accepts registers of mode MODE
-   or 11-bit signed integers.  */
-int
-arith11_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  return (register_operand (op, mode)
-         || (GET_CODE (op) == CONST_INT && INT_11_BITS (op)));
-}
-
-/* Return truth value of whether OP can be used as an operand in a
-   adddi3 insn.  */
-int
-adddi3_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  return (register_operand (op, mode)
-         || (GET_CODE (op) == CONST_INT
-             && (TARGET_64BIT ? INT_14_BITS (op) : INT_11_BITS (op))));
-}
-
-/* A constant integer suitable for use in a PRE_MODIFY memory
-   reference.  */
-int
-pre_cint_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return (GET_CODE (op) == CONST_INT
-         && INTVAL (op) >= -0x2000 && INTVAL (op) < 0x10);
-}
-
-/* A constant integer suitable for use in a POST_MODIFY memory
-   reference.  */
-int
-post_cint_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return (GET_CODE (op) == CONST_INT
-         && INTVAL (op) < 0x2000 && INTVAL (op) >= -0x10);
-}
-
-int
-arith_double_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  return (register_operand (op, mode)
-         || (GET_CODE (op) == CONST_DOUBLE
-             && GET_MODE (op) == mode
-             && VAL_14_BITS_P (CONST_DOUBLE_LOW (op))
-             && ((CONST_DOUBLE_HIGH (op) >= 0)
-                 == ((CONST_DOUBLE_LOW (op) & 0x1000) == 0))));
-}
-
-/* Return truth value of whether OP is an integer which fits the
-   range constraining immediate operands in three-address insns, or
-   is an integer register.  */
-
-int
-ireg_or_int5_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return ((GET_CODE (op) == CONST_INT && INT_5_BITS (op))
-         || (GET_CODE (op) == REG && REGNO (op) > 0 && REGNO (op) < 32));
-}
-
-/* Return nonzero if OP is an integer register, else return zero.  */
-int
-ireg_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return (GET_CODE (op) == REG && REGNO (op) > 0 && REGNO (op) < 32);
-}
-
-/* Return truth value of whether OP is an integer which fits the
-   range constraining immediate operands in three-address insns.  */
-
-int
-int5_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return (GET_CODE (op) == CONST_INT && INT_5_BITS (op));
-}
-
-int
-uint5_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return (GET_CODE (op) == CONST_INT && INT_U5_BITS (op));
-}
-
-int
-int11_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return (GET_CODE (op) == CONST_INT && INT_11_BITS (op));
-}
-
-int
-uint32_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-#if HOST_BITS_PER_WIDE_INT > 32
-  /* All allowed constants will fit a CONST_INT.  */
-  return (GET_CODE (op) == CONST_INT
-         && (INTVAL (op) >= 0 && INTVAL (op) < (HOST_WIDE_INT) 1 << 32));
-#else
-  return (GET_CODE (op) == CONST_INT
-         || (GET_CODE (op) == CONST_DOUBLE
-             && CONST_DOUBLE_HIGH (op) == 0));
-#endif
-}
-
-int
-arith5_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  return register_operand (op, mode) || int5_operand (op, mode);
-}
-
-/* True iff zdepi can be used to generate this CONST_INT.
-   zdepi first sign extends a 5 bit signed number to a given field
-   length, then places this field anywhere in a zero.  */
-int
-zdepi_cint_p (x)
-     unsigned HOST_WIDE_INT x;
-{
-  unsigned HOST_WIDE_INT lsb_mask, t;
-
-  /* This might not be obvious, but it's at least fast.
-     This function is critical; we don't have the time loops would take.  */
-  lsb_mask = x & -x;
-  t = ((x >> 4) + lsb_mask) & ~(lsb_mask - 1);
-  /* Return true iff t is a power of two.  */
-  return ((t & (t - 1)) == 0);
-}
-
-/* True iff depi or extru can be used to compute (reg & mask).
-   Accept bit pattern like these:
-   0....01....1
-   1....10....0
-   1..10..01..1  */
-int
-and_mask_p (mask)
-     unsigned HOST_WIDE_INT mask;
-{
-  mask = ~mask;
-  mask += mask & -mask;
-  return (mask & (mask - 1)) == 0;
-}
-
-/* True iff depi or extru can be used to compute (reg & OP).  */
-int
-and_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  return (register_operand (op, mode)
-         || (GET_CODE (op) == CONST_INT && and_mask_p (INTVAL (op))));
-}
+      if (GET_CODE (orig) == CONST_INT)
+       {
+         if (INT_14_BITS (orig))
+           return plus_constant (base, INTVAL (orig));
+         orig = force_reg (Pmode, orig);
+       }
+      pic_ref = gen_rtx_PLUS (Pmode, base, orig);
+      /* Likewise, should we set special REG_NOTEs here?  */
+    }
 
-/* True iff depi can be used to compute (reg | MASK).  */
-int
-ior_mask_p (mask)
-     unsigned HOST_WIDE_INT mask;
-{
-  mask += mask & -mask;
-  return (mask & (mask - 1)) == 0;
+  return pic_ref;
 }
 
-/* True iff depi can be used to compute (reg | OP).  */
-int
-ior_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return (GET_CODE (op) == CONST_INT && ior_mask_p (INTVAL (op)));
-}
+static GTY(()) rtx gen_tls_tga;
 
-int
-lhs_lshift_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
+static rtx
+gen_tls_get_addr (void)
 {
-  return register_operand (op, mode) || lhs_lshift_cint_operand (op, mode);
+  if (!gen_tls_tga)
+    gen_tls_tga = init_one_libfunc ("__tls_get_addr");
+  return gen_tls_tga;
 }
 
-/* True iff OP is a CONST_INT of the forms 0...0xxxx or 0...01...1xxxx.
-   Such values can be the left hand side x in (x << r), using the zvdepi
-   instruction.  */
-int
-lhs_lshift_cint_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+static rtx
+hppa_tls_call (rtx arg)
 {
-  unsigned HOST_WIDE_INT x;
-  if (GET_CODE (op) != CONST_INT)
-    return 0;
-  x = INTVAL (op) >> 4;
-  return (x & (x + 1)) == 0;
-}
+  rtx ret;
 
-int
-arith32_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  return register_operand (op, mode) || GET_CODE (op) == CONST_INT;
-}
+  ret = gen_reg_rtx (Pmode);
+  emit_library_call_value (gen_tls_get_addr (), ret,
+                          LCT_CONST, Pmode, 1, arg, Pmode);
 
-int
-pc_or_label_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return (GET_CODE (op) == PC || GET_CODE (op) == LABEL_REF);
+  return ret;
 }
-\f
-/* Legitimize PIC addresses.  If the address is already
-   position-independent, we return ORIG.  Newly generated
-   position-independent addresses go to REG.  If we need more
-   than one register, we lose.  */
 
-rtx
-legitimize_pic_address (orig, mode, reg)
-     rtx orig, reg;
-     enum machine_mode mode;
+static rtx
+legitimize_tls_address (rtx addr)
 {
-  rtx pic_ref = orig;
+  rtx ret, insn, tmp, t1, t2, tp;
+  enum tls_model model = SYMBOL_REF_TLS_MODEL (addr);
 
-  /* Labels need special handling.  */
-  if (pic_label_operand (orig, mode))
+  switch (model) 
     {
-      /* We do not want to go through the movXX expanders here since that
-        would create recursion.
-
-        Nor do we really want to call a generator for a named pattern
-        since that requires multiple patterns if we want to support
-        multiple word sizes.
+      case TLS_MODEL_GLOBAL_DYNAMIC:
+       tmp = gen_reg_rtx (Pmode);
+       if (flag_pic)
+         emit_insn (gen_tgd_load_pic (tmp, addr));
+       else
+         emit_insn (gen_tgd_load (tmp, addr));
+       ret = hppa_tls_call (tmp);
+       break;
 
-        So instead we just emit the raw set, which avoids the movXX
-        expanders completely.  */
-      emit_insn (gen_rtx_SET (VOIDmode, reg, orig));
-      current_function_uses_pic_offset_table = 1;
-      return reg;
-    }
-  if (GET_CODE (orig) == SYMBOL_REF)
-    {
-      if (reg == 0)
-       abort ();
-
-      emit_move_insn (reg,
-                     gen_rtx_PLUS (word_mode, pic_offset_table_rtx,
-                                   gen_rtx_HIGH (word_mode, orig)));
-      pic_ref
-       = gen_rtx_MEM (Pmode,
-                      gen_rtx_LO_SUM (Pmode, reg,
-                                      gen_rtx_UNSPEC (Pmode,
-                                                      gen_rtvec (1, orig),
-                                                      0)));
-
-      current_function_uses_pic_offset_table = 1;
-      RTX_UNCHANGING_P (pic_ref) = 1;
-      emit_move_insn (reg, pic_ref);
-      return reg;
-    }
-  else if (GET_CODE (orig) == CONST)
-    {
-      rtx base;
+      case TLS_MODEL_LOCAL_DYNAMIC:
+       ret = gen_reg_rtx (Pmode);
+       tmp = gen_reg_rtx (Pmode);
+       start_sequence ();
+       if (flag_pic)
+         emit_insn (gen_tld_load_pic (tmp, addr));
+       else
+         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;
 
-      if (GET_CODE (XEXP (orig, 0)) == PLUS
-         && XEXP (XEXP (orig, 0), 0) == pic_offset_table_rtx)
-       return orig;
+      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));
+       if (flag_pic)
+         emit_insn (gen_tie_load_pic (tmp, addr));
+       else
+         emit_insn (gen_tie_load (tmp, addr));
+       emit_move_insn (ret, gen_rtx_PLUS (Pmode, tp, tmp));
+       break;
 
-      if (reg == 0)
-       abort ();
+      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;
 
-      if (GET_CODE (XEXP (orig, 0)) == PLUS)
-       {
-         base = legitimize_pic_address (XEXP (XEXP (orig, 0), 0), Pmode, reg);
-         orig = legitimize_pic_address (XEXP (XEXP (orig, 0), 1), Pmode,
-                                        base == reg ? 0 : reg);
-       }
-      else abort ();
-      if (GET_CODE (orig) == CONST_INT)
-       {
-         if (INT_14_BITS (orig))
-           return plus_constant (base, INTVAL (orig));
-         orig = force_reg (Pmode, orig);
-       }
-      pic_ref = gen_rtx_PLUS (Pmode, base, orig);
-      /* Likewise, should we set special REG_NOTEs here?  */
+      default:
+       gcc_unreachable ();
     }
-  return pic_ref;
+
+  return ret;
 }
 
 /* Try machine-dependent ways of modifying an illegitimate address
@@ -854,7 +893,7 @@ legitimize_pic_address (orig, mode, reg)
 
    This is for CSE to find several similar references, and only use one Z.
 
-   X can either be a SYMBOL_REF or REG, but because combine can not
+   X can either be a SYMBOL_REF or REG, but because combine cannot
    perform a 4->2 combination we do nothing for SYMBOL_REF + D where
    D will not fit in 14 bits.
 
@@ -878,13 +917,26 @@ legitimize_pic_address (orig, mode, reg)
    a register.  */
 
 rtx
-hppa_legitimize_address (x, oldx, mode)
-     rtx x, oldx ATTRIBUTE_UNUSED;
-     enum machine_mode mode;
+hppa_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
+                        enum machine_mode mode)
 {
   rtx orig = x;
 
-  if (flag_pic)
+  /* We need to canonicalize the order of operands in unscaled indexed
+     addresses since the code that checks if an address is valid doesn't
+     always try both orders.  */
+  if (!TARGET_NO_SPACE_REGS
+      && GET_CODE (x) == PLUS
+      && GET_MODE (x) == Pmode
+      && REG_P (XEXP (x, 0))
+      && REG_P (XEXP (x, 1))
+      && REG_POINTER (XEXP (x, 0))
+      && !REG_POINTER (XEXP (x, 1)))
+    return gen_rtx_PLUS (Pmode, XEXP (x, 1), XEXP (x, 0));
+
+  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.  */
@@ -915,7 +967,7 @@ hppa_legitimize_address (x, oldx, mode)
       int mask;
 
       mask = (GET_MODE_CLASS (mode) == MODE_FLOAT
-             ? (TARGET_PA_20 ? 0x3fff : 0x1f) : 0x3fff);
+             ? (INT14_OK_STRICT ? 0x3fff : 0x1f) : 0x3fff);
 
       /* Choose which way to round the offset.  Round up if we
         are >= halfway to the next boundary.  */
@@ -962,7 +1014,7 @@ hppa_legitimize_address (x, oldx, mode)
   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)
     {
@@ -1226,35 +1278,135 @@ hppa_legitimize_address (x, oldx, mode)
 
    It is no coincidence that this has the same structure
    as GO_IF_LEGITIMATE_ADDRESS.  */
-int
-hppa_address_cost (X)
-     rtx X;
+
+static int
+hppa_address_cost (rtx X,
+                  bool speed ATTRIBUTE_UNUSED)
 {
-  if (GET_CODE (X) == PLUS)
+  switch (GET_CODE (X))
+    {
+    case REG:
+    case PLUS:
+    case LO_SUM:
       return 1;
-  else if (GET_CODE (X) == LO_SUM)
-    return 1;
-  else if (GET_CODE (X) == HIGH)
-    return 2;
-  return 4;
+    case HIGH:
+      return 2;
+    default:
+      return 4;
+    }
+}
+
+/* Compute a (partial) cost for rtx X.  Return true if the complete
+   cost has been computed, and false if subexpressions should be
+   scanned.  In either case, *TOTAL contains the cost result.  */
+
+static bool
+hppa_rtx_costs (rtx x, int code, int outer_code, int *total,
+               bool speed ATTRIBUTE_UNUSED)
+{
+  switch (code)
+    {
+    case CONST_INT:
+      if (INTVAL (x) == 0)
+       *total = 0;
+      else if (INT_14_BITS (x))
+       *total = 1;
+      else
+       *total = 2;
+      return true;
+
+    case HIGH:
+      *total = 2;
+      return true;
+
+    case CONST:
+    case LABEL_REF:
+    case SYMBOL_REF:
+      *total = 4;
+      return true;
+
+    case CONST_DOUBLE:
+      if ((x == CONST0_RTX (DFmode) || x == CONST0_RTX (SFmode))
+         && outer_code != SET)
+       *total = 0;
+      else
+        *total = 8;
+      return true;
+
+    case MULT:
+      if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+        *total = COSTS_N_INSNS (3);
+      else if (TARGET_PA_11 && !TARGET_DISABLE_FPREGS && !TARGET_SOFT_FLOAT)
+       *total = COSTS_N_INSNS (8);
+      else
+       *total = COSTS_N_INSNS (20);
+      return true;
+
+    case DIV:
+      if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+       {
+         *total = COSTS_N_INSNS (14);
+         return true;
+       }
+      /* FALLTHRU */
+
+    case UDIV:
+    case MOD:
+    case UMOD:
+      *total = COSTS_N_INSNS (60);
+      return true;
+
+    case PLUS: /* this includes shNadd insns */
+    case MINUS:
+      if (GET_MODE_CLASS (GET_MODE (x)) == MODE_FLOAT)
+       *total = COSTS_N_INSNS (3);
+      else
+        *total = COSTS_N_INSNS (1);
+      return true;
+
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+      *total = COSTS_N_INSNS (1);
+      return true;
+
+    default:
+      return false;
+    }
 }
 
 /* Ensure mode of ORIG, a REG rtx, is MODE.  Returns either ORIG or a
    new rtx with the correct mode.  */
 static inline rtx
-force_mode (mode, orig)
-     enum machine_mode mode;
-     rtx orig;
+force_mode (enum machine_mode mode, rtx orig)
 {
   if (mode == GET_MODE (orig))
     return orig;
 
-  if (REGNO (orig) >= FIRST_PSEUDO_REGISTER)
-    abort ();
+  gcc_assert (REGNO (orig) < FIRST_PSEUDO_REGISTER);
 
   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
@@ -1262,19 +1414,44 @@ force_mode (mode, orig)
    normally.
 
    Note SCRATCH_REG may not be in the proper mode depending on how it
-   will be used.  This routine is resposible for creating a new copy
+   will be used.  This routine is responsible for creating a new copy
    of SCRATCH_REG in the proper mode.  */
 
 int
-emit_move_sequence (operands, mode, scratch_reg)
-     rtx *operands;
-     enum machine_mode mode;
-     rtx scratch_reg;
+emit_move_sequence (rtx *operands, enum machine_mode mode, rtx scratch_reg)
 {
   register rtx operand0 = operands[0];
   register rtx operand1 = operands[1];
   register rtx tem;
 
+  /* We can only handle indexed addresses in the destination operand
+     of floating point stores.  Thus, we need to break out indexed
+     addresses from the destination operand.  */
+  if (GET_CODE (operand0) == MEM && IS_INDEX_ADDR_P (XEXP (operand0, 0)))
+    {
+      gcc_assert (can_create_pseudo_p ());
+
+      tem = copy_to_mode_reg (Pmode, XEXP (operand0, 0));
+      operand0 = replace_equiv_address (operand0, tem);
+    }
+
+  /* On targets with non-equivalent space registers, break out unscaled
+     indexed addresses from the source operand before the final CSE.
+     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 opportunities to create insns with
+     unscaled indexed addresses.  */
+  if (!TARGET_NO_SPACE_REGS
+      && !cse_not_expected
+      && GET_CODE (operand1) == MEM
+      && GET_CODE (XEXP (operand1, 0)) == PLUS
+      && REG_P (XEXP (XEXP (operand1, 0), 0))
+      && REG_P (XEXP (XEXP (operand1, 0), 1)))
+    operand1
+      = replace_equiv_address (operand1,
+                              copy_to_mode_reg (Pmode, XEXP (operand1, 0)));
+
   if (scratch_reg
       && reload_in_progress && GET_CODE (operand0) == REG
       && REGNO (operand0) >= FIRST_PSEUDO_REGISTER)
@@ -1312,22 +1489,26 @@ emit_move_sequence (operands, mode, 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 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);
@@ -1342,24 +1523,29 @@ emit_move_sequence (operands, mode, scratch_reg)
       if (!memory_address_p (Pmode, XEXP (operand1, 0)))
        {
          emit_move_insn (scratch_reg, XEXP (XEXP (operand1, 0), 1));
-         emit_move_insn (scratch_reg, gen_rtx_fmt_ee (GET_CODE (XEXP (operand1, 0)),
-                                                      Pmode,
-                                                      XEXP (XEXP (operand1, 0), 0),
-                                                      scratch_reg));
+         emit_move_insn (scratch_reg,
+                         gen_rtx_fmt_ee (GET_CODE (XEXP (operand1, 0)),
+                                         Pmode,
+                                         XEXP (XEXP (operand1, 0), 0),
+                                         scratch_reg));
        }
       else
        emit_move_insn (scratch_reg, XEXP (operand1, 0));
       emit_insn (gen_rtx_SET (VOIDmode, operand0,
-                             gen_rtx_MEM (mode, scratch_reg)));
+                             replace_equiv_address (operand1, 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);
@@ -1383,24 +1569,25 @@ emit_move_sequence (operands, mode, 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;
     }
   /* Handle secondary reloads for loads of FP registers from constant
      expressions by forcing the constant into memory.
 
-     use scratch_reg to hold the address of the memory location.
+     Use scratch_reg to hold the address of the memory location.
 
      The proper fix is to change PREFERRED_RELOAD_CLASS to return
-     NO_REGS when presented with a const_int and an register class
+     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];
+      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
@@ -1409,30 +1596,31 @@ emit_move_sequence (operands, mode, 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
      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.  */
       if (GET_CODE (operand1) == MEM
-         && !memory_address_p (Pmode, XEXP (operand1, 0)))
+         && !memory_address_p (GET_MODE (operand0), XEXP (operand1, 0)))
        {
          /* We are reloading the address into the scratch register, so we
             want to make sure the scratch register is a full register.  */
@@ -1452,8 +1640,8 @@ emit_move_sequence (operands, mode, 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
        {
@@ -1470,7 +1658,7 @@ emit_move_sequence (operands, mode, scratch_reg)
       emit_move_insn (operand0, scratch_reg);
       return 1;
     }
-  /* Handle most common case: storing into a register.  */
+  /* Handle the most common case: storing into a register.  */
   else if (register_operand (operand0, mode))
     {
       if (register_operand (operand1, mode)
@@ -1482,7 +1670,66 @@ emit_move_sequence (operands, mode, scratch_reg)
          /* Only `general_operands' can come here, so MEM is ok.  */
          || GET_CODE (operand1) == MEM)
        {
-         /* Run this case quickly.  */
+         /* Various sets are created during RTL generation which don't
+            have the REG_POINTER flag correctly set.  After the CSE pass,
+            instruction recognition can fail if we don't consistently
+            set this flag when performing register copies.  This should
+            also improve the opportunities for creating insns that use
+            unscaled indexing.  */
+         if (REG_P (operand0) && REG_P (operand1))
+           {
+             if (REG_POINTER (operand1)
+                 && !REG_POINTER (operand0)
+                 && !HARD_REGISTER_P (operand0))
+               copy_reg_pointer (operand0, operand1);
+             else if (REG_POINTER (operand0)
+                      && !REG_POINTER (operand1)
+                      && !HARD_REGISTER_P (operand1))
+               copy_reg_pointer (operand1, operand0);
+           }
+         
+         /* When MEMs are broken out, the REG_POINTER flag doesn't
+            get set.  In some cases, we can set the REG_POINTER flag
+            from the declaration for the MEM.  */
+         if (REG_P (operand0)
+             && GET_CODE (operand1) == MEM
+             && !REG_POINTER (operand0))
+           {
+             tree decl = MEM_EXPR (operand1);
+
+             /* Set the register pointer flag and register alignment
+                if the declaration for this memory reference is a
+                pointer type.  Fortran indirect argument references
+                are ignored.  */
+             if (decl
+                 && !(flag_argument_noalias > 1
+                      && TREE_CODE (decl) == INDIRECT_REF
+                      && TREE_CODE (TREE_OPERAND (decl, 0)) == PARM_DECL))
+               {
+                 tree type;
+
+                 /* If this is a COMPONENT_REF, use the FIELD_DECL from
+                    tree operand 1.  */
+                 if (TREE_CODE (decl) == COMPONENT_REF)
+                   decl = TREE_OPERAND (decl, 1);
+
+                 type = TREE_TYPE (decl);
+                 type = strip_array_types (type);
+
+                 if (POINTER_TYPE_P (type))
+                   {
+                     int align;
+
+                     type = TREE_TYPE (type);
+                     /* Using TYPE_ALIGN_OK is rather conservative as
+                        only the ada frontend actually sets it.  */
+                     align = (TYPE_ALIGN_OK (type) ? TYPE_ALIGN (type)
+                              : BITS_PER_UNIT);
+                     mark_reg_pointer (operand0, align);
+                   }
+               }
+           }
+
          emit_insn (gen_rtx_SET (VOIDmode, operand0, operand1));
          return 1;
        }
@@ -1557,8 +1804,7 @@ emit_move_sequence (operands, mode, scratch_reg)
                {
                  /* Save away the constant part of the expression.  */
                  const_part = XEXP (XEXP (operand1, 0), 1);
-                 if (GET_CODE (const_part) != CONST_INT)
-                   abort ();
+                 gcc_assert (GET_CODE (const_part) == CONST_INT);
 
                  /* Force the function label into memory.  */
                  temp = force_const_mem (mode, XEXP (XEXP (operand1, 0), 0));
@@ -1621,14 +1867,17 @@ emit_move_sequence (operands, mode, 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] = replace_equiv_address (const_mem, operands[1]);
                  emit_move_sequence (operands, mode, temp);
                }
              else
                {
                  operands[1] = legitimize_pic_address (operand1, mode, temp);
+                 if (REG_P (operand0) && REG_P (operands[1]))
+                   copy_reg_pointer (operand0, operands[1]);
                  emit_insn (gen_rtx_SET (VOIDmode, operand0, operands[1]));
                }
            }
@@ -1656,9 +1905,10 @@ emit_move_sequence (operands, mode, scratch_reg)
                 Don't mark hard registers though.  That loses.  */
              if (GET_CODE (operand0) == REG
                  && REGNO (operand0) >= FIRST_PSEUDO_REGISTER)
-               REG_POINTER (operand0) = 1;
+               mark_reg_pointer (operand0, BITS_PER_UNIT);
              if (REGNO (temp) >= FIRST_PSEUDO_REGISTER)
-               REG_POINTER (temp) = 1;
+               mark_reg_pointer (temp, BITS_PER_UNIT);
+
              if (ishighonly)
                set = gen_rtx_SET (mode, operand0, temp);
              else
@@ -1674,37 +1924,65 @@ emit_move_sequence (operands, mode, scratch_reg)
            }
          return 1;
        }
-      else if (GET_CODE (operand1) != CONST_INT
-              || ! cint_ok_for_move (INTVAL (operand1)))
+      else if (pa_tls_referenced_p (operand1))
        {
-         rtx extend = NULL_RTX;
-         rtx temp;
+         rtx tmp = operand1;
+         rtx addend = NULL;
 
-         if (TARGET_64BIT && GET_CODE (operand1) == CONST_INT
-             && HOST_BITS_PER_WIDE_INT > 32
-             && GET_MODE_BITSIZE (GET_MODE (operand0)) > 32)
+         if (GET_CODE (tmp) == CONST && GET_CODE (XEXP (tmp, 0)) == PLUS)
            {
-             HOST_WIDE_INT val = INTVAL (operand1);
-             HOST_WIDE_INT nval;
+             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)))
+       {
+         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
+             && HOST_BITS_PER_WIDE_INT > 32
+             && GET_MODE_BITSIZE (GET_MODE (operand0)) > 32)
+           {
+             HOST_WIDE_INT nval;
 
              /* Extract the low order 32 bits of the value and sign extend.
                 If the new value is the same as the original value, we can
                 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);
 
@@ -1712,7 +1990,9 @@ emit_move_sequence (operands, mode, 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) <= BITS_PER_WORD
+             && 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
@@ -1720,7 +2000,6 @@ emit_move_sequence (operands, mode, 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;
 
@@ -1744,11 +2023,59 @@ emit_move_sequence (operands, mode, 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]);
+
+         /* 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;
+                   }
+               }
+           }
 
-         if (extend != NULL_RTX)
-           emit_insn (gen_insv (operands[0], GEN_INT (32), const0_rtx,
-                                extend));
+         set_unique_reg_note (insn, REG_EQUAL, op1);
 
          return 1;
        }
@@ -1761,8 +2088,7 @@ emit_move_sequence (operands, mode, scratch_reg)
    it will need a link/runtime reloc).  */
 
 int
-reloc_needed (exp)
-     tree exp;
+reloc_needed (tree exp)
 {
   int reloc = 0;
 
@@ -1771,24 +2097,26 @@ reloc_needed (exp)
     case ADDR_EXPR:
       return 1;
 
+    case POINTER_PLUS_EXPR:
     case PLUS_EXPR:
     case MINUS_EXPR:
       reloc = reloc_needed (TREE_OPERAND (exp, 0));
       reloc |= reloc_needed (TREE_OPERAND (exp, 1));
       break;
 
-    case NOP_EXPR:
-    case CONVERT_EXPR:
+    CASE_CONVERT:
     case NON_LVALUE_EXPR:
       reloc = reloc_needed (TREE_OPERAND (exp, 0));
       break;
 
     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;
 
@@ -1801,13 +2129,12 @@ reloc_needed (exp)
   return reloc;
 }
 
-/* Does operand (which is a symbolic_operand) live in text space? If
-   so SYMBOL_REF_FLAG, which is set by ENCODE_SECTION_INFO, will be true.  */
+/* Does operand (which is a symbolic_operand) live in text space?
+   If so, SYMBOL_REF_FLAG, which is set by pa_encode_section_info,
+   will be true.  */
 
 int
-read_only_operand (operand, mode)
-     rtx operand;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+read_only_operand (rtx operand, enum machine_mode mode ATTRIBUTE_UNUSED)
 {
   if (GET_CODE (operand) == CONST)
     operand = XEXP (XEXP (operand, 0), 0);
@@ -1826,10 +2153,9 @@ read_only_operand (operand, mode)
 
 \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 (operands)
-     rtx *operands;
+singlemove_string (rtx *operands)
 {
   HOST_WIDE_INT intval;
 
@@ -1842,8 +2168,7 @@ singlemove_string (operands)
       long i;
       REAL_VALUE_TYPE d;
 
-      if (GET_MODE (operands[1]) != SFmode)
-       abort ();
+      gcc_assert (GET_MODE (operands[1]) == SFmode);
 
       /* Translate the CONST_DOUBLE to a CONST_INT with the same target
         bit pattern.  */
@@ -1874,9 +2199,7 @@ singlemove_string (operands)
    useful for copying IMM to a register using the zdepi
    instructions.  Store the immediate value to insert in OP[0].  */
 static void
-compute_zdepwi_operands (imm, op)
-     unsigned HOST_WIDE_INT imm;
-     unsigned *op;
+compute_zdepwi_operands (unsigned HOST_WIDE_INT imm, unsigned *op)
 {
   int lsb, len;
 
@@ -1894,9 +2217,9 @@ compute_zdepwi_operands (imm, op)
   else
     {
       /* Find the width of the bitstring in IMM.  */
-      for (len = 5; len < 32; len++)
+      for (len = 5; len < 32 - lsb; len++)
        {
-         if ((imm & (1 << len)) == 0)
+         if ((imm & ((unsigned HOST_WIDE_INT) 1 << len)) == 0)
            break;
        }
 
@@ -1913,14 +2236,14 @@ compute_zdepwi_operands (imm, op)
    useful for copying IMM to a register using the depdi,z
    instructions.  Store the immediate value to insert in OP[0].  */
 void
-compute_zdepdi_operands (imm, op)
-     unsigned HOST_WIDE_INT imm;
-     unsigned *op;
+compute_zdepdi_operands (unsigned HOST_WIDE_INT imm, unsigned *op)
 {
-  HOST_WIDE_INT lsb, len;
+  int lsb, len, maxlen;
+
+  maxlen = MIN (HOST_BITS_PER_WIDE_INT, 64);
 
   /* Find the least significant set bit in IMM.  */
-  for (lsb = 0; lsb < HOST_BITS_PER_WIDE_INT; lsb++)
+  for (lsb = 0; lsb < maxlen; lsb++)
     {
       if ((imm & 1) != 0)
         break;
@@ -1929,17 +2252,20 @@ compute_zdepdi_operands (imm, op)
 
   /* Choose variants based on *sign* of the 5-bit field.  */
   if ((imm & 0x10) == 0)
-    len = ((lsb <= HOST_BITS_PER_WIDE_INT - 4)
-          ? 4 : HOST_BITS_PER_WIDE_INT - lsb);
+    len = (lsb <= maxlen - 4) ? 4 : maxlen - lsb;
   else
     {
       /* Find the width of the bitstring in IMM.  */
-      for (len = 5; len < HOST_BITS_PER_WIDE_INT; len++)
+      for (len = 5; len < maxlen - lsb; len++)
        {
          if ((imm & ((unsigned HOST_WIDE_INT) 1 << len)) == 0)
            break;
        }
 
+      /* Extend length if host is narrow and IMM is negative.  */
+      if (HOST_BITS_PER_WIDE_INT == 32 && len == maxlen - lsb)
+       len += 32;
+
       /* Sign extend IMM as a 5-bit value.  */
       imm = (imm & 0xf) - 0x10;
     }
@@ -1953,8 +2279,7 @@ compute_zdepdi_operands (imm, op)
    with operands OPERANDS.  */
 
 const char *
-output_move_double (operands)
-     rtx *operands;
+output_move_double (rtx *operands)
 {
   enum { REGOP, OFFSOP, MEMOP, CNSTOP, RNDOP } optype0, optype1;
   rtx latehalf[2];
@@ -1983,11 +2308,27 @@ output_move_double (operands)
     optype1 = RNDOP;
 
   /* Check for the cases that the operand constraints are not
-     supposed to allow to happen.  Abort if we get one,
-     because generating code for these cases is painful.  */
+     supposed to allow to happen.  */
+  gcc_assert (optype0 == REGOP || optype1 == REGOP);
 
-  if (optype0 != REGOP && optype1 != REGOP)
-    abort ();
+  /* Handle copies between general and floating registers.  */
+
+  if (optype0 == REGOP && optype1 == REGOP
+      && FP_REG_P (operands[0]) ^ FP_REG_P (operands[1]))
+    {
+      if (FP_REG_P (operands[0]))
+       {
+         output_asm_insn ("{stws|stw} %1,-16(%%sp)", operands);
+         output_asm_insn ("{stws|stw} %R1,-12(%%sp)", operands);
+         return "{fldds|fldd} -16(%%sp),%0";
+       }
+      else
+       {
+         output_asm_insn ("{fstds|fstd} %1,-16(%%sp)", operands);
+         output_asm_insn ("{ldws|ldw} -16(%%sp),%0", operands);
+         return "{ldws|ldw} -12(%%sp),%R0";
+       }
+    }
 
    /* Handle auto decrementing and incrementing loads and stores
      specifically, since the structure of the function doesn't work
@@ -2006,40 +2347,33 @@ output_move_double (operands)
          rtx high_reg = gen_rtx_SUBREG (SImode, operands[1], 0);
 
          operands[0] = XEXP (addr, 0);
-         if (GET_CODE (operands[1]) != REG || GET_CODE (operands[0]) != REG)
-           abort ();
-
-         if (!reg_overlap_mentioned_p (high_reg, addr))
-           {
-             /* No overlap between high target register and address
-                register.  (We do this in a non-obvious way to
-                save a register file writeback)  */
-             if (GET_CODE (addr) == POST_INC)
-               return "{stws|stw},ma %1,8(%0)\n\tstw %R1,-4(%0)";
-             return "{stws|stw},ma %1,-8(%0)\n\tstw %R1,12(%0)";
-           }
-         else
-           abort ();
+         gcc_assert (GET_CODE (operands[1]) == REG
+                     && GET_CODE (operands[0]) == REG);
+
+         gcc_assert (!reg_overlap_mentioned_p (high_reg, addr));
+         
+         /* No overlap between high target register and address
+            register.  (We do this in a non-obvious way to
+            save a register file writeback)  */
+         if (GET_CODE (addr) == POST_INC)
+           return "{stws|stw},ma %1,8(%0)\n\tstw %R1,-4(%0)";
+         return "{stws|stw},ma %1,-8(%0)\n\tstw %R1,12(%0)";
        }
       else if (GET_CODE (addr) == PRE_INC || GET_CODE (addr) == PRE_DEC)
        {
          rtx high_reg = gen_rtx_SUBREG (SImode, operands[1], 0);
 
          operands[0] = XEXP (addr, 0);
-         if (GET_CODE (operands[1]) != REG || GET_CODE (operands[0]) != REG)
-           abort ();
-
-         if (!reg_overlap_mentioned_p (high_reg, addr))
-           {
-             /* No overlap between high target register and address
-                register.  (We do this in a non-obvious way to
-                save a register file writeback)  */
-             if (GET_CODE (addr) == PRE_INC)
-               return "{stws|stw},mb %1,8(%0)\n\tstw %R1,4(%0)";
-             return "{stws|stw},mb %1,-8(%0)\n\tstw %R1,4(%0)";
-           }
-         else
-           abort ();
+         gcc_assert (GET_CODE (operands[1]) == REG
+                     && GET_CODE (operands[0]) == REG);
+         
+         gcc_assert (!reg_overlap_mentioned_p (high_reg, addr));
+         /* No overlap between high target register and address
+            register.  (We do this in a non-obvious way to save a
+            register file writeback)  */
+         if (GET_CODE (addr) == PRE_INC)
+           return "{stws|stw},mb %1,8(%0)\n\tstw %R1,4(%0)";
+         return "{stws|stw},mb %1,-8(%0)\n\tstw %R1,4(%0)";
        }
     }
   if (optype1 == MEMOP)
@@ -2053,8 +2387,8 @@ output_move_double (operands)
          rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0);
 
          operands[1] = XEXP (addr, 0);
-         if (GET_CODE (operands[0]) != REG || GET_CODE (operands[1]) != REG)
-           abort ();
+         gcc_assert (GET_CODE (operands[0]) == REG
+                     && GET_CODE (operands[1]) == REG);
 
          if (!reg_overlap_mentioned_p (high_reg, addr))
            {
@@ -2080,8 +2414,8 @@ output_move_double (operands)
          rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0);
 
          operands[1] = XEXP (addr, 0);
-         if (GET_CODE (operands[0]) != REG || GET_CODE (operands[1]) != REG)
-           abort ();
+         gcc_assert (GET_CODE (operands[0]) == REG
+                     && GET_CODE (operands[1]) == REG);
 
          if (!reg_overlap_mentioned_p (high_reg, addr))
            {
@@ -2105,12 +2439,11 @@ output_move_double (operands)
       else if (GET_CODE (addr) == PLUS
               && GET_CODE (XEXP (addr, 0)) == MULT)
        {
+         rtx xoperands[4];
          rtx high_reg = gen_rtx_SUBREG (SImode, operands[0], 0);
 
          if (!reg_overlap_mentioned_p (high_reg, addr))
            {
-             rtx xoperands[3];
-
              xoperands[0] = high_reg;
              xoperands[1] = XEXP (addr, 1);
              xoperands[2] = XEXP (XEXP (addr, 0), 0);
@@ -2121,8 +2454,6 @@ output_move_double (operands)
            }
          else
            {
-             rtx xoperands[3];
-
              xoperands[0] = high_reg;
              xoperands[1] = XEXP (addr, 1);
              xoperands[2] = XEXP (XEXP (addr, 0), 0);
@@ -2229,8 +2560,7 @@ output_move_double (operands)
 }
 \f
 const char *
-output_fp_move_double (operands)
-     rtx *operands;
+output_fp_move_double (rtx *operands)
 {
   if (FP_REG_P (operands[0]))
     {
@@ -2244,23 +2574,22 @@ output_fp_move_double (operands)
     {
       output_asm_insn ("fstd%F0 %1,%0", operands);
     }
-  else if (operands[1] == CONST0_RTX (GET_MODE (operands[0])))
+  else
     {
-      if (GET_CODE (operands[0]) == REG)
-       {
-         rtx xoperands[2];
-         xoperands[1] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
-         xoperands[0] = operands[0];
-         output_asm_insn ("copy %%r0,%0\n\tcopy %%r0,%1", xoperands);
-       }
+      rtx xoperands[2];
+      
+      gcc_assert (operands[1] == CONST0_RTX (GET_MODE (operands[0])));
+      
       /* This is a pain.  You have to be prepared to deal with an
         arbitrary address here including pre/post increment/decrement.
 
         so avoid this in the MD.  */
-      else
-       abort ();
+      gcc_assert (GET_CODE (operands[0]) == REG);
+      
+      xoperands[1] = gen_rtx_REG (SImode, REGNO (operands[0]) + 1);
+      xoperands[0] = operands[0];
+      output_asm_insn ("copy %%r0,%0\n\tcopy %%r0,%1", xoperands);
     }
-  else abort ();
   return "";
 }
 \f
@@ -2268,8 +2597,7 @@ output_fp_move_double (operands)
    ADDR can be effectively incremented by incrementing REG.  */
 
 static rtx
-find_addr_reg (addr)
-     rtx addr;
+find_addr_reg (rtx addr)
 {
   while (GET_CODE (addr) == PLUS)
     {
@@ -2282,11 +2610,10 @@ find_addr_reg (addr)
       else if (CONSTANT_P (XEXP (addr, 1)))
        addr = XEXP (addr, 0);
       else
-       abort ();
+       gcc_unreachable ();
     }
-  if (GET_CODE (addr) == REG)
-    return addr;
-  abort ();
+  gcc_assert (GET_CODE (addr) == REG);
+  return addr;
 }
 
 /* Emit code to perform a block move.
@@ -2294,28 +2621,54 @@ find_addr_reg (addr)
    OPERANDS[0] is the destination pointer as a REG, clobbered.
    OPERANDS[1] is the source pointer as a REG, clobbered.
    OPERANDS[2] is a register for temporary storage.
-   OPERANDS[4] is the size as a CONST_INT
    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 (operands, size_is_constant)
-     rtx *operands;
-     int size_is_constant ATTRIBUTE_UNUSED;
+output_block_move (rtx *operands, int size_is_constant ATTRIBUTE_UNUSED)
 {
   int align = INTVAL (operands[5]);
   unsigned long n_bytes = INTVAL (operands[4]);
 
-  /* We can't move more than four bytes at a time because the PA
+  /* We can't move more than a word at a time because the PA
      has no longer integer move insns.  (Could use fp mem ops?)  */
-  if (align > 4)
-    align = 4;
+  if (align > (TARGET_64BIT ? 8 : 4))
+    align = (TARGET_64BIT ? 8 : 4);
 
   /* Note that we know each loop below will execute at least twice
      (else we would have open-coded the copy).  */
   switch (align)
     {
+      case 8:
+       /* Pre-adjust the loop counter.  */
+       operands[4] = GEN_INT (n_bytes - 16);
+       output_asm_insn ("ldi %4,%2", operands);
+
+       /* Copying loop.  */
+       output_asm_insn ("ldd,ma 8(%1),%3", operands);
+       output_asm_insn ("ldd,ma 8(%1),%6", operands);
+       output_asm_insn ("std,ma %3,8(%0)", operands);
+       output_asm_insn ("addib,>= -16,%2,.-12", operands);
+       output_asm_insn ("std,ma %6,8(%0)", operands);
+
+       /* Handle the residual.  There could be up to 7 bytes of
+          residual to copy!  */
+       if (n_bytes % 16 != 0)
+         {
+           operands[4] = GEN_INT (n_bytes % 8);
+           if (n_bytes % 16 >= 8)
+             output_asm_insn ("ldd,ma 8(%1),%3", operands);
+           if (n_bytes % 8 != 0)
+             output_asm_insn ("ldd 0(%1),%6", operands);
+           if (n_bytes % 16 >= 8)
+             output_asm_insn ("std,ma %3,8(%0)", operands);
+           if (n_bytes % 8 != 0)
+             output_asm_insn ("stdby,e %6,%4(%0)", operands);
+         }
+       return "";
+
       case 4:
        /* Pre-adjust the loop counter.  */
        operands[4] = GEN_INT (n_bytes - 8);
@@ -2391,7 +2744,7 @@ output_block_move (operands, size_is_constant)
        return "";
 
       default:
-       abort ();
+       gcc_unreachable ();
     }
 }
 
@@ -2401,8 +2754,7 @@ output_block_move (operands, size_is_constant)
    count insns rather than emit them.  */
 
 static int
-compute_movstrsi_length (insn)
-     rtx insn;
+compute_movmem_length (rtx insn)
 {
   rtx pat = PATTERN (insn);
   unsigned int align = INTVAL (XEXP (XVECEXP (pat, 0, 7), 0));
@@ -2411,8 +2763,8 @@ compute_movstrsi_length (insn)
 
   /* We can't move more than four bytes at a time because the PA
      has no longer integer move insns.  (Could use fp mem ops?)  */
-  if (align > 4)
-    align = 4;
+  if (align > (TARGET_64BIT ? 8 : 4))
+    align = (TARGET_64BIT ? 8 : 4);
 
   /* The basic copying loop.  */
   n_insns = 6;
@@ -2430,11 +2782,152 @@ compute_movstrsi_length (insn)
   /* Lengths are expressed in bytes now; each insn is 4 bytes.  */
   return n_insns * 4;
 }
+
+/* Emit code to perform a block clear.
+
+   OPERANDS[0] is the destination pointer as a REG, clobbered.
+   OPERANDS[1] is a register for temporary storage.
+   OPERANDS[2] is the size as a CONST_INT
+   OPERANDS[3] is the alignment safe to use, as a CONST_INT.  */
+
+const char *
+output_block_clear (rtx *operands, int size_is_constant ATTRIBUTE_UNUSED)
+{
+  int align = INTVAL (operands[3]);
+  unsigned long n_bytes = INTVAL (operands[2]);
+
+  /* We can't clear more than a word at a time because the PA
+     has no longer integer move insns.  */
+  if (align > (TARGET_64BIT ? 8 : 4))
+    align = (TARGET_64BIT ? 8 : 4);
+
+  /* Note that we know each loop below will execute at least twice
+     (else we would have open-coded the copy).  */
+  switch (align)
+    {
+      case 8:
+       /* Pre-adjust the loop counter.  */
+       operands[2] = GEN_INT (n_bytes - 16);
+       output_asm_insn ("ldi %2,%1", operands);
+
+       /* Loop.  */
+       output_asm_insn ("std,ma %%r0,8(%0)", operands);
+       output_asm_insn ("addib,>= -16,%1,.-4", operands);
+       output_asm_insn ("std,ma %%r0,8(%0)", operands);
+
+       /* Handle the residual.  There could be up to 7 bytes of
+          residual to copy!  */
+       if (n_bytes % 16 != 0)
+         {
+           operands[2] = GEN_INT (n_bytes % 8);
+           if (n_bytes % 16 >= 8)
+             output_asm_insn ("std,ma %%r0,8(%0)", operands);
+           if (n_bytes % 8 != 0)
+             output_asm_insn ("stdby,e %%r0,%2(%0)", operands);
+         }
+       return "";
+
+      case 4:
+       /* Pre-adjust the loop counter.  */
+       operands[2] = GEN_INT (n_bytes - 8);
+       output_asm_insn ("ldi %2,%1", operands);
+
+       /* Loop.  */
+       output_asm_insn ("{stws|stw},ma %%r0,4(%0)", operands);
+       output_asm_insn ("addib,>= -8,%1,.-4", operands);
+       output_asm_insn ("{stws|stw},ma %%r0,4(%0)", operands);
+
+       /* Handle the residual.  There could be up to 7 bytes of
+          residual to copy!  */
+       if (n_bytes % 8 != 0)
+         {
+           operands[2] = GEN_INT (n_bytes % 4);
+           if (n_bytes % 8 >= 4)
+             output_asm_insn ("{stws|stw},ma %%r0,4(%0)", operands);
+           if (n_bytes % 4 != 0)
+             output_asm_insn ("{stbys|stby},e %%r0,%2(%0)", operands);
+         }
+       return "";
+
+      case 2:
+       /* Pre-adjust the loop counter.  */
+       operands[2] = GEN_INT (n_bytes - 4);
+       output_asm_insn ("ldi %2,%1", operands);
+
+       /* Loop.  */
+       output_asm_insn ("{sths|sth},ma %%r0,2(%0)", operands);
+       output_asm_insn ("addib,>= -4,%1,.-4", operands);
+       output_asm_insn ("{sths|sth},ma %%r0,2(%0)", operands);
+
+       /* Handle the residual.  */
+       if (n_bytes % 4 != 0)
+         {
+           if (n_bytes % 4 >= 2)
+             output_asm_insn ("{sths|sth},ma %%r0,2(%0)", operands);
+           if (n_bytes % 2 != 0)
+             output_asm_insn ("stb %%r0,0(%0)", operands);
+         }
+       return "";
+
+      case 1:
+       /* Pre-adjust the loop counter.  */
+       operands[2] = GEN_INT (n_bytes - 2);
+       output_asm_insn ("ldi %2,%1", operands);
+
+       /* Loop.  */
+       output_asm_insn ("{stbs|stb},ma %%r0,1(%0)", operands);
+       output_asm_insn ("addib,>= -2,%1,.-4", operands);
+       output_asm_insn ("{stbs|stb},ma %%r0,1(%0)", operands);
+
+       /* Handle the residual.  */
+       if (n_bytes % 2 != 0)
+         output_asm_insn ("stb %%r0,0(%0)", operands);
+
+       return "";
+
+      default:
+       gcc_unreachable ();
+    }
+}
+
+/* Count the number of insns necessary to handle this block move.
+
+   Basic structure is the same as emit_block_move, except that we
+   count insns rather than emit them.  */
+
+static int
+compute_clrmem_length (rtx insn)
+{
+  rtx pat = PATTERN (insn);
+  unsigned int align = INTVAL (XEXP (XVECEXP (pat, 0, 4), 0));
+  unsigned long n_bytes = INTVAL (XEXP (XVECEXP (pat, 0, 3), 0));
+  unsigned int n_insns = 0;
+
+  /* We can't clear more than a word at a time because the PA
+     has no longer integer move insns.  */
+  if (align > (TARGET_64BIT ? 8 : 4))
+    align = (TARGET_64BIT ? 8 : 4);
+
+  /* The basic loop.  */
+  n_insns = 4;
+
+  /* Residuals.  */
+  if (n_bytes % (2 * align) != 0)
+    {
+      if ((n_bytes % (2 * align)) >= align)
+       n_insns++;
+
+      if ((n_bytes % align) != 0)
+       n_insns++;
+    }
+
+  /* Lengths are expressed in bytes now; each insn is 4 bytes.  */
+  return n_insns * 4;
+}
 \f
 
 const char *
-output_and (operands)
-     rtx *operands;
+output_and (rtx *operands)
 {
   if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) != 0)
     {
@@ -2453,15 +2946,13 @@ output_and (operands)
        if ((mask & (1 << ms0)) == 0)
          break;
 
-      if (ms0 != 32)
-       abort ();
+      gcc_assert (ms0 == 32);
 
       if (ls1 == 32)
        {
          len = ls0;
 
-         if (len == 0)
-           abort ();
+         gcc_assert (len);
 
          operands[2] = GEN_INT (len);
          return "{extru|extrw,u} %1,31,%2,%0";
@@ -2486,8 +2977,7 @@ output_and (operands)
 /* Return a string to perform a bitwise-and of operands[1] with operands[2]
    storing the result in operands[0].  */
 const char *
-output_64bit_and (operands)
-     rtx *operands;
+output_64bit_and (rtx *operands)
 {
   if (GET_CODE (operands[2]) == CONST_INT && INTVAL (operands[2]) != 0)
     {
@@ -2506,15 +2996,13 @@ output_64bit_and (operands)
        if ((mask & ((unsigned HOST_WIDE_INT) 1 << ms0)) == 0)
          break;
 
-      if (ms0 != HOST_BITS_PER_WIDE_INT)
-       abort ();
+      gcc_assert (ms0 == HOST_BITS_PER_WIDE_INT);
 
       if (ls1 == HOST_BITS_PER_WIDE_INT)
        {
          len = ls0;
 
-         if (len == 0)
-           abort ();
+         gcc_assert (len);
 
          operands[2] = GEN_INT (len);
          return "extrd,u %1,63,%2,%0";
@@ -2537,8 +3025,7 @@ output_64bit_and (operands)
 }
 
 const char *
-output_ior (operands)
-     rtx *operands;
+output_ior (rtx *operands)
 {
   unsigned HOST_WIDE_INT mask = INTVAL (operands[2]);
   int bs0, bs1, p, len;
@@ -2554,8 +3041,7 @@ output_ior (operands)
     if ((mask & (1 << bs1)) == 0)
       break;
 
-  if (bs1 != 32 && ((unsigned HOST_WIDE_INT) 1 << bs1) <= mask)
-    abort ();
+  gcc_assert (bs1 == 32 || ((unsigned HOST_WIDE_INT) 1 << bs1) > mask);
 
   p = 31 - bs0;
   len = bs1 - bs0;
@@ -2568,8 +3054,7 @@ output_ior (operands)
 /* Return a string to perform a bitwise-and of operands[1] with operands[2]
    storing the result in operands[0].  */
 const char *
-output_64bit_ior (operands)
-     rtx *operands;
+output_64bit_ior (rtx *operands)
 {
   unsigned HOST_WIDE_INT mask = INTVAL (operands[2]);
   int bs0, bs1, p, len;
@@ -2585,9 +3070,8 @@ output_64bit_ior (operands)
     if ((mask & ((unsigned HOST_WIDE_INT) 1 << bs1)) == 0)
       break;
 
-  if (bs1 != HOST_BITS_PER_WIDE_INT
-      && ((unsigned HOST_WIDE_INT) 1 << bs1) <= mask)
-    abort ();
+  gcc_assert (bs1 == HOST_BITS_PER_WIDE_INT
+             || ((unsigned HOST_WIDE_INT) 1 << bs1) > mask);
 
   p = 63 - bs0;
   len = bs1 - bs0;
@@ -2598,16 +3082,14 @@ output_64bit_ior (operands)
 }
 \f
 /* Target hook for assembling integer objects.  This code handles
-   aligned SI and DI integers specially, since function references must
-   be preceded by P%.  */
+   aligned SI and DI integers specially since function references
+   must be preceded by P%.  */
 
 static bool
-pa_assemble_integer (x, size, aligned_p)
-     rtx x;
-     unsigned int size;
-     int aligned_p;
+pa_assemble_integer (rtx x, unsigned int size, int aligned_p)
 {
-  if (size == UNITS_PER_WORD && aligned_p
+  if (size == UNITS_PER_WORD
+      && aligned_p
       && function_label_operand (x, VOIDmode))
     {
       fputs (size == 8? "\t.dword\tP%" : "\t.word\tP%", asm_out_file);
@@ -2620,14 +3102,11 @@ pa_assemble_integer (x, size, aligned_p)
 \f
 /* Output an ascii string.  */
 void
-output_ascii (file, p, size)
-     FILE *file;
-     const char *p;
-     int size;
+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
@@ -2670,7 +3149,7 @@ output_ascii (file, p, size)
          fputs ("\"\n\t.STRING \"", file);
          chars_output = 0;
        }
-      fwrite (partial_output, 1, co, file);
+      fwrite (partial_output, 1, (size_t) co, file);
       chars_output += co;
       co = 0;
     }
@@ -2690,9 +3169,7 @@ output_ascii (file, p, size)
    when there's a 1:1 correspondence between fcmp and ftest/fbranch
    instructions.  */
 static void
-remove_useless_addtr_insns (insns, check_notes)
-     rtx insns;
-     int check_notes;
+remove_useless_addtr_insns (int check_notes)
 {
   rtx insn;
   static int pass = 0;
@@ -2705,8 +3182,7 @@ remove_useless_addtr_insns (insns, check_notes)
 
       /* Walk all the insns in this function looking for fcmp & fbranch
         instructions.  Keep track of how many of each we find.  */
-      insns = get_insns ();
-      for (insn = insns; insn; insn = next_insn (insn))
+      for (insn = get_insns (); insn; insn = next_insn (insn))
        {
          rtx tmp;
 
@@ -2745,7 +3221,7 @@ remove_useless_addtr_insns (insns, check_notes)
 
       /* Find all floating point compare + branch insns.  If possible,
         reverse the comparison & the branch to avoid add,tr insns.  */
-      for (insn = insns; insn; insn = next_insn (insn))
+      for (insn = get_insns (); insn; insn = next_insn (insn))
        {
          rtx tmp, next;
 
@@ -2794,7 +3270,7 @@ remove_useless_addtr_insns (insns, check_notes)
            {
              rtx pattern = PATTERN (next);
 
-             /* If it a reversed fp conditional branch (eg uses add,tr)
+             /* If it a reversed fp conditional branch (e.g. uses add,tr)
                 and CCFP dies, then reverse our conditional and the branch
                 to avoid the add,tr.  */
              if (GET_CODE (pattern) == SET
@@ -2894,8 +3370,8 @@ remove_useless_addtr_insns (insns, 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.
@@ -2904,8 +3380,7 @@ static int local_fsize, save_fregs;
    in %r1.  There is code in expand_hppa_{prologue,epilogue} that knows this.*/
 
 static void
-store_reg (reg, disp, base)
-     int reg, disp, base;
+store_reg (int reg, HOST_WIDE_INT disp, int base)
 {
   rtx insn, dest, src, basereg;
 
@@ -2916,11 +3391,31 @@ store_reg (reg, disp, 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);
+      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, 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
     {
       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);
@@ -2941,15 +3436,41 @@ store_reg (reg, disp, base)
     RTX_FRAME_RELATED_P (insn) = 1;
 }
 
-/* Emit RTL to set REG to the value specified by BASE+DISP.
-   Handle case where DISP > 8k by using the add_high_const patterns.
+/* Emit RTL to store REG at the memory location specified by BASE and then
+   add MOD to BASE.  MOD must be <= 8k.  */
 
-   Note in DISP > 8k case, we will leave the high part of the address
-   in %r1.  There is code in expand_hppa_{prologue,epilogue} that knows this.*/
+static void
+store_reg_modify (int base, int reg, HOST_WIDE_INT mod)
+{
+  rtx insn, basereg, srcreg, delta;
+
+  gcc_assert (VAL_14_BITS_P (mod));
+
+  basereg = gen_rtx_REG (Pmode, base);
+  srcreg = gen_rtx_REG (word_mode, reg);
+  delta = GEN_INT (mod);
+
+  insn = emit_insn (gen_post_store (basereg, srcreg, delta));
+  if (DO_FRAME_NOTES)
+    {
+      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.  */
+      RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 0)) = 1;
+      RTX_FRAME_RELATED_P (XVECEXP (PATTERN (insn), 0, 1)) = 1;
+    }
+}
+
+/* Emit RTL to set REG to the value specified by BASE+DISP.  Handle case
+   where DISP > 8k by using the add_high_const patterns.  NOTE indicates
+   whether to add a frame note or not.
+
+   In the DISP > 8k case, we leave the high part of the address in %r1.
+   There is code in expand_hppa_{prologue,epilogue} that knows about this.  */
 
 static void
-set_reg_plus_d (reg, base, disp)
-     int reg, base, disp;
+set_reg_plus_d (int reg, int base, HOST_WIDE_INT disp, int note)
 {
   rtx insn;
 
@@ -2958,69 +3479,113 @@ set_reg_plus_d (reg, base, disp)
       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));
+      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
     {
       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 && reg == STACK_POINTER_REGNUM)
+  if (DO_FRAME_NOTES && note)
     RTX_FRAME_RELATED_P (insn) = 1;
 }
 
-int
-compute_frame_size (size, fregs_live)
-     int size;
-     int *fregs_live;
+HOST_WIDE_INT
+compute_frame_size (HOST_WIDE_INT size, int *fregs_live)
 {
-  int i, fsize;
-
-  /* Space for frame pointer + filler. If any frame is allocated
-     we need to add this in because of STARTING_FRAME_OFFSET.
-
-     Similar code also appears in hppa_expand_prologue.  Change both
-     of them at the same time.  */
-  fsize = size + (size || frame_pointer_needed ? STARTING_FRAME_OFFSET : 0);
+  int freg_saved = 0;
+  int i, j;
+
+  /* The code in hppa_expand_prologue and hppa_expand_epilogue must
+     be consistent with the rounding and size calculation done here.
+     Change them at the same time.  */
+
+  /* We do our own stack alignment.  First, round the size of the
+     stack locals up to a word boundary.  */
+  size = (size + UNITS_PER_WORD - 1) & ~(UNITS_PER_WORD - 1);
+
+  /* Space for previous frame pointer + filler.  If any frame is
+     allocated, we need to add in the STARTING_FRAME_OFFSET.  We
+     waste some space here for the sake of HP compatibility.  The
+     first slot is only used when the frame pointer is needed.  */
+  if (size || frame_pointer_needed)
+    size += STARTING_FRAME_OFFSET;
+  
+  /* If the current function calls __builtin_eh_return, then we need
+     to allocate stack space for registers that will hold data for
+     the exception handler.  */
+  if (DO_FRAME_NOTES && crtl->calls_eh_return)
+    {
+      unsigned int i;
+
+      for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM; ++i)
+       continue;
+      size += i * UNITS_PER_WORD;
+    }
 
   /* Account for space used by the callee general register saves.  */
-  for (i = 18; i >= 3; i--)
-    if (regs_ever_live[i])
-      fsize += UNITS_PER_WORD;
-
-  /* Round the stack.  */
-  fsize = (fsize + 7) & ~7;
+  for (i = 18, j = frame_pointer_needed ? 4 : 3; i >= j; i--)
+    if (df_regs_ever_live_p (i))
+      size += UNITS_PER_WORD;
 
   /* Account for space used by the callee floating point register saves.  */
   for (i = FP_SAVED_REG_LAST; i >= FP_SAVED_REG_FIRST; i -= FP_REG_STEP)
-    if (regs_ever_live[i]
-       || (! TARGET_64BIT && regs_ever_live[i + 1]))
+    if (df_regs_ever_live_p (i)
+       || (!TARGET_64BIT && df_regs_ever_live_p (i + 1)))
       {
-       if (fregs_live)
-         *fregs_live = 1;
+       freg_saved = 1;
 
        /* We always save both halves of the FP register, so always
           increment the frame size by 8 bytes.  */
-       fsize += 8;
+       size += 8;
       }
 
+  /* If any of the floating registers are saved, account for the
+     alignment needed for the floating point register save block.  */
+  if (freg_saved)
+    {
+      size = (size + 7) & ~7;
+      if (fregs_live)
+       *fregs_live = 1;
+    }
+
   /* The various ABIs include space for the outgoing parameters in the
-     size of the current function's stack frame.  */
-  fsize += current_function_outgoing_args_size;
+     size of the current function's stack frame.  We don't need to align
+     for the outgoing arguments as their alignment is set by the final
+     rounding for the frame as a whole.  */
+  size += crtl->outgoing_args_size;
 
   /* Allocate space for the fixed frame marker.  This space must be
-     allocated for any function that makes calls or otherwise allocates
+     allocated for any function that makes calls or allocates
      stack space.  */
-  if (!current_function_is_leaf || fsize)
-    fsize += TARGET_64BIT ? 16 : 32;
+  if (!current_function_is_leaf || size)
+    size += TARGET_64BIT ? 48 : 32;
 
-  return (fsize + STACK_BOUNDARY - 1) & ~(STACK_BOUNDARY - 1);
+  /* Finally, round to the preferred stack boundary.  */
+  return ((size + PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT - 1)
+         & ~(PREFERRED_STACK_BOUNDARY / BITS_PER_UNIT - 1));
 }
 
 /* Generate the assembly code for function entry.  FILE is a stdio
@@ -3040,10 +3605,8 @@ compute_frame_size (size, fregs_live)
    to do a "save" insn.  The decision about whether or not
    to do this is made in regclass.c.  */
 
-void
-pa_output_function_prologue (file, size)
-     FILE *file;
-     HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+static void
+pa_output_function_prologue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 {
   /* The function's label and associated .PROC must never be
      separated and must be output *after* any profiling declarations
@@ -3054,12 +3617,23 @@ pa_output_function_prologue (file, size)
   /* 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);
-  if (regs_ever_live[2])
-    fputs (",CALLS,SAVE_RP", file);
-  else
+  fprintf (file, "\t.CALLINFO FRAME=" HOST_WIDE_INT_PRINT_DEC, actual_fsize);
+  if (current_function_is_leaf)
     fputs (",NO_CALLS", file);
-
+  else
+    fputs (",CALLS", file);
+  if (rp_saved)
+    fputs (",SAVE_RP", file);
+
+  /* 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 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.
+     However, SAVE_SP is supposed to indicate that the previous stack
+     pointer has been saved in the frame marker.  */
   if (frame_pointer_needed)
     fputs (",SAVE_SP", file);
 
@@ -3079,40 +3653,44 @@ pa_output_function_prologue (file, size)
 
   fputs ("\n\t.ENTRY\n", file);
 
-  remove_useless_addtr_insns (get_insns (), 0);
+  remove_useless_addtr_insns (0);
 }
 
 void
-hppa_expand_prologue ()
+hppa_expand_prologue (void)
 {
-  int size = get_frame_size ();
   int merge_sp_adjust_with_store = 0;
-  int i, offset;
-  rtx tmpreg, size_rtx;
+  HOST_WIDE_INT size = get_frame_size ();
+  HOST_WIDE_INT offset;
+  int i;
+  rtx insn, tmpreg;
 
   gr_saved = 0;
   fr_saved = 0;
   save_fregs = 0;
 
-  /* Allocate space for frame pointer + filler. If any frame is allocated
-     we need to add this in because of STARTING_FRAME_OFFSET.
-
-     Similar code also appears in compute_frame_size.  Change both
-     of them at the same time.  */
-  local_fsize = size + (size || frame_pointer_needed
-                       ? STARTING_FRAME_OFFSET : 0);
+  /* Compute total size for frame pointer, filler, locals and rounding to
+     the next word boundary.  Similar code appears in compute_frame_size
+     and must be changed in tandem with this code.  */
+  local_fsize = (size + UNITS_PER_WORD - 1) & ~(UNITS_PER_WORD - 1);
+  if (local_fsize || frame_pointer_needed)
+    local_fsize += STARTING_FRAME_OFFSET;
 
   actual_fsize = compute_frame_size (size, &save_fregs);
 
   /* Compute a few things we will use often.  */
   tmpreg = gen_rtx_REG (word_mode, 1);
-  size_rtx = GEN_INT (actual_fsize);
 
   /* Save RP first.  The calling conventions manual states RP will
      always be stored into the caller's frame at sp - 20 or sp - 16
      depending on which ABI is in use.  */
-  if (regs_ever_live[2])
-    store_reg (2, TARGET_64BIT ? -16 : -20, STACK_POINTER_REGNUM);
+  if (df_regs_ever_live_p (2) || crtl->calls_eh_return)
+    {
+      store_reg (2, TARGET_64BIT ? -16 : -20, STACK_POINTER_REGNUM);
+      rp_saved = true;
+    }
+  else
+    rp_saved = false;
 
   /* Allocate the local frame and set up the frame pointer if needed.  */
   if (actual_fsize != 0)
@@ -3121,36 +3699,20 @@ hppa_expand_prologue ()
        {
          /* Copy the old frame pointer temporarily into %r1.  Set up the
             new stack pointer, then store away the saved old frame pointer
-            into the stack at sp+actual_fsize and at the same time update
-            the stack pointer by actual_fsize bytes.  Two versions, first
+            into the stack at sp and at the same time update the stack
+            pointer by actual_fsize bytes.  Two versions, first
             handles small (<8k) frames.  The second handles large (>=8k)
             frames.  */
-         emit_move_insn (tmpreg, frame_pointer_rtx);
-         FRP (emit_move_insn (frame_pointer_rtx, stack_pointer_rtx));
+         insn = emit_move_insn (tmpreg, frame_pointer_rtx);
+         if (DO_FRAME_NOTES)
+           RTX_FRAME_RELATED_P (insn) = 1;
+
+         insn = emit_move_insn (frame_pointer_rtx, stack_pointer_rtx);
+         if (DO_FRAME_NOTES)
+           RTX_FRAME_RELATED_P (insn) = 1;
+
          if (VAL_14_BITS_P (actual_fsize))
-           {
-             rtx insn = emit_insn (gen_post_store (stack_pointer_rtx, tmpreg,
-                                                   size_rtx));
-             if (DO_FRAME_NOTES)
-               {
-                 rtvec vec;
-                 RTX_FRAME_RELATED_P (insn) = 1;
-                 vec = gen_rtvec (2,
-                                  gen_rtx_SET (VOIDmode,
-                                               gen_rtx_MEM (word_mode,
-                                                            stack_pointer_rtx),
-                                               frame_pointer_rtx),
-                                  gen_rtx_SET (VOIDmode,
-                                               stack_pointer_rtx,
-                                               gen_rtx_PLUS (word_mode,
-                                                             stack_pointer_rtx,
-                                                             size_rtx)));
-                 REG_NOTES (insn)
-                   = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                                        gen_rtx_SEQUENCE (VOIDmode, vec),
-                                        REG_NOTES (insn));
-               }
-           }
+           store_reg_modify (STACK_POINTER_REGNUM, 1, actual_fsize);
          else
            {
              /* It is incorrect to store the saved frame pointer at *sp,
@@ -3159,39 +3721,40 @@ hppa_expand_prologue ()
                 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;
-             rtx delta = GEN_INT (adjust1);
-             rtx insn = emit_insn (gen_post_store (stack_pointer_rtx, tmpreg,
-                                                   delta));
-             if (DO_FRAME_NOTES)
-               {
-                 rtvec vec;
-                 RTX_FRAME_RELATED_P (insn) = 1;
-                 vec = gen_rtvec (2,
-                                  gen_rtx_SET (VOIDmode,
-                                               gen_rtx_MEM (word_mode,
-                                                            stack_pointer_rtx),
-                                               frame_pointer_rtx),
-                                  gen_rtx_SET (VOIDmode,
-                                               stack_pointer_rtx,
-                                               gen_rtx_PLUS (word_mode,
-                                                             stack_pointer_rtx,
-                                                             delta)));
-                 REG_NOTES (insn)
-                   = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                                        gen_rtx_SEQUENCE (VOIDmode, vec),
-                                        REG_NOTES (insn));
-               }
+             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,
-                             adjust2);
+                             adjust2, 1);
+           }
+
+         /* We set SAVE_SP in frames that need a frame pointer.  Thus,
+            we need to store the previous stack pointer (frame pointer)
+            into the frame marker on targets that use the HP unwind
+            library.  This allows the HP unwind library to be used to
+            unwind GCC frames.  However, we are not fully compatible
+            with the HP library because our frame layout differs from
+            that specified in the HP runtime specification.
+
+            We don't want a frame note on this instruction as the frame
+            marker moves during dynamic stack allocation.
+
+            This instruction also serves as a blockage to prevent
+            register spills from being scheduled before the stack
+            pointer is raised.  This is necessary as we store
+            registers using the frame pointer as a base register,
+            and the frame pointer is set before sp is raised.  */
+         if (TARGET_HPUX_UNWIND_LIBRARY)
+           {
+             rtx addr = gen_rtx_PLUS (word_mode, stack_pointer_rtx,
+                                      GEN_INT (TARGET_64BIT ? -8 : -4));
+
+             emit_move_insn (gen_rtx_MEM (word_mode, addr),
+                             frame_pointer_rtx);
            }
-         /* Prevent register spills from being scheduled before the
-            stack pointer is raised.  Necessary as we will be storing
-            registers using the frame pointer as a base register, and
-            we happen to set fp before raising sp.  */
-         emit_insn (gen_blockage ());
+         else
+           emit_insn (gen_blockage ());
        }
       /* no frame pointer needed.  */
       else
@@ -3206,7 +3769,7 @@ hppa_expand_prologue ()
             bytes.  */
          else
            set_reg_plus_d (STACK_POINTER_REGNUM, STACK_POINTER_REGNUM,
-                           actual_fsize);
+                           actual_fsize, 1);
        }
     }
 
@@ -3216,8 +3779,28 @@ hppa_expand_prologue ()
      was done earlier.  */
   if (frame_pointer_needed)
     {
-      for (i = 18, offset = local_fsize; i >= 4; i--)
-       if (regs_ever_live[i] && ! call_used_regs[i])
+      offset = local_fsize;
+
+      /* Saving the EH return data registers in the frame is the simplest
+        way to get the frame unwind information emitted.  We put them
+        just before the general registers.  */
+      if (DO_FRAME_NOTES && crtl->calls_eh_return)
+       {
+         unsigned int i, regno;
+
+         for (i = 0; ; ++i)
+           {
+             regno = EH_RETURN_DATA_REGNO (i);
+             if (regno == INVALID_REGNUM)
+               break;
+
+             store_reg (regno, offset, FRAME_POINTER_REGNUM);
+             offset += UNITS_PER_WORD;
+           }
+       }
+
+      for (i = 18; i >= 4; i--)
+       if (df_regs_ever_live_p (i) && ! call_used_regs[i])
          {
            store_reg (i, offset, FRAME_POINTER_REGNUM);
            offset += UNITS_PER_WORD;
@@ -3229,30 +3812,54 @@ hppa_expand_prologue ()
   /* No frame pointer needed.  */
   else
     {
-      for (i = 18, offset = local_fsize - actual_fsize; i >= 3; i--)
-       if (regs_ever_live[i] && ! call_used_regs[i])
-         {
-           /* If merge_sp_adjust_with_store is nonzero, then we can
-              optimize the first GR save.  */
-           if (merge_sp_adjust_with_store)
-             {
-               rtx delta = GEN_INT (-offset);
-               merge_sp_adjust_with_store = 0;
-               FRP (emit_insn (gen_post_store (stack_pointer_rtx,
-                                               gen_rtx_REG (word_mode, i),
-                                               delta)));
-             }
-           else
-             store_reg (i, offset, STACK_POINTER_REGNUM);
-           offset += UNITS_PER_WORD;
-           gr_saved++;
+      offset = local_fsize - actual_fsize;
+
+      /* Saving the EH return data registers in the frame is the simplest
+         way to get the frame unwind information emitted.  */
+      if (DO_FRAME_NOTES && crtl->calls_eh_return)
+       {
+         unsigned int i, regno;
+
+         for (i = 0; ; ++i)
+           {
+             regno = EH_RETURN_DATA_REGNO (i);
+             if (regno == INVALID_REGNUM)
+               break;
+
+             /* If merge_sp_adjust_with_store is nonzero, then we can
+                optimize the first save.  */
+             if (merge_sp_adjust_with_store)
+               {
+                 store_reg_modify (STACK_POINTER_REGNUM, regno, -offset);
+                 merge_sp_adjust_with_store = 0;
+               }
+             else
+               store_reg (regno, offset, STACK_POINTER_REGNUM);
+             offset += UNITS_PER_WORD;
+           }
+       }
+
+      for (i = 18; i >= 3; i--)
+       if (df_regs_ever_live_p (i) && ! call_used_regs[i])
+         {
+           /* If merge_sp_adjust_with_store is nonzero, then we can
+              optimize the first GR save.  */
+           if (merge_sp_adjust_with_store)
+             {
+               store_reg_modify (STACK_POINTER_REGNUM, i, -offset);
+               merge_sp_adjust_with_store = 0;
+             }
+           else
+             store_reg (i, offset, STACK_POINTER_REGNUM);
+           offset += UNITS_PER_WORD;
+           gr_saved++;
          }
 
       /* If we wanted to merge the SP adjustment with a GR save, but we never
         did any GR saves, then just emit the adjustment here.  */
       if (merge_sp_adjust_with_store)
        set_reg_plus_d (STACK_POINTER_REGNUM, STACK_POINTER_REGNUM,
-                       actual_fsize);
+                       actual_fsize, 1);
     }
 
   /* The hppa calling conventions say that %r19, the pic offset
@@ -3260,9 +3867,17 @@ hppa_expand_prologue ()
      when generating PIC code.  FIXME:  What is the correct thing
      to do for functions which make no calls and allocate no
      frame?  Do we need to allocate a frame, or can we just omit
-     the save?   For now we'll just omit the save.  */
+     the save?   For now we'll just omit the save.
+     
+     We don't want a note on this insn as the frame marker can
+     move if there is a dynamic stack allocation.  */
   if (flag_pic && actual_fsize != 0 && !TARGET_64BIT)
-    store_reg (PIC_OFFSET_TABLE_REGNUM, -32, STACK_POINTER_REGNUM);
+    {
+      rtx addr = gen_rtx_PLUS (word_mode, stack_pointer_rtx, GEN_INT (-32));
+
+      emit_move_insn (gen_rtx_MEM (word_mode, addr), pic_offset_table_rtx);
+
+    }
 
   /* Align pointer properly (doubleword boundary).  */
   offset = (offset + 7) & ~7;
@@ -3270,18 +3885,26 @@ hppa_expand_prologue ()
   /* Floating point register store.  */
   if (save_fregs)
     {
+      rtx base;
+
       /* First get the frame or stack pointer to the start of the FP register
         save area.  */
       if (frame_pointer_needed)
-       set_reg_plus_d (1, FRAME_POINTER_REGNUM, offset);
+       {
+         set_reg_plus_d (1, FRAME_POINTER_REGNUM, offset, 0);
+         base = frame_pointer_rtx;
+       }
       else
-       set_reg_plus_d (1, STACK_POINTER_REGNUM, offset);
+       {
+         set_reg_plus_d (1, STACK_POINTER_REGNUM, offset, 0);
+         base = stack_pointer_rtx;
+       }
 
       /* Now actually save the FP registers.  */
       for (i = FP_SAVED_REG_LAST; i >= FP_SAVED_REG_FIRST; i -= FP_REG_STEP)
        {
-         if (regs_ever_live[i]
-             || (! TARGET_64BIT && regs_ever_live[i + 1]))
+         if (df_regs_ever_live_p (i)
+             || (! TARGET_64BIT && df_regs_ever_live_p (i + 1)))
            {
              rtx addr, insn, reg;
              addr = gen_rtx_MEM (DFmode, gen_rtx_POST_INC (DFmode, tmpreg));
@@ -3290,14 +3913,35 @@ hppa_expand_prologue ()
              if (DO_FRAME_NOTES)
                {
                  RTX_FRAME_RELATED_P (insn) = 1;
-                 REG_NOTES (insn)
-                   = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                       gen_rtx_SET (VOIDmode,
-                         gen_rtx_MEM (DFmode,
-                                      plus_constant (stack_pointer_rtx,
-                                                     offset)),
-                         reg),
-                       REG_NOTES (insn));
+                 if (TARGET_64BIT)
+                   {
+                     rtx mem = gen_rtx_MEM (DFmode,
+                                            plus_constant (base, offset));
+                     REG_NOTES (insn)
+                       = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+                                            gen_rtx_SET (VOIDmode, mem, reg),
+                                            REG_NOTES (insn));
+                   }
+                 else
+                   {
+                     rtx meml = gen_rtx_MEM (SFmode,
+                                             plus_constant (base, offset));
+                     rtx memr = gen_rtx_MEM (SFmode,
+                                             plus_constant (base, offset + 4));
+                     rtx regl = gen_rtx_REG (SFmode, i);
+                     rtx regr = gen_rtx_REG (SFmode, i + 1);
+                     rtx setl = gen_rtx_SET (VOIDmode, meml, regl);
+                     rtx setr = gen_rtx_SET (VOIDmode, memr, regr);
+                     rtvec vec;
+
+                     RTX_FRAME_RELATED_P (setl) = 1;
+                     RTX_FRAME_RELATED_P (setr) = 1;
+                     vec = gen_rtvec (2, setl, setr);
+                     REG_NOTES (insn)
+                       = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+                                            gen_rtx_SEQUENCE (VOIDmode, vec),
+                                            REG_NOTES (insn));
+                   }
                }
              offset += GET_MODE_SIZE (DFmode);
              fr_saved++;
@@ -3310,45 +3954,57 @@ hppa_expand_prologue ()
    Handle case where DISP > 8k by using the add_high_const patterns.  */
 
 static void
-load_reg (reg, disp, base)
-     int reg, disp, 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);
 }
 
-/* Return the last nonnote insn emitted in current sequence or current
-   function.  This routine looks inside SEQUENCEs.  */
+/* Update the total code bytes output to the text section.  */
 
-static rtx
-get_last_nonnote_insn ()
+static void
+update_total_code_bytes (unsigned int nbytes)
 {
-  rtx insn = get_last_insn ();
-
-  while (insn)
+  if ((TARGET_PORTABLE_RUNTIME || !TARGET_GAS || !TARGET_SOM)
+      && !IN_NAMED_SECTION_P (cfun->decl))
     {
-      insn = previous_insn (insn);
-      if (insn == 0 || GET_CODE (insn) != NOTE)
-       break;
-    }
+      unsigned int old_total = total_code_bytes;
 
-  return insn;
+      total_code_bytes += nbytes;
+
+      /* Be prepared to handle overflows.  */
+      if (old_total > total_code_bytes)
+        total_code_bytes = UINT_MAX;
+    }
 }
 
 /* This function generates the assembly code for function exit.
@@ -3360,13 +4016,12 @@ get_last_nonnote_insn ()
    adjustments before returning.  */
 
 static void
-pa_output_function_epilogue (file, size)
-     FILE *file;
-     HOST_WIDE_INT size ATTRIBUTE_UNUSED;
+pa_output_function_epilogue (FILE *file, HOST_WIDE_INT size ATTRIBUTE_UNUSED)
 {
-  int last_address = 0;
   rtx insn = get_last_insn ();
 
+  last_address = 0;
+
   /* hppa_expand_epilogue does the dirty work now.  We just need
      to output the assembler directives which denote the end
      of a function.
@@ -3394,38 +4049,39 @@ pa_output_function_epilogue (file, size)
 
   fputs ("\t.EXIT\n\t.PROCEND\n", file);
 
-  /* Finally, update the total number of code bytes output so far.  */
-  if ((TARGET_PORTABLE_RUNTIME || !TARGET_GAS || !TARGET_SOM)
-      && !flag_function_sections)
+  if (TARGET_SOM && TARGET_GAS)
     {
-      if (INSN_ADDRESSES_SET_P ())
-       {
-         unsigned long old_total = total_code_bytes;
-
-         insn = get_last_nonnote_insn ();
-         last_address += INSN_ADDRESSES (INSN_UID (insn));
-         if (INSN_P (insn))
-           last_address += insn_default_length (insn);
-
-         total_code_bytes += last_address;
-         total_code_bytes += FUNCTION_BOUNDARY / BITS_PER_UNIT;
+      /* 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.  */
+      in_section = NULL;
+      cfun->machine->in_nsubspa = 2;
+    }
 
-         /* Be prepared to handle overflows.  */
-         if (old_total > total_code_bytes)
-           total_code_bytes = -1;
-       }
-      else
-       total_code_bytes = -1;
+  if (INSN_ADDRESSES_SET_P ())
+    {
+      insn = get_last_nonnote_insn ();
+      last_address += INSN_ADDRESSES (INSN_UID (insn));
+      if (INSN_P (insn))
+       last_address += insn_default_length (insn);
+      last_address = ((last_address + FUNCTION_BOUNDARY / BITS_PER_UNIT - 1)
+                     & ~(FUNCTION_BOUNDARY / BITS_PER_UNIT - 1));
     }
+  else
+    last_address = UINT_MAX;
+
+  /* Finally, update the total number of code bytes output so far.  */
+  update_total_code_bytes (last_address);
 }
 
 void
-hppa_expand_epilogue ()
+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);
@@ -3433,7 +4089,7 @@ hppa_expand_epilogue ()
   /* Try to restore RP early to avoid load/use interlocks when
      RP gets used in the return (bv) instruction.  This appears to still
      be necessary even when we schedule the prologue and epilogue.  */
-  if (regs_ever_live [2])
+  if (rp_saved)
     {
       ret_off = TARGET_64BIT ? -16 : -20;
       if (frame_pointer_needed)
@@ -3455,8 +4111,27 @@ hppa_expand_epilogue ()
   /* General register restores.  */
   if (frame_pointer_needed)
     {
-      for (i = 18, offset = local_fsize; i >= 4; i--)
-       if (regs_ever_live[i] && ! call_used_regs[i])
+      offset = local_fsize;
+
+      /* If the current function calls __builtin_eh_return, then we need
+         to restore the saved EH data registers.  */
+      if (DO_FRAME_NOTES && crtl->calls_eh_return)
+       {
+         unsigned int i, regno;
+
+         for (i = 0; ; ++i)
+           {
+             regno = EH_RETURN_DATA_REGNO (i);
+             if (regno == INVALID_REGNUM)
+               break;
+
+             load_reg (regno, offset, FRAME_POINTER_REGNUM);
+             offset += UNITS_PER_WORD;
+           }
+       }
+
+      for (i = 18; i >= 4; i--)
+       if (df_regs_ever_live_p (i) && ! call_used_regs[i])
          {
            load_reg (i, offset, FRAME_POINTER_REGNUM);
            offset += UNITS_PER_WORD;
@@ -3464,9 +4139,36 @@ hppa_expand_epilogue ()
     }
   else
     {
-      for (i = 18, offset = local_fsize - actual_fsize; i >= 3; i--)
+      offset = local_fsize - actual_fsize;
+
+      /* If the current function calls __builtin_eh_return, then we need
+         to restore the saved EH data registers.  */
+      if (DO_FRAME_NOTES && crtl->calls_eh_return)
+       {
+         unsigned int i, regno;
+
+         for (i = 0; ; ++i)
+           {
+             regno = EH_RETURN_DATA_REGNO (i);
+             if (regno == INVALID_REGNUM)
+               break;
+
+             /* Only for the first load.
+                merge_sp_adjust_with_load holds the register load
+                with which we will merge the sp adjustment.  */
+             if (merge_sp_adjust_with_load == 0
+                 && local_fsize == 0
+                 && VAL_14_BITS_P (-actual_fsize))
+               merge_sp_adjust_with_load = regno;
+             else
+               load_reg (regno, offset, STACK_POINTER_REGNUM);
+             offset += UNITS_PER_WORD;
+           }
+       }
+
+      for (i = 18; i >= 3; i--)
        {
-         if (regs_ever_live[i] && ! call_used_regs[i])
+         if (df_regs_ever_live_p (i) && ! call_used_regs[i])
            {
              /* Only for the first load.
                 merge_sp_adjust_with_load holds the register load
@@ -3490,14 +4192,14 @@ hppa_expand_epilogue ()
     {
       /* Adjust the register to index off of.  */
       if (frame_pointer_needed)
-       set_reg_plus_d (1, FRAME_POINTER_REGNUM, offset);
+       set_reg_plus_d (1, FRAME_POINTER_REGNUM, offset, 0);
       else
-       set_reg_plus_d (1, STACK_POINTER_REGNUM, offset);
+       set_reg_plus_d (1, STACK_POINTER_REGNUM, offset, 0);
 
       /* Actually do the restores now.  */
       for (i = FP_SAVED_REG_LAST; i >= FP_SAVED_REG_FIRST; i -= FP_REG_STEP)
-       if (regs_ever_live[i]
-           || (! TARGET_64BIT && regs_ever_live[i + 1]))
+       if (df_regs_ever_live_p (i)
+           || (! TARGET_64BIT && df_regs_ever_live_p (i + 1)))
          {
            rtx src = gen_rtx_MEM (DFmode, gen_rtx_POST_INC (DFmode, tmpreg));
            rtx dest = gen_rtx_REG (DFmode, i);
@@ -3517,63 +4219,89 @@ hppa_expand_epilogue ()
   if (frame_pointer_needed)
     {
       rtx delta = GEN_INT (-64);
-      rtx insn;
-      set_reg_plus_d (STACK_POINTER_REGNUM, FRAME_POINTER_REGNUM, 64);
-      insn = emit_insn (gen_pre_load (frame_pointer_rtx, stack_pointer_rtx,
-                                     delta));
-      if (DO_FRAME_NOTES)
-       {
-         RTX_FRAME_RELATED_P (insn) = 1;
-         REG_NOTES (insn)
-           = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-               gen_rtx_SET (VOIDmode, stack_pointer_rtx,
-                            gen_rtx_PLUS (word_mode, stack_pointer_rtx,
-                                          delta)),
-                REG_NOTES (insn));
-       }
+
+      set_reg_plus_d (STACK_POINTER_REGNUM, FRAME_POINTER_REGNUM, 64, 0);
+      emit_insn (gen_pre_load (frame_pointer_rtx, stack_pointer_rtx, delta));
     }
   /* If we were deferring a callee register restore, do it now.  */
   else if (merge_sp_adjust_with_load)
     {
       rtx delta = GEN_INT (-actual_fsize);
       rtx dest = gen_rtx_REG (word_mode, merge_sp_adjust_with_load);
-      rtx insn = emit_insn (gen_pre_load (dest, stack_pointer_rtx, delta));
-      if (DO_FRAME_NOTES)
-       {
-         RTX_FRAME_RELATED_P (insn) = 1;
-         REG_NOTES (insn)
-           = gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-               gen_rtx_SET (VOIDmode, stack_pointer_rtx,
-                            gen_rtx_PLUS (word_mode, stack_pointer_rtx,
-                                          delta)),
-                REG_NOTES (insn));
-       }
+
+      emit_insn (gen_pre_load (dest, stack_pointer_rtx, delta));
     }
   else if (actual_fsize != 0)
-    set_reg_plus_d (STACK_POINTER_REGNUM, STACK_POINTER_REGNUM, - actual_fsize);
+    set_reg_plus_d (STACK_POINTER_REGNUM, STACK_POINTER_REGNUM,
+                   - actual_fsize, 0);
 
   /* If we haven't restored %r2 yet (no frame pointer, and a stack
      frame greater than 8k), do so now.  */
   if (ret_off != 0)
     load_reg (2, ret_off, STACK_POINTER_REGNUM);
+
+  if (DO_FRAME_NOTES && crtl->calls_eh_return)
+    {
+      rtx sa = EH_RETURN_STACKADJ_RTX;
+
+      emit_insn (gen_blockage ());
+      emit_insn (TARGET_64BIT
+                ? gen_subdi3 (stack_pointer_rtx, stack_pointer_rtx, sa)
+                : gen_subsi3 (stack_pointer_rtx, stack_pointer_rtx, sa));
+    }
 }
 
 rtx
-hppa_pic_save_rtx ()
+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
+
+
+/* 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;
+
+  switch_to_section (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 (label_no)
-     int label_no;
+hppa_profile_hook (int label_no)
 {
+  /* We use SImode for the address of the function in both 32 and
+     64-bit code to avoid having to provide DImode versions of the
+     lcla2 and load_offset_label_address insn patterns.  */
+  rtx reg = gen_reg_rtx (SImode);
+  rtx label_rtx = gen_label_rtx ();
   rtx begin_label_rtx, call_insn;
   char begin_label_name[16];
 
   ASM_GENERATE_INTERNAL_LABEL (begin_label_name, FUNC_BEGIN_PROLOG_LABEL,
                               label_no);
-  begin_label_rtx = gen_rtx_SYMBOL_REF (Pmode, ggc_strdup (begin_label_name));
+  begin_label_rtx = gen_rtx_SYMBOL_REF (SImode, ggc_strdup (begin_label_name));
 
   if (TARGET_64BIT)
     emit_move_insn (arg_pointer_rtx,
@@ -3582,11 +4310,24 @@ hppa_profile_hook (label_no)
 
   emit_move_insn (gen_rtx_REG (word_mode, 26), gen_rtx_REG (word_mode, 2));
 
-#ifndef NO_PROFILE_COUNTERS
+  /* The address of the function is loaded into %r25 with an instruction-
+     relative sequence that avoids the use of relocations.  The sequence
+     is split so that the load_offset_label_address instruction can
+     occupy the delay slot of the call to _mcount.  */
+  if (TARGET_PA_20)
+    emit_insn (gen_lcla2 (reg, label_rtx));
+  else
+    emit_insn (gen_lcla1 (reg, label_rtx));
+
+  emit_insn (gen_load_offset_label_address (gen_rtx_REG (SImode, 25), 
+                                           reg, begin_label_rtx, label_rtx));
+
+#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));
 
@@ -3594,35 +4335,31 @@ hppa_profile_hook (label_no)
     r24 = gen_rtx_REG (Pmode, 24);
     emit_move_insn (r24, addr);
 
-    /* %r25 is set from within the output pattern.  */
     call_insn =
-      emit_call_insn (gen_call_profiler (gen_rtx_SYMBOL_REF (Pmode, "_mcount"),
-                                        GEN_INT (TARGET_64BIT ? 24 : 12),
-                                        begin_label_rtx));
+      emit_call_insn (gen_call (gen_rtx_MEM (Pmode, 
+                                            gen_rtx_SYMBOL_REF (Pmode, 
+                                                                "_mcount")),
+                               GEN_INT (TARGET_64BIT ? 24 : 12)));
 
     use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), r24);
   }
 #else
-    /* %r25 is set from within the output pattern.  */
+
   call_insn =
-    emit_call_insn (gen_call_profiler (gen_rtx_SYMBOL_REF (Pmode, "_mcount"),
-                                      GEN_INT (TARGET_64BIT ? 16 : 8),
-                                      begin_label_rtx));
+    emit_call_insn (gen_call (gen_rtx_MEM (Pmode, 
+                                          gen_rtx_SYMBOL_REF (Pmode, 
+                                                              "_mcount")),
+                             GEN_INT (TARGET_64BIT ? 16 : 8)));
+
 #endif
 
+  use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), gen_rtx_REG (SImode, 25));
+  use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), gen_rtx_REG (SImode, 26));
+
   /* Indicate the _mcount call cannot throw, nor will it execute a
      non-local goto.  */
   REG_NOTES (call_insn)
     = gen_rtx_EXPR_LIST (REG_EH_REGION, constm1_rtx, REG_NOTES (call_insn));
-
-  if (flag_pic)
-    {
-      use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), pic_offset_table_rtx);
-      if (TARGET_64BIT)
-       use_reg (&CALL_INSN_FUNCTION_USAGE (call_insn), arg_pointer_rtx);
-
-      emit_move_insn (pic_offset_table_rtx, hppa_pic_save_rtx ());
-    }
 }
 
 /* Fetch the return address for the frame COUNT steps up from
@@ -3652,9 +4389,7 @@ hppa_profile_hook (label_no)
    return location is in a shared library.  */
 
 rtx
-return_addr_rtx (count, frameaddr)
-     int count;
-     rtx frameaddr;
+return_addr_rtx (int count, rtx frameaddr)
 {
   rtx label;
   rtx rp;
@@ -3702,8 +4437,10 @@ return_addr_rtx (count, frameaddr)
                 GEN_INT (0x00011820), NE, NULL_RTX, SImode, 1);
   emit_jump_insn (gen_bne (label));
 
+  /* 0xe0400002 must be specified as -532676606 so that it won't be
+     rejected as an invalid immediate operand on 64-bit hosts.  */
   emit_cmp_insn (gen_rtx_MEM (SImode, plus_constant (ins, 12)),
-                GEN_INT (0xe0400002), NE, NULL_RTX, SImode, 1);
+                GEN_INT (-532676606), NE, NULL_RTX, SImode, 1);
 
   /* If there is no export stub then just use the value saved from
      the return pointer register.  */
@@ -3725,26 +4462,8 @@ return_addr_rtx (count, frameaddr)
   return saved_rp;
 }
 
-/* This is only valid once reload has completed because it depends on
-   knowing exactly how much (if any) frame there is and...
-
-   It's only valid if there is no frame marker to de-allocate and...
-
-   It's only valid if %r2 hasn't been saved into the caller's frame
-   (we're not profiling and %r2 isn't live anywhere).  */
-int
-hppa_can_use_return_insn_p ()
-{
-  return (reload_completed
-         && (compute_frame_size (get_frame_size (), 0) ? 0 : 1)
-         && ! regs_ever_live[2]
-         && ! frame_pointer_needed);
-}
-
 void
-emit_bcond_fp (code, operand0)
-     enum rtx_code code;
-     rtx operand0;
+emit_bcond_fp (enum rtx_code code, rtx operand0)
 {
   emit_jump_insn (gen_rtx_SET (VOIDmode, pc_rtx,
                               gen_rtx_IF_THEN_ELSE (VOIDmode,
@@ -3758,9 +4477,7 @@ emit_bcond_fp (code, operand0)
 }
 
 rtx
-gen_cmp_fp (code, operand0, operand1)
-     enum rtx_code code;
-     rtx operand0, operand1;
+gen_cmp_fp (enum rtx_code code, rtx operand0, rtx operand1)
 {
   return gen_rtx_SET (VOIDmode, gen_rtx_REG (CCFPmode, 0),
                      gen_rtx_fmt_ee (code, CCFPmode, operand0, operand1));
@@ -3770,16 +4487,13 @@ gen_cmp_fp (code, operand0, operand1)
    a dependency LINK or INSN on DEP_INSN.  COST is the current cost.  */
 
 static int
-pa_adjust_cost (insn, link, dep_insn, cost)
-     rtx insn;
-     rtx link;
-     rtx dep_insn;
-     int cost;
+pa_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
 {
   enum attr_type attr_type;
 
-  /* Don't adjust costs for a pa8000 chip.  */
-  if (pa_cpu >= PROCESSOR_8000)
+  /* Don't adjust costs for a pa8000 chip, also do not adjust any
+     true dependencies as they are described with bypasses now.  */
+  if (pa_cpu >= PROCESSOR_8000 || REG_NOTE_KIND (link) == 0)
     return cost;
 
   if (! recog_memoized (insn))
@@ -3787,61 +4501,9 @@ pa_adjust_cost (insn, link, dep_insn, cost)
 
   attr_type = get_attr_type (insn);
 
-  if (REG_NOTE_KIND (link) == 0)
-    {
-      /* Data dependency; DEP_INSN writes a register that INSN reads some
-        cycles later.  */
-
-      if (attr_type == TYPE_FPSTORE)
-       {
-         rtx pat = PATTERN (insn);
-         rtx dep_pat = PATTERN (dep_insn);
-         if (GET_CODE (pat) == PARALLEL)
-           {
-             /* This happens for the fstXs,mb patterns.  */
-             pat = XVECEXP (pat, 0, 0);
-           }
-         if (GET_CODE (pat) != SET || GET_CODE (dep_pat) != SET)
-           /* If this happens, we have to extend this to schedule
-              optimally.  Return 0 for now.  */
-         return 0;
-
-         if (rtx_equal_p (SET_DEST (dep_pat), SET_SRC (pat)))
-           {
-             if (! recog_memoized (dep_insn))
-               return 0;
-             /* DEP_INSN is writing its result to the register
-                being stored in the fpstore INSN.  */
-             switch (get_attr_type (dep_insn))
-               {
-               case TYPE_FPLOAD:
-                 /* This cost 3 cycles, not 2 as the md says for the
-                    700 and 7100.  */
-                 return cost + 1;
-
-               case TYPE_FPALU:
-               case TYPE_FPMULSGL:
-               case TYPE_FPMULDBL:
-               case TYPE_FPDIVSGL:
-               case TYPE_FPDIVDBL:
-               case TYPE_FPSQRTSGL:
-               case TYPE_FPSQRTDBL:
-                 /* In these important cases, we save one cycle compared to
-                    when flop instruction feed each other.  */
-                 return cost - 1;
-
-               default:
-                 return cost;
-               }
-           }
-       }
-
-      /* For other data dependencies, the default cost specified in the
-        md is correct.  */
-      return cost;
-    }
-  else if (REG_NOTE_KIND (link) == REG_DEP_ANTI)
+  switch (REG_NOTE_KIND (link))
     {
+    case REG_DEP_ANTI:
       /* Anti dependency; DEP_INSN reads a register that INSN writes some
         cycles later.  */
 
@@ -3876,7 +4538,7 @@ pa_adjust_cost (insn, link, dep_insn, cost)
                     preceding arithmetic operation has finished if
                     the target of the fpload is any of the sources
                     (or destination) of the arithmetic operation.  */
-                 return cost - 1;
+                 return insn_default_latency (dep_insn) - 1;
 
                default:
                  return 0;
@@ -3911,7 +4573,7 @@ pa_adjust_cost (insn, link, dep_insn, cost)
                     preceding divide or sqrt operation has finished if
                     the target of the ALU flop is any of the sources
                     (or destination) of the divide or sqrt operation.  */
-                 return cost - 2;
+                 return insn_default_latency (dep_insn) - 2;
 
                default:
                  return 0;
@@ -3921,9 +4583,8 @@ pa_adjust_cost (insn, link, dep_insn, cost)
 
       /* For other anti dependencies, the cost is 0.  */
       return 0;
-    }
-  else if (REG_NOTE_KIND (link) == REG_DEP_OUTPUT)
-    {
+
+    case REG_DEP_OUTPUT:
       /* Output dependency; DEP_INSN writes a register that INSN writes some
         cycles later.  */
       if (attr_type == TYPE_FPLOAD)
@@ -3956,8 +4617,12 @@ pa_adjust_cost (insn, link, dep_insn, cost)
                  /* A fpload can't be issued until one cycle before a
                     preceding arithmetic operation has finished if
                     the target of the fpload is the destination of the
-                    arithmetic operation.  */
-                 return cost - 1;
+                    arithmetic operation. 
+
+                    Exception: For PA7100LC, PA7200 and PA7300, the cost
+                    is 3 cycles, unless they bundle together.   We also
+                    pay the penalty if the second insn is a fpload.  */
+                 return insn_default_latency (dep_insn) - 1;
 
                default:
                  return 0;
@@ -3992,7 +4657,7 @@ pa_adjust_cost (insn, link, dep_insn, cost)
                     preceding divide or sqrt operation has finished if
                     the target of the ALU flop is also the target of
                     the divide or sqrt operation.  */
-                 return cost - 2;
+                 return insn_default_latency (dep_insn) - 2;
 
                default:
                  return 0;
@@ -4002,17 +4667,16 @@ pa_adjust_cost (insn, link, dep_insn, cost)
 
       /* For other output dependencies, the cost is 0.  */
       return 0;
+
+    default:
+      gcc_unreachable ();
     }
-  else
-    abort ();
 }
 
 /* Adjust scheduling priorities.  We use this to try and keep addil
    and the next use of %r1 close together.  */
 static int
-pa_adjust_priority (insn, priority)
-     rtx insn;
-     int priority;
+pa_adjust_priority (rtx insn, int priority)
 {
   rtx set = single_set (insn);
   rtx src, dest;
@@ -4044,7 +4708,7 @@ pa_adjust_priority (insn, priority)
    The 7XXX processors can issue two insns at a time.
    The 8000 can issue 4 insns at a time.  */
 static int
-pa_issue_rate ()
+pa_issue_rate (void)
 {
   switch (pa_cpu)
     {
@@ -4052,10 +4716,11 @@ pa_issue_rate ()
     case PROCESSOR_7100:       return 2;
     case PROCESSOR_7100LC:     return 2;
     case PROCESSOR_7200:       return 2;
+    case PROCESSOR_7300:       return 2;
     case PROCESSOR_8000:       return 4;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 
@@ -4071,31 +4736,15 @@ pa_issue_rate ()
    Also compute the length of an inline block move here as it is too
    complicated to express as a length attribute in pa.md.  */
 int
-pa_adjust_insn_length (insn, length)
-    rtx insn;
-    int length;
+pa_adjust_insn_length (rtx insn, int length)
 {
   rtx pat = PATTERN (insn);
 
-  /* Call insns which are *not* indirect and have unfilled delay slots.  */
-  if (GET_CODE (insn) == CALL_INSN)
-    {
-
-      if (GET_CODE (XVECEXP (pat, 0, 0)) == CALL
-         && GET_CODE (XEXP (XEXP (XVECEXP (pat, 0, 0), 0), 0)) == SYMBOL_REF)
-       return 4;
-      else if (GET_CODE (XVECEXP (pat, 0, 0)) == SET
-              && GET_CODE (XEXP (XEXP (XEXP (XVECEXP (pat, 0, 0), 1), 0), 0))
-                 == SYMBOL_REF)
-       return 4;
-      else
-       return 0;
-    }
-  /* Jumps inside switch tables which have unfilled delay slots
-     also need adjustment.  */
-  else if (GET_CODE (insn) == JUMP_INSN
-          && simplejump_p (insn)
-          && GET_MODE (insn) == SImode)
+  /* Jumps inside switch tables which have unfilled delay slots need
+     adjustment.  */
+  if (GET_CODE (insn) == JUMP_INSN
+      && GET_CODE (pat) == PARALLEL
+      && get_attr_type (insn) == TYPE_BTABLE_BRANCH)
     return 4;
   /* Millicode insn with an unfilled delay slot.  */
   else if (GET_CODE (insn) == INSN
@@ -4112,7 +4761,15 @@ 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) - 4;
+    return compute_movmem_length (insn) - 4;
+  /* Block clear pattern.  */
+  else if (GET_CODE (insn) == INSN
+          && GET_CODE (pat) == PARALLEL
+          && GET_CODE (XVECEXP (pat, 0, 0)) == SET
+          && 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_clrmem_length (insn) - 4;
   /* Conditional branch with an unfilled delay slot.  */
   else if (GET_CODE (insn) == JUMP_INSN && ! simplejump_p (insn))
     {
@@ -4146,10 +4803,7 @@ pa_adjust_insn_length (insn, length)
    For `%' followed by punctuation, CODE is the punctuation and X is null.  */
 
 void
-print_operand (file, x, code)
-     FILE *file;
-     rtx x;
-     int code;
+print_operand (FILE *file, rtx x, int code)
 {
   switch (code)
     {
@@ -4159,7 +4813,7 @@ print_operand (file, x, code)
        fputs ("\n\tnop", file);
       return;
     case '*':
-      /* Output an nullification completer if there's nothing for the */
+      /* Output a nullification completer if there's nothing for the */
       /* delay slot or nullification is requested.  */
       if (dbr_sequence_length () == 0 ||
          (final_sequence &&
@@ -4229,7 +4883,7 @@ print_operand (file, x, code)
        case LTU:
          fputs ("<<", file);  break;
        default:
-         abort ();
+         gcc_unreachable ();
        }
       return;
     case 'N':                  /* Condition, (N)egated */
@@ -4256,11 +4910,14 @@ print_operand (file, x, code)
        case LTU:
          fputs (">>=", file);  break;
        default:
-         abort ();
+         gcc_unreachable ();
        }
       return;
     /* For floating point comparisons.  Note that the output
-       predicates are the complement of the desired mode.  */
+       predicates are the complement of the desired mode.  The
+       conditions for GT, GE, LT, LE and LTGT cause an invalid
+       operation exception if the result is unordered and this
+       exception is enabled in the floating-point status register.  */
     case 'Y':
       switch (GET_CODE (x))
        {
@@ -4279,21 +4936,21 @@ print_operand (file, x, code)
        case LTGT:
          fputs ("!<>", file);  break;
        case UNLE:
-         fputs (">", file);  break;
+         fputs ("!?<=", file);  break;
        case UNLT:
-         fputs (">=", file);  break;
+         fputs ("!?<", file);  break;
        case UNGE:
-         fputs ("<", file);  break;
+         fputs ("!?>=", file);  break;
        case UNGT:
-         fputs ("<=", file);  break;
+         fputs ("!?>", file);  break;
        case UNEQ:
-         fputs ("<>", file);  break;
+         fputs ("!?=", file);  break;
        case UNORDERED:
-         fputs ("<=>", file);  break;
+         fputs ("!?", file);  break;
        case ORDERED:
-         fputs ("!<=>", file);  break;
+         fputs ("?", file);  break;
        default:
-         abort ();
+         gcc_unreachable ();
        }
       return;
     case 'S':                  /* Condition, operands are (S)wapped.  */
@@ -4320,7 +4977,7 @@ print_operand (file, x, code)
        case LTU:
          fputs (">>", file);  break;
        default:
-         abort ();
+         gcc_unreachable ();
        }
       return;
     case 'B':                  /* Condition, (B)oth swapped and negate.  */
@@ -4347,51 +5004,33 @@ print_operand (file, x, code)
        case LTU:
          fputs ("<<=", file);  break;
        default:
-         abort ();
+         gcc_unreachable ();
        }
       return;
     case 'k':
-      if (GET_CODE (x) == CONST_INT)
-       {
-         fprintf (file, HOST_WIDE_INT_PRINT_DEC, ~INTVAL (x));
-         return;
-       }
-      abort ();
+      gcc_assert (GET_CODE (x) == CONST_INT);
+      fprintf (file, HOST_WIDE_INT_PRINT_DEC, ~INTVAL (x));
+      return;
     case 'Q':
-      if (GET_CODE (x) == CONST_INT)
-       {
-         fprintf (file, HOST_WIDE_INT_PRINT_DEC, 64 - (INTVAL (x) & 63));
-         return;
-       }
-      abort ();
+      gcc_assert (GET_CODE (x) == CONST_INT);
+      fprintf (file, HOST_WIDE_INT_PRINT_DEC, 64 - (INTVAL (x) & 63));
+      return;
     case 'L':
-      if (GET_CODE (x) == CONST_INT)
-       {
-         fprintf (file, HOST_WIDE_INT_PRINT_DEC, 32 - (INTVAL (x) & 31));
-         return;
-       }
-      abort ();
+      gcc_assert (GET_CODE (x) == CONST_INT);
+      fprintf (file, HOST_WIDE_INT_PRINT_DEC, 32 - (INTVAL (x) & 31));
+      return;
     case 'O':
-      if (GET_CODE (x) == CONST_INT && exact_log2 (INTVAL (x)) >= 0)
-       {
-         fprintf (file, "%d", exact_log2 (INTVAL (x)));
-         return;
-       }
-      abort ();
+      gcc_assert (GET_CODE (x) == CONST_INT && exact_log2 (INTVAL (x)) >= 0);
+      fprintf (file, "%d", exact_log2 (INTVAL (x)));
+      return;
     case 'p':
-      if (GET_CODE (x) == CONST_INT)
-       {
-         fprintf (file, HOST_WIDE_INT_PRINT_DEC, 63 - (INTVAL (x) & 63));
-         return;
-       }
-      abort ();
+      gcc_assert (GET_CODE (x) == CONST_INT);
+      fprintf (file, HOST_WIDE_INT_PRINT_DEC, 63 - (INTVAL (x) & 63));
+      return;
     case 'P':
-      if (GET_CODE (x) == CONST_INT)
-       {
-         fprintf (file, HOST_WIDE_INT_PRINT_DEC, 31 - (INTVAL (x) & 31));
-         return;
-       }
-      abort ();
+      gcc_assert (GET_CODE (x) == CONST_INT);
+      fprintf (file, HOST_WIDE_INT_PRINT_DEC, 31 - (INTVAL (x) & 31));
+      return;
     case 'I':
       if (GET_CODE (x) == CONST_INT)
        fputs ("i", file);
@@ -4415,8 +5054,14 @@ print_operand (file, x, code)
            fputs (",ma", file);
          break;
        case PLUS:
-         if (GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT
-             || GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT)
+         if (GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
+             && GET_CODE (XEXP (XEXP (x, 0), 1)) == REG)
+           {
+             if (ASSEMBLER_DIALECT == 0)
+               fputs ("x", file);
+           }
+         else if (GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT
+                  || GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT)
            {
              if (ASSEMBLER_DIALECT == 0)
                fputs ("x,s", file);
@@ -4460,7 +5105,7 @@ print_operand (file, x, code)
         addresses.  */
       break;
     default:
-      abort ();
+      gcc_unreachable ();
     }
   if (GET_CODE (x) == REG)
     {
@@ -4491,20 +5136,33 @@ print_operand (file, x, code)
           base = XEXP (XEXP (x, 0), 0);
          fprintf (file, "%d(%s)", size, reg_names [REGNO (base)]);
          break;
-       default:
-         if (GET_CODE (XEXP (x, 0)) == PLUS
-             && GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT)
+       case PLUS:
+         if (GET_CODE (XEXP (XEXP (x, 0), 0)) == MULT)
            fprintf (file, "%s(%s)",
                     reg_names [REGNO (XEXP (XEXP (XEXP (x, 0), 0), 0))],
                     reg_names [REGNO (XEXP (XEXP (x, 0), 1))]);
-         else if (GET_CODE (XEXP (x, 0)) == PLUS
-                  && GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT)
+         else if (GET_CODE (XEXP (XEXP (x, 0), 1)) == MULT)
            fprintf (file, "%s(%s)",
                     reg_names [REGNO (XEXP (XEXP (XEXP (x, 0), 1), 0))],
                     reg_names [REGNO (XEXP (XEXP (x, 0), 0))]);
+         else if (GET_CODE (XEXP (XEXP (x, 0), 0)) == REG
+                  && GET_CODE (XEXP (XEXP (x, 0), 1)) == REG)
+           {
+             /* Because the REG_POINTER flag can get lost during reload,
+                GO_IF_LEGITIMATE_ADDRESS canonicalizes the order of the
+                index and base registers in the combined move patterns.  */
+             rtx base = XEXP (XEXP (x, 0), 1);
+             rtx index = XEXP (XEXP (x, 0), 0);
+
+             fprintf (file, "%s(%s)",
+                      reg_names [REGNO (index)], reg_names [REGNO (base)]);
+           }
          else
            output_address (XEXP (x, 0));
          break;
+       default:
+         output_address (XEXP (x, 0));
+         break;
        }
     }
   else
@@ -4514,10 +5172,7 @@ print_operand (file, x, code)
 /* output a SYMBOL_REF or a CONST expression involving a SYMBOL_REF.  */
 
 void
-output_global_address (file, x, round_constant)
-     FILE *file;
-     rtx x;
-     int round_constant;
+output_global_address (FILE *file, rtx x, int round_constant)
 {
 
   /* Imagine  (high (const (plus ...))).  */
@@ -4525,10 +5180,10 @@ output_global_address (file, x, round_constant)
     x = XEXP (x, 0);
 
   if (GET_CODE (x) == SYMBOL_REF && read_only_operand (x, VOIDmode))
-    assemble_name (file, XSTR (x, 0));
+    output_addr_const (file, x);
   else if (GET_CODE (x) == SYMBOL_REF && !flag_pic)
     {
-      assemble_name (file, XSTR (x, 0));
+      output_addr_const (file, x);
       fputs ("-$global$", file);
     }
   else if (GET_CODE (x) == CONST)
@@ -4537,23 +5192,31 @@ output_global_address (file, x, round_constant)
       int offset = 0;          /* assembler wants -$global$ at end */
       rtx base = NULL_RTX;
 
-      if (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF)
+      switch (GET_CODE (XEXP (XEXP (x, 0), 0)))
        {
+       case SYMBOL_REF:
          base = XEXP (XEXP (x, 0), 0);
          output_addr_const (file, base);
+         break;
+       case CONST_INT:
+         offset = INTVAL (XEXP (XEXP (x, 0), 0));
+         break;
+       default:
+         gcc_unreachable ();
        }
-      else if (GET_CODE (XEXP (XEXP (x, 0), 0)) == CONST_INT)
-       offset = INTVAL (XEXP (XEXP (x, 0), 0));
-      else abort ();
 
-      if (GET_CODE (XEXP (XEXP (x, 0), 1)) == SYMBOL_REF)
+      switch (GET_CODE (XEXP (XEXP (x, 0), 1)))
        {
+       case SYMBOL_REF:
          base = XEXP (XEXP (x, 0), 1);
          output_addr_const (file, base);
+         break;
+       case CONST_INT:
+         offset = INTVAL (XEXP (XEXP (x, 0), 1));
+         break;
+       default:
+         gcc_unreachable ();
        }
-      else if (GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
-       offset = INTVAL (XEXP (XEXP (x, 0), 1));
-      else abort ();
 
       /* How bogus.  The compiler is apparently responsible for
         rounding the constant if it uses an LR field selector.
@@ -4567,8 +5230,9 @@ output_global_address (file, x, round_constant)
       if (round_constant)
        offset = ((offset + 0x1000) & ~0x1fff);
 
-      if (GET_CODE (XEXP (x, 0)) == PLUS)
+      switch (GET_CODE (XEXP (x, 0)))
        {
+       case PLUS:
          if (offset < 0)
            {
              offset = -offset;
@@ -4576,12 +5240,17 @@ output_global_address (file, x, round_constant)
            }
          else
            sep = "+";
-       }
-      else if (GET_CODE (XEXP (x, 0)) == MINUS
-              && (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF))
-       sep = "-";
-      else abort ();
+         break;
+
+       case MINUS:
+         gcc_assert (GET_CODE (XEXP (XEXP (x, 0), 0)) == SYMBOL_REF);
+         sep = "-";
+         break;
 
+       default:
+         gcc_unreachable ();
+       }
+      
       if (!read_only_operand (base, VOIDmode) && !flag_pic)
        fputs ("-$global$", file);
       if (offset)
@@ -4591,42 +5260,226 @@ output_global_address (file, x, round_constant)
     output_addr_const (file, x);
 }
 
-void
-output_deferred_plabels (file)
-     FILE *file;
+/* Output boilerplate text to appear at the beginning of the file.
+   There are several possible versions.  */
+#define aputs(x) fputs(x, asm_out_file)
+static inline void
+pa_file_start_level (void)
 {
-  int i;
-  /* If we have deferred plabels, then we need to switch into the data
-     section and align it to a 4 byte boundary before we output the
-     deferred plabels.  */
+  if (TARGET_64BIT)
+    aputs ("\t.LEVEL 2.0w\n");
+  else if (TARGET_PA_20)
+    aputs ("\t.LEVEL 2.0\n");
+  else if (TARGET_PA_11)
+    aputs ("\t.LEVEL 1.1\n");
+  else
+    aputs ("\t.LEVEL 1.0\n");
+}
+
+static inline void
+pa_file_start_space (int sortspace)
+{
+  aputs ("\t.SPACE $PRIVATE$");
+  if (sortspace)
+    aputs (",SORT=16");
+  aputs ("\n\t.SUBSPA $DATA$,QUAD=1,ALIGN=8,ACCESS=31"
+         "\n\t.SUBSPA $BSS$,QUAD=1,ALIGN=8,ACCESS=31,ZERO,SORT=82"
+         "\n\t.SPACE $TEXT$");
+  if (sortspace)
+    aputs (",SORT=8");
+  aputs ("\n\t.SUBSPA $LIT$,QUAD=0,ALIGN=8,ACCESS=44"
+         "\n\t.SUBSPA $CODE$,QUAD=0,ALIGN=8,ACCESS=44,CODE_ONLY\n");
+}
+
+static inline void
+pa_file_start_file (int want_version)
+{
+  if (write_symbols != NO_DEBUG)
+    {
+      output_file_directive (asm_out_file, main_input_filename);
+      if (want_version)
+       aputs ("\t.version\t\"01.01\"\n");
+    }
+}
+
+static inline void
+pa_file_start_mcount (const char *aswhat)
+{
+  if (profile_flag)
+    fprintf (asm_out_file, "\t.IMPORT _mcount,%s\n", aswhat);
+}
+  
+static void
+pa_elf_file_start (void)
+{
+  pa_file_start_level ();
+  pa_file_start_mcount ("ENTRY");
+  pa_file_start_file (0);
+}
+
+static void
+pa_som_file_start (void)
+{
+  pa_file_start_level ();
+  pa_file_start_space (0);
+  aputs ("\t.IMPORT $global$,DATA\n"
+         "\t.IMPORT $$dyncall,MILLICODE\n");
+  pa_file_start_mcount ("CODE");
+  pa_file_start_file (0);
+}
+
+static void
+pa_linux_file_start (void)
+{
+  pa_file_start_file (1);
+  pa_file_start_level ();
+  pa_file_start_mcount ("CODE");
+}
+
+static void
+pa_hpux64_gas_file_start (void)
+{
+  pa_file_start_level ();
+#ifdef ASM_OUTPUT_TYPE_DIRECTIVE
+  if (profile_flag)
+    ASM_OUTPUT_TYPE_DIRECTIVE (asm_out_file, "_mcount", "function");
+#endif
+  pa_file_start_file (1);
+}
+
+static void
+pa_hpux64_hpas_file_start (void)
+{
+  pa_file_start_level ();
+  pa_file_start_space (1);
+  pa_file_start_mcount ("CODE");
+  pa_file_start_file (0);
+}
+#undef aputs
+
+/* 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;
+
+  /* See if we have already put this function on the list of deferred
+     plabels.  This list is generally small, so a liner search is not
+     too ugly.  If it proves too slow replace it with something faster.  */
+  for (i = 0; i < n_deferred_plabels; i++)
+    if (strcmp (fname, XSTR (deferred_plabels[i].symbol, 0)) == 0)
+      break;
+
+  /* If the deferred plabel list is empty, or this entry was not found
+     on the list, create a new entry on the list.  */
+  if (deferred_plabels == NULL || i == n_deferred_plabels)
+    {
+      tree id;
+
+      if (deferred_plabels == 0)
+       deferred_plabels = (struct deferred_plabel *)
+         ggc_alloc (sizeof (struct deferred_plabel));
+      else
+       deferred_plabels = (struct deferred_plabel *)
+         ggc_realloc (deferred_plabels,
+                      ((n_deferred_plabels + 1)
+                       * sizeof (struct deferred_plabel)));
+
+      i = n_deferred_plabels++;
+      deferred_plabels[i].internal_label = gen_label_rtx ();
+      deferred_plabels[i].symbol = symbol;
+
+      /* Gross.  We have just implicitly taken the address of this
+        function.  Mark it in the same manner as assemble_name.  */
+      id = maybe_get_identifier (targetm.strip_name_encoding (fname));
+      if (id)
+       mark_referenced (id);
+    }
+
+  return deferred_plabels[i].internal_label;
+}
+
+static void
+output_deferred_plabels (void)
+{
+  size_t i;
+
+  /* If we have some deferred plabels, then we need to switch into the
+     data or readonly data section, and align it to a 4 byte boundary
+     before outputting the deferred plabels.  */
   if (n_deferred_plabels)
     {
-      data_section ();
-      ASM_OUTPUT_ALIGN (file, 2);
+      switch_to_section (flag_pic ? data_section : readonly_data_section);
+      ASM_OUTPUT_ALIGN (asm_out_file, TARGET_64BIT ? 3 : 2);
     }
 
   /* Now output the deferred plabels.  */
   for (i = 0; i < n_deferred_plabels; i++)
     {
-      ASM_OUTPUT_INTERNAL_LABEL (file, "L", CODE_LABEL_NUMBER (deferred_plabels[i].internal_label));
-      assemble_integer (gen_rtx_SYMBOL_REF (Pmode, deferred_plabels[i].name),
-                       4, 32, 1);
+      targetm.asm_out.internal_label (asm_out_file, "L",
+                CODE_LABEL_NUMBER (deferred_plabels[i].internal_label));
+      assemble_integer (deferred_plabels[i].symbol,
+                       TARGET_64BIT ? 8 : 4, TARGET_64BIT ? 64 : 32, 1);
     }
 }
 
+#ifdef HPUX_LONG_DOUBLE_LIBRARY
+/* Initialize optabs to point to HPUX long double emulation routines.  */
+static void
+pa_hpux_init_libfuncs (void)
+{
+  set_optab_libfunc (add_optab, TFmode, "_U_Qfadd");
+  set_optab_libfunc (sub_optab, TFmode, "_U_Qfsub");
+  set_optab_libfunc (smul_optab, TFmode, "_U_Qfmpy");
+  set_optab_libfunc (sdiv_optab, TFmode, "_U_Qfdiv");
+  set_optab_libfunc (smin_optab, TFmode, "_U_Qmin");
+  set_optab_libfunc (smax_optab, TFmode, "_U_Qfmax");
+  set_optab_libfunc (sqrt_optab, TFmode, "_U_Qfsqrt");
+  set_optab_libfunc (abs_optab, TFmode, "_U_Qfabs");
+  set_optab_libfunc (neg_optab, TFmode, "_U_Qfneg");
+
+  set_optab_libfunc (eq_optab, TFmode, "_U_Qfeq");
+  set_optab_libfunc (ne_optab, TFmode, "_U_Qfne");
+  set_optab_libfunc (gt_optab, TFmode, "_U_Qfgt");
+  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");
+  set_conv_libfunc (trunc_optab,  SFmode, TFmode, "_U_Qfcnvff_quad_to_sgl");
+  set_conv_libfunc (trunc_optab,  DFmode, TFmode, "_U_Qfcnvff_quad_to_dbl");
+
+  set_conv_libfunc (sfix_optab,   SImode, TFmode, TARGET_64BIT
+                                                 ? "__U_Qfcnvfxt_quad_to_sgl"
+                                                 : "_U_Qfcnvfxt_quad_to_sgl");
+  set_conv_libfunc (sfix_optab,   DImode, TFmode, "_U_Qfcnvfxt_quad_to_dbl");
+  set_conv_libfunc (ufix_optab,   SImode, TFmode, "_U_Qfcnvfxt_quad_to_usgl");
+  set_conv_libfunc (ufix_optab,   DImode, TFmode, "_U_Qfcnvfxt_quad_to_udbl");
+
+  set_conv_libfunc (sfloat_optab, TFmode, SImode, "_U_Qfcnvxf_sgl_to_quad");
+  set_conv_libfunc (sfloat_optab, TFmode, DImode, "_U_Qfcnvxf_dbl_to_quad");
+  set_conv_libfunc (ufloat_optab, TFmode, SImode, "_U_Qfcnvxf_usgl_to_quad");
+  set_conv_libfunc (ufloat_optab, TFmode, DImode, "_U_Qfcnvxf_udbl_to_quad");
+}
+#endif
+
 /* HP's millicode routines mean something special to the assembler.
    Keep track of which ones we have used.  */
 
-enum millicodes { remI, remU, divI, divU, mulI, mulU, end1000 };
-static void import_milli                       PARAMS ((enum millicodes));
+enum millicodes { remI, remU, divI, divU, mulI, end1000 };
+static void import_milli (enum millicodes);
 static char imported[(int) end1000];
-static const char * const milli_names[] = {"remI", "remU", "divI", "divU", "mulI", "mulU"};
+static const char * const milli_names[] = {"remI", "remU", "divI", "divU", "mulI"};
 static const char import_string[] = ".IMPORT $$....,MILLICODE";
 #define MILLI_START 10
 
 static void
-import_milli (code)
-     enum millicodes code;
+import_milli (enum millicodes code)
 {
   char str[sizeof (import_string)];
 
@@ -4643,9 +5496,7 @@ import_milli (code)
    the proper registers.  */
 
 const char *
-output_mul_insn (unsignedp, insn)
-     int unsignedp ATTRIBUTE_UNUSED;
-     rtx insn;
+output_mul_insn (int unsignedp ATTRIBUTE_UNUSED, rtx insn)
 {
   import_milli (mulI);
   return output_millicode_call (insn, gen_rtx_SYMBOL_REF (Pmode, "$$mulI"));
@@ -4654,8 +5505,7 @@ output_mul_insn (unsignedp, insn)
 /* Emit the rtl for doing a division by a constant.  */
 
 /* Do magic division millicodes exist for this value? */
-static const int magic_milli[]= {0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0,
-                                1, 1};
+const int magic_milli[]= {0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0, 1, 1};
 
 /* We'll use an array to keep track of the magic millicodes and
    whether or not we've used them already. [n][0] is signed, [n][1] is
@@ -4664,20 +5514,7 @@ static const int magic_milli[]= {0, 0, 0, 1, 0, 1, 1, 1, 0, 1, 1, 0, 1, 0,
 static int div_milli[16][2];
 
 int
-div_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  return (mode == SImode
-         && ((GET_CODE (op) == REG && REGNO (op) == 25)
-             || (GET_CODE (op) == CONST_INT && INTVAL (op) > 0
-                 && INTVAL (op) < 16 && magic_milli[INTVAL (op)])));
-}
-
-int
-emit_hpdiv_const (operands, unsignedp)
-     rtx *operands;
-     int unsignedp;
+emit_hpdiv_const (rtx *operands, int unsignedp)
 {
   if (GET_CODE (operands[2]) == CONST_INT
       && INTVAL (operands[2]) > 0
@@ -4688,8 +5525,8 @@ emit_hpdiv_const (operands, 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,
@@ -4707,10 +5544,7 @@ emit_hpdiv_const (operands, unsignedp)
 }
 
 const char *
-output_div_insn (operands, unsignedp, insn)
-     rtx *operands;
-     int unsignedp;
-     rtx insn;
+output_div_insn (rtx *operands, int unsignedp, rtx insn)
 {
   int divisor;
 
@@ -4730,15 +5564,15 @@ output_div_insn (operands, unsignedp, insn)
        }
       if (unsignedp)
        {
-         sprintf (buf, "$$divU_");
-         sprintf (buf + 7, HOST_WIDE_INT_PRINT_DEC, INTVAL (operands[0]));
+         sprintf (buf, "$$divU_" HOST_WIDE_INT_PRINT_DEC,
+                  INTVAL (operands[0]));
          return output_millicode_call (insn,
                                        gen_rtx_SYMBOL_REF (SImode, buf));
        }
       else
        {
-         sprintf (buf, "$$divI_");
-         sprintf (buf + 7, HOST_WIDE_INT_PRINT_DEC, INTVAL (operands[0]));
+         sprintf (buf, "$$divI_" HOST_WIDE_INT_PRINT_DEC,
+                  INTVAL (operands[0]));
          return output_millicode_call (insn,
                                        gen_rtx_SYMBOL_REF (SImode, buf));
        }
@@ -4764,9 +5598,7 @@ output_div_insn (operands, unsignedp, insn)
 /* Output a $$rem millicode to do mod.  */
 
 const char *
-output_mod_insn (unsignedp, insn)
-     int unsignedp;
-     rtx insn;
+output_mod_insn (int unsignedp, rtx insn)
 {
   if (unsignedp)
     {
@@ -4783,8 +5615,7 @@ output_mod_insn (unsignedp, insn)
 }
 
 void
-output_arg_descriptor (call_insn)
-     rtx call_insn;
+output_arg_descriptor (rtx call_insn)
 {
   const char *arg_regs[4];
   enum machine_mode arg_mode;
@@ -4809,9 +5640,9 @@ output_arg_descriptor (call_insn)
       return;
     }
 
-  if (GET_CODE (call_insn) != CALL_INSN)
-    abort ();
-  for (link = CALL_INSN_FUNCTION_USAGE (call_insn); link; link = XEXP (link, 1))
+  gcc_assert (GET_CODE (call_insn) == CALL_INSN);
+  for (link = CALL_INSN_FUNCTION_USAGE (call_insn);
+       link; link = XEXP (link, 1))
     {
       rtx use = XEXP (link, 0);
 
@@ -4857,128 +5688,239 @@ output_arg_descriptor (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 (class, mode, in)
-     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 rclass,
+                    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 (rclass == 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 (rclass == 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;
 
-  /* 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;
+  /* Trying to load a constant into a FP register during PIC code
+     generation requires %r1 as a scratch register.  */
+  if (flag_pic
+      && (mode == SImode || mode == DImode)
+      && FP_REG_CLASS_P (rclass)
+      && (GET_CODE (x) == CONST_INT || GET_CODE (x) == CONST_DOUBLE))
+    {
+      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);
+
+  /* In order to allow 14-bit displacements in integer loads and stores,
+     we need to prevent reload from generating out of range integer mode
+     loads and stores to the floating point registers.  Previously, we
+     used to call for a secondary reload and have emit_move_sequence()
+     fix the instruction sequence.  However, reload occasionally wouldn't
+     generate the reload and we would end up with an invalid REG+D memory
+     address.  So, now we use an intermediate general register for most
+     memory loads and stores.  */
+  if ((regno >= FIRST_PSEUDO_REGISTER || regno == -1)
+      && GET_MODE_CLASS (mode) == MODE_INT
+      && FP_REG_CLASS_P (rclass))
+    {
+      /* Reload passes (mem:SI (reg/f:DI 30 %r30) when it wants to check
+        the secondary reload needed for a pseudo.  It never passes a
+        REG+D address.  */
+      if (GET_CODE (x) == MEM)
+       {
+         x = XEXP (x, 0);
+
+         /* We don't need an intermediate for indexed and LO_SUM DLT
+            memory addresses.  When INT14_OK_STRICT is true, it might
+            appear that we could directly allow register indirect
+            memory addresses.  However, this doesn't work because we
+            don't support SUBREGs in floating-point register copies
+            and reload doesn't tell us when it's going to use a SUBREG.  */
+         if (IS_INDEX_ADDR_P (x)
+             || IS_LO_SUM_DLT_ADDR_P (x))
+           return NO_REGS;
+
+         /* Otherwise, we need an intermediate general register.  */
+         return GENERAL_REGS;
+       }
+
+      /* Request a secondary reload with a general scratch register
+        for everthing else.  ??? Could symbolic operands be handled
+        directly when generating non-pic PA 2.0 code?  */
+      sri->icode = in_p ? reload_in_optab[mode] : reload_out_optab[mode];
+      return NO_REGS;
+    }
+
+  /* We need a secondary register (GPR) for copies between the SAR
+     and anything other than a general register.  */
+  if (rclass == SHIFT_REGS && (regno <= 0 || regno >= 32))
+    {
+      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;
+      && (REGNO_REG_CLASS (regno) == SHIFT_REGS
+      && FP_REG_CLASS_P (rclass)))
+    {
+      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 when 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;
 }
 
-enum direction
-function_arg_padding (mode, type)
-     enum machine_mode mode;
-     tree type;
+/* Implement TARGET_EXTRA_LIVE_ON_ENTRY.  The argument pointer
+   is only marked as live on entry by df-scan when it is a fixed
+   register.  It isn't a fixed register in the 64-bit runtime,
+   so we need to mark it here.  */
+
+static void
+pa_extra_live_on_entry (bitmap regs)
+{
+  if (TARGET_64BIT)
+    bitmap_set_bit (regs, ARG_POINTER_REGNUM);
+}
+
+/* Implement EH_RETURN_HANDLER_RTX.  The MEM needs to be volatile
+   to prevent it from being deleted.  */
+
+rtx
+pa_eh_return_handler_rtx (void)
 {
-  int size;
+  rtx tmp;
 
-  if (mode == BLKmode)
-    {
-      if (type && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST)
-       size = int_size_in_bytes (type) * BITS_PER_UNIT;
+  tmp = gen_rtx_PLUS (word_mode, frame_pointer_rtx,
+                     TARGET_64BIT ? GEN_INT (-16) : GEN_INT (-20));
+  tmp = gen_rtx_MEM (word_mode, tmp);
+  tmp->volatil = 1;
+  return tmp;
+}
+
+/* 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, const_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, const_tree type)
+{
+  if (mode == BLKmode
+      || (TARGET_64BIT
+         && type
+         && (AGGREGATE_TYPE_P (type)
+             || TREE_CODE (type) == COMPLEX_TYPE
+             || TREE_CODE (type) == VECTOR_TYPE)))
+    {
+      /* Return none if justification is not required.  */
+      if (type
+         && TREE_CODE (TYPE_SIZE (type)) == INTEGER_CST
+         && (int_size_in_bytes (type) * BITS_PER_UNIT) % PARM_BOUNDARY == 0)
+       return none;
+
+      /* The directions set here are ignored when a BLKmode argument larger
+        than a word is placed in a register.  Different code is used for
+        the stack and registers.  This makes it difficult to have a
+        consistent data representation for both the stack and registers.
+        For both runtimes, the justification and padding for arguments on
+        the stack and in registers should be identical.  */
+      if (TARGET_64BIT)
+       /* The 64-bit runtime specifies left justification for aggregates.  */
+        return upward;
       else
-       return upward;          /* Don't know if this is right, but */
-                               /* same as old definition.  */
+       /* The 32-bit runtime architecture specifies right justification.
+          When the argument is passed on the stack, the argument is padded
+          with garbage on the left.  The HP compiler pads with zeros.  */
+       return downward;
     }
-  else
-    size = GET_MODE_BITSIZE (mode);
-  if (size < PARM_BOUNDARY)
+
+  if (GET_MODE_BITSIZE (mode) < PARM_BOUNDARY)
     return downward;
-  else if (size % PARM_BOUNDARY)
-    return upward;
   else
     return none;
 }
@@ -4988,8 +5930,8 @@ function_arg_padding (mode, 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 *
-hppa_builtin_saveregs ()
+static rtx
+hppa_builtin_saveregs (void)
 {
   rtx offset, dest;
   tree fntype = TREE_TYPE (current_function_decl);
@@ -4999,9 +5941,9 @@ hppa_builtin_saveregs ()
                ? UNITS_PER_WORD : 0);
 
   if (argadj)
-    offset = plus_constant (current_function_arg_offset_rtx, argadj);
+    offset = plus_constant (crtl->args.arg_offset_rtx, argadj);
   else
-    offset = current_function_arg_offset_rtx;
+    offset = crtl->args.arg_offset_rtx;
 
   if (TARGET_64BIT)
     {
@@ -5009,9 +5951,9 @@ hppa_builtin_saveregs ()
 
       /* Adjust for varargs/stdarg differences.  */
       if (argadj)
-       offset = plus_constant (current_function_arg_offset_rtx, -argadj);
+       offset = plus_constant (crtl->args.arg_offset_rtx, -argadj);
       else
-       offset = current_function_arg_offset_rtx;
+       offset = crtl->args.arg_offset_rtx;
 
       /* We need to save %r26 .. %r19 inclusive starting at offset -64
         from the incoming arg pointer and growing to larger addresses.  */
@@ -5035,11 +5977,11 @@ hppa_builtin_saveregs ()
 
   /* Store general registers on the stack.  */
   dest = gen_rtx_MEM (BLKmode,
-                     plus_constant (current_function_internal_arg_pointer,
+                     plus_constant (crtl->args.internal_arg_pointer,
                                     -16));
   set_mem_alias_set (dest, get_varargs_alias_set ());
   set_mem_align (dest, BITS_PER_WORD);
-  move_block_from_reg (23, dest, 4, 4 * UNITS_PER_WORD);
+  move_block_from_reg (23, dest, 4);
 
   /* move_block_from_reg will emit code to store the argument registers
      individually as scalar stores.
@@ -5053,95 +5995,121 @@ hppa_builtin_saveregs ()
   emit_insn (gen_blockage ());
 
   return copy_to_reg (expand_binop (Pmode, add_optab,
-                                   current_function_internal_arg_pointer,
+                                   crtl->args.internal_arg_pointer,
                                    offset, 0, 0, OPTAB_LIB_WIDEN));
 }
 
-void
-hppa_va_start (stdarg_p, valist, nextarg)
-     int stdarg_p ATTRIBUTE_UNUSED;
-     tree valist;
-     rtx nextarg;
+static void
+hppa_va_start (tree valist, rtx nextarg)
 {
   nextarg = expand_builtin_saveregs ();
-  std_expand_builtin_va_start (1, valist, nextarg);
+  std_expand_builtin_va_start (valist, nextarg);
 }
 
-rtx
-hppa_va_arg (valist, type)
-     tree valist, type;
+static tree
+hppa_gimplify_va_arg_expr (tree valist, tree type, gimple_seq *pre_p,
+                          gimple_seq *post_p)
 {
-  HOST_WIDE_INT align, size, ofs;
-  tree t, ptr, pptr;
-
   if (TARGET_64BIT)
     {
-      /* Every argument in PA64 is passed by value (including large structs).
-         Arguments with size greater than 8 must be aligned 0 MOD 16.  */
-
-      size = int_size_in_bytes (type);
-      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);
-        }
-      return std_expand_builtin_va_arg (valist, type);
+      /* Args grow upward.  We can use the generic routines.  */
+      return std_gimplify_va_arg_expr (valist, type, pre_p, post_p);
     }
-
-  /* Compute the rounded size of the type.  */
-  align = PARM_BOUNDARY / BITS_PER_UNIT;
-  size = int_size_in_bytes (type);
-
-  ptr = build_pointer_type (type);
-
-  /* "Large" types are passed by reference.  */
-  if (size > 8)
+  else /* !TARGET_64BIT */
     {
-      t = build (PREDECREMENT_EXPR, TREE_TYPE (valist), valist,
-                build_int_2 (POINTER_SIZE / BITS_PER_UNIT, 0));
-      TREE_SIDE_EFFECTS (t) = 1;
+      tree ptr = build_pointer_type (type);
+      tree valist_type;
+      tree t, u;
+      unsigned int size, ofs;
+      bool indirect;
 
-      pptr = build_pointer_type (ptr);
-      t = build1 (NOP_EXPR, pptr, t);
-      TREE_SIDE_EFFECTS (t) = 1;
+      indirect = pass_by_reference (NULL, TYPE_MODE (type), type, 0);
+      if (indirect)
+       {
+         type = ptr;
+         ptr = build_pointer_type (type);
+       }
+      size = int_size_in_bytes (type);
+      valist_type = TREE_TYPE (valist);
 
-      t = build1 (INDIRECT_REF, ptr, t);
-      TREE_SIDE_EFFECTS (t) = 1;
-    }
-  else
-    {
-      t = build (PLUS_EXPR, TREE_TYPE (valist), valist,
-                build_int_2 (-size, -1));
+      /* Args grow down.  Not handled by generic routines.  */
 
-      /* 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));
+      u = fold_convert (sizetype, size_in_bytes (type));
+      u = fold_build1 (NEGATE_EXPR, sizetype, u);
+      t = build2 (POINTER_PLUS_EXPR, valist_type, valist, u);
 
-      t = build (MODIFY_EXPR, TREE_TYPE (valist), valist, t);
-      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 = size_int (size > 4 ? -8 : -4);
+      t = fold_convert (sizetype, t);
+      t = build2 (BIT_AND_EXPR, sizetype, t, u);
+      t = fold_convert (valist_type, t);
+
+      t = build2 (MODIFY_EXPR, valist_type, valist, t);
 
       ofs = (8 - size) % 4;
-      if (ofs)
+      if (ofs != 0)
        {
-         t = build (PLUS_EXPR, TREE_TYPE (valist), t, build_int_2 (ofs, 0));
-         TREE_SIDE_EFFECTS (t) = 1;
+         u = size_int (ofs);
+         t = build2 (POINTER_PLUS_EXPR, valist_type, t, u);
        }
 
-      t = build1 (NOP_EXPR, ptr, t);
-      TREE_SIDE_EFFECTS (t) = 1;
-    }
+      t = fold_convert (ptr, t);
+      t = build_va_arg_indirect_ref (t);
 
-  /* Calculate!  */
-  return expand_expr (t, NULL_RTX, Pmode, EXPAND_NORMAL);
+      if (indirect)
+       t = build_va_arg_indirect_ref (t);
+
+      return t;
+    }
 }
 
+/* True if MODE is valid for the target.  By "valid", we mean able to
+   be manipulated in non-trivial ways.  In particular, this means all
+   the arithmetic is supported.
+
+   Currently, TImode is not valid as the HP 64-bit runtime documentation
+   doesn't document the alignment and calling conventions for this type. 
+   Thus, we return false when PRECISION is 2 * BITS_PER_WORD and
+   2 * BITS_PER_WORD isn't equal LONG_LONG_TYPE_SIZE.  */
+
+static bool
+pa_scalar_mode_supported_p (enum machine_mode mode)
+{
+  int precision = GET_MODE_PRECISION (mode);
+
+  switch (GET_MODE_CLASS (mode))
+    {
+    case MODE_PARTIAL_INT:
+    case MODE_INT:
+      if (precision == CHAR_TYPE_SIZE)
+       return true;
+      if (precision == SHORT_TYPE_SIZE)
+       return true;
+      if (precision == INT_TYPE_SIZE)
+       return true;
+      if (precision == LONG_TYPE_SIZE)
+       return true;
+      if (precision == LONG_LONG_TYPE_SIZE)
+       return true;
+      return false;
+
+    case MODE_FLOAT:
+      if (precision == FLOAT_TYPE_SIZE)
+       return true;
+      if (precision == DOUBLE_TYPE_SIZE)
+       return true;
+      if (precision == LONG_DOUBLE_TYPE_SIZE)
+       return true;
+      return false;
+
+    case MODE_DECIMAL_FLOAT:
+      return false;
 
+    default:
+      gcc_unreachable ();
+    }
+}
 
 /* This routine handles all the normal conditional branch sequences we
    might need to generate.  It handles compare immediate vs compare
@@ -5151,27 +6119,34 @@ hppa_va_arg (valist, type)
    parameters.  */
 
 const char *
-output_cbranch (operands, nullify, length, negated, insn)
-  rtx *operands;
-  int nullify, length, negated;
-  rtx insn;
+output_cbranch (rtx *operands, int negated, rtx insn)
 {
   static char buf[100];
   int useskip = 0;
+  int nullify = INSN_ANNULLED_BRANCH_P (insn);
+  int length = get_attr_length (insn);
+  int xdelay;
 
-  /* A conditional branch to the following instruction (eg the delay slot)
+  /* A conditional branch to the following instruction (e.g. the delay slot)
      is asking for a disaster.  This can happen when not optimizing and
      when jump optimization fails.
 
-     While it is usually safe to emit nothing, this can fail if the preceding
-     instruction is a nullified branch with an empty delay slot and the
-     same branch target as this branch.  We could check for this but
-     jump optimization should eliminate these jumps.  It is always
-     safe to emit a nop.  */
-
-  if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn))
+     While it is usually safe to emit nothing, this can fail if the
+     preceding instruction is a nullified branch with an empty delay
+     slot and the same branch target as this branch.  We could check
+     for this but jump optimization should eliminate nop jumps.  It
+     is always safe to emit a nop.  */
+  if (next_real_insn (JUMP_LABEL (insn)) == next_real_insn (insn))
     return "nop";
 
+  /* The doubleword form of the cmpib instruction doesn't have the LEU
+     and GTU conditions while the cmpb instruction does.  Since we accept
+     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.  */
   if (length == 8 && dbr_sequence_length () == 0)
@@ -5215,12 +6190,12 @@ output_cbranch (operands, nullify, length, negated, insn)
          strcat (buf, " %2,%r1,%0");
        break;
 
-     /* All long conditionals.  Note an short backward branch with an
+     /* All long conditionals.  Note a 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.  */
+          which is nullified.  */
        if (dbr_sequence_length () != 0
            && ! forward_branch_p (insn)
            && nullify)
@@ -5267,90 +6242,201 @@ output_cbranch (operands, nullify, length, negated, insn)
          }
        break;
 
-      case 20:
-       /* Very long branch.  Right now we only handle these when not
-          optimizing.  See "jump" pattern in pa.md for details.  */
-       if (optimize)
-         abort ();
-
-       /* Create a reversed conditional branch which branches around
-          the following insns.  */
-       if (negated)
-         strcpy (buf, "{com%I2b,%S3,n %2,%r1,.+20|cmp%I2b,%S3,n %2,%r1,.+20}");
+      default:
+       /* The reversed conditional branch must branch over one additional
+          instruction if the delay slot is filled and needs to be extracted
+          by output_lbranch.  If the delay slot is empty or this is a
+          nullified forward branch, the instruction after the reversed
+          condition branch must be nullified.  */
+       if (dbr_sequence_length () == 0
+           || (nullify && forward_branch_p (insn)))
+         {
+           nullify = 1;
+           xdelay = 0;
+           operands[4] = GEN_INT (length);
+         }
        else
-         strcpy (buf, "{com%I2b,%B3,n %2,%r1,.+20|cmp%I2b,%B3,n %2,%r1,.+20}");
-       if (GET_MODE (operands[1]) == DImode)
          {
-           if (negated)
-             strcpy (buf,
-                     "{com%I2b,*%S3,n %2,%r1,.+20|cmp%I2b,*%S3,n %2,%r1,.+20}");
-           else
-             strcpy (buf,
-                     "{com%I2b,*%B3,n %2,%r1,.+20|cmp%I2b,*%B3,n %2,%r1,.+20}");
+           xdelay = 1;
+           operands[4] = GEN_INT (length + 4);
          }
-       output_asm_insn (buf, operands);
-
-       /* Output an insn to save %r1.  */
-       output_asm_insn ("stw %%r1,-16(%%r30)", operands);
-
-       /* Now output a very long branch to the original target.  */
-       output_asm_insn ("ldil L'%l0,%%r1\n\tbe R'%l0(%%sr4,%%r1)", operands);
-
-       /* Now restore the value of %r1 in the delay slot.  We're not
-          optimizing so we know nothing else can be in the delay slot.  */
-       return "ldw -16(%%r30),%%r1";
-
-      case 28:
-       /* Very long branch when generating PIC code.  Right now we only
-          handle these when not optimizing.  See "jump" pattern in pa.md
-          for details.  */
-       if (optimize)
-         abort ();
 
        /* Create a reversed conditional branch which branches around
           the following insns.  */
-       if (negated)
-         strcpy (buf, "{com%I2b,%S3,n %2,%r1,.+28|cmp%I2b,%S3,n %2,%r1,.+28}");
+       if (GET_MODE (operands[1]) != DImode)
+         {
+           if (nullify)
+             {
+               if (negated)
+                 strcpy (buf,
+                   "{com%I2b,%S3,n %2,%r1,.+%4|cmp%I2b,%S3,n %2,%r1,.+%4}");
+               else
+                 strcpy (buf,
+                   "{com%I2b,%B3,n %2,%r1,.+%4|cmp%I2b,%B3,n %2,%r1,.+%4}");
+             }
+           else
+             {
+               if (negated)
+                 strcpy (buf,
+                   "{com%I2b,%S3 %2,%r1,.+%4|cmp%I2b,%S3 %2,%r1,.+%4}");
+               else
+                 strcpy (buf,
+                   "{com%I2b,%B3 %2,%r1,.+%4|cmp%I2b,%B3 %2,%r1,.+%4}");
+             }
+         }
        else
-         strcpy (buf, "{com%I2b,%B3,n %2,%r1,.+28|cmp%I2b,%B3,n %2,%r1,.+28}");
-       if (GET_MODE (operands[1]) == DImode)
          {
-           if (negated)
-             strcpy (buf, "{com%I2b,*%S3,n %2,%r1,.+28|cmp%I2b,*%S3,n %2,%r1,.+28}");
+           if (nullify)
+             {
+               if (negated)
+                 strcpy (buf,
+                   "{com%I2b,*%S3,n %2,%r1,.+%4|cmp%I2b,*%S3,n %2,%r1,.+%4}");
+               else
+                 strcpy (buf,
+                   "{com%I2b,*%B3,n %2,%r1,.+%4|cmp%I2b,*%B3,n %2,%r1,.+%4}");
+             }
            else
-             strcpy (buf, "{com%I2b,*%B3,n %2,%r1,.+28|cmp%I2b,*%B3,n %2,%r1,.+28}");
+             {
+               if (negated)
+                 strcpy (buf,
+                   "{com%I2b,*%S3 %2,%r1,.+%4|cmp%I2b,*%S3 %2,%r1,.+%4}");
+               else
+                 strcpy (buf,
+                   "{com%I2b,*%B3 %2,%r1,.+%4|cmp%I2b,*%B3 %2,%r1,.+%4}");
+             }
          }
+
        output_asm_insn (buf, operands);
+       return output_lbranch (operands[0], insn, xdelay);
+    }
+  return buf;
+}
 
-       /* Output an insn to save %r1.  */
-       output_asm_insn ("stw %%r1,-16(%%r30)", operands);
+/* This routine handles output of long unconditional branches that
+   exceed the maximum range of a simple branch instruction.  Since
+   we don't have a register available for the branch, we save register
+   %r1 in the frame marker, load the branch destination DEST into %r1,
+   execute the branch, and restore %r1 in the delay slot of the branch.
 
-       /* Now output a very long PIC branch to the original target.  */
-       {
-         rtx xoperands[5];
+   Since long branches may have an insn in the delay slot and the
+   delay slot is used to restore %r1, we in general need to extract
+   this insn and execute it before the branch.  However, to facilitate
+   use of this function by conditional branches, we also provide an
+   option to not extract the delay insn so that it will be emitted
+   after the long branch.  So, if there is an insn in the delay slot,
+   it is extracted if XDELAY is nonzero.
 
-         xoperands[0] = operands[0];
-         xoperands[1] = operands[1];
-         xoperands[2] = operands[2];
-         xoperands[3] = operands[3];
-         xoperands[4] = gen_label_rtx ();
+   The lengths of the various long-branch sequences are 20, 16 and 24
+   bytes for the portable runtime, non-PIC and PIC cases, respectively.  */
 
-         output_asm_insn ("{bl|b,l} .+8,%%r1\n\taddil L'%l0-%l4,%%r1",
-                          xoperands);
-         ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
-                                    CODE_LABEL_NUMBER (xoperands[4]));
-         output_asm_insn ("ldo R'%l0-%l4(%%r1),%%r1\n\tbv %%r0(%%r1)",
-                          xoperands);
-       }
+const char *
+output_lbranch (rtx dest, rtx insn, int xdelay)
+{
+  rtx xoperands[2];
+  xoperands[0] = dest;
+
+  /* First, free up the delay slot.  */
+  if (xdelay && dbr_sequence_length () != 0)
+    {
+      /* We can't handle a jump in the delay slot.  */
+      gcc_assert (GET_CODE (NEXT_INSN (insn)) != JUMP_INSN);
+
+      final_scan_insn (NEXT_INSN (insn), asm_out_file,
+                      optimize, 0, NULL);
+
+      /* Now delete the delay insn.  */
+      SET_INSN_DELETED (NEXT_INSN (insn));
+    }
+
+  /* Output an insn to save %r1.  The runtime documentation doesn't
+     specify whether the "Clean Up" slot in the callers frame can
+     be clobbered by the callee.  It isn't copied by HP's builtin
+     alloca, so this suggests that it can be clobbered if necessary.
+     The "Static Link" location is copied by HP builtin alloca, so
+     we avoid using it.  Using the cleanup slot might be a problem
+     if we have to interoperate with languages that pass cleanup
+     information.  However, it should be possible to handle these
+     situations with GCC's asm feature.
+
+     The "Current RP" slot is reserved for the called procedure, so
+     we try to use it when we don't have a frame of our own.  It's
+     rather unlikely that we won't have a frame when we need to emit
+     a very long branch.
+
+     Really the way to go long term is a register scavenger; goto
+     the target of the jump and find a register which we can use
+     as a scratch to hold the value in %r1.  Then, we wouldn't have
+     to free up the delay slot or clobber a slot that may be needed
+     for other purposes.  */
+  if (TARGET_64BIT)
+    {
+      if (actual_fsize == 0 && !df_regs_ever_live_p (2))
+       /* Use the return pointer slot in the frame marker.  */
+       output_asm_insn ("std %%r1,-16(%%r30)", xoperands);
+      else
+       /* Use the slot at -40 in the frame marker since HP builtin
+          alloca doesn't copy it.  */
+       output_asm_insn ("std %%r1,-40(%%r30)", xoperands);
+    }
+  else
+    {
+      if (actual_fsize == 0 && !df_regs_ever_live_p (2))
+       /* Use the return pointer slot in the frame marker.  */
+       output_asm_insn ("stw %%r1,-20(%%r30)", xoperands);
+      else
+       /* Use the "Clean Up" slot in the frame marker.  In GCC,
+          the only other use of this location is for copying a
+          floating point double argument from a floating-point
+          register to two general registers.  The copy is done
+          as an "atomic" operation when outputting a call, so it
+          won't interfere with our using the location here.  */
+       output_asm_insn ("stw %%r1,-12(%%r30)", xoperands);
+    }
 
-       /* Now restore the value of %r1 in the delay slot.  We're not
-          optimizing so we know nothing else can be in the delay slot.  */
-       return "ldw -16(%%r30),%%r1";
+  if (TARGET_PORTABLE_RUNTIME)
+    {
+      output_asm_insn ("ldil L'%0,%%r1", xoperands);
+      output_asm_insn ("ldo R'%0(%%r1),%%r1", xoperands);
+      output_asm_insn ("bv %%r0(%%r1)", xoperands);
+    }
+  else if (flag_pic)
+    {
+      output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
+      if (TARGET_SOM || !TARGET_GAS)
+       {
+         xoperands[1] = gen_label_rtx ();
+         output_asm_insn ("addil L'%l0-%l1,%%r1", xoperands);
+         targetm.asm_out.internal_label (asm_out_file, "L",
+                                         CODE_LABEL_NUMBER (xoperands[1]));
+         output_asm_insn ("ldo R'%l0-%l1(%%r1),%%r1", xoperands);
+       }
+      else
+       {
+         output_asm_insn ("addil L'%l0-$PIC_pcrel$0+4,%%r1", xoperands);
+         output_asm_insn ("ldo R'%l0-$PIC_pcrel$0+8(%%r1),%%r1", xoperands);
+       }
+      output_asm_insn ("bv %%r0(%%r1)", xoperands);
+    }
+  else
+    /* Now output a very long branch to the original target.  */
+    output_asm_insn ("ldil L'%l0,%%r1\n\tbe R'%l0(%%sr4,%%r1)", xoperands);
 
-      default:
-       abort ();
+  /* Now restore the value of %r1 in the delay slot.  */
+  if (TARGET_64BIT)
+    {
+      if (actual_fsize == 0 && !df_regs_ever_live_p (2))
+       return "ldd -16(%%r30),%%r1";
+      else
+       return "ldd -40(%%r30),%%r1";
+    }
+  else
+    {
+      if (actual_fsize == 0 && !df_regs_ever_live_p (2))
+       return "ldw -20(%%r30),%%r1";
+      else
+       return "ldw -12(%%r30),%%r1";
     }
-  return buf;
 }
 
 /* This routine handles all the branch-on-bit conditional branch sequences we
@@ -5359,21 +6445,20 @@ output_cbranch (operands, nullify, length, negated, insn)
    above.  it returns the appropriate output template to emit the branch.  */
 
 const char *
-output_bb (operands, nullify, length, negated, insn, which)
-  rtx *operands ATTRIBUTE_UNUSED;
-  int nullify, length, negated;
-  rtx insn;
-  int which;
+output_bb (rtx *operands ATTRIBUTE_UNUSED, int negated, rtx insn, int which)
 {
   static char buf[100];
   int useskip = 0;
+  int nullify = INSN_ANNULLED_BRANCH_P (insn);
+  int length = get_attr_length (insn);
+  int xdelay;
 
-  /* A conditional branch to the following instruction (eg the delay slot) is
+  /* A conditional branch to the following instruction (e.g. the delay slot) is
      asking for a disaster.  I do not think this can happen as this pattern
      is only used when optimizing; jump optimization should eliminate the
      jump.  But be prepared just in case.  */
 
-  if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn))
+  if (next_real_insn (JUMP_LABEL (insn)) == next_real_insn (insn))
     return "nop";
 
   /* If this is a long branch with its delay slot unfilled, set `nullify'
@@ -5428,12 +6513,12 @@ output_bb (operands, nullify, length, negated, insn, which)
          strcat (buf, " %0,%1,%2");
        break;
 
-     /* All long conditionals.  Note an short backward branch with an
+     /* All long conditionals.  Note a 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.  */
+          which is nullified.  */
        if (dbr_sequence_length () != 0
            && ! forward_branch_p (insn)
            && nullify)
@@ -5475,9 +6560,10 @@ output_bb (operands, nullify, length, negated, insn, which)
          }
        else
          {
-           strcpy (buf, "{extrs,|extrw,s,}");
            if (GET_MODE (operands[0]) == DImode)
              strcpy (buf, "extrd,s,*");
+           else
+             strcpy (buf, "{extrs,|extrw,s,}");
            if ((which == 0 && negated)
                || (which == 1 && ! negated))
              strcat (buf, "<");
@@ -5495,7 +6581,40 @@ output_bb (operands, nullify, length, negated, insn, which)
        break;
 
       default:
-       abort ();
+       /* The reversed conditional branch must branch over one additional
+          instruction if the delay slot is filled and needs to be extracted
+          by output_lbranch.  If the delay slot is empty or this is a
+          nullified forward branch, the instruction after the reversed
+          condition branch must be nullified.  */
+       if (dbr_sequence_length () == 0
+           || (nullify && forward_branch_p (insn)))
+         {
+           nullify = 1;
+           xdelay = 0;
+           operands[4] = GEN_INT (length);
+         }
+       else
+         {
+           xdelay = 1;
+           operands[4] = GEN_INT (length + 4);
+         }
+
+       if (GET_MODE (operands[0]) == DImode)
+         strcpy (buf, "bb,*");
+       else
+         strcpy (buf, "bb,");
+       if ((which == 0 && negated)
+           || (which == 1 && !negated))
+         strcat (buf, "<");
+       else
+         strcat (buf, ">=");
+       if (nullify)
+         strcat (buf, ",n %0,%1,.+%4");
+       else
+         strcat (buf, " %0,%1,.+%4");
+       output_asm_insn (buf, operands);
+       return output_lbranch (negated ? operands[3] : operands[2],
+                              insn, xdelay);
     }
   return buf;
 }
@@ -5507,21 +6626,20 @@ output_bb (operands, nullify, length, negated, insn, which)
    branch.  */
 
 const char *
-output_bvb (operands, nullify, length, negated, insn, which)
-  rtx *operands ATTRIBUTE_UNUSED;
-  int nullify, length, negated;
-  rtx insn;
-  int which;
+output_bvb (rtx *operands ATTRIBUTE_UNUSED, int negated, rtx insn, int which)
 {
   static char buf[100];
   int useskip = 0;
+  int nullify = INSN_ANNULLED_BRANCH_P (insn);
+  int length = get_attr_length (insn);
+  int xdelay;
 
-  /* A conditional branch to the following instruction (eg the delay slot) is
+  /* A conditional branch to the following instruction (e.g. the delay slot) is
      asking for a disaster.  I do not think this can happen as this pattern
      is only used when optimizing; jump optimization should eliminate the
      jump.  But be prepared just in case.  */
 
-  if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn))
+  if (next_real_insn (JUMP_LABEL (insn)) == next_real_insn (insn))
     return "nop";
 
   /* If this is a long branch with its delay slot unfilled, set `nullify'
@@ -5556,7 +6674,7 @@ output_bvb (operands, nullify, length, negated, insn, which)
        else
          strcpy (buf, "{bvb,|bb,}");
        if (useskip && GET_MODE (operands[0]) == DImode)
-         strcpy (buf, "extrd,s,*}");
+         strcpy (buf, "extrd,s,*");
        else if (GET_MODE (operands[0]) == DImode)
          strcpy (buf, "bb,*");
        if ((which == 0 && negated)
@@ -5576,12 +6694,12 @@ output_bvb (operands, nullify, length, negated, insn, which)
          strcat (buf, "{ %0,%2| %0,%%sar,%2}");
        break;
 
-     /* All long conditionals.  Note an short backward branch with an
+     /* All long conditionals.  Note a 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.  */
+          which is nullified.  */
        if (dbr_sequence_length () != 0
            && ! forward_branch_p (insn)
            && nullify)
@@ -5643,26 +6761,57 @@ output_bvb (operands, nullify, length, negated, insn, which)
        break;
 
       default:
-       abort ();
-    }
-  return buf;
-}
+       /* The reversed conditional branch must branch over one additional
+          instruction if the delay slot is filled and needs to be extracted
+          by output_lbranch.  If the delay slot is empty or this is a
+          nullified forward branch, the instruction after the reversed
+          condition branch must be nullified.  */
+       if (dbr_sequence_length () == 0
+           || (nullify && forward_branch_p (insn)))
+         {
+           nullify = 1;
+           xdelay = 0;
+           operands[4] = GEN_INT (length);
+         }
+       else
+         {
+           xdelay = 1;
+           operands[4] = GEN_INT (length + 4);
+         }
+
+       if (GET_MODE (operands[0]) == DImode)
+         strcpy (buf, "bb,*");
+       else
+         strcpy (buf, "{bvb,|bb,}");
+       if ((which == 0 && negated)
+           || (which == 1 && !negated))
+         strcat (buf, "<");
+       else
+         strcat (buf, ">=");
+       if (nullify)
+         strcat (buf, ",n {%0,.+%4|%0,%%sar,.+%4}");
+       else
+         strcat (buf, " {%0,.+%4|%0,%%sar,.+%4}");
+       output_asm_insn (buf, operands);
+       return output_lbranch (negated ? operands[3] : operands[2],
+                              insn, xdelay);
+    }
+  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.  */
 const char *
-output_dbra (operands, insn, which_alternative)
-     rtx *operands;
-     rtx insn;
-     int which_alternative;
+output_dbra (rtx *operands, rtx insn, int which_alternative)
 {
+  int length = get_attr_length (insn);
 
-  /* A conditional branch to the following instruction (eg the delay slot) is
+  /* A conditional branch to the following instruction (e.g. the delay slot) is
      asking for a disaster.  Be prepared!  */
 
-  if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn))
+  if (next_real_insn (JUMP_LABEL (insn)) == next_real_insn (insn))
     {
       if (which_alternative == 0)
        return "ldo %1(%0),%0";
@@ -5683,7 +6832,7 @@ output_dbra (operands, insn, which_alternative)
   if (which_alternative == 0)
     {
       int nullify = INSN_ANNULLED_BRANCH_P (insn);
-      int length = get_attr_length (insn);
+      int xdelay;
 
       /* If this is a long branch with its delay slot unfilled, set `nullify'
         as it can nullify the delay slot and save a nop.  */
@@ -5695,13 +6844,15 @@ output_dbra (operands, insn, which_alternative)
       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)
+      switch (length)
        {
+       case 4:
+         if (nullify)
+           return "addib,%C2,n %1,%0,%3";
+         else
+           return "addib,%C2 %1,%0,%3";
+      
+       case 8:
          /* Handle weird backwards branch with a fulled delay slot
             which is nullified.  */
          if (dbr_sequence_length () != 0
@@ -5723,9 +6874,34 @@ output_dbra (operands, insn, which_alternative)
            return "addi,%N2 %1,%0,%0\n\tb,n %3";
          else
            return "addi,%N2 %1,%0,%0\n\tb %3";
+
+       default:
+         /* The reversed conditional branch must branch over one additional
+            instruction if the delay slot is filled and needs to be extracted
+            by output_lbranch.  If the delay slot is empty or this is a
+            nullified forward branch, the instruction after the reversed
+            condition branch must be nullified.  */
+         if (dbr_sequence_length () == 0
+             || (nullify && forward_branch_p (insn)))
+           {
+             nullify = 1;
+             xdelay = 0;
+             operands[4] = GEN_INT (length);
+           }
+         else
+           {
+             xdelay = 1;
+             operands[4] = GEN_INT (length + 4);
+           }
+
+         if (nullify)
+           output_asm_insn ("addib,%N2,n %1,%0,.+%4", operands);
+         else
+           output_asm_insn ("addib,%N2 %1,%0,.+%4", operands);
+
+         return output_lbranch (operands[3], insn, xdelay);
        }
-      else
-       abort ();
+      
     }
   /* Deal with gross reload from FP register case.  */
   else if (which_alternative == 1)
@@ -5736,40 +6912,51 @@ output_dbra (operands, insn, which_alternative)
       output_asm_insn ("{fstws|fstw} %0,-16(%%r30)\n\tldw -16(%%r30),%4",
                       operands);
       output_asm_insn ("ldo %1(%4),%4\n\tstw %4,-16(%%r30)", operands);
-      if (get_attr_length (insn) == 24)
+      if (length == 24)
        return "{comb|cmpb},%S2 %%r0,%4,%3\n\t{fldws|fldw} -16(%%r30),%0";
-      else
+      else if (length == 28)
        return "{comclr|cmpclr},%B2 %%r0,%4,%%r0\n\tb %3\n\t{fldws|fldw} -16(%%r30),%0";
+      else
+       {
+         operands[5] = GEN_INT (length - 16);
+         output_asm_insn ("{comb|cmpb},%B2 %%r0,%4,.+%5", operands);
+         output_asm_insn ("{fldws|fldw} -16(%%r30),%0", operands);
+         return output_lbranch (operands[3], insn, 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.   */
+        happens in the branch's delay slot.  */
       output_asm_insn ("ldw %0,%4", operands);
-      if (get_attr_length (insn) == 12)
+      if (length == 12)
        return "addib,%C2 %1,%4,%3\n\tstw %4,%0";
-      else
+      else if (length == 16)
        return "addi,%N2 %1,%4,%4\n\tb %3\n\tstw %4,%0";
+      else
+       {
+         operands[5] = GEN_INT (length - 4);
+         output_asm_insn ("addib,%N2 %1,%4,.+%5\n\tstw %4,%0", operands);
+         return output_lbranch (operands[3], insn, 0);
+       }
     }
 }
 
-/* Return the output template for emitting a dbra type insn.
+/* Return the output template for emitting a movb type insn.
 
    Note it may perform some output operations on its own before
    returning the final output string.  */
 const char *
-output_movb (operands, insn, which_alternative, reverse_comparison)
-     rtx *operands;
-     rtx insn;
-     int which_alternative;
-     int reverse_comparison;
+output_movb (rtx *operands, rtx insn, int which_alternative,
+            int reverse_comparison)
 {
+  int length = get_attr_length (insn);
 
-  /* A conditional branch to the following instruction (eg the delay slot) is
+  /* A conditional branch to the following instruction (e.g. the delay slot) is
      asking for a disaster.  Be prepared!  */
 
-  if (next_active_insn (JUMP_LABEL (insn)) == next_active_insn (insn))
+  if (next_real_insn (JUMP_LABEL (insn)) == next_real_insn (insn))
     {
       if (which_alternative == 0)
        return "copy %1,%0";
@@ -5791,7 +6978,7 @@ output_movb (operands, insn, which_alternative, reverse_comparison)
   if (which_alternative == 0)
     {
       int nullify = INSN_ANNULLED_BRANCH_P (insn);
-      int length = get_attr_length (insn);
+      int xdelay;
 
       /* If this is a long branch with its delay slot unfilled, set `nullify'
         as it can nullify the delay slot and save a nop.  */
@@ -5803,13 +6990,15 @@ output_movb (operands, insn, which_alternative, reverse_comparison)
       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)
+      switch (length)
        {
+       case 4:
+         if (nullify)
+           return "movb,%C2,n %1,%0,%3";
+         else
+           return "movb,%C2 %1,%0,%3";
+
+       case 8:
          /* Handle weird backwards branch with a filled delay slot
             which is nullified.  */
          if (dbr_sequence_length () != 0
@@ -5831,112 +7020,301 @@ output_movb (operands, insn, which_alternative, reverse_comparison)
            return "or,%N2 %1,%%r0,%0\n\tb,n %3";
          else
            return "or,%N2 %1,%%r0,%0\n\tb %3";
+
+       default:
+         /* The reversed conditional branch must branch over one additional
+            instruction if the delay slot is filled and needs to be extracted
+            by output_lbranch.  If the delay slot is empty or this is a
+            nullified forward branch, the instruction after the reversed
+            condition branch must be nullified.  */
+         if (dbr_sequence_length () == 0
+             || (nullify && forward_branch_p (insn)))
+           {
+             nullify = 1;
+             xdelay = 0;
+             operands[4] = GEN_INT (length);
+           }
+         else
+           {
+             xdelay = 1;
+             operands[4] = GEN_INT (length + 4);
+           }
+
+         if (nullify)
+           output_asm_insn ("movb,%N2,n %1,%0,.+%4", operands);
+         else
+           output_asm_insn ("movb,%N2 %1,%0,.+%4", operands);
+
+         return output_lbranch (operands[3], insn, xdelay);
        }
-      else
-       abort ();
     }
-  /* Deal with gross reload from FP register case.  */
+  /* Deal with gross reload for FP destination 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.  */
+      /* Move source register to MEM, perform the branch test, then
+        finally load the FP register from MEM from within the branch's
+        delay slot.  */
       output_asm_insn ("stw %1,-16(%%r30)", operands);
-      if (get_attr_length (insn) == 12)
+      if (length == 12)
        return "{comb|cmpb},%S2 %%r0,%1,%3\n\t{fldws|fldw} -16(%%r30),%0";
-      else
+      else if (length == 16)
        return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tb %3\n\t{fldws|fldw} -16(%%r30),%0";
+      else
+       {
+         operands[4] = GEN_INT (length - 4);
+         output_asm_insn ("{comb|cmpb},%B2 %%r0,%1,.+%4", operands);
+         output_asm_insn ("{fldws|fldw} -16(%%r30),%0", operands);
+         return output_lbranch (operands[3], insn, 0);
+       }
     }
   /* Deal with gross reload from memory case.  */
   else if (which_alternative == 2)
     {
       /* Reload loop counter from memory, the store back to memory
-        happens in the branch's delay slot.   */
-      if (get_attr_length (insn) == 8)
+        happens in the branch's delay slot.  */
+      if (length == 8)
        return "{comb|cmpb},%S2 %%r0,%1,%3\n\tstw %1,%0";
-      else
+      else if (length == 12)
        return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tb %3\n\tstw %1,%0";
+      else
+       {
+         operands[4] = GEN_INT (length);
+         output_asm_insn ("{comb|cmpb},%B2 %%r0,%1,.+%4\n\tstw %1,%0",
+                          operands);
+         return output_lbranch (operands[3], insn, 0);
+       }
     }
   /* Handle SAR as a destination.  */
   else
     {
-      if (get_attr_length (insn) == 8)
+      if (length == 8)
        return "{comb|cmpb},%S2 %%r0,%1,%3\n\tmtsar %r1";
+      else if (length == 12)
+       return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tb %3\n\tmtsar %r1";
       else
-       return "{comclr|cmpclr},%B2 %%r0,%1,%%r0\n\tbl %3\n\tmtsar %r1";
+       {
+         operands[4] = GEN_INT (length);
+         output_asm_insn ("{comb|cmpb},%B2 %%r0,%1,.+%4\n\tmtsar %r1",
+                          operands);
+         return output_lbranch (operands[3], insn, 0);
+       }
     }
 }
 
+/* Copy any FP arguments in INSN into integer registers.  */
+static void
+copy_fp_args (rtx insn)
+{
+  rtx link;
+  rtx xoperands[2];
+
+  for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
+    {
+      int arg_mode, regno;
+      rtx use = XEXP (link, 0);
+
+      if (! (GET_CODE (use) == USE
+         && GET_CODE (XEXP (use, 0)) == REG
+         && FUNCTION_ARG_REGNO_P (REGNO (XEXP (use, 0)))))
+       continue;
 
-/* INSN is a millicode call.  It may have an unconditional jump in its delay
-   slot.
+      arg_mode = GET_MODE (XEXP (use, 0));
+      regno = REGNO (XEXP (use, 0));
+
+      /* Is it a floating point register?  */
+      if (regno >= 32 && regno <= 39)
+       {
+         /* Copy the FP register into an integer register via memory.  */
+         if (arg_mode == SFmode)
+           {
+             xoperands[0] = XEXP (use, 0);
+             xoperands[1] = gen_rtx_REG (SImode, 26 - (regno - 32) / 2);
+             output_asm_insn ("{fstws|fstw} %0,-16(%%sr0,%%r30)", xoperands);
+             output_asm_insn ("ldw -16(%%sr0,%%r30),%1", xoperands);
+           }
+         else
+           {
+             xoperands[0] = XEXP (use, 0);
+             xoperands[1] = gen_rtx_REG (DImode, 25 - (regno - 34) / 2);
+             output_asm_insn ("{fstds|fstd} %0,-16(%%sr0,%%r30)", xoperands);
+             output_asm_insn ("ldw -12(%%sr0,%%r30),%R1", xoperands);
+             output_asm_insn ("ldw -16(%%sr0,%%r30),%1", xoperands);
+           }
+       }
+    }
+}
+
+/* Compute length of the FP argument copy sequence for INSN.  */
+static int
+length_fp_args (rtx insn)
+{
+  int length = 0;
+  rtx link;
+
+  for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
+    {
+      int arg_mode, regno;
+      rtx use = XEXP (link, 0);
+
+      if (! (GET_CODE (use) == USE
+         && GET_CODE (XEXP (use, 0)) == REG
+         && FUNCTION_ARG_REGNO_P (REGNO (XEXP (use, 0)))))
+       continue;
+
+      arg_mode = GET_MODE (XEXP (use, 0));
+      regno = REGNO (XEXP (use, 0));
+
+      /* Is it a floating point register?  */
+      if (regno >= 32 && regno <= 39)
+       {
+         if (arg_mode == SFmode)
+           length += 8;
+         else
+           length += 12;
+       }
+    }
+
+  return length;
+}
+
+/* Return the attribute length for the millicode call instruction INSN.
+   The length must match the code generated by output_millicode_call.
+   We include the delay slot in the returned length as it is better to
+   over estimate the length than to under estimate it.  */
+
+int
+attr_length_millicode_call (rtx insn)
+{
+  unsigned long distance = -1;
+  unsigned long total = IN_NAMED_SECTION_P (cfun->decl) ? 0 : total_code_bytes;
+
+  if (INSN_ADDRESSES_SET_P ())
+    {
+      distance = (total + insn_current_reference_address (insn));
+      if (distance < total)
+       distance = -1;
+    }
+
+  if (TARGET_64BIT)
+    {
+      if (!TARGET_LONG_CALLS && distance < 7600000)
+       return 8;
+
+      return 20;
+    }
+  else if (TARGET_PORTABLE_RUNTIME)
+    return 24;
+  else
+    {
+      if (!TARGET_LONG_CALLS && distance < 240000)
+       return 8;
+
+      if (TARGET_LONG_ABS_CALL && !flag_pic)
+       return 12;
+
+      return 24;
+    }
+}
+
+/* INSN is a function call.  It may have an unconditional jump
+   in its delay slot.
 
    CALL_DEST is the routine we are calling.  */
 
 const char *
-output_millicode_call (insn, call_dest)
-  rtx insn;
-  rtx call_dest;
+output_millicode_call (rtx insn, rtx call_dest)
 {
+  int attr_length = get_attr_length (insn);
+  int seq_length = dbr_sequence_length ();
   int distance;
-  rtx xoperands[4];
   rtx seq_insn;
+  rtx xoperands[3];
 
-  xoperands[3] = gen_rtx_REG (Pmode, TARGET_64BIT ? 2 : 31);
-
-  /* Handle common case -- empty delay slot or no jump in the delay slot,
-     and we're sure that the branch will reach the beginning of the $CODE$
-     subspace.  The within reach form of the $$sh_func_adrs call has
-     a length of 28 and attribute type of multi.  This length is the
-     same as the maximum length of an out of reach PIC call to $$div.  */
-  if ((dbr_sequence_length () == 0
-       && (get_attr_length (insn) == 8
-          || (get_attr_length (insn) == 28 
-              && get_attr_type (insn) == TYPE_MULTI)))
-      || (dbr_sequence_length () != 0
-         && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN
-         && get_attr_length (insn) == 4))
-    {
-      xoperands[0] = call_dest;
-      output_asm_insn ("{bl|b,l} %0,%3%#", xoperands);
-      return "";
+  xoperands[0] = call_dest;
+  xoperands[2] = gen_rtx_REG (Pmode, TARGET_64BIT ? 2 : 31);
+
+  /* Handle the common case where we are sure that the branch will
+     reach the beginning of the $CODE$ subspace.  The within reach
+     form of the $$sh_func_adrs call has a length of 28.  Because
+     it has an attribute type of multi, it never has a nonzero
+     sequence length.  The length of the $$sh_func_adrs is the same
+     as certain out of reach PIC calls to other routines.  */
+  if (!TARGET_LONG_CALLS
+      && ((seq_length == 0
+          && (attr_length == 12
+              || (attr_length == 28 && get_attr_type (insn) == TYPE_MULTI)))
+         || (seq_length != 0 && attr_length == 8)))
+    {
+      output_asm_insn ("{bl|b,l} %0,%2", xoperands);
     }
-
-  /* This call may not reach the beginning of the $CODE$ subspace.  */
-  if (get_attr_length (insn) > 8)
+  else
     {
-      int delay_insn_deleted = 0;
-
-      /* We need to emit an inline long-call branch.  */
-      if (dbr_sequence_length () != 0
-         && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN)
+      if (TARGET_64BIT)
        {
-         /* A non-jump insn in the delay slot.  By definition we can
-            emit this insn before the call.  */
-         final_scan_insn (NEXT_INSN (insn), asm_out_file, optimize, 0, 0);
+         /* It might seem that one insn could be saved by accessing
+            the millicode function using the linkage table.  However,
+            this doesn't work in shared libraries and other dynamically
+            loaded objects.  Using a pc-relative sequence also avoids
+            problems related to the implicit use of the gp register.  */
+         output_asm_insn ("b,l .+8,%%r1", xoperands);
+
+         if (TARGET_GAS)
+           {
+             output_asm_insn ("addil L'%0-$PIC_pcrel$0+4,%%r1", xoperands);
+             output_asm_insn ("ldo R'%0-$PIC_pcrel$0+8(%%r1),%%r1", xoperands);
+           }
+         else
+           {
+             xoperands[1] = gen_label_rtx ();
+             output_asm_insn ("addil L'%0-%l1,%%r1", xoperands);
+             targetm.asm_out.internal_label (asm_out_file, "L",
+                                        CODE_LABEL_NUMBER (xoperands[1]));
+             output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands);
+           }
 
-         /* Now delete the delay insn.  */
-         PUT_CODE (NEXT_INSN (insn), NOTE);
-         NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
-         NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0;
-         delay_insn_deleted = 1;
+         output_asm_insn ("bve,l (%%r1),%%r2", xoperands);
        }
+      else if (TARGET_PORTABLE_RUNTIME)
+       {
+         /* Pure portable runtime doesn't allow be/ble; we also don't
+            have PIC support in the assembler/linker, so this sequence
+            is needed.  */
 
-      /* PIC long millicode call sequence.  */
-      if (flag_pic)
+         /* Get the address of our target into %r1.  */
+         output_asm_insn ("ldil L'%0,%%r1", xoperands);
+         output_asm_insn ("ldo R'%0(%%r1),%%r1", xoperands);
+
+         /* Get our return address into %r31.  */
+         output_asm_insn ("{bl|b,l} .+8,%%r31", xoperands);
+         output_asm_insn ("addi 8,%%r31,%%r31", xoperands);
+
+         /* Jump to our target address in %r1.  */
+         output_asm_insn ("bv %%r0(%%r1)", xoperands);
+       }
+      else if (!flag_pic)
+       {
+         output_asm_insn ("ldil L'%0,%%r1", xoperands);
+         if (TARGET_PA_20)
+           output_asm_insn ("be,l R'%0(%%sr4,%%r1),%%sr0,%%r31", xoperands);
+         else
+           output_asm_insn ("ble R'%0(%%sr4,%%r1)", xoperands);
+       }
+      else
        {
-         xoperands[0] = call_dest;
-         xoperands[1] = gen_label_rtx ();
-         /* Get our address + 8 into %r1.  */
          output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
+         output_asm_insn ("addi 16,%%r1,%%r31", xoperands);
 
-         /* Add %r1 to the offset of our target from the next insn.  */
          if (TARGET_SOM || !TARGET_GAS)
            {
-             output_asm_insn ("addil L%%%0-%1,%%r1", xoperands);
-             ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+             /* The HP assembler can generate relocations for the
+                difference of two symbols.  GAS can do this for a
+                millicode symbol but not an arbitrary external
+                symbol when generating SOM output.  */
+             xoperands[1] = gen_label_rtx ();
+             targetm.asm_out.internal_label (asm_out_file, "L",
                                         CODE_LABEL_NUMBER (xoperands[1]));
-             output_asm_insn ("ldo R%%%0-%1(%%r1),%%r1", xoperands);
+             output_asm_insn ("addil L'%0-%l1,%%r1", xoperands);
+             output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands);
            }
          else
            {
@@ -5945,424 +7323,938 @@ output_millicode_call (insn, call_dest)
                               xoperands);
            }
 
-         /* Get the return address into %r31.  */
-         output_asm_insn ("blr 0,%3", xoperands);
+         /* Jump to our target address in %r1.  */
+         output_asm_insn ("bv %%r0(%%r1)", xoperands);
+       }
+    }
 
-         /* Branch to our target which is in %r1.  */
-         output_asm_insn ("bv,n %%r0(%%r1)", xoperands);
+  if (seq_length == 0)
+    output_asm_insn ("nop", xoperands);
 
-         /* Empty delay slot.  Note this insn gets fetched twice and
-            executed once.  To be safe we use a nop.  */
-         output_asm_insn ("nop", xoperands);
-       }
-      /* Pure portable runtime doesn't allow be/ble; we also don't have
-        PIC support in the assembler/linker, so this sequence is needed.  */
-      else if (TARGET_PORTABLE_RUNTIME)
-       {
-         xoperands[0] = call_dest;
-         /* Get the address of our target into %r29.  */
-         output_asm_insn ("ldil L%%%0,%%r29", xoperands);
-         output_asm_insn ("ldo R%%%0(%%r29),%%r29", xoperands);
+  /* We are done if there isn't a jump in the delay slot.  */
+  if (seq_length == 0 || GET_CODE (NEXT_INSN (insn)) != JUMP_INSN)
+    return "";
 
-         /* Get our return address into %r31.  */
-         output_asm_insn ("blr %%r0,%3", xoperands);
+  /* This call has an unconditional jump in its delay slot.  */
+  xoperands[0] = XEXP (PATTERN (NEXT_INSN (insn)), 1);
 
-         /* Jump to our target address in %r29.  */
-         output_asm_insn ("bv,n %%r0(%%r29)", xoperands);
+  /* See if the return address can be adjusted.  Use the containing
+     sequence insn's address.  */
+  if (INSN_ADDRESSES_SET_P ())
+    {
+      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);
 
-         /* Empty delay slot.  Note this insn gets fetched twice and
-            executed once.  To be safe we use a nop.  */
-         output_asm_insn ("nop", xoperands);
-       }
-      /* If we're allowed to use be/ble instructions, then this is the
-        best sequence to use for a long millicode call.  */
-      else
+      if (VAL_14_BITS_P (distance))
        {
-         xoperands[0] = call_dest;
-         output_asm_insn ("ldil L%%%0,%3", xoperands);
-         if (TARGET_PA_20)
-           output_asm_insn ("be,l R%%%0(%%sr4,%3),%%sr0,%%r31", xoperands);
-         else
-           output_asm_insn ("ble R%%%0(%%sr4,%3)", xoperands);
-         output_asm_insn ("nop", xoperands);
+         xoperands[1] = gen_label_rtx ();
+         output_asm_insn ("ldo %0-%1(%2),%2", xoperands);
+         targetm.asm_out.internal_label (asm_out_file, "L",
+                                         CODE_LABEL_NUMBER (xoperands[1]));
        }
+      else
+       /* ??? This branch may not reach its target.  */
+       output_asm_insn ("nop\n\tb,n %0", xoperands);
+    }
+  else
+    /* ??? This branch may not reach its target.  */
+    output_asm_insn ("nop\n\tb,n %0", xoperands);
 
-      /* If we had a jump in the call's delay slot, output it now.  */
-      if (dbr_sequence_length () != 0
-         && !delay_insn_deleted)
-       {
-         xoperands[0] = XEXP (PATTERN (NEXT_INSN (insn)), 1);
-         output_asm_insn ("b,n %0", xoperands);
+  /* Delete the jump.  */
+  SET_INSN_DELETED (NEXT_INSN (insn));
 
-         /* Now delete the delay insn.  */
-         PUT_CODE (NEXT_INSN (insn), NOTE);
-         NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
-         NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0;
-       }
-      return "";
+  return "";
+}
+
+/* Return the attribute length of the call instruction INSN.  The SIBCALL
+   flag indicates whether INSN is a regular call or a sibling call.  The
+   length returned must be longer than the code actually generated by
+   output_call.  Since branch shortening is done before delay branch
+   sequencing, there is no way to determine whether or not the delay
+   slot will be filled during branch shortening.  Even when the delay
+   slot is filled, we may have to add a nop if the delay slot contains
+   a branch that can't reach its target.  Thus, we always have to include
+   the delay slot in the length estimate.  This used to be done in
+   pa_adjust_insn_length but we do it here now as some sequences always
+   fill the delay slot and we can save four bytes in the estimate for
+   these sequences.  */
+
+int
+attr_length_call (rtx insn, int sibcall)
+{
+  int local_call;
+  rtx call, call_dest;
+  tree call_decl;
+  int length = 0;
+  rtx pat = PATTERN (insn);
+  unsigned long distance = -1;
+
+  gcc_assert (GET_CODE (insn) == CALL_INSN);
+
+  if (INSN_ADDRESSES_SET_P ())
+    {
+      unsigned long total;
+
+      total = IN_NAMED_SECTION_P (cfun->decl) ? 0 : total_code_bytes;
+      distance = (total + insn_current_reference_address (insn));
+      if (distance < total)
+       distance = -1;
     }
 
-  /* This call has an unconditional jump in its delay slot and the
-     call is known to reach its target or the beginning of the current
-     subspace.  */
+  gcc_assert (GET_CODE (pat) == PARALLEL);
 
-  /* Use the containing sequence insn's address.  */
-  seq_insn = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0)));
+  /* Get the call rtx.  */
+  call = XVECEXP (pat, 0, 0);
+  if (GET_CODE (call) == SET)
+    call = SET_SRC (call);
 
-  distance = INSN_ADDRESSES (INSN_UID (JUMP_LABEL (NEXT_INSN (insn))))
-              - INSN_ADDRESSES (INSN_UID (seq_insn)) - 8;
+  gcc_assert (GET_CODE (call) == CALL);
 
-  /* If the branch was too far away, emit a normal call followed
-     by a nop, followed by the unconditional branch.
+  /* Determine if this is a local call.  */
+  call_dest = XEXP (XEXP (call, 0), 0);
+  call_decl = SYMBOL_REF_DECL (call_dest);
+  local_call = call_decl && targetm.binds_local_p (call_decl);
 
-     If the branch is close, then adjust %r2 from within the
-     call's delay slot.  */
+  /* pc-relative branch.  */
+  if (!TARGET_LONG_CALLS
+      && ((TARGET_PA_20 && !sibcall && distance < 7600000)
+         || distance < 240000))
+    length += 8;
 
-  xoperands[0] = call_dest;
-  xoperands[1] = XEXP (PATTERN (NEXT_INSN (insn)), 1);
-  if (! VAL_14_BITS_P (distance))
-    output_asm_insn ("{bl|b,l} %0,%3\n\tnop\n\tb,n %1", xoperands);
+  /* 64-bit plabel sequence.  */
+  else if (TARGET_64BIT && !local_call)
+    length += sibcall ? 28 : 24;
+
+  /* non-pic long absolute branch sequence.  */
+  else if ((TARGET_LONG_ABS_CALL || local_call) && !flag_pic)
+    length += 12;
+
+  /* long pc-relative branch sequence.  */
+  else if (TARGET_LONG_PIC_SDIFF_CALL
+          || (TARGET_GAS && !TARGET_SOM
+              && (TARGET_LONG_PIC_PCREL_CALL || local_call)))
+    {
+      length += 20;
+
+      if (!TARGET_PA_20 && !TARGET_NO_SPACE_REGS && (!local_call || flag_pic))
+       length += 8;
+    }
+
+  /* 32-bit plabel sequence.  */
   else
     {
-      xoperands[2] = gen_label_rtx ();
-      output_asm_insn ("\n\t{bl|b,l} %0,%3\n\tldo %1-%2(%3),%3",
-                      xoperands);
-      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
-                                CODE_LABEL_NUMBER (xoperands[2]));
+      length += 32;
+
+      if (TARGET_SOM)
+       length += length_fp_args (insn);
+
+      if (flag_pic)
+       length += 4;
+
+      if (!TARGET_PA_20)
+       {
+         if (!sibcall)
+           length += 8;
+
+         if (!TARGET_NO_SPACE_REGS && (!local_call || flag_pic))
+           length += 8;
+       }
     }
 
-  /* 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 "";
+  return length;
 }
 
-extern struct obstack permanent_obstack;
-
-/* INSN is either a function call.  It may have an unconditional jump
+/* INSN is a function call.  It may have an unconditional jump
    in its delay slot.
 
    CALL_DEST is the routine we are calling.  */
 
 const char *
-output_call (insn, call_dest, sibcall)
-  rtx insn;
-  rtx call_dest;
-  int sibcall;
+output_call (rtx insn, rtx call_dest, int sibcall)
 {
-  int distance;
-  rtx xoperands[4];
-  rtx seq_insn;
+  int delay_insn_deleted = 0;
+  int delay_slot_filled = 0;
+  int seq_length = dbr_sequence_length ();
+  tree call_decl = SYMBOL_REF_DECL (call_dest);
+  int local_call = call_decl && targetm.binds_local_p (call_decl);
+  rtx xoperands[2];
+
+  xoperands[0] = call_dest;
 
-  /* Handle common case -- empty delay slot or no jump in the delay slot,
-     and we're sure that the branch will reach the beginning of the $CODE$
-     subspace.  */
-  if ((dbr_sequence_length () == 0
-       && get_attr_length (insn) == 8)
-      || (dbr_sequence_length () != 0
-         && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN
-         && get_attr_length (insn) == 4))
+  /* Handle the common case where we're sure that the branch will reach
+     the beginning of the "$CODE$" subspace.  This is the beginning of
+     the current function if we are in a named section.  */
+  if (!TARGET_LONG_CALLS && attr_length_call (insn, sibcall) == 8)
     {
-      xoperands[0] = call_dest;
       xoperands[1] = gen_rtx_REG (word_mode, sibcall ? 0 : 2);
-      output_asm_insn ("{bl|b,l} %0,%1%#", xoperands);
-      return "";
+      output_asm_insn ("{bl|b,l} %0,%1", xoperands);
     }
-
-  /* This call may not reach the beginning of the $CODE$ subspace.  */
-  if (get_attr_length (insn) > 8)
+  else
     {
-      int delay_insn_deleted = 0;
-      rtx xoperands[2];
-      rtx link;
-
-      /* We need to emit an inline long-call branch.  Furthermore,
-        because we're changing a named function call into an indirect
-        function call well after the parameters have been set up, we
-        need to make sure any FP args appear in both the integer
-        and FP registers.  Also, we need move any delay slot insn
-        out of the delay slot.  And finally, we can't rely on the linker
-        being able to fix the call to $$dyncall!  -- Yuk!.  */
-      if (dbr_sequence_length () != 0
-         && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN)
-       {
-         /* A non-jump insn in the delay slot.  By definition we can
-            emit this insn before the call (and in fact before argument
-            relocating.  */
-         final_scan_insn (NEXT_INSN (insn), asm_out_file, optimize, 0, 0);
-
-         /* Now delete the delay insn.  */
-         PUT_CODE (NEXT_INSN (insn), NOTE);
-         NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
-         NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0;
-         delay_insn_deleted = 1;
-       }
-
-      /* Now copy any FP arguments into integer registers.  */
-      for (link = CALL_INSN_FUNCTION_USAGE (insn); link; link = XEXP (link, 1))
-       {
-         int arg_mode, regno;
-         rtx use = XEXP (link, 0);
-         if (! (GET_CODE (use) == USE
-                && GET_CODE (XEXP (use, 0)) == REG
-                && FUNCTION_ARG_REGNO_P (REGNO (XEXP (use, 0)))))
-           continue;
+      if (TARGET_64BIT && !local_call)
+       {
+         /* ??? 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.  */
+         xoperands[0] = get_deferred_plabel (call_dest);
+         xoperands[1] = gen_label_rtx ();
 
-         arg_mode = GET_MODE (XEXP (use, 0));
-         regno = REGNO (XEXP (use, 0));
-         /* Is it a floating point register?  */
-         if (regno >= 32 && regno <= 39)
+         /* If this isn't a sibcall, we put the load of %r27 into the
+            delay slot.  We can't do this in a sibcall as we don't
+            have a second call-clobbered scratch register available.  */
+         if (seq_length != 0
+             && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN
+             && !sibcall)
            {
-             /* Copy from the FP register into an integer register
-                (via memory).  */
-             if (arg_mode == SFmode)
-               {
-                 xoperands[0] = XEXP (use, 0);
-                 xoperands[1] = gen_rtx_REG (SImode, 26 - (regno - 32) / 2);
-                 output_asm_insn ("{fstws|fstw} %0,-16(%%sr0,%%r30)",
-                                   xoperands);
-                 output_asm_insn ("ldw -16(%%sr0,%%r30),%1", xoperands);
-               }
-             else
-               {
-                 xoperands[0] = XEXP (use, 0);
-                 xoperands[1] = gen_rtx_REG (DImode, 25 - (regno - 34) / 2);
-                 output_asm_insn ("{fstds|fstd} %0,-16(%%sr0,%%r30)",
-                                   xoperands);
-                 output_asm_insn ("ldw -12(%%sr0,%%r30),%R1", xoperands);
-                 output_asm_insn ("ldw -16(%%sr0,%%r30),%1", xoperands);
-               }
-           }
-       }
+             final_scan_insn (NEXT_INSN (insn), asm_out_file,
+                              optimize, 0, NULL);
 
-      /* Don't have to worry about TARGET_PORTABLE_RUNTIME here since
-        we don't have any direct calls in that case.  */
-       {
-         int i;
-         const char *name = XSTR (call_dest, 0);
+             /* Now delete the delay insn.  */
+             SET_INSN_DELETED (NEXT_INSN (insn));
+             delay_insn_deleted = 1;
+           }
 
-         /* See if we have already put this function on the list
-            of deferred plabels.  This list is generally small,
-            so a liner search is not too ugly.  If it proves too
-            slow replace it with something faster.  */
-         for (i = 0; i < n_deferred_plabels; i++)
-           if (strcmp (name, deferred_plabels[i].name) == 0)
-             break;
+         output_asm_insn ("addil LT'%0,%%r27", xoperands);
+         output_asm_insn ("ldd RT'%0(%%r1),%%r1", xoperands);
+         output_asm_insn ("ldd 0(%%r1),%%r1", xoperands);
 
-         /* If the deferred plabel list is empty, or this entry was
-            not found on the list, create a new entry on the list.  */
-         if (deferred_plabels == NULL || i == n_deferred_plabels)
+         if (sibcall)
            {
-             const char *real_name;
-
-             if (deferred_plabels == 0)
-               deferred_plabels = (struct deferred_plabel *)
-                 xmalloc (1 * sizeof (struct deferred_plabel));
-             else
-               deferred_plabels = (struct deferred_plabel *)
-                 xrealloc (deferred_plabels,
-                           ((n_deferred_plabels + 1)
-                            * sizeof (struct deferred_plabel)));
-
-             i = n_deferred_plabels++;
-             deferred_plabels[i].internal_label = gen_label_rtx ();
-             deferred_plabels[i].name = obstack_alloc (&permanent_obstack,
-                                                       strlen (name) + 1);
-             strcpy (deferred_plabels[i].name, name);
-
-             /* Gross.  We have just implicitly taken the address of this
-                function, mark it as such.  */
-             STRIP_NAME_ENCODING (real_name, name);
-             TREE_SYMBOL_REFERENCED (get_identifier (real_name)) = 1;
+             output_asm_insn ("ldd 24(%%r1),%%r27", xoperands);
+             output_asm_insn ("ldd 16(%%r1),%%r1", xoperands);
+             output_asm_insn ("bve (%%r1)", xoperands);
            }
-
-         /* We have to load the address of the function using a procedure
-            label (plabel).  Inline plabels can lose for PIC and other
-            cases, so avoid them by creating a 32bit plabel in the data
-            segment.  */
-         if (flag_pic)
+         else
            {
-             xoperands[0] = deferred_plabels[i].internal_label;
-             xoperands[1] = gen_label_rtx ();
-
-             output_asm_insn ("addil LT%%%0,%%r19", xoperands);
-             output_asm_insn ("ldw RT%%%0(%%r1),%%r22", xoperands);
-             output_asm_insn ("ldw 0(%%r22),%%r22", xoperands);
+             output_asm_insn ("ldd 16(%%r1),%%r2", xoperands);
+             output_asm_insn ("bve,l (%%r2),%%r2", xoperands);
+             output_asm_insn ("ldd 24(%%r1),%%r27", xoperands);
+             delay_slot_filled = 1;
+           }
+       }
+      else
+       {
+         int indirect_call = 0;
+
+         /* Emit a long call.  There are several different sequences
+            of increasing length and complexity.  In most cases,
+             they don't allow an instruction in the delay slot.  */
+         if (!((TARGET_LONG_ABS_CALL || local_call) && !flag_pic)
+             && !TARGET_LONG_PIC_SDIFF_CALL
+             && !(TARGET_GAS && !TARGET_SOM
+                  && (TARGET_LONG_PIC_PCREL_CALL || local_call))
+             && !TARGET_64BIT)
+           indirect_call = 1;
+
+         if (seq_length != 0
+             && GET_CODE (NEXT_INSN (insn)) != JUMP_INSN
+             && !sibcall
+             && (!TARGET_PA_20
+                 || indirect_call
+                 || ((TARGET_LONG_ABS_CALL || local_call) && !flag_pic)))
+           {
+             /* A non-jump insn in the delay slot.  By definition we can
+                emit this insn before the call (and in fact before argument
+                relocating.  */
+             final_scan_insn (NEXT_INSN (insn), asm_out_file, optimize, 0,
+                              NULL);
+
+             /* Now delete the delay insn.  */
+             SET_INSN_DELETED (NEXT_INSN (insn));
+             delay_insn_deleted = 1;
+           }
 
-             /* Get our address + 8 into %r1.  */
-             output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
+         if ((TARGET_LONG_ABS_CALL || local_call) && !flag_pic)
+           {
+             /* This is the best sequence for making long calls in
+                non-pic code.  Unfortunately, GNU ld doesn't provide
+                the stub needed for external calls, and GAS's support
+                for this with the SOM linker is buggy.  It is safe
+                to use this for local calls.  */
+             output_asm_insn ("ldil L'%0,%%r1", xoperands);
+             if (sibcall)
+               output_asm_insn ("be R'%0(%%sr4,%%r1)", xoperands);
+             else
+               {
+                 if (TARGET_PA_20)
+                   output_asm_insn ("be,l R'%0(%%sr4,%%r1),%%sr0,%%r31",
+                                    xoperands);
+                 else
+                   output_asm_insn ("ble R'%0(%%sr4,%%r1)", xoperands);
 
-             /* Add %r1 to the offset of dyncall from the next insn.  */
-             if (TARGET_SOM || !TARGET_GAS)
+                 output_asm_insn ("copy %%r31,%%r2", xoperands);
+                 delay_slot_filled = 1;
+               }
+           }
+         else
+           {
+             if (TARGET_LONG_PIC_SDIFF_CALL)
                {
-                 output_asm_insn ("addil L%%$$dyncall-%1,%%r1", xoperands);
-                 ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
+                 /* The HP assembler and linker can handle relocations
+                    for the difference of two symbols.  The HP assembler
+                    recognizes the sequence as a pc-relative call and
+                    the linker provides stubs when needed.  */
+                 xoperands[1] = gen_label_rtx ();
+                 output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
+                 output_asm_insn ("addil L'%0-%l1,%%r1", xoperands);
+                 targetm.asm_out.internal_label (asm_out_file, "L",
                                             CODE_LABEL_NUMBER (xoperands[1]));
-                 output_asm_insn ("ldo R%%$$dyncall-%1(%%r1),%%r1", xoperands);
+                 output_asm_insn ("ldo R'%0-%l1(%%r1),%%r1", xoperands);
                }
-             else
+             else if (TARGET_GAS && !TARGET_SOM
+                      && (TARGET_LONG_PIC_PCREL_CALL || local_call))
                {
-                 output_asm_insn ("addil L%%$$dyncall-$PIC_pcrel$0+4,%%r1",
+                 /*  GAS currently can't generate the relocations that
+                     are needed for the SOM linker under HP-UX using this
+                     sequence.  The GNU linker doesn't generate the stubs
+                     that are needed for external calls on TARGET_ELF32
+                     with this sequence.  For now, we have to use a
+                     longer plabel sequence when using GAS.  */
+                 output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
+                 output_asm_insn ("addil L'%0-$PIC_pcrel$0+4,%%r1",
                                   xoperands);
-                 output_asm_insn ("ldo R%%$$dyncall-$PIC_pcrel$0+8(%%r1),%%r1",
+                 output_asm_insn ("ldo R'%0-$PIC_pcrel$0+8(%%r1),%%r1",
                                   xoperands);
                }
+             else
+               {
+                 /* Emit a long plabel-based call sequence.  This is
+                    essentially an inline implementation of $$dyncall.
+                    We don't actually try to call $$dyncall as this is
+                    as difficult as calling the function itself.  */
+                 xoperands[0] = get_deferred_plabel (call_dest);
+                 xoperands[1] = gen_label_rtx ();
+
+                 /* Since the call is indirect, FP arguments in registers
+                    need to be copied to the general registers.  Then, the
+                    argument relocation stub will copy them back.  */
+                 if (TARGET_SOM)
+                   copy_fp_args (insn);
+
+                 if (flag_pic)
+                   {
+                     output_asm_insn ("addil LT'%0,%%r19", xoperands);
+                     output_asm_insn ("ldw RT'%0(%%r1),%%r1", xoperands);
+                     output_asm_insn ("ldw 0(%%r1),%%r1", xoperands);
+                   }
+                 else
+                   {
+                     output_asm_insn ("addil LR'%0-$global$,%%r27",
+                                      xoperands);
+                     output_asm_insn ("ldw RR'%0-$global$(%%r1),%%r1",
+                                      xoperands);
+                   }
 
-             /* Get the return address into %r31.  */
-             output_asm_insn ("blr %%r0,%%r31", xoperands);
+                 output_asm_insn ("bb,>=,n %%r1,30,.+16", xoperands);
+                 output_asm_insn ("depi 0,31,2,%%r1", xoperands);
+                 output_asm_insn ("ldw 4(%%sr0,%%r1),%%r19", xoperands);
+                 output_asm_insn ("ldw 0(%%sr0,%%r1),%%r1", xoperands);
 
-             /* Branch to our target which is in %r1.  */
-             output_asm_insn ("bv %%r0(%%r1)", xoperands);
+                 if (!sibcall && !TARGET_PA_20)
+                   {
+                     output_asm_insn ("{bl|b,l} .+8,%%r2", xoperands);
+                     if (TARGET_NO_SPACE_REGS || (local_call && !flag_pic))
+                       output_asm_insn ("addi 8,%%r2,%%r2", xoperands);
+                     else
+                       output_asm_insn ("addi 16,%%r2,%%r2", xoperands);
+                   }
+               }
 
-             if (sibcall)
+             if (TARGET_PA_20)
                {
-                 /* This call never returns, so we do not need to fix the
-                    return pointer.  */
-                 output_asm_insn ("nop", xoperands);
+                 if (sibcall)
+                   output_asm_insn ("bve (%%r1)", xoperands);
+                 else
+                   {
+                     if (indirect_call)
+                       {
+                         output_asm_insn ("bve,l (%%r1),%%r2", xoperands);
+                         output_asm_insn ("stw %%r2,-24(%%sp)", xoperands);
+                         delay_slot_filled = 1;
+                       }
+                     else
+                       output_asm_insn ("bve,l (%%r1),%%r2", xoperands);
+                   }
                }
              else
                {
-                 /* Copy the return address into %r2 also.  */
-                 output_asm_insn ("copy %%r31,%%r2", xoperands);
+                 if (!TARGET_NO_SPACE_REGS && (!local_call || flag_pic))
+                   output_asm_insn ("ldsid (%%r1),%%r31\n\tmtsp %%r31,%%sr0",
+                                    xoperands);
+
+                 if (sibcall)
+                   {
+                     if (TARGET_NO_SPACE_REGS || (local_call && !flag_pic))
+                       output_asm_insn ("be 0(%%sr4,%%r1)", xoperands);
+                     else
+                       output_asm_insn ("be 0(%%sr0,%%r1)", xoperands);
+                   }
+                 else
+                   {
+                     if (TARGET_NO_SPACE_REGS || (local_call && !flag_pic))
+                       output_asm_insn ("ble 0(%%sr4,%%r1)", xoperands);
+                     else
+                       output_asm_insn ("ble 0(%%sr0,%%r1)", xoperands);
+
+                     if (indirect_call)
+                       output_asm_insn ("stw %%r31,-24(%%sp)", xoperands);
+                     else
+                       output_asm_insn ("copy %%r31,%%r2", xoperands);
+                     delay_slot_filled = 1;
+                   }
                }
            }
-         else
-           {
-             xoperands[0] = deferred_plabels[i].internal_label;
+       }
+    }
 
-             /* Get the address of our target into %r22.  */
-             output_asm_insn ("addil LR%%%0-$global$,%%r27", xoperands);
-             output_asm_insn ("ldw RR%%%0-$global$(%%r1),%%r22", xoperands);
+  if (!delay_slot_filled && (seq_length == 0 || delay_insn_deleted))
+    output_asm_insn ("nop", xoperands);
 
-             /* Get the high part of the  address of $dyncall into %r2, then
-                add in the low part in the branch instruction.  */
-             output_asm_insn ("ldil L%%$$dyncall,%%r2", xoperands);
-             if (TARGET_PA_20)
-               output_asm_insn ("be,l R%%$$dyncall(%%sr4,%%r2),%%sr0,%%r31",
-                                xoperands);
-             else
-               output_asm_insn ("ble R%%$$dyncall(%%sr4,%%r2)", xoperands);
+  /* We are done if there isn't a jump in the delay slot.  */
+  if (seq_length == 0
+      || delay_insn_deleted
+      || GET_CODE (NEXT_INSN (insn)) != JUMP_INSN)
+    return "";
 
-             if (sibcall)
-               {
-                 /* This call never returns, so we do not need to fix the
-                    return pointer.  */
-                 output_asm_insn ("nop", xoperands);
-               }
-             else
-               {
-                 /* Copy the return address into %r2 also.  */
-                 output_asm_insn ("copy %%r31,%%r2", xoperands);
-               }
-           }
+  /* A sibcall should never have a branch in the delay slot.  */
+  gcc_assert (!sibcall);
+
+  /* This call has an unconditional jump in its delay slot.  */
+  xoperands[0] = XEXP (PATTERN (NEXT_INSN (insn)), 1);
+
+  if (!delay_slot_filled && INSN_ADDRESSES_SET_P ())
+    {
+      /* See if the return address can be adjusted.  Use the containing
+         sequence insn's address.  */
+      rtx seq_insn = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0)));
+      int distance = (INSN_ADDRESSES (INSN_UID (JUMP_LABEL (NEXT_INSN (insn))))
+                     - INSN_ADDRESSES (INSN_UID (seq_insn)) - 8);
+
+      if (VAL_14_BITS_P (distance))
+       {
+         xoperands[1] = gen_label_rtx ();
+         output_asm_insn ("ldo %0-%1(%%r2),%%r2", xoperands);
+         targetm.asm_out.internal_label (asm_out_file, "L",
+                                         CODE_LABEL_NUMBER (xoperands[1]));
+       }
+      else
+       output_asm_insn ("nop\n\tb,n %0", xoperands);
+    }
+  else
+    output_asm_insn ("b,n %0", xoperands);
+
+  /* Delete the jump.  */
+  SET_INSN_DELETED (NEXT_INSN (insn));
+
+  return "";
+}
+
+/* Return the attribute length of the indirect call instruction INSN.
+   The length must match the code generated by output_indirect call.
+   The returned length includes the delay slot.  Currently, the delay
+   slot of an indirect call sequence is not exposed and it is used by
+   the sequence itself.  */
+
+int
+attr_length_indirect_call (rtx insn)
+{
+  unsigned long distance = -1;
+  unsigned long total = IN_NAMED_SECTION_P (cfun->decl) ? 0 : total_code_bytes;
+
+  if (INSN_ADDRESSES_SET_P ())
+    {
+      distance = (total + insn_current_reference_address (insn));
+      if (distance < total)
+       distance = -1;
+    }
+
+  if (TARGET_64BIT)
+    return 12;
+
+  if (TARGET_FAST_INDIRECT_CALLS
+      || (!TARGET_PORTABLE_RUNTIME
+         && ((TARGET_PA_20 && !TARGET_SOM && distance < 7600000)
+             || distance < 240000)))
+    return 8;
+
+  if (flag_pic)
+    return 24;
+
+  if (TARGET_PORTABLE_RUNTIME)
+    return 20;
+
+  /* Out of reach, can use ble.  */
+  return 12;
+}
+
+const char *
+output_indirect_call (rtx insn, rtx call_dest)
+{
+  rtx xoperands[1];
+
+  if (TARGET_64BIT)
+    {
+      xoperands[0] = call_dest;
+      output_asm_insn ("ldd 16(%0),%%r2", xoperands);
+      output_asm_insn ("bve,l (%%r2),%%r2\n\tldd 24(%0),%%r27", xoperands);
+      return "";
+    }
+
+  /* First the special case for kernels, level 0 systems, etc.  */
+  if (TARGET_FAST_INDIRECT_CALLS)
+    return "ble 0(%%sr4,%%r22)\n\tcopy %%r31,%%r2"; 
+
+  /* Now the normal case -- we can reach $$dyncall directly or
+     we're sure that we can get there via a long-branch stub. 
+
+     No need to check target flags as the length uniquely identifies
+     the remaining cases.  */
+  if (attr_length_indirect_call (insn) == 8)
+    {
+      /* The HP linker sometimes substitutes a BLE for BL/B,L calls to
+        $$dyncall.  Since BLE uses %r31 as the link register, the 22-bit
+        variant of the B,L instruction can't be used on the SOM target.  */
+      if (TARGET_PA_20 && !TARGET_SOM)
+       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.  */
+  if (attr_length_indirect_call (insn) == 12)
+    return ".CALL\tARGW0=GR\n\tldil L'$$dyncall,%%r2\n\tble R'$$dyncall(%%sr4,%%r2)\n\tcopy %%r31,%%r2";
+
+  /* Long millicode call for portable runtime.  */
+  if (attr_length_indirect_call (insn) == 20)
+    return "ldil L'$$dyncall,%%r31\n\tldo R'$$dyncall(%%r31),%%r31\n\tblr %%r0,%%r2\n\tbv,n %%r0(%%r31)\n\tnop";
+
+  /* We need a long PIC call to $$dyncall.  */
+  xoperands[0] = NULL_RTX;
+  output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
+  if (TARGET_SOM || !TARGET_GAS)
+    {
+      xoperands[0] = gen_label_rtx ();
+      output_asm_insn ("addil L'$$dyncall-%0,%%r1", xoperands);
+      targetm.asm_out.internal_label (asm_out_file, "L",
+                                     CODE_LABEL_NUMBER (xoperands[0]));
+      output_asm_insn ("ldo R'$$dyncall-%0(%%r1),%%r1", xoperands);
+    }
+  else
+    {
+      output_asm_insn ("addil L'$$dyncall-$PIC_pcrel$0+4,%%r1", xoperands);
+      output_asm_insn ("ldo R'$$dyncall-$PIC_pcrel$0+8(%%r1),%%r1",
+                      xoperands);
+    }
+  output_asm_insn ("blr %%r0,%%r2", xoperands);
+  output_asm_insn ("bv,n %%r0(%%r1)\n\tnop", xoperands);
+  return "";
+}
+
+/* Return the total length of the save and restore instructions needed for
+   the data linkage table pointer (i.e., the PIC register) across the call
+   instruction INSN.  No-return calls do not require a save and restore.
+   In addition, we may be able to avoid the save and restore for calls
+   within the same translation unit.  */
+
+int
+attr_length_save_restore_dltp (rtx insn)
+{
+  if (find_reg_note (insn, REG_NORETURN, NULL_RTX))
+    return 0;
+
+  return 8;
+}
+
+/* In HPUX 8.0's shared library scheme, special relocations are needed
+   for function labels if they might be passed to a function
+   in a shared library (because shared libraries don't live in code
+   space), and special magic is needed to construct their address.  */
+
+void
+hppa_encode_label (rtx sym)
+{
+  const char *str = XSTR (sym, 0);
+  int len = strlen (str) + 1;
+  char *newstr, *p;
+
+  p = newstr = XALLOCAVEC (char, len + 1);
+  *p++ = '@';
+  strcpy (p, str);
+
+  XSTR (sym, 0) = ggc_alloc_string (newstr, len);
+}
+
+static void
+pa_encode_section_info (tree decl, rtx rtl, int first)
+{
+  int old_referenced = 0;
+
+  if (!first && MEM_P (rtl) && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF)
+    old_referenced
+      = SYMBOL_REF_FLAGS (XEXP (rtl, 0)) & SYMBOL_FLAG_REFERENCED;
+
+  default_encode_section_info (decl, rtl, first);
+
+  if (first && TEXT_SPACE_P (decl))
+    {
+      SYMBOL_REF_FLAG (XEXP (rtl, 0)) = 1;
+      if (TREE_CODE (decl) == FUNCTION_DECL)
+       hppa_encode_label (XEXP (rtl, 0));
+    }
+  else if (old_referenced)
+    SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= old_referenced;
+}
+
+/* This is sort of inverse to pa_encode_section_info.  */
+
+static const char *
+pa_strip_name_encoding (const char *str)
+{
+  str += (*str == '@');
+  str += (*str == '*');
+  return str;
+}
+
+int
+function_label_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  return GET_CODE (op) == SYMBOL_REF && FUNCTION_NAME_P (XSTR (op, 0));
+}
+
+/* Returns 1 if OP is a function label involved in a simple addition
+   with a constant.  Used to keep certain patterns from matching
+   during instruction combination.  */
+int
+is_function_label_plus_const (rtx op)
+{
+  /* Strip off any CONST.  */
+  if (GET_CODE (op) == CONST)
+    op = XEXP (op, 0);
+
+  return (GET_CODE (op) == PLUS
+         && function_label_operand (XEXP (op, 0), Pmode)
+         && GET_CODE (XEXP (op, 1)) == CONST_INT);
+}
+
+/* Output assembly code for a thunk to FUNCTION.  */
+
+static void
+pa_asm_output_mi_thunk (FILE *file, tree thunk_fndecl, HOST_WIDE_INT delta,
+                       HOST_WIDE_INT vcall_offset ATTRIBUTE_UNUSED,
+                       tree function)
+{
+  static unsigned int current_thunk_number;
+  int val_14 = VAL_14_BITS_P (delta);
+  unsigned int old_last_address = last_address, nbytes = 0;
+  char label[16];
+  rtx xoperands[4];
+
+  xoperands[0] = XEXP (DECL_RTL (function), 0);
+  xoperands[1] = XEXP (DECL_RTL (thunk_fndecl), 0);
+  xoperands[2] = GEN_INT (delta);
+
+  ASM_OUTPUT_LABEL (file, XSTR (xoperands[1], 0));
+  fprintf (file, "\t.PROC\n\t.CALLINFO FRAME=0,NO_CALLS\n\t.ENTRY\n");
+
+  /* Output the thunk.  We know that the function is in the same
+     translation unit (i.e., the same space) as the thunk, and that
+     thunks are output after their method.  Thus, we don't need an
+     external branch to reach the function.  With SOM and GAS,
+     functions and thunks are effectively in different sections.
+     Thus, we can always use a IA-relative branch and the linker
+     will add a long branch stub if necessary.
+
+     However, we have to be careful when generating PIC code on the
+     SOM port to ensure that the sequence does not transfer to an
+     import stub for the target function as this could clobber the
+     return value saved at SP-24.  This would also apply to the
+     32-bit linux port if the multi-space model is implemented.  */
+  if ((!TARGET_LONG_CALLS && TARGET_SOM && !TARGET_PORTABLE_RUNTIME
+       && !(flag_pic && TREE_PUBLIC (function))
+       && (TARGET_GAS || last_address < 262132))
+      || (!TARGET_LONG_CALLS && !TARGET_SOM && !TARGET_PORTABLE_RUNTIME
+         && ((targetm.have_named_sections
+              && DECL_SECTION_NAME (thunk_fndecl) != NULL
+              /* The GNU 64-bit linker has rather poor stub management.
+                 So, we use a long branch from thunks that aren't in
+                 the same section as the target function.  */
+              && ((!TARGET_64BIT
+                   && (DECL_SECTION_NAME (thunk_fndecl)
+                       != DECL_SECTION_NAME (function)))
+                  || ((DECL_SECTION_NAME (thunk_fndecl)
+                       == DECL_SECTION_NAME (function))
+                      && last_address < 262132)))
+             || (targetm.have_named_sections
+                 && DECL_SECTION_NAME (thunk_fndecl) == NULL
+                 && DECL_SECTION_NAME (function) == NULL
+                 && last_address < 262132)
+             || (!targetm.have_named_sections && last_address < 262132))))
+    {
+      if (!val_14)
+       output_asm_insn ("addil L'%2,%%r26", xoperands);
+
+      output_asm_insn ("b %0", xoperands);
+
+      if (val_14)
+       {
+         output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
+         nbytes += 8;
+       }
+      else
+       {
+         output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+         nbytes += 12;
+       }
+    }
+  else if (TARGET_64BIT)
+    {
+      /* We only have one call-clobbered scratch register, so we can't
+         make use of the delay slot if delta doesn't fit in 14 bits.  */
+      if (!val_14)
+       {
+         output_asm_insn ("addil L'%2,%%r26", xoperands);
+         output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+       }
+
+      output_asm_insn ("b,l .+8,%%r1", xoperands);
+
+      if (TARGET_GAS)
+       {
+         output_asm_insn ("addil L'%0-$PIC_pcrel$0+4,%%r1", xoperands);
+         output_asm_insn ("ldo R'%0-$PIC_pcrel$0+8(%%r1),%%r1", xoperands);
+       }
+      else
+       {
+         xoperands[3] = GEN_INT (val_14 ? 8 : 16);
+         output_asm_insn ("addil L'%0-%1-%3,%%r1", xoperands);
        }
 
-      /* If we had a jump in the call's delay slot, output it now.  */
-      if (dbr_sequence_length () != 0
-         && !delay_insn_deleted)
+      if (val_14)
+       {
+         output_asm_insn ("bv %%r0(%%r1)", xoperands);
+         output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
+         nbytes += 20;
+       }
+      else
+       {
+         output_asm_insn ("bv,n %%r0(%%r1)", xoperands);
+         nbytes += 24;
+       }
+    }
+  else if (TARGET_PORTABLE_RUNTIME)
+    {
+      output_asm_insn ("ldil L'%0,%%r1", xoperands);
+      output_asm_insn ("ldo R'%0(%%r1),%%r22", xoperands);
+
+      if (!val_14)
+       output_asm_insn ("addil L'%2,%%r26", xoperands);
+
+      output_asm_insn ("bv %%r0(%%r22)", xoperands);
+
+      if (val_14)
+       {
+         output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
+         nbytes += 16;
+       }
+      else
+       {
+         output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+         nbytes += 20;
+       }
+    }
+  else if (TARGET_SOM && flag_pic && TREE_PUBLIC (function))
+    {
+      /* The function is accessible from outside this module.  The only
+        way to avoid an import stub between the thunk and function is to
+        call the function directly with an indirect sequence similar to
+        that used by $$dyncall.  This is possible because $$dyncall acts
+        as the import stub in an indirect call.  */
+      ASM_GENERATE_INTERNAL_LABEL (label, "LTHN", current_thunk_number);
+      xoperands[3] = gen_rtx_SYMBOL_REF (Pmode, label);
+      output_asm_insn ("addil LT'%3,%%r19", xoperands);
+      output_asm_insn ("ldw RT'%3(%%r1),%%r22", xoperands);
+      output_asm_insn ("ldw 0(%%sr0,%%r22),%%r22", xoperands);
+      output_asm_insn ("bb,>=,n %%r22,30,.+16", xoperands);
+      output_asm_insn ("depi 0,31,2,%%r22", xoperands);
+      output_asm_insn ("ldw 4(%%sr0,%%r22),%%r19", xoperands);
+      output_asm_insn ("ldw 0(%%sr0,%%r22),%%r22", xoperands);
+
+      if (!val_14)
+       {
+         output_asm_insn ("addil L'%2,%%r26", xoperands);
+         nbytes += 4;
+       }
+
+      if (TARGET_PA_20)
+       {
+         output_asm_insn ("bve (%%r22)", xoperands);
+         nbytes += 36;
+       }
+      else if (TARGET_NO_SPACE_REGS)
+       {
+         output_asm_insn ("be 0(%%sr4,%%r22)", xoperands);
+         nbytes += 36;
+       }
+      else
+       {
+         output_asm_insn ("ldsid (%%sr0,%%r22),%%r21", xoperands);
+         output_asm_insn ("mtsp %%r21,%%sr0", xoperands);
+         output_asm_insn ("be 0(%%sr0,%%r22)", xoperands);
+         nbytes += 44;
+       }
+
+      if (val_14)
+       output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
+      else
+       output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+    }
+  else if (flag_pic)
+    {
+      output_asm_insn ("{bl|b,l} .+8,%%r1", xoperands);
+
+      if (TARGET_SOM || !TARGET_GAS)
+       {
+         output_asm_insn ("addil L'%0-%1-8,%%r1", xoperands);
+         output_asm_insn ("ldo R'%0-%1-8(%%r1),%%r22", xoperands);
+       }
+      else
+       {
+         output_asm_insn ("addil L'%0-$PIC_pcrel$0+4,%%r1", xoperands);
+         output_asm_insn ("ldo R'%0-$PIC_pcrel$0+8(%%r1),%%r22", xoperands);
+       }
+
+      if (!val_14)
+       output_asm_insn ("addil L'%2,%%r26", xoperands);
+
+      output_asm_insn ("bv %%r0(%%r22)", xoperands);
+
+      if (val_14)
+       {
+         output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
+         nbytes += 20;
+       }
+      else
        {
-         xoperands[0] = XEXP (PATTERN (NEXT_INSN (insn)), 1);
-         output_asm_insn ("b,n %0", xoperands);
-
-         /* Now delete the delay insn.  */
-         PUT_CODE (NEXT_INSN (insn), NOTE);
-         NOTE_LINE_NUMBER (NEXT_INSN (insn)) = NOTE_INSN_DELETED;
-         NOTE_SOURCE_FILE (NEXT_INSN (insn)) = 0;
+         output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+         nbytes += 24;
        }
-      return "";
     }
+  else
+    {
+      if (!val_14)
+       output_asm_insn ("addil L'%2,%%r26", xoperands);
 
-  /* This call has an unconditional jump in its delay slot and the
-     call is known to reach its target or the beginning of the current
-     subspace.  */
-
-  /* Use the containing sequence insn's address.  */
-  seq_insn = NEXT_INSN (PREV_INSN (XVECEXP (final_sequence, 0, 0)));
+      output_asm_insn ("ldil L'%0,%%r22", xoperands);
+      output_asm_insn ("be R'%0(%%sr4,%%r22)", xoperands);
 
-  distance = INSN_ADDRESSES (INSN_UID (JUMP_LABEL (NEXT_INSN (insn))))
-              - INSN_ADDRESSES (INSN_UID (seq_insn)) - 8;
+      if (val_14)
+       {
+         output_asm_insn ("ldo %2(%%r26),%%r26", xoperands);
+         nbytes += 12;
+       }
+      else
+       {
+         output_asm_insn ("ldo R'%2(%%r1),%%r26", xoperands);
+         nbytes += 16;
+       }
+    }
 
-  /* If the branch was too far away, emit a normal call followed
-     by a nop, followed by the unconditional branch.
+  fprintf (file, "\t.EXIT\n\t.PROCEND\n");
 
-     If the branch is close, then adjust %r2 from within the
-     call's delay slot.  */
+  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.  */
+      in_section = NULL;
+      cfun->machine->in_nsubspa = 2;
+    }
 
-  xoperands[0] = call_dest;
-  xoperands[1] = XEXP (PATTERN (NEXT_INSN (insn)), 1);
-  if (! VAL_14_BITS_P (distance))
-    output_asm_insn ("{bl|b,l} %0,%%r2\n\tnop\n\tb,n %1", xoperands);
-  else
+  if (TARGET_SOM && flag_pic && TREE_PUBLIC (function))
     {
-      xoperands[3] = gen_label_rtx ();
-      output_asm_insn ("\n\t{bl|b,l} %0,%%r2\n\tldo %1-%3(%%r2),%%r2",
-                      xoperands);
-      ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, "L",
-                                CODE_LABEL_NUMBER (xoperands[3]));
+      switch_to_section (data_section);
+      output_asm_insn (".align 4", xoperands);
+      ASM_OUTPUT_LABEL (file, label);
+      output_asm_insn (".word P'%0", 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 "";
+  current_thunk_number++;
+  nbytes = ((nbytes + FUNCTION_BOUNDARY / BITS_PER_UNIT - 1)
+           & ~(FUNCTION_BOUNDARY / BITS_PER_UNIT - 1));
+  last_address += nbytes;
+  if (old_last_address > last_address)
+    last_address = UINT_MAX;
+  update_total_code_bytes (nbytes);
 }
 
-/* In HPUX 8.0's shared library scheme, special relocations are needed
-   for function labels if they might be passed to a function
-   in a shared library (because shared libraries don't live in code
-   space), and special magic is needed to construct their address.  */
+/* Only direct calls to static functions are allowed to be sibling (tail)
+   call optimized.
 
-void
-hppa_encode_label (sym)
-     rtx sym;
-{
-  const char *str = XSTR (sym, 0);
-  int len = strlen (str) + 1;
-  char *newstr, *p;
+   This restriction is necessary because some linker generated stubs will
+   store return pointers into rp' in some cases which might clobber a
+   live value already in rp'.
 
-  p = newstr = alloca (len + 1);
-  if (str[0] == '*')
-    {
-      str++;
-      len--;
-    }
-  *p++ = '@';
-  strcpy (p, str);
+   In a sibcall the current function and the target function share stack
+   space.  Thus if the path to the current function and the path to the
+   target function save a value in rp', they save the value into the
+   same stack slot, which has undesirable consequences.
 
-  XSTR (sym, 0) = ggc_alloc_string (newstr, len);
-}
+   Because of the deferred binding nature of shared libraries any function
+   with external scope could be in a different load module and thus require
+   rp' to be saved when calling that function.  So sibcall optimizations
+   can only be safe for static function.
 
-int
-function_label_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+   Note that GCC never needs return value relocations, so we don't have to
+   worry about static calls with return value relocations (which require
+   saving rp').
+
+   It is safe to perform a sibcall optimization when the target function
+   will never return.  */
+static bool
+pa_function_ok_for_sibcall (tree decl, tree exp ATTRIBUTE_UNUSED)
 {
-  return GET_CODE (op) == SYMBOL_REF && FUNCTION_NAME_P (XSTR (op, 0));
+  if (TARGET_PORTABLE_RUNTIME)
+    return false;
+
+  /* Sibcalls are ok for TARGET_ELF32 as along as the linker is used in
+     single subspace mode and the call is not indirect.  As far as I know,
+     there is no operating system support for the multiple subspace mode.
+     It might be possible to support indirect calls if we didn't use
+     $$dyncall (see the indirect sequence generated in output_call).  */
+  if (TARGET_ELF32)
+    return (decl != NULL_TREE);
+
+  /* Sibcalls are not ok because the arg pointer register is not a fixed
+     register.  This prevents the sibcall optimization from occurring.  In
+     addition, there are problems with stub placement using GNU ld.  This
+     is because a normal sibcall branch uses a 17-bit relocation while
+     a regular call branch uses a 22-bit relocation.  As a result, more
+     care needs to be taken in the placement of long-branch stubs.  */
+  if (TARGET_64BIT)
+    return false;
+
+  /* Sibcalls are only ok within a translation unit.  */
+  return (decl && !TREE_PUBLIC (decl));
 }
 
-/* Returns 1 if OP is a function label involved in a simple addition
-   with a constant.  Used to keep certain patterns from matching
-   during instruction combination.  */
-int
-is_function_label_plus_const (op)
-     rtx op;
+/* ??? 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 (const_rtx x, int outer_code)
 {
-  /* Strip off any CONST.  */
-  if (GET_CODE (op) == CONST)
-    op = XEXP (op, 0);
-
-  return (GET_CODE (op) == PLUS
-         && function_label_operand (XEXP (op, 0), Pmode)
-         && GET_CODE (XEXP (op, 1)) == CONST_INT);
+  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
-fmpyaddoperands (operands)
-     rtx *operands;
+fmpyaddoperands (rtx *operands)
 {
   enum machine_mode mode = GET_MODE (operands[0]);
 
@@ -6392,13 +8284,13 @@ fmpyaddoperands (operands)
       && ! rtx_equal_p (operands[3], operands[5]))
     return 0;
 
-  /* Inout operand of add can not conflict with any operands from multiply.  */
+  /* Inout operand of add cannot conflict with any operands from multiply.  */
   if (rtx_equal_p (operands[3], operands[0])
      || rtx_equal_p (operands[3], operands[1])
      || rtx_equal_p (operands[3], operands[2]))
     return 0;
 
-  /* multiply can not feed into addition operands.  */
+  /* multiply cannot feed into addition operands.  */
   if (rtx_equal_p (operands[4], operands[0])
       || rtx_equal_p (operands[5], operands[0]))
     return 0;
@@ -6417,11 +8309,130 @@ fmpyaddoperands (operands)
   return 1;
 }
 
+#if !defined(USE_COLLECT2)
+static void
+pa_asm_out_constructor (rtx symbol, int priority)
+{
+  if (!function_label_operand (symbol, VOIDmode))
+    hppa_encode_label (symbol);
+
+#ifdef CTORS_SECTION_ASM_OP
+  default_ctor_section_asm_out_constructor (symbol, priority);
+#else
+# ifdef TARGET_ASM_NAMED_SECTION
+  default_named_section_asm_out_constructor (symbol, priority);
+# else
+  default_stabs_asm_out_constructor (symbol, priority);
+# endif
+#endif
+}
+
+static void
+pa_asm_out_destructor (rtx symbol, int priority)
+{
+  if (!function_label_operand (symbol, VOIDmode))
+    hppa_encode_label (symbol);
+
+#ifdef DTORS_SECTION_ASM_OP
+  default_dtor_section_asm_out_destructor (symbol, priority);
+#else
+# ifdef TARGET_ASM_NAMED_SECTION
+  default_named_section_asm_out_destructor (symbol, priority);
+# else
+  default_stabs_asm_out_destructor (symbol, priority);
+# endif
+#endif
+}
+#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)
+{
+  switch_to_section (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)
+{
+  unsigned int max_common_align;
+
+  max_common_align = TARGET_64BIT ? 128 : (size >= 4096 ? 256 : 64);
+  if (align > max_common_align)
+    {
+      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;
+    }
+
+  switch_to_section (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)
+{
+  switch_to_section (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
-fmpysuboperands (operands)
-     rtx *operands;
+fmpysuboperands (rtx *operands)
 {
   enum machine_mode mode = GET_MODE (operands[0]);
 
@@ -6450,11 +8461,11 @@ fmpysuboperands (operands)
   if (! rtx_equal_p (operands[3], operands[4]))
     return 0;
 
-  /* multiply can not feed into subtraction.  */
+  /* multiply cannot feed into subtraction.  */
   if (rtx_equal_p (operands[5], operands[0]))
     return 0;
 
-  /* Inout operand of sub can not conflict with any operands from multiply.  */
+  /* Inout operand of sub cannot conflict with any operands from multiply.  */
   if (rtx_equal_p (operands[3], operands[0])
      || rtx_equal_p (operands[3], operands[1])
      || rtx_equal_p (operands[3], operands[2]))
@@ -6474,20 +8485,10 @@ fmpysuboperands (operands)
   return 1;
 }
 
-int
-plus_xor_ior_operator (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return (GET_CODE (op) == PLUS || GET_CODE (op) == XOR
-         || GET_CODE (op) == IOR);
-}
-
 /* Return 1 if the given constant is 2, 4, or 8.  These are the valid
    constants for shadd instructions.  */
-static int
-shadd_constant_p (val)
-     int val;
+int
+shadd_constant_p (int val)
 {
   if (val == 2 || val == 4 || val == 8)
     return 1;
@@ -6495,52 +8496,39 @@ shadd_constant_p (val)
     return 0;
 }
 
-/* Return 1 if OP is a CONST_INT with the value 2, 4, or 8.  These are
-   the valid constant for shadd instructions.  */
-int
-shadd_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  return (GET_CODE (op) == CONST_INT && shadd_constant_p (INTVAL (op)));
-}
-
-/* Return 1 if OP is valid as a base register in a reg + reg address.  */
+/* Return 1 if OP is valid as a base or index register in a
+   REG+REG address.  */
 
 int
-basereg_operand (op, mode)
-     rtx op;
-     enum machine_mode mode;
-{
-  /* cse will create some unscaled indexed addresses, however; it
-     generally isn't a win on the PA, so avoid creating unscaled
-     indexed addresses until after cse is finished.  */
-  if (!cse_not_expected)
+borx_reg_operand (rtx op, enum machine_mode mode)
+{
+  if (GET_CODE (op) != REG)
     return 0;
 
-  /* Allow any register when TARGET_NO_SPACE_REGS is in effect since
-     we don't have to worry about the braindamaged implicit space
-     register selection from the basereg.  */
-  if (TARGET_NO_SPACE_REGS)
-    return (GET_CODE (op) == REG);
+  /* We must reject virtual registers as the only expressions that
+     can be instantiated are REG and REG+CONST.  */
+  if (op == virtual_incoming_args_rtx
+      || op == virtual_stack_vars_rtx
+      || op == virtual_stack_dynamic_rtx
+      || op == virtual_outgoing_args_rtx
+      || op == virtual_cfa_rtx)
+    return 0;
 
   /* While it's always safe to index off the frame pointer, it's not
-     always profitable, particularly when the frame pointer is being
-     eliminated.  */
-  if (! flag_omit_frame_pointer && op == frame_pointer_rtx)
-    return 1;
+     profitable to do so when the frame pointer is being eliminated.  */
+  if (!reload_completed
+      && flag_omit_frame_pointer
+      && !cfun->calls_alloca
+      && op == frame_pointer_rtx)
+    return 0;
 
-  return (GET_CODE (op) == REG
-          && REG_POINTER (op)
-          && register_operand (op, mode));
+  return register_operand (op, mode);
 }
 
 /* Return 1 if this operand is anything other than a hard register.  */
 
 int
-non_hard_reg_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+non_hard_reg_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 {
   return ! (GET_CODE (op) == REG && REGNO (op) < FIRST_PSEUDO_REGISTER);
 }
@@ -6548,8 +8536,7 @@ non_hard_reg_operand (op, mode)
 /* Return 1 if INSN branches forward.  Should be using insn_addresses
    to avoid walking through all the insns...  */
 static int
-forward_branch_p (insn)
-     rtx insn;
+forward_branch_p (rtx insn)
 {
   rtx label = JUMP_LABEL (insn);
 
@@ -6566,27 +8553,14 @@ forward_branch_p (insn)
 
 /* Return 1 if OP is an equality comparison, else return 0.  */
 int
-eq_neq_comparison_operator (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+eq_neq_comparison_operator (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 {
   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 ATTRIBUTE_UNUSED;
-{
-  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;
+jump_in_call_delay (rtx insn)
 {
 
   if (GET_CODE (insn) != JUMP_INSN)
@@ -6594,9 +8568,9 @@ jump_in_call_delay (insn)
 
   if (PREV_INSN (insn)
       && PREV_INSN (PREV_INSN (insn))
-      && GET_CODE (next_active_insn (PREV_INSN (PREV_INSN (insn)))) == INSN)
+      && GET_CODE (next_real_insn (PREV_INSN (PREV_INSN (insn)))) == INSN)
     {
-      rtx test_insn = next_active_insn (PREV_INSN (PREV_INSN (insn)));
+      rtx test_insn = next_real_insn (PREV_INSN (PREV_INSN (insn)));
 
       return (GET_CODE (PATTERN (test_insn)) == SEQUENCE
              && XVECEXP (PATTERN (test_insn), 0, 1) == insn);
@@ -6609,41 +8583,50 @@ jump_in_call_delay (insn)
 /* Output an unconditional move and branch insn.  */
 
 const char *
-output_parallel_movb (operands, length)
-     rtx *operands;
-     int length;
+output_parallel_movb (rtx *operands, rtx insn)
 {
+  int length = get_attr_length (insn);
+
   /* These are the cases in which we win.  */
   if (length == 4)
     return "mov%I1b,tr %1,%0,%2";
 
-  /* None of these cases wins, but they don't lose either.  */
-  if (dbr_sequence_length () == 0)
+  /* None of the following cases win, but they don't lose either.  */
+  if (length == 8)
     {
-      /* Nothing in the delay slot, fake it by putting the combined
-        insn (the copy or add) in the delay slot of a bl.  */
-      if (GET_CODE (operands[1]) == CONST_INT)
-       return "b %2\n\tldi %1,%0";
+      if (dbr_sequence_length () == 0)
+       {
+         /* Nothing in the delay slot, fake it by putting the combined
+            insn (the copy or add) in the delay slot of a bl.  */
+         if (GET_CODE (operands[1]) == CONST_INT)
+           return "b %2\n\tldi %1,%0";
+         else
+           return "b %2\n\tcopy %1,%0";
+       }
       else
-       return "b %2\n\tcopy %1,%0";
+       {
+         /* Something in the delay slot, but we've got a long branch.  */
+         if (GET_CODE (operands[1]) == CONST_INT)
+           return "ldi %1,%0\n\tb %2";
+         else
+           return "copy %1,%0\n\tb %2";
+       }
     }
+
+  if (GET_CODE (operands[1]) == CONST_INT)
+    output_asm_insn ("ldi %1,%0", operands);
   else
-    {
-      /* Something in the delay slot, but we've got a long branch.  */
-      if (GET_CODE (operands[1]) == CONST_INT)
-       return "ldi %1,%0\n\tb %2";
-      else
-       return "copy %1,%0\n\tb %2";
-    }
+    output_asm_insn ("copy %1,%0", operands);
+  return output_lbranch (operands[2], insn, 1);
 }
 
 /* Output an unconditional add and branch insn.  */
 
 const char *
-output_parallel_addb (operands, length)
-     rtx *operands;
-     int length;
+output_parallel_addb (rtx *operands, rtx insn)
 {
+  int length = get_attr_length (insn);
+
   /* To make life easy we want operand0 to be the shared input/output
      operand and operand1 to be the readonly operand.  */
   if (operands[0] == operands[1])
@@ -6653,36 +8636,31 @@ output_parallel_addb (operands, length)
   if (length == 4)
     return "add%I1b,tr %1,%0,%3";
 
-  /* None of these cases win, but they don't lose either.  */
-  if (dbr_sequence_length () == 0)
-    {
-      /* Nothing in the delay slot, fake it by putting the combined
-        insn (the copy or add) in the delay slot of a bl.  */
-      return "b %3\n\tadd%I1 %1,%0,%0";
-    }
-  else
+  /* None of the following cases win, but they don't lose either.  */
+  if (length == 8)
     {
-      /* Something in the delay slot, but we've got a long branch.  */
-      return "add%I1 %1,%0,%0\n\tb %3";
+      if (dbr_sequence_length () == 0)
+       /* Nothing in the delay slot, fake it by putting the combined
+          insn (the copy or add) in the delay slot of a bl.  */
+       return "b %3\n\tadd%I1 %1,%0,%0";
+      else
+       /* Something in the delay slot, but we've got a long branch.  */
+       return "add%I1 %1,%0,%0\n\tb %3";
     }
-}
 
-/* Return nonzero if INSN (a jump insn) immediately follows a call to
-   a named function.  This is used to discourage creating parallel movb/addb
-   insns since a jump which immediately follows a call can execute in the
-   delay slot of the call.
+  output_asm_insn ("add%I1 %1,%0,%0", operands);
+  return output_lbranch (operands[3], insn, 1);
+}
 
-   It is also used to avoid filling the delay slot of a jump which
-   immediately follows a call since the jump can usually be eliminated
-   completely by modifying RP in the delay slot of the call.  */
+/* Return nonzero if INSN (a jump insn) immediately follows a call
+   to a named function.  This is used to avoid filling the delay slot
+   of the jump since it can usually be eliminated by modifying RP in
+   the delay slot of the call.  */
 
 int
-following_call (insn)
-     rtx insn;
+following_call (rtx insn)
 {
-  /* We do not parallel movb,addb or place jumps into call delay slots when
-     optimizing for the PA8000.  */
-  if (pa_cpu != PROCESSOR_8000)
+  if (! TARGET_JUMP_IN_DELAY)
     return 0;
 
   /* Find the previous real insn, skipping NOTEs.  */
@@ -6719,7 +8697,7 @@ following_call (insn)
    will adhere to those rules.
 
    So, late in the compilation process we find all the jump tables, and
-   expand them into real code -- eg each entry in the jump table vector
+   expand them into real code -- e.g. each entry in the jump table vector
    will get an appropriate label followed by a jump to the final target.
 
    Reorg and the final jump pass can then optimize these branches and
@@ -6728,7 +8706,8 @@ following_call (insn)
    The jump instructions within the table are special; we must be able
    to identify them during assembly output (if the jumps don't get filled
    we need to emit a nop rather than nullifying the delay slot)).  We
-   identify jumps in switch tables by marking the SET with DImode.
+   identify jumps in switch tables by using insns with the attribute
+   type TYPE_BTABLE_BRANCH.
 
    We also surround the jump table itself with BEGIN_BRTAB and END_BRTAB
    insns.  This serves two purposes, first it prevents jump.c from
@@ -6737,26 +8716,24 @@ following_call (insn)
    insns mark where we should emit .begin_brtab and .end_brtab directives
    when using GAS (allows for better link time optimizations).  */
 
-void
-pa_reorg (insns)
-     rtx insns;
+static void
+pa_reorg (void)
 {
   rtx insn;
 
-  remove_useless_addtr_insns (insns, 1);
+  remove_useless_addtr_insns (1);
 
   if (pa_cpu < PROCESSOR_8000)
-    pa_combine_instructions (get_insns ());
+    pa_combine_instructions ();
 
 
   /* This is fairly cheap, so always run it if optimizing.  */
   if (optimize > 0 && !TARGET_BIG_SWITCH)
     {
       /* Find and explode all ADDR_VEC or ADDR_DIFF_VEC insns.  */
-      insns = get_insns ();
-      for (insn = insns; insn; insn = NEXT_INSN (insn))
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
        {
-         rtx pattern, tmp, location;
+         rtx pattern, tmp, location, label;
          unsigned int length, i;
 
          /* Find an ADDR_VEC or ADDR_DIFF_VEC insn to explode.  */
@@ -6782,57 +8759,17 @@ pa_reorg (insns)
              location = NEXT_INSN (location);
 
              if (GET_CODE (pattern) == ADDR_VEC)
-               {
-                 /* Emit the jump itself.  */
-                 tmp = gen_jump (XEXP (XVECEXP (pattern, 0, i), 0));
-                 tmp = emit_jump_insn_after (tmp, location);
-                 JUMP_LABEL (tmp) = XEXP (XVECEXP (pattern, 0, i), 0);
-                 /* It is easy to rely on the branch table markers
-                    during assembly output to trigger the correct code
-                    for a switch table jump with an unfilled delay slot,
-
-                    However, that requires state and assumes that we look
-                    at insns in order.
-
-                    We can't make such assumptions when computing the length
-                    of instructions.  Ugh.  We could walk the insn chain to
-                    determine if this instruction is in a branch table, but
-                    that can get rather expensive, particularly during the
-                    branch shortening phase of the compiler.
-
-                    So instead we mark this jump as being special.  This is
-                    far from ideal and knows that no code after this will
-                    muck around with the mode of the JUMP_INSN itself.  */
-                 PUT_MODE (tmp, SImode);
-                 LABEL_NUSES (JUMP_LABEL (tmp))++;
-                 location = NEXT_INSN (location);
-               }
+               label = XEXP (XVECEXP (pattern, 0, i), 0);
              else
-               {
-                 /* Emit the jump itself.  */
-                 tmp = gen_jump (XEXP (XVECEXP (pattern, 1, i), 0));
-                 tmp = emit_jump_insn_after (tmp, location);
-                 JUMP_LABEL (tmp) = XEXP (XVECEXP (pattern, 1, i), 0);
-                 /* It is easy to rely on the branch table markers
-                    during assembly output to trigger the correct code
-                    for a switch table jump with an unfilled delay slot,
-
-                    However, that requires state and assumes that we look
-                    at insns in order.
-
-                    We can't make such assumptions when computing the length
-                    of instructions.  Ugh.  We could walk the insn chain to
-                    determine if this instruction is in a branch table, but
-                    that can get rather expensive, particularly during the
-                    branch shortening phase of the compiler.
-
-                    So instead we mark this jump as being special.  This is
-                    far from ideal and knows that no code after this will
-                    muck around with the mode of the JUMP_INSN itself.  */
-                 PUT_MODE (tmp, SImode);
-                 LABEL_NUSES (JUMP_LABEL (tmp))++;
-                 location = NEXT_INSN (location);
-               }
+               label = XEXP (XVECEXP (pattern, 1, i), 0);
+
+             tmp = gen_short_jump (label);
+
+             /* Emit the jump itself.  */
+             tmp = emit_jump_insn_after (tmp, location);
+             JUMP_LABEL (tmp) = label;
+             LABEL_NUSES (label)++;
+             location = NEXT_INSN (location);
 
              /* Emit a BARRIER after the jump.  */
              emit_barrier_after (location);
@@ -6850,9 +8787,13 @@ pa_reorg (insns)
     }
   else
     {
-      /* Sill need an end_brtab insn.  */
-      insns = get_insns ();
-      for (insn = insns; insn; insn = NEXT_INSN (insn))
+      /* Still need brtab marker insns.  FIXME: the presence of these
+        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't matter at the moment since the tables are
+        always output in the text section.  */
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
        {
          /* Find an ADDR_VEC insn.  */
          if (GET_CODE (insn) != JUMP_INSN
@@ -6912,10 +8853,9 @@ pa_reorg (insns)
       branch length restrictions.  */
 
 static void
-pa_combine_instructions (insns)
-     rtx insns ATTRIBUTE_UNUSED;
+pa_combine_instructions (void)
 {
-  rtx anchor, new;
+  rtx anchor, new_rtx;
 
   /* This can get expensive since the basic algorithm is on the
      order of O(n^2) (or worse).  Only do it for -O2 or higher
@@ -6927,8 +8867,8 @@ pa_combine_instructions (insns)
      may be combined with "floating" insns.  As the name implies,
      "anchor" instructions don't move, while "floating" insns may
      move around.  */
-  new = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, NULL_RTX, NULL_RTX));
-  new = make_insn_raw (new);
+  new_rtx = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, NULL_RTX, NULL_RTX));
+  new_rtx = make_insn_raw (new_rtx);
 
   for (anchor = get_insns (); anchor; anchor = NEXT_INSN (anchor))
     {
@@ -6984,7 +8924,7 @@ pa_combine_instructions (insns)
                {
                  /* If ANCHOR and FLOATER can be combined, then we're
                     done with this pass.  */
-                 if (pa_can_combine_p (new, anchor, floater, 0,
+                 if (pa_can_combine_p (new_rtx, anchor, floater, 0,
                                        SET_DEST (PATTERN (floater)),
                                        XEXP (SET_SRC (PATTERN (floater)), 0),
                                        XEXP (SET_SRC (PATTERN (floater)), 1)))
@@ -6996,7 +8936,7 @@ pa_combine_instructions (insns)
                {
                  if (GET_CODE (SET_SRC (PATTERN (floater))) == PLUS)
                    {
-                     if (pa_can_combine_p (new, anchor, floater, 0,
+                     if (pa_can_combine_p (new_rtx, anchor, floater, 0,
                                            SET_DEST (PATTERN (floater)),
                                        XEXP (SET_SRC (PATTERN (floater)), 0),
                                        XEXP (SET_SRC (PATTERN (floater)), 1)))
@@ -7004,7 +8944,7 @@ pa_combine_instructions (insns)
                    }
                  else
                    {
-                     if (pa_can_combine_p (new, anchor, floater, 0,
+                     if (pa_can_combine_p (new_rtx, anchor, floater, 0,
                                            SET_DEST (PATTERN (floater)),
                                            SET_SRC (PATTERN (floater)),
                                            SET_SRC (PATTERN (floater))))
@@ -7046,7 +8986,7 @@ pa_combine_instructions (insns)
                    {
                      /* If ANCHOR and FLOATER can be combined, then we're
                         done with this pass.  */
-                     if (pa_can_combine_p (new, anchor, floater, 1,
+                     if (pa_can_combine_p (new_rtx, anchor, floater, 1,
                                            SET_DEST (PATTERN (floater)),
                                            XEXP (SET_SRC (PATTERN (floater)),
                                                  0),
@@ -7070,9 +9010,7 @@ pa_combine_instructions (insns)
                                            PATTERN (floater))),
                                anchor);
 
-             PUT_CODE (anchor, NOTE);
-             NOTE_LINE_NUMBER (anchor) = NOTE_INSN_DELETED;
-             NOTE_SOURCE_FILE (anchor) = 0;
+             SET_INSN_DELETED (anchor);
 
              /* Emit a special USE insn for FLOATER, then delete
                 the floating insn.  */
@@ -7094,9 +9032,7 @@ pa_combine_instructions (insns)
                                         anchor);
 
              JUMP_LABEL (temp) = JUMP_LABEL (anchor);
-             PUT_CODE (anchor, NOTE);
-             NOTE_LINE_NUMBER (anchor) = NOTE_INSN_DELETED;
-             NOTE_SOURCE_FILE (anchor) = 0;
+             SET_INSN_DELETED (anchor);
 
              /* Emit a special USE insn for FLOATER, then delete
                 the floating insn.  */
@@ -7109,10 +9045,8 @@ pa_combine_instructions (insns)
 }
 
 static int
-pa_can_combine_p (new, anchor, floater, reversed, dest, src1, src2)
-     rtx new, anchor, floater;
-     int reversed;
-     rtx dest, src1, src2;
+pa_can_combine_p (rtx new_rtx, rtx anchor, rtx floater, int reversed, rtx dest,
+                 rtx src1, rtx src2)
 {
   int insn_code_number;
   rtx start, end;
@@ -7124,12 +9058,12 @@ pa_can_combine_p (new, anchor, floater, reversed, dest, src1, src2)
      If the pattern doesn't match or the constraints
      aren't met keep searching for a suitable floater
      insn.  */
-  XVECEXP (PATTERN (new), 0, 0) = PATTERN (anchor);
-  XVECEXP (PATTERN (new), 0, 1) = PATTERN (floater);
-  INSN_CODE (new) = -1;
-  insn_code_number = recog_memoized (new);
+  XVECEXP (PATTERN (new_rtx), 0, 0) = PATTERN (anchor);
+  XVECEXP (PATTERN (new_rtx), 0, 1) = PATTERN (floater);
+  INSN_CODE (new_rtx) = -1;
+  insn_code_number = recog_memoized (new_rtx);
   if (insn_code_number < 0
-      || (extract_insn (new), ! constrain_operands (1)))
+      || (extract_insn (new_rtx), ! constrain_operands (1)))
     return 0;
 
   if (reversed)
@@ -7179,7 +9113,7 @@ pa_can_combine_p (new, anchor, floater, reversed, dest, src1, src2)
    delay slot of the millicode call -- thus they act more like traditional
    CALL_INSNs.
 
-   Note we can not consider side effects of the insn to be delayed because
+   Note we cannot consider side effects of the insn to be delayed because
    the branch and link insn will clobber the return pointer.  If we happened
    to use the return pointer in the delay slot of the call, then we lose.
 
@@ -7187,8 +9121,7 @@ pa_can_combine_p (new, anchor, floater, reversed, dest, src1, src2)
    filter out things it will not accept -- SEQUENCE, USE and CLOBBER insns
    in particular.  */
 int
-insn_refs_are_delayed (insn)
-     rtx insn;
+insn_refs_are_delayed (rtx insn)
 {
   return ((GET_CODE (insn) == INSN
           && GET_CODE (PATTERN (insn)) != SEQUENCE
@@ -7197,6 +9130,74 @@ insn_refs_are_delayed (insn)
           && get_attr_type (insn) == TYPE_MILLI));
 }
 
+/* On the HP-PA the value is found in register(s) 28(-29), unless
+   the mode is SF or DF. Then the value is returned in fr4 (32).
+
+   This must perform the same promotions as PROMOTE_MODE, else
+   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.  */
+
+rtx
+function_value (const_tree valtype, const_tree func ATTRIBUTE_UNUSED)
+{
+  enum machine_mode valmode;
+
+  if (AGGREGATE_TYPE_P (valtype)
+      || TREE_CODE (valtype) == COMPLEX_TYPE
+      || TREE_CODE (valtype) == VECTOR_TYPE)
+    {
+      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;
+           }
+
+         return gen_rtx_PARALLEL (BLKmode, gen_rtvec_v (ub, loc));
+       }
+      else if (int_size_in_bytes (valtype) > UNITS_PER_WORD)
+       {
+         /* 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));
+       }
+    }
+
+  if ((INTEGRAL_TYPE_P (valtype)
+       && GET_MODE_BITSIZE (TYPE_MODE (valtype)) < BITS_PER_WORD)
+      || POINTER_TYPE_P (valtype))
+    valmode = word_mode;
+  else
+    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);
+
+  return gen_rtx_REG (valmode, 28);
+}
+
 /* Return the location of a parameter that is passed in a register or NULL
    if the parameter has any component that is passed in memory.
 
@@ -7206,34 +9207,36 @@ insn_refs_are_delayed (insn)
    ??? We might want to restructure this so that it looks more like other
    ports.  */
 rtx
-function_arg (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum;
-     enum machine_mode mode;
-     tree type;
-     int named ATTRIBUTE_UNUSED;
+function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
+             int named ATTRIBUTE_UNUSED)
 {
   int max_arg_words = (TARGET_64BIT ? 8 : 4);
+  int alignment = 0;
+  int arg_size;
   int fpr_reg_base;
   int gpr_reg_base;
   rtx retval;
 
-  if (! TARGET_64BIT)
+  if (mode == VOIDmode)
+    return NULL_RTX;
+
+  arg_size = FUNCTION_ARG_SIZE (mode, type);
+
+  /* If this arg would be passed partially or totally on the stack, then
+     this routine should return zero.  pa_arg_partial_bytes will
+     handle arguments which are split between regs and stack slots if
+     the ABI mandates split arguments.  */
+  if (!TARGET_64BIT)
     {
-      /* If this arg would be passed partially or totally on the stack, then
-         this routine should return zero.  FUNCTION_ARG_PARTIAL_NREGS will
-         handle arguments which are split between regs and stack slots if
-         the ABI mandates split arguments.  */
-      if (cum->words + FUNCTION_ARG_SIZE (mode, type) > max_arg_words
-          || mode == VOIDmode)
+      /* The 32-bit ABI does not split arguments.  */
+      if (cum->words + arg_size > max_arg_words)
        return NULL_RTX;
     }
   else
     {
-      int offset = 0;
-      if (FUNCTION_ARG_SIZE (mode, type) > 1 && (cum->words & 1))
-       offset = 1;
-      if (cum->words + offset >= max_arg_words
-         || mode == VOIDmode)
+      if (arg_size > 1)
+       alignment = cum->words & 1;
+      if (cum->words + alignment >= max_arg_words)
        return NULL_RTX;
     }
 
@@ -7241,70 +9244,62 @@ function_arg (cum, mode, type, named)
      particularly in their handling of FP registers.  We might
      be able to cleverly share code between them, but I'm not
      going to bother in the hope that splitting them up results
-     in code that is more easily understood.
+     in code that is more easily understood.  */
 
-     The 64bit code probably is very wrong for structure passing.  */
   if (TARGET_64BIT)
     {
       /* Advance the base registers to their current locations.
 
          Remember, gprs grow towards smaller register numbers while
-        fprs grow to higher register numbers.  Also remember FP regs
-        are always 4 bytes wide, while the size of an integer register
-        varies based on the size of the target word.  */
+        fprs grow to higher register numbers.  Also remember that
+        although FP regs are 32-bit addressable, we pretend that
+        the registers are 64-bits wide.  */
       gpr_reg_base = 26 - cum->words;
       fpr_reg_base = 32 + cum->words;
 
-      /* If the argument is more than a word long, then we need to align
-        the base registers.  Same caveats as above.  */
-      if (FUNCTION_ARG_SIZE (mode, type) > 1)
+      /* Arguments wider than one word and small aggregates need special
+        treatment.  */
+      if (arg_size > 1
+         || mode == BLKmode
+         || (type && (AGGREGATE_TYPE_P (type)
+                      || TREE_CODE (type) == COMPLEX_TYPE
+                      || TREE_CODE (type) == VECTOR_TYPE)))
        {
-         if (mode != BLKmode)
-           {
-             /* First deal with alignment of the doubleword.  */
-             gpr_reg_base -= (cum->words & 1);
-
-             /* This seems backwards, but it is what HP specifies.  We need
-                gpr_reg_base to point to the smaller numbered register of
-                the integer register pair.  So if we have an even register
-                 number, then decrement the gpr base.  */
-             gpr_reg_base -= ((gpr_reg_base % 2) == 0);
-
-             /* FP values behave sanely, except that each FP reg is only
-                half of word.  */
-             fpr_reg_base += ((fpr_reg_base % 2) == 0);
-            }
-         else
+         /* Double-extended precision (80-bit), quad-precision (128-bit)
+            and aggregates including complex numbers are aligned on
+            128-bit boundaries.  The first eight 64-bit argument slots
+            are associated one-to-one, with general registers r26
+            through r19, and also with floating-point registers fr4
+            through fr11.  Arguments larger than one word are always
+            passed in general registers.
+
+            Using a PARALLEL with a word mode register results in left
+            justified data on a big-endian target.  */
+
+         rtx loc[8];
+         int i, offset = 0, ub = arg_size;
+
+         /* Align the base register.  */
+         gpr_reg_base -= alignment;
+
+         ub = MIN (ub, max_arg_words - cum->words - alignment);
+         for (i = 0; i < ub; i++)
            {
-             rtx loc[8];
-             int i, offset = 0, ub;
-              ub = FUNCTION_ARG_SIZE (mode, type);
-             ub = MIN (ub,
-                       MAX (0, max_arg_words - cum->words - (cum->words & 1)));
-             gpr_reg_base -= (cum->words & 1);
-             for (i = 0; i < ub; i++)
-               {
-                 loc[i] = gen_rtx_EXPR_LIST (VOIDmode,
-                                             gen_rtx_REG (DImode,
-                                                          gpr_reg_base),
-                                             GEN_INT (offset));
-                 gpr_reg_base -= 1;
-                 offset += 8;
-               }
-             if (ub == 0)
-               return NULL_RTX;
-             else if (ub == 1)
-               return XEXP (loc[0], 0);
-             else
-               return gen_rtx_PARALLEL (mode, gen_rtvec_v (ub, loc));
+             loc[i] = gen_rtx_EXPR_LIST (VOIDmode,
+                                         gen_rtx_REG (DImode, gpr_reg_base),
+                                         GEN_INT (offset));
+             gpr_reg_base -= 1;
+             offset += 8;
            }
+
+         return gen_rtx_PARALLEL (mode, gen_rtvec_v (ub, loc));
        }
-    }
+     }
   else
     {
       /* If the argument is larger than a word, then we know precisely
         which registers we must use.  */
-      if (FUNCTION_ARG_SIZE (mode, type) > 1)
+      if (arg_size > 1)
        {
          if (cum->words)
            {
@@ -7316,6 +9311,25 @@ function_arg (cum, mode, type, named)
              gpr_reg_base = 25;
              fpr_reg_base = 34;
            }
+
+         /* 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 opposite to the normal justification
+            used on big endian targets and requires special treatment.
+            We now define BLOCK_REG_PADDING to pad these objects.
+            Aggregates, complex and vector types are passed in the same
+            manner as structures.  */
+         if (mode == BLKmode
+             || (type && (AGGREGATE_TYPE_P (type)
+                          || TREE_CODE (type) == COMPLEX_TYPE
+                          || TREE_CODE (type) == VECTOR_TYPE)))
+           {
+             rtx loc = gen_rtx_EXPR_LIST (VOIDmode,
+                                          gen_rtx_REG (DImode, gpr_reg_base),
+                                          const0_rtx);
+             return gen_rtx_PARALLEL (BLKmode, gen_rtvec (1, loc));
+           }
        }
       else
         {
@@ -7326,28 +9340,15 @@ function_arg (cum, mode, type, named)
        }
     }
 
-  if (TARGET_64BIT && mode == TFmode)
-    {
-      return
-       gen_rtx_PARALLEL
-         (mode,
-          gen_rtvec (2,
-                     gen_rtx_EXPR_LIST (VOIDmode,
-                                        gen_rtx_REG (DImode, gpr_reg_base + 1),
-                                        const0_rtx),
-                     gen_rtx_EXPR_LIST (VOIDmode,
-                                        gen_rtx_REG (DImode, gpr_reg_base),
-                                        GEN_INT (8))));
-    }
   /* Determine if the argument needs to be passed in both general and
      floating point registers.  */
   if (((TARGET_PORTABLE_RUNTIME || TARGET_64BIT || TARGET_ELF32)
        /* If we are doing soft-float with portable runtime, then there
          is no need to worry about FP regs.  */
        && !TARGET_SOFT_FLOAT
-       /* The parameter must be some kind of float, else we can just
+       /* The parameter must be some kind of scalar float, else we just
          pass it in integer registers.  */
-       && FLOAT_MODE_P (mode)
+       && GET_MODE_CLASS (mode) == MODE_FLOAT
        /* The target function must not have a prototype.  */
        && cum->nargs_prototype <= 0
        /* libcalls do not need to pass items in both FP and general
@@ -7363,7 +9364,7 @@ function_arg (cum, mode, type, named)
          && !TARGET_GAS
          && !cum->incoming
          && cum->indirect
-         && FLOAT_MODE_P (mode)))
+         && GET_MODE_CLASS (mode) == MODE_FLOAT))
     {
       retval
        = gen_rtx_PARALLEL
@@ -7386,9 +9387,11 @@ function_arg (cum, mode, type, named)
              && !TARGET_64BIT
              && !TARGET_ELF32
              && cum->indirect)
-         /* If the parameter is not a floating point parameter, then
-            it belongs in GPRs.  */
-         || !FLOAT_MODE_P (mode))
+         /* If the parameter is not a scalar floating-point parameter,
+            then it belongs in GPRs.  */
+         || GET_MODE_CLASS (mode) != MODE_FLOAT
+         /* 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);
@@ -7398,18 +9401,18 @@ function_arg (cum, mode, type, named)
 
 
 /* If this arg would be passed totally in registers or totally on the stack,
-   then this routine should return zero. It is currently called only for
-   the 64-bit target.  */
-int
-function_arg_partial_nregs (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum;
-     enum machine_mode mode;
-     tree type;
-     int named ATTRIBUTE_UNUSED;
+   then this routine should return zero.  */
+
+static int
+pa_arg_partial_bytes (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                     tree type, bool named ATTRIBUTE_UNUSED)
 {
   unsigned int max_arg_words = 8;
   unsigned int offset = 0;
 
+  if (!TARGET_64BIT)
+    return 0;
+
   if (FUNCTION_ARG_SIZE (mode, type) > 1 && (cum->words & 1))
     offset = 1;
 
@@ -7421,50 +9424,313 @@ function_arg_partial_nregs (cum, mode, type, named)
     return 0;
   else
     /* Arg is split.  */
-    return max_arg_words - cum->words - offset;
+    return (max_arg_words - cum->words - offset) * UNITS_PER_WORD;
 }
 
 
-/* Return 1 if this is a comparison operator.  This allows the use of
-   MATCH_OPERATOR to recognize all the branch insns.  */
+/* A get_unnamed_section callback for switching to the text section.
 
-int
-cmpib_comparison_operator (op, mode)
-    register rtx op;
-    enum machine_mode mode;
+   This function is only used with SOM.  Because we don't support
+   named subspaces, we can only create a new subspace or switch back
+   to the default text subspace.  */
+
+static void
+som_output_text_section_asm_op (const void *data ATTRIBUTE_UNUSED)
 {
-  return ((mode == VOIDmode || GET_MODE (op) == mode)
-          && (GET_CODE (op) == EQ
-             || GET_CODE (op) == NE
-             || GET_CODE (op) == GT
-             || GET_CODE (op) == GTU
-             || GET_CODE (op) == GE
-             || GET_CODE (op) == LT
-             || GET_CODE (op) == LE
-             || GET_CODE (op) == LEU));
+  gcc_assert (TARGET_SOM);
+  if (TARGET_GAS)
+    {
+      if (cfun && cfun->machine && !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))
+           {
+             output_section_asm_op ("\t.SPACE $TEXT$\n"
+                                    "\t.NSUBSPA $CODE$,QUAD=0,ALIGN=8,"
+                                    "ACCESS=44,SORT=24,COMDAT");
+             return;
+           }
+       }
+      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.  Thus, we
+            need to forget that we are in the text section so that
+            varasm.c will call us when text_section is selected again.  */
+         gcc_assert (!cfun || !cfun->machine
+                     || cfun->machine->in_nsubspa == 2);
+         in_section = NULL;
+       }
+      output_section_asm_op ("\t.SPACE $TEXT$\n\t.NSUBSPA $CODE$");
+      return;
+    }
+  output_section_asm_op ("\t.SPACE $TEXT$\n\t.SUBSPA $CODE$");
 }
 
-/* Mark ARG (which is really a struct deferred_plabel **) for GC.  */
+/* A get_unnamed_section callback for switching to comdat data
+   sections.  This function is only used with SOM.  */
 
 static void
-mark_deferred_plabels (arg)
-     void *arg;
+som_output_comdat_data_section_asm_op (const void *data)
 {
-  struct deferred_plabel *dp = *(struct deferred_plabel **) arg;
-  int i;
+  in_section = NULL;
+  output_section_asm_op (data);
+}
+
+/* Implement TARGET_ASM_INITIALIZE_SECTIONS  */
+
+static void
+pa_som_asm_init_sections (void)
+{
+  text_section
+    = get_unnamed_section (0, som_output_text_section_asm_op, NULL);
+
+  /* SOM puts readonly data in the default $LIT$ subspace when PIC code
+     is not being generated.  */
+  som_readonly_data_section
+    = get_unnamed_section (0, output_section_asm_op,
+                          "\t.SPACE $TEXT$\n\t.SUBSPA $LIT$");
+
+  /* When secondary definitions are not supported, SOM makes readonly
+     data one-only by creating a new $LIT$ subspace in $TEXT$ with
+     the comdat flag.  */
+  som_one_only_readonly_data_section
+    = get_unnamed_section (0, som_output_comdat_data_section_asm_op,
+                          "\t.SPACE $TEXT$\n"
+                          "\t.NSUBSPA $LIT$,QUAD=0,ALIGN=8,"
+                          "ACCESS=0x2c,SORT=16,COMDAT");
+
+
+  /* When secondary definitions are not supported, SOM makes data one-only
+     by creating a new $DATA$ subspace in $PRIVATE$ with the comdat flag.  */
+  som_one_only_data_section
+    = get_unnamed_section (SECTION_WRITE,
+                          som_output_comdat_data_section_asm_op,
+                          "\t.SPACE $PRIVATE$\n"
+                          "\t.NSUBSPA $DATA$,QUAD=1,ALIGN=8,"
+                          "ACCESS=31,SORT=24,COMDAT");
+
+  /* FIXME: HPUX ld generates incorrect GOT entries for "T" fixups
+     which reference data within the $TEXT$ space (for example constant
+     strings in the $LIT$ subspace).
+
+     The assemblers (GAS and HP as) both have problems with handling
+     the difference of two symbols which is the other correct way to
+     reference constant data during PIC code generation.
+
+     So, there's no way to reference constant data which is in the
+     $TEXT$ space during PIC generation.  Instead place all constant
+     data into the $PRIVATE$ subspace (this reduces sharing, but it
+     works correctly).  */
+  readonly_data_section = flag_pic ? data_section : som_readonly_data_section;
+
+  /* We must not have a reference to an external symbol defined in a
+     shared library in a readonly section, else the SOM linker will
+     complain.
+
+     So, we force exception information into the data section.  */
+  exception_section = data_section;
+}
+
+/* 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
+   not be placed in the read-only data section.  */
+
+static section *
+pa_select_section (tree exp, int reloc,
+                  unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
+{
+  if (TREE_CODE (exp) == VAR_DECL
+      && TREE_READONLY (exp)
+      && !TREE_THIS_VOLATILE (exp)
+      && DECL_INITIAL (exp)
+      && (DECL_INITIAL (exp) == error_mark_node
+          || TREE_CONSTANT (DECL_INITIAL (exp)))
+      && !reloc)
+    {
+      if (TARGET_SOM
+         && DECL_ONE_ONLY (exp)
+         && !DECL_WEAK (exp))
+       return som_one_only_readonly_data_section;
+      else
+       return readonly_data_section;
+    }
+  else if (CONSTANT_CLASS_P (exp) && !reloc)
+    return readonly_data_section;
+  else if (TARGET_SOM
+          && TREE_CODE (exp) == VAR_DECL
+          && DECL_ONE_ONLY (exp)
+          && !DECL_WEAK (exp))
+    return som_one_only_data_section;
+  else
+    return data_section;
+}
+
+static void
+pa_globalize_label (FILE *stream, const char *name)
+{
+  /* We only handle DATA objects here, functions are globalized in
+     ASM_DECLARE_FUNCTION_NAME.  */
+  if (! FUNCTION_NAME_P (name))
+  {
+    fputs ("\t.EXPORT ", stream);
+    assemble_name (stream, 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 (const_tree type, const_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);
+}
+
+/* Structure to hold declaration and name of external symbols that are
+   emitted by GCC.  We generate a vector of these symbols and output them
+   at the end of the file if and only if SYMBOL_REF_REFERENCED_P is true.
+   This avoids putting out names that are never really used.  */
+
+typedef struct extern_symbol GTY(())
+{
+  tree decl;
+  const char *name;
+} extern_symbol;
+
+/* Define gc'd vector type for extern_symbol.  */
+DEF_VEC_O(extern_symbol);
+DEF_VEC_ALLOC_O(extern_symbol,gc);
+
+/* Vector of extern_symbol pointers.  */
+static GTY(()) VEC(extern_symbol,gc) *extern_symbols;
+
+#ifdef ASM_OUTPUT_EXTERNAL_REAL
+/* Mark DECL (name NAME) as an external reference (assembler output
+   file FILE).  This saves the names to output at the end of the file
+   if actually referenced.  */
 
-  for (i = 0; i < n_deferred_plabels; ++i)
-    ggc_mark_rtx (dp[i].internal_label);
+void
+pa_hpux_asm_output_external (FILE *file, tree decl, const char *name)
+{
+  extern_symbol * p = VEC_safe_push (extern_symbol, gc, extern_symbols, NULL);
+
+  gcc_assert (file == asm_out_file);
+  p->decl = decl;
+  p->name = name;
 }
 
-/* Called to register all of our global variables with the garbage
-   collector.  */
+/* Output text required at the end of an assembler file.
+   This includes deferred plabels and .import directives for
+   all external symbols that were actually referenced.  */
 
 static void
-pa_add_gc_roots ()
+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++)
+    {
+      tree decl = p->decl;
+
+      if (!TREE_ASM_WRITTEN (decl)
+         && SYMBOL_REF_REFERENCED_P (XEXP (DECL_RTL (decl), 0)))
+       ASM_OUTPUT_EXTERNAL_REAL (asm_out_file, decl, p->name);
+    }
+
+  VEC_free (extern_symbol, gc, extern_symbols);
+}
+#endif
+
+/* Return true if a change from mode FROM to mode TO for a register
+   in register class RCLASS is invalid.  */
+
+bool
+pa_cannot_change_mode_class (enum machine_mode from, enum machine_mode to,
+                            enum reg_class rclass)
+{
+  if (from == to)
+    return false;
+
+  /* Reject changes to/from complex and vector modes.  */
+  if (COMPLEX_MODE_P (from) || VECTOR_MODE_P (from)
+      || COMPLEX_MODE_P (to) || VECTOR_MODE_P (to))
+    return true;
+      
+  if (GET_MODE_SIZE (from) == GET_MODE_SIZE (to))
+    return false;
+
+  /* There is no way to load QImode or HImode values directly from
+     memory.  SImode loads to the FP registers are not zero extended.
+     On the 64-bit target, this conflicts with the definition of
+     LOAD_EXTEND_OP.  Thus, we can't allow changing between modes
+     with different sizes in the floating-point registers.  */
+  if (MAYBE_FP_REG_CLASS_P (rclass))
+    return true;
+
+  /* HARD_REGNO_MODE_OK places modes with sizes larger than a word
+     in specific sets of registers.  Thus, we cannot allow changing
+     to a larger mode when it's larger than a word.  */
+  if (GET_MODE_SIZE (to) > UNITS_PER_WORD
+      && GET_MODE_SIZE (to) > GET_MODE_SIZE (from))
+    return true;
+
+  return false;
+}
+
+/* Returns TRUE if it is a good idea to tie two pseudo registers
+   when one has mode MODE1 and one has mode MODE2.
+   If HARD_REGNO_MODE_OK could produce different values for MODE1 and MODE2,
+   for any hard reg, then this must be FALSE for correct output.
+   
+   We should return FALSE for QImode and HImode because these modes
+   are not ok in the floating-point registers.  However, this prevents
+   tieing these modes to SImode and DImode in the general registers.
+   So, this isn't a good idea.  We rely on HARD_REGNO_MODE_OK and
+   CANNOT_CHANGE_MODE_CLASS to prevent these modes from being used
+   in the floating-point registers.  */
+
+bool
+pa_modes_tieable_p (enum machine_mode mode1, enum machine_mode mode2)
 {
-  ggc_add_rtx_root (&hppa_compare_op0, 1);
-  ggc_add_rtx_root (&hppa_compare_op1, 1);
-  ggc_add_root (&deferred_plabels, 1, sizeof (&deferred_plabels),
-               &mark_deferred_plabels);
+  /* Don't tie modes in different classes.  */
+  if (GET_MODE_CLASS (mode1) != GET_MODE_CLASS (mode2))
+    return false;
+
+  return true;
 }
+
+#include "gt-pa.h"