OSDN Git Service

2004-02-07 Paolo Bonzini <bonzini@gnu.org>
[pf3gnuchains/gcc-fork.git] / gcc / config / s390 / s390.c
index 295fdef..7e7489a 100644 (file)
@@ -1,24 +1,25 @@
 /* Subroutines used for code generation on IBM S/390 and zSeries
-   Copyright (C) 1999, 2000, 2001, 2002 Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
+   Free Software Foundation, Inc.
    Contributed by Hartmut Penner (hpenner@de.ibm.com) and
                   Ulrich Weigand (uweigand@de.ibm.com).
 
-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
-it under the terms of the GNU General Public License as published by
-the Free Software Foundation; either version 2, or (at your option)
-any later version.
+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) any later
+version.
 
-GNU CC 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.
+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 COPYING.  If not, write to the Free
+Software Foundation, 59 Temple Place - Suite 330, Boston, MA
+02111-1307, USA.  */
 
 #include "config.h"
 #include "system.h"
@@ -50,14 +51,33 @@ Boston, MA 02111-1307, USA.  */
 #include "langhooks.h"
 #include "optabs.h"
 
-static bool s390_assemble_integer PARAMS ((rtx, unsigned int, int));
-static int s390_adjust_cost PARAMS ((rtx, rtx, rtx, int));
-static int s390_adjust_priority PARAMS ((rtx, int));
-static void s390_select_rtx_section PARAMS ((enum machine_mode, rtx, 
-                                            unsigned HOST_WIDE_INT));
-static void s390_encode_section_info PARAMS ((tree, int));
-static void s390_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT,
-                                         HOST_WIDE_INT, tree));
+/* Machine-specific symbol_ref flags.  */
+#define SYMBOL_FLAG_ALIGN1     (SYMBOL_FLAG_MACH_DEP << 0)
+
+
+static bool s390_assemble_integer (rtx, unsigned int, int);
+static void s390_select_rtx_section (enum machine_mode, rtx,
+                                    unsigned HOST_WIDE_INT);
+static void s390_encode_section_info (tree, rtx, int);
+static bool s390_cannot_force_const_mem (rtx);
+static rtx s390_delegitimize_address (rtx);
+static bool s390_return_in_memory (tree, tree);
+static void s390_init_builtins (void);
+static rtx s390_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
+static void s390_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
+                                 HOST_WIDE_INT, tree);
+static enum attr_type s390_safe_attr_type (rtx);
+
+static int s390_adjust_cost (rtx, rtx, rtx, int);
+static int s390_adjust_priority (rtx, int);
+static int s390_issue_rate (void);
+static int s390_use_dfa_pipeline_interface (void);
+static int s390_first_cycle_multipass_dfa_lookahead (void);
+static bool s390_rtx_costs (rtx, int, int, int *);
+static int s390_address_cost (rtx);
+static void s390_reorg (void);
+static bool s390_valid_pointer_mode (enum machine_mode);
+static tree s390_build_builtin_va_list (void);
 
 #undef  TARGET_ASM_ALIGNED_HI_OP
 #define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
@@ -75,20 +95,62 @@ static void s390_output_mi_thunk PARAMS ((FILE *, tree, HOST_WIDE_INT,
 #undef TARGET_ASM_SELECT_RTX_SECTION
 #define        TARGET_ASM_SELECT_RTX_SECTION  s390_select_rtx_section
 
-#undef  TARGET_SCHED_ADJUST_COST
-#define TARGET_SCHED_ADJUST_COST s390_adjust_cost
-
-#undef  TARGET_SCHED_ADJUST_PRIORITY
-#define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority
-
 #undef TARGET_ENCODE_SECTION_INFO
 #define TARGET_ENCODE_SECTION_INFO s390_encode_section_info
 
+#ifdef HAVE_AS_TLS
+#undef TARGET_HAVE_TLS
+#define TARGET_HAVE_TLS true
+#endif
+#undef TARGET_CANNOT_FORCE_CONST_MEM
+#define TARGET_CANNOT_FORCE_CONST_MEM s390_cannot_force_const_mem
+
+#undef TARGET_DELEGITIMIZE_ADDRESS
+#define TARGET_DELEGITIMIZE_ADDRESS s390_delegitimize_address
+
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY s390_return_in_memory
+
+#undef  TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS s390_init_builtins
+#undef  TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN s390_expand_builtin
+
 #undef TARGET_ASM_OUTPUT_MI_THUNK
 #define TARGET_ASM_OUTPUT_MI_THUNK s390_output_mi_thunk
 #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
 #define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
 
+#undef  TARGET_SCHED_ADJUST_COST
+#define TARGET_SCHED_ADJUST_COST s390_adjust_cost
+#undef  TARGET_SCHED_ADJUST_PRIORITY
+#define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority
+#undef TARGET_SCHED_ISSUE_RATE
+#define TARGET_SCHED_ISSUE_RATE s390_issue_rate
+#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE
+#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE s390_use_dfa_pipeline_interface
+#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
+#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD s390_first_cycle_multipass_dfa_lookahead
+
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS s390_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST s390_address_cost
+
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG s390_reorg
+
+#undef TARGET_VALID_POINTER_MODE
+#define TARGET_VALID_POINTER_MODE s390_valid_pointer_mode
+
+#undef TARGET_BUILD_BUILTIN_VA_LIST
+#define TARGET_BUILD_BUILTIN_VA_LIST s390_build_builtin_va_list
+
+#undef TARGET_PROMOTE_FUNCTION_ARGS
+#define TARGET_PROMOTE_FUNCTION_ARGS hook_bool_tree_true
+#undef TARGET_PROMOTE_FUNCTION_RETURN
+#define TARGET_PROMOTE_FUNCTION_RETURN hook_bool_tree_true
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 extern int reload_completed;
@@ -117,16 +179,27 @@ struct s390_address
   int pointer;
 };
 
+/* Which cpu are we tuning for.  */
+enum processor_type s390_tune;
+enum processor_flags s390_tune_flags;
+/* Which instruction set architecture to use.  */
+enum processor_type s390_arch;
+enum processor_flags s390_arch_flags;
+
+/* Strings to hold which cpu and instruction set architecture  to use.  */
+const char *s390_tune_string;          /* for -mtune=<xxx> */
+const char *s390_arch_string;          /* for -march=<xxx> */
+
 /* Define the structure for the machine field in struct function.  */
 
 struct machine_function GTY(())
 {
-  /* Label of start of initial literal pool.  */
-  rtx literal_pool_label;
-
   /* Set, if some of the fprs 8-15 need to be saved (64 bit abi).  */
   int save_fprs_p;
 
+  /* Set if return address needs to be saved.  */
+  bool save_return_addr_p;
+
   /* Number of first and last gpr to be saved, restored.  */
   int first_save_gpr;
   int first_restore_gpr;
@@ -134,41 +207,52 @@ struct machine_function GTY(())
 
   /* Size of stack frame.  */
   HOST_WIDE_INT frame_size;
+
+  /* Some local-dynamic TLS symbol name.  */
+  const char *some_ld_name;
 };
 
-static int s390_match_ccmode_set PARAMS ((rtx, enum machine_mode));
-static int s390_branch_condition_mask PARAMS ((rtx));
-static const char *s390_branch_condition_mnemonic PARAMS ((rtx, int));
-static int check_mode PARAMS ((rtx, enum machine_mode *));
-static int general_s_operand PARAMS ((rtx, enum machine_mode, int));
-static int s390_decompose_address PARAMS ((rtx, struct s390_address *));
-static int reg_used_in_mem_p PARAMS ((int, rtx));
-static int addr_generation_dependency_p PARAMS ((rtx, rtx));
-static int s390_split_branches PARAMS ((rtx, bool *));
-static void find_constant_pool_ref PARAMS ((rtx, rtx *));
-static void replace_constant_pool_ref PARAMS ((rtx *, rtx, rtx));
-static int find_base_register_in_addr PARAMS ((struct s390_address *));
-static bool find_base_register_ref PARAMS ((rtx));
-static void replace_base_register_ref PARAMS ((rtx *, rtx));
-static void s390_optimize_prolog PARAMS ((int));
-static bool s390_fixup_clobbered_return_reg PARAMS ((rtx));
-static int find_unused_clobbered_reg PARAMS ((void));
-static void s390_frame_info PARAMS ((void));
-static rtx save_fpr PARAMS ((rtx, int, int));
-static rtx restore_fpr PARAMS ((rtx, int, int));
-static rtx save_gprs PARAMS ((rtx, int, int, int));
-static rtx restore_gprs PARAMS ((rtx, int, int, int));
-static int s390_function_arg_size PARAMS ((enum machine_mode, tree));
-static struct machine_function * s390_init_machine_status PARAMS ((void));
+static int s390_match_ccmode_set (rtx, enum machine_mode);
+static int s390_branch_condition_mask (rtx);
+static const char *s390_branch_condition_mnemonic (rtx, int);
+static int check_mode (rtx, enum machine_mode *);
+static int general_s_operand (rtx, enum machine_mode, int);
+static int s390_short_displacement (rtx);
+static int s390_decompose_address (rtx, struct s390_address *);
+static rtx get_thread_pointer (void);
+static rtx legitimize_tls_address (rtx, rtx);
+static void print_shift_count_operand (FILE *, rtx);
+static const char *get_some_local_dynamic_name (void);
+static int get_some_local_dynamic_name_1 (rtx *, void *);
+static int reg_used_in_mem_p (int, rtx);
+static int addr_generation_dependency_p (rtx, rtx);
+static int s390_split_branches (void);
+static void find_constant_pool_ref (rtx, rtx *);
+static void replace_constant_pool_ref (rtx *, rtx, rtx);
+static rtx find_ltrel_base (rtx);
+static void replace_ltrel_base (rtx *, rtx);
+static void s390_optimize_prolog (bool);
+static int find_unused_clobbered_reg (void);
+static void s390_frame_info (void);
+static rtx save_fpr (rtx, int, int);
+static rtx restore_fpr (rtx, int, int);
+static rtx save_gprs (rtx, int, int, int);
+static rtx restore_gprs (rtx, int, int, int);
+static int s390_function_arg_size (enum machine_mode, tree);
+static bool s390_function_arg_float (enum machine_mode, tree);
+static struct machine_function * s390_init_machine_status (void);
+
+/* Check whether integer displacement is in range.  */
+#define DISP_IN_RANGE(d) \
+  (TARGET_LONG_DISPLACEMENT? ((d) >= -524288 && (d) <= 524287) \
+                           : ((d) >= 0 && (d) <= 4095))
+
 /* Return true if SET either doesn't set the CC register, or else
-   the source and destination have matching CC modes and that 
+   the source and destination have matching CC modes and that
    CC mode is at least as constrained as REQ_MODE.  */
+
 static int
-s390_match_ccmode_set (set, req_mode)
-     rtx set;
-     enum machine_mode req_mode;
+s390_match_ccmode_set (rtx set, enum machine_mode req_mode)
 {
   enum machine_mode set_mode;
 
@@ -206,23 +290,21 @@ s390_match_ccmode_set (set, req_mode)
       if (req_mode != CCAmode)
         return 0;
       break;
+
     default:
       abort ();
     }
+
   return (GET_MODE (SET_SRC (set)) == set_mode);
 }
 
-/* Return true if every SET in INSN that sets the CC register 
-   has source and destination with matching CC modes and that 
-   CC mode is at least as constrained as REQ_MODE.  
+/* Return true if every SET in INSN that sets the CC register
+   has source and destination with matching CC modes and that
+   CC mode is at least as constrained as REQ_MODE.
    If REQ_MODE is VOIDmode, always return false.  */
+
 int
-s390_match_ccmode (insn, req_mode)
-     rtx insn;
-     enum machine_mode req_mode;
+s390_match_ccmode (rtx insn, enum machine_mode req_mode)
 {
   int i;
 
@@ -245,18 +327,15 @@ s390_match_ccmode (insn, req_mode)
   return 1;
 }
 
-/* If a test-under-mask instruction can be used to implement 
+/* If a test-under-mask instruction can be used to implement
    (compare (and ... OP1) OP2), return the CC mode required
-   to do that.  Otherwise, return VOIDmode.  
+   to do that.  Otherwise, return VOIDmode.
    MIXED is true if the instruction can distinguish between
    CC1 and CC2 for mixed selected bits (TMxx), it is false
    if the instruction cannot (TM).  */
 
 enum machine_mode
-s390_tm_ccmode (op1, op2, mixed)
-     rtx op1;
-     rtx op2;
-     int mixed;
+s390_tm_ccmode (rtx op1, rtx op2, int mixed)
 {
   int bit0, bit1;
 
@@ -284,25 +363,23 @@ s390_tm_ccmode (op1, op2, mixed)
   return VOIDmode;
 }
 
-/* Given a comparison code OP (EQ, NE, etc.) and the operands 
-   OP0 and OP1 of a COMPARE, return the mode to be used for the 
+/* Given a comparison code OP (EQ, NE, etc.) and the operands
+   OP0 and OP1 of a COMPARE, return the mode to be used for the
    comparison.  */
 
 enum machine_mode
-s390_select_ccmode (code, op0, op1) 
-     enum rtx_code code;
-     rtx op0;
-     rtx op1;
+s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
 {
   switch (code)
     {
       case EQ:
       case NE:
        if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT
-           && CONST_OK_FOR_LETTER_P (INTVAL (XEXP (op0, 1)), 'K')) 
+           && CONST_OK_FOR_CONSTRAINT_P (INTVAL (XEXP (op0, 1)), 'K', "K"))
          return CCAPmode;
-       if (GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
-           || GET_CODE (op1) == NEG)
+       if ((GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
+            || GET_CODE (op1) == NEG)
+           && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
          return CCLmode;
 
        if (GET_CODE (op0) == AND)
@@ -318,11 +395,11 @@ s390_select_ccmode (code, op0, op1)
              }
          }
 
-       if (register_operand (op0, HImode) 
+       if (register_operand (op0, HImode)
            && GET_CODE (op1) == CONST_INT
            && (INTVAL (op1) == -1 || INTVAL (op1) == 65535))
          return CCT3mode;
-       if (register_operand (op0, QImode) 
+       if (register_operand (op0, QImode)
            && GET_CODE (op1) == CONST_INT
            && (INTVAL (op1) == -1 || INTVAL (op1) == 255))
          return CCT3mode;
@@ -334,7 +411,7 @@ s390_select_ccmode (code, op0, op1)
       case GE:
       case GT:
          if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT
-             && CONST_OK_FOR_LETTER_P (INTVAL (XEXP (op0, 1)), 'K')) 
+             && CONST_OK_FOR_CONSTRAINT_P (INTVAL (XEXP (op0, 1)), 'K', "K"))
             {
              if (INTVAL (XEXP((op0), 1)) < 0)
                return CCANmode;
@@ -356,7 +433,8 @@ s390_select_ccmode (code, op0, op1)
 
       case LTU:
       case GEU:
-       if (GET_CODE (op0) == PLUS)
+       if (GET_CODE (op0) == PLUS
+           && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
          return CCL1mode;
 
        if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND)
@@ -366,7 +444,8 @@ s390_select_ccmode (code, op0, op1)
 
       case LEU:
       case GTU:
-       if (GET_CODE (op0) == MINUS)
+       if (GET_CODE (op0) == MINUS
+           && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
          return CCL2mode;
 
        if ((GET_CODE (op0) == SIGN_EXTEND || GET_CODE (op0) == ZERO_EXTEND)
@@ -379,13 +458,96 @@ s390_select_ccmode (code, op0, op1)
     }
 }
 
-/* Return branch condition mask to implement a branch 
+/* Return nonzero if OP is a valid comparison operator
+   for an ALC condition in mode MODE.  */
+
+int
+s390_alc_comparison (rtx op, enum machine_mode mode)
+{
+  if (mode != VOIDmode && mode != GET_MODE (op))
+    return 0;
+
+  if (!COMPARISON_P (op))
+    return 0;
+
+  if (GET_CODE (XEXP (op, 0)) != REG
+      || REGNO (XEXP (op, 0)) != CC_REGNUM
+      || XEXP (op, 1) != const0_rtx)
+    return 0;
+
+  switch (GET_MODE (XEXP (op, 0)))
+    {
+    case CCL1mode:
+      return GET_CODE (op) == LTU;
+
+    case CCL2mode:
+      return GET_CODE (op) == LEU;
+
+    case CCUmode:
+      return GET_CODE (op) == GTU;
+
+    case CCURmode:
+      return GET_CODE (op) == LTU;
+
+    case CCSmode:
+      return GET_CODE (op) == UNGT;
+
+    case CCSRmode:
+      return GET_CODE (op) == UNLT;
+
+    default:
+      return 0;
+    }
+}
+
+/* Return nonzero if OP is a valid comparison operator
+   for an SLB condition in mode MODE.  */
+
+int
+s390_slb_comparison (rtx op, enum machine_mode mode)
+{
+  if (mode != VOIDmode && mode != GET_MODE (op))
+    return 0;
+
+  if (!COMPARISON_P (op))
+    return 0;
+
+  if (GET_CODE (XEXP (op, 0)) != REG
+      || REGNO (XEXP (op, 0)) != CC_REGNUM
+      || XEXP (op, 1) != const0_rtx)
+    return 0;
+
+  switch (GET_MODE (XEXP (op, 0)))
+    {
+    case CCL1mode:
+      return GET_CODE (op) == GEU;
+
+    case CCL2mode:
+      return GET_CODE (op) == GTU;
+
+    case CCUmode:
+      return GET_CODE (op) == LEU;
+
+    case CCURmode:
+      return GET_CODE (op) == GEU;
+
+    case CCSmode:
+      return GET_CODE (op) == LE;
+
+    case CCSRmode:
+      return GET_CODE (op) == GE;
+
+    default:
+      return 0;
+    }
+}
+
+/* Return branch condition mask to implement a branch
    specified by CODE.  */
 
 static int
-s390_branch_condition_mask (code)
-    rtx code;
-{ 
+s390_branch_condition_mask (rtx code)
+{
   const int CC0 = 1 << 3;
   const int CC1 = 1 << 2;
   const int CC2 = 1 << 1;
@@ -573,14 +735,12 @@ s390_branch_condition_mask (code)
     }
 }
 
-/* If INV is false, return assembler mnemonic string to implement 
-   a branch specified by CODE.  If INV is true, return mnemonic 
+/* If INV is false, return assembler mnemonic string to implement
+   a branch specified by CODE.  If INV is true, return mnemonic
    for the corresponding inverted branch.  */
 
 static const char *
-s390_branch_condition_mnemonic (code, inv)
-     rtx code;
-     int inv;
+s390_branch_condition_mnemonic (rtx code, int inv)
 {
   static const char *const mnemonic[16] =
     {
@@ -601,214 +761,104 @@ s390_branch_condition_mnemonic (code, inv)
   return mnemonic[mask];
 }
 
-/* If OP is an integer constant of mode MODE with exactly one
-   HImode subpart unequal to DEF, return the number of that 
-   subpart.  As a special case, all HImode subparts of OP are
-   equal to DEF, return zero.  Otherwise, return -1.  */
+/* Return the part of op which has a value different from def.
+   The size of the part is determined by mode.
+   Use this function only if you already know that op really 
+   contains such a part.  */
 
-int
-s390_single_hi (op, mode, def)
-     rtx op;
-     enum machine_mode mode;
-     int def;
+unsigned HOST_WIDE_INT
+s390_extract_part (rtx op, enum machine_mode mode, int def)
 {
-  if (GET_CODE (op) == CONST_INT)
-    {
-      unsigned HOST_WIDE_INT value = 0;
-      int n_parts = GET_MODE_SIZE (mode) / 2;
-      int i, part = -1;
-
-      for (i = 0; i < n_parts; i++)
-        {
-          if (i == 0)
-            value = (unsigned HOST_WIDE_INT) INTVAL (op);
-          else
-            value >>= 16;
-
-          if ((value & 0xffff) != (unsigned)(def & 0xffff))
-            {
-              if (part != -1)
-                return -1;
-              else
-                part = i;
-            }
-        }
-
-      return part == -1 ? 0 : (n_parts - 1 - part);
-    }
-
-  else if (GET_CODE (op) == CONST_DOUBLE
-           && GET_MODE (op) == VOIDmode)
-    {
-      unsigned HOST_WIDE_INT value = 0;
-      int n_parts = GET_MODE_SIZE (mode) / 2;
-      int i, part = -1;
-
-      for (i = 0; i < n_parts; i++)
-        {
-          if (i == 0)
-            value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
-          else if (i == HOST_BITS_PER_WIDE_INT / 16)
-            value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (op);
-          else
-            value >>= 16;
-
-          if ((value & 0xffff) != (unsigned)(def & 0xffff))
-            {
-              if (part != -1)
-                return -1;
-              else
-                part = i;
-            }
-        }
-
-      return part == -1 ? 0 : (n_parts - 1 - part);
-    }
-
-  return -1;      
-}
-
-/* Extract the HImode part number PART from integer 
-   constant OP of mode MODE.  */
-
-int
-s390_extract_hi (op, mode, part)
-    rtx op;
-    enum machine_mode mode;
-    int part;
-{
-  int n_parts = GET_MODE_SIZE (mode) / 2;
-  if (part < 0 || part >= n_parts)
-    abort();
-  else
-    part = n_parts - 1 - part;
-
-  if (GET_CODE (op) == CONST_INT)
-    {
-      unsigned HOST_WIDE_INT value = (unsigned HOST_WIDE_INT) INTVAL (op);
-      return ((value >> (16 * part)) & 0xffff);
-    }
-  else if (GET_CODE (op) == CONST_DOUBLE
-           && GET_MODE (op) == VOIDmode)
+  unsigned HOST_WIDE_INT value = 0;
+  int max_parts = HOST_BITS_PER_WIDE_INT / GET_MODE_BITSIZE (mode);
+  int part_bits = GET_MODE_BITSIZE (mode);
+  unsigned HOST_WIDE_INT part_mask = (1 << part_bits) - 1;
+  int i;
+  
+  for (i = 0; i < max_parts; i++)
     {
-      unsigned HOST_WIDE_INT value;
-      if (part < HOST_BITS_PER_WIDE_INT / 16)
-        value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
+      if (i == 0)
+       value = (unsigned HOST_WIDE_INT) INTVAL (op);
       else
-        value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (op),
-        part -= HOST_BITS_PER_WIDE_INT / 16;
-
-      return ((value >> (16 * part)) & 0xffff); 
+       value >>= part_bits;
+      
+      if ((value & part_mask) != (def & part_mask))
+       return value & part_mask;
     }
-
+  
   abort ();
 }
 
 /* If OP is an integer constant of mode MODE with exactly one
-   QImode subpart unequal to DEF, return the number of that 
-   subpart.  As a special case, all QImode subparts of OP are
-   equal to DEF, return zero.  Otherwise, return -1.  */
+   part of mode PART_MODE unequal to DEF, return the number of that
+   part. Otherwise, return -1.  */
 
 int
-s390_single_qi (op, mode, def)
-     rtx op;
-     enum machine_mode mode;
-     int def;
+s390_single_part (rtx op, 
+                 enum machine_mode mode, 
+                 enum machine_mode part_mode,
+                 int def)
 {
-  if (GET_CODE (op) == CONST_INT)
-    {
-      unsigned HOST_WIDE_INT value = 0;
-      int n_parts = GET_MODE_SIZE (mode);
-      int i, part = -1;
-
-      for (i = 0; i < n_parts; i++)
-        {
-          if (i == 0)
-            value = (unsigned HOST_WIDE_INT) INTVAL (op);
-          else
-            value >>= 8;
-
-          if ((value & 0xff) != (unsigned)(def & 0xff))
-            {
-              if (part != -1)
-                return -1;
-              else
-                part = i;
-            }
-        }
-
-      return part == -1 ? 0 : (n_parts - 1 - part);
-    }
+  unsigned HOST_WIDE_INT value = 0;
+  int n_parts = GET_MODE_SIZE (mode) / GET_MODE_SIZE (part_mode);
+  unsigned HOST_WIDE_INT part_mask = (1 << GET_MODE_BITSIZE (part_mode)) - 1;
+  int i, part = -1;
 
-  else if (GET_CODE (op) == CONST_DOUBLE
-           && GET_MODE (op) == VOIDmode)
+  if (GET_CODE (op) != CONST_INT)
+    return -1;
+  
+  for (i = 0; i < n_parts; i++)
     {
-      unsigned HOST_WIDE_INT value = 0;
-      int n_parts = GET_MODE_SIZE (mode);
-      int i, part = -1;
-
-      for (i = 0; i < n_parts; i++)
-        {
-          if (i == 0)
-            value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
-          else if (i == HOST_BITS_PER_WIDE_INT / 8)
-            value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (op);
-          else
-            value >>= 8;
-
-          if ((value & 0xff) != (unsigned)(def & 0xff))
-            {
-              if (part != -1)
-                return -1;
-              else
-                part = i;
-            }
-        }
-
-      return part == -1 ? 0 : (n_parts - 1 - part);
+      if (i == 0)
+       value = (unsigned HOST_WIDE_INT) INTVAL (op);
+      else
+       value >>= GET_MODE_BITSIZE (part_mode);
+      
+      if ((value & part_mask) != (def & part_mask))
+       {
+         if (part != -1)
+           return -1;
+         else
+           part = i;
+       }
     }
-
-  return -1;      
+  return part == -1 ? -1 : n_parts - 1 - part;
 }
 
-/* Extract the QImode part number PART from integer 
-   constant OP of mode MODE.  */
-
-int
-s390_extract_qi (op, mode, part)
-    rtx op;
-    enum machine_mode mode;
-    int part;
-{
-  int n_parts = GET_MODE_SIZE (mode);
-  if (part < 0 || part >= n_parts)
-    abort();
-  else
-    part = n_parts - 1 - part;
-
-  if (GET_CODE (op) == CONST_INT)
-    {
-      unsigned HOST_WIDE_INT value = (unsigned HOST_WIDE_INT) INTVAL (op);
-      return ((value >> (8 * part)) & 0xff);
-    }
-  else if (GET_CODE (op) == CONST_DOUBLE
-           && GET_MODE (op) == VOIDmode)
-    {
-      unsigned HOST_WIDE_INT value;
-      if (part < HOST_BITS_PER_WIDE_INT / 8)
-        value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_LOW (op);
-      else
-        value = (unsigned HOST_WIDE_INT) CONST_DOUBLE_HIGH (op),
-        part -= HOST_BITS_PER_WIDE_INT / 8;
+/* Check whether we can (and want to) split a double-word
+   move in mode MODE from SRC to DST into two single-word
+   moves, moving the subword FIRST_SUBWORD first.  */
 
-      return ((value >> (8 * part)) & 0xff); 
+bool
+s390_split_ok_p (rtx dst, rtx src, enum machine_mode mode, int first_subword)
+{
+  /* Floating point registers cannot be split.  */
+  if (FP_REG_P (src) || FP_REG_P (dst))
+    return false;
+
+  /* We don't need to split if operands are directly accessible.  */
+  if (s_operand (src, mode) || s_operand (dst, mode))
+    return false;
+
+  /* Non-offsettable memory references cannot be split.  */
+  if ((GET_CODE (src) == MEM && !offsettable_memref_p (src))
+      || (GET_CODE (dst) == MEM && !offsettable_memref_p (dst)))
+    return false;
+
+  /* Moving the first subword must not clobber a register
+     needed to move the second subword.  */
+  if (register_operand (dst, mode))
+    {
+      rtx subreg = operand_subword (dst, first_subword, 0, mode);
+      if (reg_overlap_mentioned_p (subreg, src))
+        return false;
     }
 
-  abort ();
+  return true;
 }
 
 
-/* Change optimizations to be performed, depending on the 
+/* Change optimizations to be performed, depending on the
    optimization level.
 
    LEVEL is the optimization level specified; 2 if `-O2' is
@@ -817,9 +867,7 @@ s390_extract_qi (op, mode, part)
    SIZE is nonzero if `-Os' is specified and zero otherwise.  */
 
 void
-optimization_options (level, size)
-     int level ATTRIBUTE_UNUSED;
-     int size ATTRIBUTE_UNUSED;
+optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
 {
   /* ??? There are apparently still problems with -fcaller-saves.  */
   flag_caller_saves = 0;
@@ -830,13 +878,80 @@ optimization_options (level, size)
 }
 
 void
-override_options ()
+override_options (void)
 {
+  int i;
+  static struct pta
+    {
+      const char *const name;          /* processor name or nickname.  */
+      const enum processor_type processor;
+      const enum processor_flags flags;
+    }
+  const processor_alias_table[] =
+    {
+      {"g5", PROCESSOR_9672_G5, PF_IEEE_FLOAT},
+      {"g6", PROCESSOR_9672_G6, PF_IEEE_FLOAT},
+      {"z900", PROCESSOR_2064_Z900, PF_IEEE_FLOAT | PF_ZARCH},
+      {"z990", PROCESSOR_2084_Z990, PF_IEEE_FLOAT | PF_ZARCH
+                                   | PF_LONG_DISPLACEMENT},
+    };
+
+  int const pta_size = ARRAY_SIZE (processor_alias_table);
+
   /* Acquire a unique set number for our register saves and restores.  */
   s390_sr_alias_set = new_alias_set ();
 
   /* Set up function hooks.  */
   init_machine_status = s390_init_machine_status;
+
+  /* Architecture mode defaults according to ABI.  */
+  if (!(target_flags_explicit & MASK_ZARCH))
+    {
+      if (TARGET_64BIT)
+       target_flags |= MASK_ZARCH;
+      else
+       target_flags &= ~MASK_ZARCH;
+    }
+
+  /* Determine processor architectural level.  */
+  if (!s390_arch_string)
+    s390_arch_string = TARGET_ZARCH? "z900" : "g5";
+
+  for (i = 0; i < pta_size; i++)
+    if (! strcmp (s390_arch_string, processor_alias_table[i].name))
+      {
+       s390_arch = processor_alias_table[i].processor;
+       s390_arch_flags = processor_alias_table[i].flags;
+       break;
+      }
+  if (i == pta_size)
+    error ("Unknown cpu used in -march=%s.", s390_arch_string);
+
+  /* Determine processor to tune for.  */
+  if (!s390_tune_string)
+    {
+      s390_tune = s390_arch;
+      s390_tune_flags = s390_arch_flags;
+      s390_tune_string = s390_arch_string;
+    }
+  else
+    {
+      for (i = 0; i < pta_size; i++)
+       if (! strcmp (s390_tune_string, processor_alias_table[i].name))
+         {
+           s390_tune = processor_alias_table[i].processor;
+           s390_tune_flags = processor_alias_table[i].flags;
+           break;
+         }
+      if (i == pta_size)
+       error ("Unknown cpu used in -mtune=%s.", s390_tune_string);
+    }
+
+  /* Sanity checks.  */
+  if (TARGET_ZARCH && !(s390_arch_flags & PF_ZARCH))
+    error ("z/Architecture mode not supported on %s.", s390_arch_string);
+  if (TARGET_64BIT && !TARGET_ZARCH)
+    error ("64-bit ABI not supported in ESA/390 mode.");
 }
 
 /* Map for smallest class containing reg regno.  */
@@ -850,18 +965,26 @@ const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
   FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
   FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
   FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
-  ADDR_REGS,    NO_REGS,   ADDR_REGS 
+  ADDR_REGS,    NO_REGS,   ADDR_REGS
 };
 
+/* Return attribute type of insn.  */
+
+static enum attr_type
+s390_safe_attr_type (rtx insn)
+{
+  if (recog_memoized (insn) >= 0)
+    return get_attr_type (insn);
+  else
+    return TYPE_NONE;
+}
 
 /* Return true if OP a (const_int 0) operand.
    OP is the current operation.
    MODE is the current operation mode.  */
+
 int
-const0_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode;
+const0_operand (register rtx op, enum machine_mode mode)
 {
   return op == CONST0_RTX (mode);
 }
@@ -871,20 +994,16 @@ const0_operand (op, mode)
    MODE is the current operation mode.  */
 
 int
-consttable_operand (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+consttable_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 {
   return CONSTANT_P (op);
 }
 
 /* Return true if the mode of operand OP matches MODE.
-   If MODE is set to VOIDmode, set it to the mode of OP.  */ 
+   If MODE is set to VOIDmode, set it to the mode of OP.  */
 
 static int
-check_mode (op, mode)
-     register rtx op;
-     enum machine_mode *mode;
+check_mode (register rtx op, enum machine_mode *mode)
 {
   if (*mode == VOIDmode)
       *mode = GET_MODE (op);
@@ -901,9 +1020,7 @@ check_mode (op, mode)
    MODE is the current operation mode.  */
 
 int
-larl_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode;
+larl_operand (register rtx op, enum machine_mode mode)
 {
   if (! check_mode (op, &mode))
     return 0;
@@ -911,39 +1028,48 @@ larl_operand (op, mode)
   /* Allow labels and local symbols.  */
   if (GET_CODE (op) == LABEL_REF)
     return 1;
-  if (GET_CODE (op) == SYMBOL_REF
-      && (!flag_pic || SYMBOL_REF_FLAG (op) 
-          || CONSTANT_POOL_ADDRESS_P (op)))
-    return 1;
+  if (GET_CODE (op) == SYMBOL_REF)
+    return ((SYMBOL_REF_FLAGS (op) & SYMBOL_FLAG_ALIGN1) == 0
+           && SYMBOL_REF_TLS_MODEL (op) == 0
+           && (!flag_pic || SYMBOL_REF_LOCAL_P (op)));
 
   /* Everything else must have a CONST, so strip it.  */
   if (GET_CODE (op) != CONST)
     return 0;
   op = XEXP (op, 0);
 
-  /* Allow adding *even* constants.  */
+  /* Allow adding *even* in-range constants.  */
   if (GET_CODE (op) == PLUS)
     {
       if (GET_CODE (XEXP (op, 1)) != CONST_INT
           || (INTVAL (XEXP (op, 1)) & 1) != 0)
         return 0;
+#if HOST_BITS_PER_WIDE_INT > 32
+      if (INTVAL (XEXP (op, 1)) >= (HOST_WIDE_INT)1 << 32
+         || INTVAL (XEXP (op, 1)) < -((HOST_WIDE_INT)1 << 32))
+        return 0;
+#endif
       op = XEXP (op, 0);
     }
 
   /* Labels and local symbols allowed here as well.  */
   if (GET_CODE (op) == LABEL_REF)
     return 1;
-  if (GET_CODE (op) == SYMBOL_REF
-      && (!flag_pic || SYMBOL_REF_FLAG (op)
-          || CONSTANT_POOL_ADDRESS_P (op)))
-    return 1;
+  if (GET_CODE (op) == SYMBOL_REF)
+    return ((SYMBOL_REF_FLAGS (op) & SYMBOL_FLAG_ALIGN1) == 0
+           && SYMBOL_REF_TLS_MODEL (op) == 0
+           && (!flag_pic || SYMBOL_REF_LOCAL_P (op)));
 
-  /* Now we must have a @GOTENT offset or @PLT stub.  */
+  /* Now we must have a @GOTENT offset or @PLT stub
+     or an @INDNTPOFF TLS offset.  */
+  if (GET_CODE (op) == UNSPEC
+      && XINT (op, 1) == UNSPEC_GOTENT)
+    return 1;
   if (GET_CODE (op) == UNSPEC
-      && XINT (op, 1) == 111)
+      && XINT (op, 1) == UNSPEC_PLT)
     return 1;
   if (GET_CODE (op) == UNSPEC
-      && XINT (op, 1) == 113)
+      && XINT (op, 1) == UNSPEC_INDNTPOFF)
     return 1;
 
   return 0;
@@ -956,10 +1082,8 @@ larl_operand (op, mode)
    be accepted or not.  */
 
 static int
-general_s_operand (op, mode, allow_immediate)
-     register rtx op;
-     enum machine_mode mode;
-     int allow_immediate;
+general_s_operand (register rtx op, enum machine_mode mode,
+                  int allow_immediate)
 {
   struct s390_address addr;
 
@@ -970,37 +1094,37 @@ general_s_operand (op, mode, allow_immediate)
 
   /* Just like memory_operand, allow (subreg (mem ...))
      after reload.  */
-  if (reload_completed 
-      && GET_CODE (op) == SUBREG 
+  if (reload_completed
+      && GET_CODE (op) == SUBREG
       && GET_CODE (SUBREG_REG (op)) == MEM)
     op = SUBREG_REG (op);
 
   switch (GET_CODE (op))
     {
-      /* Constants that we are sure will be forced to the
-         literal pool in reload are OK as s-operand.  Note
-        that we cannot call s390_preferred_reload_class here
-        because it might not be known yet at this point 
-        whether the current function is a leaf or not.  */
+      /* Constants are OK as s-operand if ALLOW_IMMEDIATE
+        is true and we are still before reload.  */
       case CONST_INT:
       case CONST_DOUBLE:
        if (!allow_immediate || reload_completed)
-         break;
-       if (!legitimate_reload_constant_p (op))
-         return 1;
-       if (!TARGET_64BIT)
-         return 1;
-       break;
+         return 0;
+       return 1;
 
       /* Memory operands are OK unless they already use an
         index register.  */
       case MEM:
        if (GET_CODE (XEXP (op, 0)) == ADDRESSOF)
          return 1;
-       if (s390_decompose_address (XEXP (op, 0), &addr) 
-           && !addr.indx)
-         return 1;
-       break;
+       if (!s390_decompose_address (XEXP (op, 0), &addr))
+         return 0;
+       if (addr.indx)
+         return 0;
+       /* Do not allow literal pool references unless ALLOW_IMMEDIATE 
+          is true.  This prevents compares between two literal pool 
+          entries from being accepted.  */
+       if (!allow_immediate 
+           && addr.base && REGNO (addr.base) == BASE_REGISTER)
+         return 0;
+       return 1;
 
       default:
        break;
@@ -1014,100 +1138,383 @@ general_s_operand (op, mode, allow_immediate)
    MODE is the current operation mode.  */
 
 int
-s_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode;
+s_operand (register rtx op, enum machine_mode mode)
 {
   return general_s_operand (op, mode, 0);
 }
 
-/* Return true if OP is a valid S-type operand or an immediate 
-   operand that can be addressed as S-type operand by forcing 
+/* Return true if OP is a valid S-type operand or an immediate
+   operand that can be addressed as S-type operand by forcing
    it into the literal pool.
    OP is the current operation.
    MODE is the current operation mode.  */
 
 int
-s_imm_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode;
+s_imm_operand (register rtx op, enum machine_mode mode)
 {
   return general_s_operand (op, mode, 1);
 }
 
-/* Return true if OP is a valid operand for a 'Q' constraint.
-   This differs from s_operand in that only memory operands
-   without index register are accepted, nothing else.  */
+/* Return true if OP a valid shift count operand.
+   OP is the current operation.
+   MODE is the current operation mode.  */
 
 int
-q_constraint (op)
-     register rtx op;
+shift_count_operand (rtx op, enum machine_mode mode)
 {
-  struct s390_address addr;
+  HOST_WIDE_INT offset = 0;
 
-  if (GET_CODE (op) != MEM)
+  if (! check_mode (op, &mode))
     return 0;
 
-  if (!s390_decompose_address (XEXP (op, 0), &addr))
+  /* We can have an integer constant, an address register,
+     or a sum of the two.  Note that reload already checks
+     that any register present is an address register, so
+     we just check for any register here.  */
+  if (GET_CODE (op) == CONST_INT)
+    {
+      offset = INTVAL (op);
+      op = NULL_RTX;
+    }
+  if (op && GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT)
+    {
+      offset = INTVAL (XEXP (op, 1));
+      op = XEXP (op, 0);
+    }
+  while (op && GET_CODE (op) == SUBREG)
+    op = SUBREG_REG (op);
+  if (op && GET_CODE (op) != REG)
     return 0;
 
-  if (addr.indx)
+  /* Unfortunately we have to reject constants that are invalid
+     for an address, or else reload will get confused.  */
+  if (!DISP_IN_RANGE (offset))
     return 0;
 
   return 1;
 }
 
-/* Return the cost of an address rtx ADDR.  */
+/* Return true if DISP is a valid short displacement.  */
 
-int
-s390_address_cost (addr)
-     rtx addr;
+static int
+s390_short_displacement (rtx disp)
 {
-  struct s390_address ad;
-  if (!s390_decompose_address (addr, &ad))
-    return 1000;
-
-  return ad.indx? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (1);
-}
+  /* No displacement is OK.  */
+  if (!disp)
+    return 1;
 
-/* Return true if OP is a valid operand for the BRAS instruction.
-   OP is the current operation.
-   MODE is the current operation mode.  */
+  /* Integer displacement in range.  */
+  if (GET_CODE (disp) == CONST_INT)
+    return INTVAL (disp) >= 0 && INTVAL (disp) < 4096;
 
-int
-bras_sym_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-{
-  register enum rtx_code code = GET_CODE (op);
+  /* GOT offset is not OK, the GOT can be large.  */
+  if (GET_CODE (disp) == CONST
+      && GET_CODE (XEXP (disp, 0)) == UNSPEC
+      && XINT (XEXP (disp, 0), 1) == UNSPEC_GOT)
+    return 0;
 
-  /* Allow SYMBOL_REFs.  */
-  if (code == SYMBOL_REF)
+  /* All other symbolic constants are literal pool references,
+     which are OK as the literal pool must be small.  */
+  if (GET_CODE (disp) == CONST)
     return 1;
 
-  /* Allow @PLT stubs.  */
-  if (code == CONST
-      && GET_CODE (XEXP (op, 0)) == UNSPEC
-      && XINT (XEXP (op, 0), 1) == 113)
-    return 1;
   return 0;
 }
 
-\f
-/* Return true if OP is a load multiple operation.  It is known to be a
-   PARALLEL and the first section will be tested. 
-   OP is the current operation.
-   MODE is the current operation mode.  */
+/* Return true if OP is a valid operand for a C constraint.  */
 
 int
-load_multiple_operation (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+s390_extra_constraint_str (rtx op, int c, const char * str)
 {
-  int count = XVECLEN (op, 0);
-  unsigned int dest_regno;
-  rtx src_addr;
-  int i, off;
+  struct s390_address addr;
+
+  if (c != str[0])
+    abort ();
+
+  switch (c)
+    {
+    case 'Q':
+      if (GET_CODE (op) != MEM)
+       return 0;
+      if (!s390_decompose_address (XEXP (op, 0), &addr))
+       return 0;
+      if (addr.indx)
+       return 0;
+
+      if (TARGET_LONG_DISPLACEMENT)
+       {
+         if (!s390_short_displacement (addr.disp))
+           return 0;
+       }
+      break;
+
+    case 'R':
+      if (GET_CODE (op) != MEM)
+       return 0;
+
+      if (TARGET_LONG_DISPLACEMENT)
+       {
+         if (!s390_decompose_address (XEXP (op, 0), &addr))
+           return 0;
+         if (!s390_short_displacement (addr.disp))
+           return 0;
+       }
+      break;
+
+    case 'S':
+      if (!TARGET_LONG_DISPLACEMENT)
+       return 0;
+      if (GET_CODE (op) != MEM)
+       return 0;
+      if (!s390_decompose_address (XEXP (op, 0), &addr))
+       return 0;
+      if (addr.indx)
+       return 0;
+      if (s390_short_displacement (addr.disp))
+       return 0;
+      break;
+
+    case 'T':
+      if (!TARGET_LONG_DISPLACEMENT)
+       return 0;
+      if (GET_CODE (op) != MEM)
+       return 0;
+      /* Any invalid address here will be fixed up by reload,
+        so accept it for the most generic constraint.  */
+      if (s390_decompose_address (XEXP (op, 0), &addr)
+         && s390_short_displacement (addr.disp))
+       return 0;
+      break;
+
+    case 'U':
+      if (TARGET_LONG_DISPLACEMENT)
+       {
+         if (!s390_decompose_address (op, &addr))
+           return 0;
+         if (!s390_short_displacement (addr.disp))
+           return 0;
+       }
+      break;
+
+    case 'W':
+      if (!TARGET_LONG_DISPLACEMENT)
+       return 0;
+      /* Any invalid address here will be fixed up by reload,
+        so accept it for the most generic constraint.  */
+      if (s390_decompose_address (op, &addr)
+         && s390_short_displacement (addr.disp))
+       return 0;
+      break;
+
+    case 'Y':
+      return shift_count_operand (op, VOIDmode);
+
+    default:
+      return 0;
+    }
+
+  return 1;
+}
+
+/* Return true if VALUE matches the constraint STR.  */
+
+int
+s390_const_ok_for_constraint_p (HOST_WIDE_INT value,
+                               int c,
+                               const char * str)
+{
+  enum machine_mode mode, part_mode;
+  int def;
+  unsigned char part;
+
+  if (c != str[0])
+    abort ();
+
+  switch (str[0])
+    {
+    case 'I':
+      return (unsigned int)value < 256;
+
+    case 'J':
+      return (unsigned int)value < 4096;
+
+    case 'K':
+      return value >= -32768 && value < 32768;
+
+    case 'L':
+      return (TARGET_LONG_DISPLACEMENT ? 
+             (value >= -524288 && value <= 524287) 
+             : (value >= 0 && value <= 4095));
+    case 'M':
+      return value == 2147483647;
+
+    case 'N':
+      part = str[1] - '0';
+
+      switch (str[2])
+       {
+       case 'H': part_mode = HImode; break;
+       case 'Q': part_mode = QImode; break;
+       default:  return 0;
+       }
+      
+      switch (str[3])
+       {
+       case 'H': mode = HImode; break;
+       case 'S': mode = SImode; break;
+       case 'D': mode = DImode; break;
+       default: return 0;
+       }
+
+      switch (str[4])
+       {
+       case '0': def = 0;  break;
+       case 'F': def = -1; break;
+       default: return 0;
+       }
+
+      if (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (part_mode))
+       return 0;
+
+      if (s390_single_part (GEN_INT (value), mode, part_mode, def) != part)
+       return 0;
+
+      break;
+
+    default:
+      return 0;
+    }
+
+  return 1;
+}
+
+/* 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
+s390_rtx_costs (rtx x, int code, int outer_code, int *total)
+{
+  switch (code)
+    {
+    case CONST:
+      if (GET_CODE (XEXP (x, 0)) == MINUS
+         && GET_CODE (XEXP (XEXP (x, 0), 1)) != CONST_INT)
+       *total = 1000;
+      else
+       *total = 0;
+      return true;
+
+    case CONST_INT:
+      /* Force_const_mem does not work out of reload, because the
+        saveable_obstack is set to reload_obstack, which does not
+        live long enough.  Because of this we cannot use force_const_mem
+        in addsi3.  This leads to problems with gen_add2_insn with a
+        constant greater than a short. Because of that we give an
+        addition of greater constants a cost of 3 (reload1.c 10096).  */
+      /* ??? saveable_obstack no longer exists.  */
+      if (outer_code == PLUS
+         && (INTVAL (x) > 32767 || INTVAL (x) < -32768))
+       *total = COSTS_N_INSNS (3);
+      else
+       *total = 0;
+      return true;
+
+    case LABEL_REF:
+    case SYMBOL_REF:
+    case CONST_DOUBLE:
+      *total = 0;
+      return true;
+
+    case ASHIFT:
+    case ASHIFTRT:
+    case LSHIFTRT:
+    case PLUS:
+    case AND:
+    case IOR:
+    case XOR:
+    case MINUS:
+    case NEG:
+    case NOT:
+      *total = COSTS_N_INSNS (1);
+      return true;
+
+    case MULT:
+      if (GET_MODE (XEXP (x, 0)) == DImode)
+        *total = COSTS_N_INSNS (40);
+      else
+        *total = COSTS_N_INSNS (7);
+      return true;
+
+    case DIV:
+    case UDIV:
+    case MOD:
+    case UMOD:
+      *total = COSTS_N_INSNS (33);
+      return true;
+
+    default:
+      return false;
+    }
+}
+
+/* Return the cost of an address rtx ADDR.  */
+
+static int
+s390_address_cost (rtx addr)
+{
+  struct s390_address ad;
+  if (!s390_decompose_address (addr, &ad))
+    return 1000;
+
+  return ad.indx? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (1);
+}
+
+/* Return true if OP is a valid operand for the BRAS instruction.
+   OP is the current operation.
+   MODE is the current operation mode.  */
+
+int
+bras_sym_operand (register rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  register enum rtx_code code = GET_CODE (op);
+
+  /* Allow SYMBOL_REFs.  */
+  if (code == SYMBOL_REF)
+    return 1;
+
+  /* Allow @PLT stubs.  */
+  if (code == CONST
+      && GET_CODE (XEXP (op, 0)) == UNSPEC
+      && XINT (XEXP (op, 0), 1) == UNSPEC_PLT)
+    return 1;
+  return 0;
+}
+
+/* If OP is a SYMBOL_REF of a thread-local symbol, return its TLS mode,
+   otherwise return 0.  */
+
+int
+tls_symbolic_operand (register rtx op)
+{
+  if (GET_CODE (op) != SYMBOL_REF)
+    return 0;
+  return SYMBOL_REF_TLS_MODEL (op);
+}
+\f
+/* Return true if OP is a load multiple operation.  It is known to be a
+   PARALLEL and the first section will be tested.
+   OP is the current operation.
+   MODE is the current operation mode.  */
+
+int
+load_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+{
+  enum machine_mode elt_mode;
+  int count = XVECLEN (op, 0);
+  unsigned int dest_regno;
+  rtx src_addr;
+  int i, off;
 
 
   /* Perform a quick check so we don't blow up below.  */
@@ -1119,13 +1526,14 @@ load_multiple_operation (op, mode)
 
   dest_regno = REGNO (SET_DEST (XVECEXP (op, 0, 0)));
   src_addr = XEXP (SET_SRC (XVECEXP (op, 0, 0)), 0);
+  elt_mode = GET_MODE (SET_DEST (XVECEXP (op, 0, 0)));
 
   /* Check, is base, or base + displacement.  */
 
   if (GET_CODE (src_addr) == REG)
     off = 0;
   else if (GET_CODE (src_addr) == PLUS
-          && GET_CODE (XEXP (src_addr, 0)) == REG 
+          && GET_CODE (XEXP (src_addr, 0)) == REG
           && GET_CODE (XEXP (src_addr, 1)) == CONST_INT)
     {
       off = INTVAL (XEXP (src_addr, 1));
@@ -1143,15 +1551,15 @@ load_multiple_operation (op, mode)
 
       if (GET_CODE (elt) != SET
          || GET_CODE (SET_DEST (elt)) != REG
-         || GET_MODE (SET_DEST (elt)) != Pmode
+         || GET_MODE (SET_DEST (elt)) != elt_mode
          || REGNO (SET_DEST (elt)) != dest_regno + i
          || GET_CODE (SET_SRC (elt)) != MEM
-         || GET_MODE (SET_SRC (elt)) != Pmode
+         || GET_MODE (SET_SRC (elt)) != elt_mode
          || GET_CODE (XEXP (SET_SRC (elt), 0)) != PLUS
          || ! rtx_equal_p (XEXP (XEXP (SET_SRC (elt), 0), 0), src_addr)
          || GET_CODE (XEXP (XEXP (SET_SRC (elt), 0), 1)) != CONST_INT
          || INTVAL (XEXP (XEXP (SET_SRC (elt), 0), 1))
-            != off + i * UNITS_PER_WORD)
+            != off + i * GET_MODE_SIZE (elt_mode))
        return 0;
     }
 
@@ -1159,15 +1567,14 @@ load_multiple_operation (op, mode)
 }
 
 /* Return true if OP is a store multiple operation.  It is known to be a
-   PARALLEL and the first section will be tested. 
+   PARALLEL and the first section will be tested.
    OP is the current operation.
    MODE is the current operation mode.  */
 
 int
-store_multiple_operation (op, mode)
-     rtx op;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+store_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
 {
+  enum machine_mode elt_mode;
   int count = XVECLEN (op, 0);
   unsigned int src_regno;
   rtx dest_addr;
@@ -1182,13 +1589,14 @@ store_multiple_operation (op, mode)
 
   src_regno = REGNO (SET_SRC (XVECEXP (op, 0, 0)));
   dest_addr = XEXP (SET_DEST (XVECEXP (op, 0, 0)), 0);
+  elt_mode = GET_MODE (SET_SRC (XVECEXP (op, 0, 0)));
 
   /* Check, is base, or base + displacement.  */
 
   if (GET_CODE (dest_addr) == REG)
     off = 0;
   else if (GET_CODE (dest_addr) == PLUS
-          && GET_CODE (XEXP (dest_addr, 0)) == REG 
+          && GET_CODE (XEXP (dest_addr, 0)) == REG
           && GET_CODE (XEXP (dest_addr, 1)) == CONST_INT)
     {
       off = INTVAL (XEXP (dest_addr, 1));
@@ -1206,15 +1614,15 @@ store_multiple_operation (op, mode)
 
       if (GET_CODE (elt) != SET
          || GET_CODE (SET_SRC (elt)) != REG
-         || GET_MODE (SET_SRC (elt)) != Pmode
+         || GET_MODE (SET_SRC (elt)) != elt_mode
          || REGNO (SET_SRC (elt)) != src_regno + i
          || GET_CODE (SET_DEST (elt)) != MEM
-         || GET_MODE (SET_DEST (elt)) != Pmode
+         || GET_MODE (SET_DEST (elt)) != elt_mode
          || GET_CODE (XEXP (SET_DEST (elt), 0)) != PLUS
          || ! rtx_equal_p (XEXP (XEXP (SET_DEST (elt), 0), 0), dest_addr)
          || GET_CODE (XEXP (XEXP (SET_DEST (elt), 0), 1)) != CONST_INT
          || INTVAL (XEXP (XEXP (SET_DEST (elt), 0), 1))
-            != off + i * UNITS_PER_WORD)
+            != off + i * GET_MODE_SIZE (elt_mode))
        return 0;
     }
   return 1;
@@ -1224,8 +1632,7 @@ store_multiple_operation (op, mode)
 /* Return true if OP contains a symbol reference */
 
 int
-symbolic_reference_mentioned_p (op)
-     rtx op;
+symbolic_reference_mentioned_p (rtx op)
 {
   register const char *fmt;
   register int i;
@@ -1252,21 +1659,50 @@ symbolic_reference_mentioned_p (op)
   return 0;
 }
 
+/* Return true if OP contains a reference to a thread-local symbol.  */
+
+int
+tls_symbolic_reference_mentioned_p (rtx op)
+{
+  register const char *fmt;
+  register int i;
+
+  if (GET_CODE (op) == SYMBOL_REF)
+    return tls_symbolic_operand (op);
+
+  fmt = GET_RTX_FORMAT (GET_CODE (op));
+  for (i = GET_RTX_LENGTH (GET_CODE (op)) - 1; i >= 0; i--)
+    {
+      if (fmt[i] == 'E')
+       {
+         register int j;
+
+         for (j = XVECLEN (op, i) - 1; j >= 0; j--)
+           if (tls_symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
+             return 1;
+       }
+
+      else if (fmt[i] == 'e' && tls_symbolic_reference_mentioned_p (XEXP (op, i)))
+       return 1;
+    }
+
+  return 0;
+}
+
 
-/* Return true if OP is a legitimate general operand when 
-   generating PIC code.  It is given that flag_pic is on 
+/* Return true if OP is a legitimate general operand when
+   generating PIC code.  It is given that flag_pic is on
    and that OP satisfies CONSTANT_P or is a CONST_DOUBLE.  */
 
 int
-legitimate_pic_operand_p (op)
-     register rtx op;
+legitimate_pic_operand_p (register rtx op)
 {
   /* Accept all non-symbolic constants.  */
   if (!SYMBOLIC_CONST (op))
     return 1;
 
-  /* Reject everything else; must be handled 
-     via emit_pic_move.  */
+  /* Reject everything else; must be handled
+     via emit_symbolic_move.  */
   return 0;
 }
 
@@ -1274,51 +1710,118 @@ legitimate_pic_operand_p (op)
    It is given that OP satisfies CONSTANT_P or is a CONST_DOUBLE.  */
 
 int
-legitimate_constant_p (op)
-     register rtx op;
+legitimate_constant_p (register rtx op)
 {
   /* Accept all non-symbolic constants.  */
   if (!SYMBOLIC_CONST (op))
     return 1;
 
+  /* Accept immediate LARL operands.  */
+  if (TARGET_CPU_ZARCH && larl_operand (op, VOIDmode))
+    return 1;
+
+  /* Thread-local symbols are never legal constants.  This is
+     so that emit_call knows that computing such addresses
+     might require a function call.  */
+  if (TLS_SYMBOLIC_CONST (op))
+    return 0;
+
   /* In the PIC case, symbolic constants must *not* be
      forced into the literal pool.  We accept them here,
-     so that they will be handled by emit_pic_move.  */
+     so that they will be handled by emit_symbolic_move.  */
   if (flag_pic)
     return 1;
 
-  /* Even in the non-PIC case, we can accept immediate
-     LARL operands here.  */
-  if (TARGET_64BIT)
-    return larl_operand (op, VOIDmode);
-
   /* All remaining non-PIC symbolic constants are
      forced into the literal pool.  */
   return 0;
 }
 
+/* Determine if it's legal to put X into the constant pool.  This
+   is not possible if X contains the address of a symbol that is
+   not constant (TLS) or not known at final link time (PIC).  */
+
+static bool
+s390_cannot_force_const_mem (rtx x)
+{
+  switch (GET_CODE (x))
+    {
+    case CONST_INT:
+    case CONST_DOUBLE:
+      /* Accept all non-symbolic constants.  */
+      return false;
+
+    case LABEL_REF:
+      /* Labels are OK iff we are non-PIC.  */
+      return flag_pic != 0;
+
+    case SYMBOL_REF:
+      /* 'Naked' TLS symbol references are never OK,
+         non-TLS symbols are OK iff we are non-PIC.  */
+      if (tls_symbolic_operand (x))
+       return true;
+      else
+       return flag_pic != 0;
+
+    case CONST:
+      return s390_cannot_force_const_mem (XEXP (x, 0));
+    case PLUS:
+    case MINUS:
+      return s390_cannot_force_const_mem (XEXP (x, 0))
+            || s390_cannot_force_const_mem (XEXP (x, 1));
+
+    case UNSPEC:
+      switch (XINT (x, 1))
+       {
+       /* Only lt-relative or GOT-relative UNSPECs are OK.  */
+       case UNSPEC_LTREL_OFFSET:
+       case UNSPEC_GOT:
+       case UNSPEC_GOTOFF:
+       case UNSPEC_PLTOFF:
+       case UNSPEC_TLSGD:
+       case UNSPEC_TLSLDM:
+       case UNSPEC_NTPOFF:
+       case UNSPEC_DTPOFF:
+       case UNSPEC_GOTNTPOFF:
+       case UNSPEC_INDNTPOFF:
+         return false;
+
+       default:
+         return true;
+       }
+      break;
+
+    default:
+      abort ();
+    }
+}
+
 /* Returns true if the constant value OP is a legitimate general
-   operand during and after reload.  The difference to 
+   operand during and after reload.  The difference to
    legitimate_constant_p is that this function will not accept
    a constant that would need to be forced to the literal pool
    before it can be used as operand.  */
 
 int
-legitimate_reload_constant_p (op)
-     register rtx op;
+legitimate_reload_constant_p (register rtx op)
 {
+  /* Accept la(y) operands.  */
+  if (GET_CODE (op) == CONST_INT
+      && DISP_IN_RANGE (INTVAL (op)))
+    return 1;
+
   /* Accept l(g)hi operands.  */
   if (GET_CODE (op) == CONST_INT
-      && CONST_OK_FOR_LETTER_P (INTVAL (op), 'K'))
+      && CONST_OK_FOR_CONSTRAINT_P (INTVAL (op), 'K', "K"))
     return 1;
 
   /* Accept lliXX operands.  */
-  if (TARGET_64BIT
-      && s390_single_hi (op, DImode, 0) >= 0)
+  if (TARGET_ZARCH
+      && s390_single_part (op, DImode, HImode, 0) >= 0)
   return 1;
 
   /* Accept larl operands.  */
-  if (TARGET_64BIT
+  if (TARGET_CPU_ZARCH
       && larl_operand (op, VOIDmode))
     return 1;
 
@@ -1330,9 +1833,7 @@ legitimate_reload_constant_p (op)
    return the class of reg to actually use.  */
 
 enum reg_class
-s390_preferred_reload_class (op, class)
-     rtx op;
-     enum reg_class class;
+s390_preferred_reload_class (rtx op, enum reg_class class)
 {
   /* This can happen if a floating point constant is being
      reloaded into an integer register.  Leave well alone.  */
@@ -1343,23 +1844,14 @@ s390_preferred_reload_class (op, class)
   switch (GET_CODE (op))
     {
       /* Constants we cannot reload must be forced into the
-        literal pool.  For constants we *could* handle directly,
-        it might still be preferable to put them in the pool and
-        use a memory-to-memory instruction.
-
-        However, try to avoid needlessly allocating a literal
-        pool in a routine that wouldn't otherwise need any.
-        Heuristically, we assume that 64-bit leaf functions
-        typically don't need a literal pool, all others do.  */
+        literal pool.  */
+
       case CONST_DOUBLE:
       case CONST_INT:
-       if (!legitimate_reload_constant_p (op))
-         return NO_REGS;
-
-       if (TARGET_64BIT && current_function_is_leaf)
+       if (legitimate_reload_constant_p (op))
          return class;
-
-       return NO_REGS;
+       else
+         return NO_REGS;
 
       /* If a symbolic constant or a PLUS is reloaded,
         it is most likely being used as an address, so
@@ -1388,10 +1880,8 @@ s390_preferred_reload_class (op, class)
    is not a legitimate operand of the LOAD ADDRESS instruction.  */
 
 enum reg_class
-s390_secondary_input_reload_class (class, mode, in)
-     enum reg_class class ATTRIBUTE_UNUSED;
-     enum machine_mode mode;
-     rtx in;
+s390_secondary_input_reload_class (enum reg_class class ATTRIBUTE_UNUSED,
+                                  enum machine_mode mode, rtx in)
 {
   if (s390_plus_operand (in, mode))
     return ADDR_REGS;
@@ -1399,15 +1889,34 @@ s390_secondary_input_reload_class (class, mode, in)
   return NO_REGS;
 }
 
+/* Return the register class of a scratch register needed to
+   store a register of class CLASS in MODE into OUT:
+
+   We need a temporary when storing a double-word to a
+   non-offsettable memory address.  */
+
+enum reg_class
+s390_secondary_output_reload_class (enum reg_class class,
+                                   enum machine_mode mode, rtx out)
+{
+  if ((TARGET_64BIT ? mode == TImode
+                    : (mode == DImode || mode == DFmode))
+      && reg_classes_intersect_p (GENERAL_REGS, class)
+      && GET_CODE (out) == MEM
+      && !offsettable_memref_p (out)
+      && !s_operand (out, VOIDmode))
+    return ADDR_REGS;
+
+  return NO_REGS;
+}
+
 /* Return true if OP is a PLUS that is not a legitimate
-   operand for the LA instruction. 
+   operand for the LA instruction.
    OP is the current operation.
    MODE is the current operation mode.  */
 
 int
-s390_plus_operand (op, mode)
-     register rtx op;
-     enum machine_mode mode;
+s390_plus_operand (register rtx op, enum machine_mode mode)
 {
   if (!check_mode (op, &mode) || mode != Pmode)
     return FALSE;
@@ -1426,10 +1935,8 @@ s390_plus_operand (op, mode)
    SCRATCH may be used as scratch register.  */
 
 void
-s390_expand_plus_operand (target, src, scratch)
-     register rtx target;
-     register rtx src;
-     register rtx scratch;
+s390_expand_plus_operand (register rtx target, register rtx src,
+                         register rtx scratch)
 {
   rtx sum1, sum2;
   struct s390_address ad;
@@ -1495,14 +2002,14 @@ s390_expand_plus_operand (target, src, scratch)
    canonical form so that they will be recognized.  */
 
 static int
-s390_decompose_address (addr, out)
-     register rtx addr;
-     struct s390_address *out;
+s390_decompose_address (register rtx addr, struct s390_address *out)
 {
   rtx base = NULL_RTX;
   rtx indx = NULL_RTX;
   rtx disp = NULL_RTX;
   int pointer = FALSE;
+  int base_ptr = FALSE;
+  int indx_ptr = FALSE;
 
   /* Decompose address into base + index + displacement.  */
 
@@ -1553,14 +2060,13 @@ s390_decompose_address (addr, out)
     {
       if (GET_CODE (base) == UNSPEC)
         {
-          if (XVECLEN (base, 0) != 1 || XINT (base, 1) != 101)
-             return FALSE;
-         base = XVECEXP (base, 0, 0);
-         pointer = TRUE;
+          if (XVECLEN (base, 0) != 1 || XINT (base, 1) != UNSPEC_LTREL_BASE)
+           return FALSE;
+         base = gen_rtx_REG (Pmode, BASE_REGISTER);
        }
 
       if (GET_CODE (base) != REG || GET_MODE (base) != Pmode)
-         return FALSE;
+       return FALSE;
 
       if (REGNO (base) == BASE_REGISTER
          || REGNO (base) == STACK_POINTER_REGNUM
@@ -1569,11 +2075,9 @@ s390_decompose_address (addr, out)
              && frame_pointer_needed
              && REGNO (base) == HARD_FRAME_POINTER_REGNUM)
          || REGNO (base) == ARG_POINTER_REGNUM
-         || (REGNO (base) >= FIRST_VIRTUAL_REGISTER
-             && REGNO (base) <= LAST_VIRTUAL_REGISTER)
           || (flag_pic
               && REGNO (base) == PIC_OFFSET_TABLE_REGNUM))
-        pointer = TRUE;
+        pointer = base_ptr = TRUE;
     }
 
   /* Validate index register.  */
@@ -1581,14 +2085,13 @@ s390_decompose_address (addr, out)
     {
       if (GET_CODE (indx) == UNSPEC)
         {
-          if (XVECLEN (indx, 0) != 1 || XINT (indx, 1) != 101)
-             return FALSE;
-         indx = XVECEXP (indx, 0, 0);
-         pointer = TRUE;
+          if (XVECLEN (indx, 0) != 1 || XINT (indx, 1) != UNSPEC_LTREL_BASE)
+           return FALSE;
+         indx = gen_rtx_REG (Pmode, BASE_REGISTER);
        }
 
       if (GET_CODE (indx) != REG || GET_MODE (indx) != Pmode)
-         return FALSE;
+       return FALSE;
 
       if (REGNO (indx) == BASE_REGISTER
          || REGNO (indx) == STACK_POINTER_REGNUM
@@ -1597,11 +2100,18 @@ s390_decompose_address (addr, out)
              && frame_pointer_needed
              && REGNO (indx) == HARD_FRAME_POINTER_REGNUM)
          || REGNO (indx) == ARG_POINTER_REGNUM
-         || (REGNO (indx) >= FIRST_VIRTUAL_REGISTER
-             && REGNO (indx) <= LAST_VIRTUAL_REGISTER)
           || (flag_pic
               && REGNO (indx) == PIC_OFFSET_TABLE_REGNUM))
-        pointer = TRUE;
+        pointer = indx_ptr = TRUE;
+    }
+
+  /* Prefer to use pointer as base, not index.  */
+  if (base && indx && !base_ptr
+      && (indx_ptr || (!REG_POINTER (base) && REG_POINTER (indx))))
+    {
+      rtx tmp = base;
+      base = indx;
+      indx = tmp;
     }
 
   /* Validate displacement.  */
@@ -1620,16 +2130,17 @@ s390_decompose_address (addr, out)
             this is fixed up by reload in any case.  */
          if (base != arg_pointer_rtx && indx != arg_pointer_rtx)
            {
-             if (INTVAL (disp) < 0 || INTVAL (disp) >= 4096)
+             if (!DISP_IN_RANGE (INTVAL (disp)))
                return FALSE;
            }
         }
 
-      /* In the small-PIC case, the linker converts @GOT12 
-         offsets to possible displacements.  */
+      /* In the small-PIC case, the linker converts @GOT
+         and @GOTNTPOFF offsets to possible displacements.  */
       else if (GET_CODE (disp) == CONST
                && GET_CODE (XEXP (disp, 0)) == UNSPEC
-               && XINT (XEXP (disp, 0), 1) == 110)
+               && (XINT (XEXP (disp, 0), 1) == UNSPEC_GOT
+                  || XINT (XEXP (disp, 0), 1) == UNSPEC_GOTNTPOFF))
         {
           if (flag_pic != 1)
             return FALSE;
@@ -1645,7 +2156,7 @@ s390_decompose_address (addr, out)
         {
          pointer = TRUE;
         }
+
       /* Likewise if a constant offset is present.  */
       else if (GET_CODE (disp) == CONST
                && GET_CODE (XEXP (disp, 0)) == PLUS
@@ -1657,7 +2168,7 @@ s390_decompose_address (addr, out)
          pointer = TRUE;
         }
 
-      /* We can convert literal pool addresses to 
+      /* We can convert literal pool addresses to
          displacements by basing them off the base register.  */
       else
         {
@@ -1679,18 +2190,12 @@ s390_decompose_address (addr, out)
               || !CONSTANT_POOL_ADDRESS_P (disp))
             return FALSE;
 
-          /* In 64-bit PIC mode we cannot accept symbolic 
-             constants in the constant pool.  */
-          if (TARGET_64BIT && flag_pic
-              && SYMBOLIC_CONST (get_pool_constant (disp)))
-            return FALSE;
-
           /* If we have an offset, make sure it does not
              exceed the size of the constant pool entry.  */
           if (offset && offset >= GET_MODE_SIZE (get_pool_mode (disp)))
             return FALSE;
 
-          /* Either base or index must be free to 
+          /* Either base or index must be free to
              hold the base register.  */
           if (base && indx)
             return FALSE;
@@ -1701,7 +2206,8 @@ s390_decompose_address (addr, out)
           else
             base = gen_rtx_REG (Pmode, BASE_REGISTER);
 
-          disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp), 100);
+          disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp),
+                                UNSPEC_LTREL_OFFSET);
           disp = gen_rtx_CONST (Pmode, disp);
 
           if (offset)
@@ -1713,7 +2219,7 @@ s390_decompose_address (addr, out)
 
   if (!base && !indx)
     pointer = TRUE;
-   
+
   if (out)
     {
       out->base = base;
@@ -1729,10 +2235,8 @@ s390_decompose_address (addr, out)
    STRICT specifies whether strict register checking applies.  */
 
 int
-legitimate_address_p (mode, addr, strict)
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-     register rtx addr;
-     int strict;
+legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
+                     register rtx addr, int strict)
 {
   struct s390_address ad;
   if (!s390_decompose_address (addr, &ad))
@@ -1761,8 +2265,7 @@ legitimate_address_p (mode, addr, strict)
    address, as LA performs only a 31-bit addition.  */
 
 int
-legitimate_la_operand_p (op)
-     register rtx op;
+legitimate_la_operand_p (register rtx op)
 {
   struct s390_address addr;
   if (!s390_decompose_address (op, &addr))
@@ -1775,14 +2278,10 @@ legitimate_la_operand_p (op)
 }
 
 /* Return 1 if OP is a valid operand for the LA instruction,
-   and we prefer to use LA over addition to compute it.
-   If STRICT is true, only accept operands that will never
-   change to something we cannot recognize as preferred.  */
-   
+   and we prefer to use LA over addition to compute it.  */
+
 int
-preferred_la_operand_p (op, strict)
-     register rtx op;
-     int strict;
+preferred_la_operand_p (register rtx op)
 {
   struct s390_address addr;
   if (!s390_decompose_address (op, &addr))
@@ -1794,10 +2293,9 @@ preferred_la_operand_p (op, strict)
   if (addr.pointer)
     return TRUE;
 
-  if (!strict) 
-    if ((addr.base && REG_P (addr.base) && REG_POINTER (addr.base))
-        || (addr.indx && REG_P (addr.indx) && REG_POINTER (addr.indx)))
-      return TRUE;
+  if ((addr.base && REG_P (addr.base) && REG_POINTER (addr.base))
+      || (addr.indx && REG_P (addr.indx) && REG_POINTER (addr.indx)))
+    return TRUE;
 
   return FALSE;
 }
@@ -1807,9 +2305,7 @@ preferred_la_operand_p (op, strict)
    where legitimate_la_operand_p (SRC) returns false.  */
 
 void
-s390_load_address (dst, src)
-     rtx dst;
-     rtx src;
+s390_load_address (rtx dst, rtx src)
 {
   if (TARGET_64BIT)
     emit_move_insn (dst, src);
@@ -1828,7 +2324,7 @@ s390_load_address (dst, src)
 
    2. Static data references, constant pool addresses, and code labels
       compute the address as an offset from the GOT, whose base is in
-      the PIC reg.  Static data objects have SYMBOL_REF_FLAG set to
+      the PIC reg.  Static data objects have SYMBOL_FLAG_LOCAL set to
       differentiate them from global data objects.  The returned
       address is the PIC reg + an unspec constant.
 
@@ -1836,41 +2332,37 @@ s390_load_address (dst, src)
    reg also appears in the address.  */
 
 rtx
-legitimize_pic_address (orig, reg)
-     rtx orig;
-     rtx reg;
+legitimize_pic_address (rtx orig, rtx reg)
 {
   rtx addr = orig;
   rtx new = orig;
   rtx base;
 
   if (GET_CODE (addr) == LABEL_REF
-      || (GET_CODE (addr) == SYMBOL_REF
-         && (SYMBOL_REF_FLAG (addr) 
-              || CONSTANT_POOL_ADDRESS_P (addr))))
+      || (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (addr)))
     {
       /* This is a local symbol.  */
-      if (TARGET_64BIT)
+      if (TARGET_CPU_ZARCH && larl_operand (addr, VOIDmode))
         {
-          /* Access local symbols PC-relative via LARL.  
-             This is the same as in the non-PIC case, so it is 
+          /* Access local symbols PC-relative via LARL.
+             This is the same as in the non-PIC case, so it is
              handled automatically ...  */
         }
       else
         {
-          /* Access local symbols relative to the literal pool.  */
+          /* Access local symbols relative to the GOT.  */
 
           rtx temp = reg? reg : gen_reg_rtx (Pmode);
 
-          addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 100);
-          addr = gen_rtx_CONST (SImode, addr);
-          addr = force_const_mem (SImode, addr);
-         emit_move_insn (temp, addr);
-
-          base = gen_rtx_REG (Pmode, BASE_REGISTER);
-          base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), 101);
-          new = gen_rtx_PLUS (Pmode, base, temp);
+         if (reload_in_progress || reload_completed)
+           regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
+
+          addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTOFF);
+          addr = gen_rtx_CONST (Pmode, addr);
+          addr = force_const_mem (Pmode, addr);
+         emit_move_insn (temp, addr);
 
+          new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
           if (reg != 0)
             {
               emit_move_insn (reg, new);
@@ -1886,12 +2378,12 @@ legitimize_pic_address (orig, reg)
       if (flag_pic == 1)
         {
           /* Assume GOT offset < 4k.  This is handled the same way
-             in both 31- and 64-bit code (@GOT12).  */
+             in both 31- and 64-bit code (@GOT).  */
 
          if (reload_in_progress || reload_completed)
            regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
 
-          new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), 110);
+          new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT);
           new = gen_rtx_CONST (Pmode, new);
           new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new);
           new = gen_rtx_MEM (Pmode, new);
@@ -1899,14 +2391,14 @@ legitimize_pic_address (orig, reg)
           emit_move_insn (reg, new);
           new = reg;
         }
-      else if (TARGET_64BIT)
+      else if (TARGET_CPU_ZARCH)
         {
           /* If the GOT offset might be >= 4k, we determine the position
              of the GOT entry via a PC-relative LARL (@GOTENT).  */
 
           rtx temp = gen_reg_rtx (Pmode);
 
-          new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), 111);
+          new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTENT);
           new = gen_rtx_CONST (Pmode, new);
           emit_move_insn (temp, new);
 
@@ -1917,7 +2409,7 @@ legitimize_pic_address (orig, reg)
         }
       else
         {
-          /* If the GOT offset might be >= 4k, we have to load it 
+          /* If the GOT offset might be >= 4k, we have to load it
              from the literal pool (@GOT).  */
 
           rtx temp = gen_reg_rtx (Pmode);
@@ -1925,9 +2417,9 @@ legitimize_pic_address (orig, reg)
          if (reload_in_progress || reload_completed)
            regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
 
-          addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 112);
-          addr = gen_rtx_CONST (SImode, addr);
-          addr = force_const_mem (SImode, addr);
+          addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT);
+          addr = gen_rtx_CONST (Pmode, addr);
+          addr = force_const_mem (Pmode, addr);
           emit_move_insn (temp, addr);
 
           new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
@@ -1936,7 +2428,7 @@ legitimize_pic_address (orig, reg)
           emit_move_insn (reg, new);
           new = reg;
         }
-    }      
+    }
   else
     {
       if (GET_CODE (addr) == CONST)
@@ -1948,35 +2440,41 @@ legitimize_pic_address (orig, reg)
                 abort ();
               switch (XINT (addr, 1))
                 {
-                  /* If someone moved an @GOT or lt-relative UNSPEC
+                  /* If someone moved a GOT-relative UNSPEC
                      out of the literal pool, force them back in.  */
-                  case 100:
-                  case 112:
-                  case 114:
-                    new = force_const_mem (SImode, orig);
+                  case UNSPEC_GOTOFF:
+                  case UNSPEC_PLTOFF:
+                    new = force_const_mem (Pmode, orig);
                     break;
 
+                  /* @GOT is OK as is if small.  */
+                 case UNSPEC_GOT:
+                   if (flag_pic == 2)
+                     new = force_const_mem (Pmode, orig);
+                   break;
+
                   /* @GOTENT is OK as is.  */
-                  case 111:
+                  case UNSPEC_GOTENT:
                     break;
 
                   /* @PLT is OK as is on 64-bit, must be converted to
-                     lt-relative PLT on 31-bit.  */
-                  case 113:
-                    if (!TARGET_64BIT)
+                     GOT-relative @PLTOFF on 31-bit.  */
+                  case UNSPEC_PLT:
+                    if (!TARGET_CPU_ZARCH)
                       {
                         rtx temp = reg? reg : gen_reg_rtx (Pmode);
 
+                       if (reload_in_progress || reload_completed)
+                         regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
+
                         addr = XVECEXP (addr, 0, 0);
-                        addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, addr), 114);
-                        addr = gen_rtx_CONST (SImode, addr);
-                        addr = force_const_mem (SImode, addr);
+                        addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr),
+                                              UNSPEC_PLTOFF);
+                        addr = gen_rtx_CONST (Pmode, addr);
+                        addr = force_const_mem (Pmode, addr);
                        emit_move_insn (temp, addr);
 
-                        base = gen_rtx_REG (Pmode, BASE_REGISTER);
-                        base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), 101);
-                        new = gen_rtx_PLUS (Pmode, base, temp);
-
+                        new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
                         if (reg != 0)
                           {
                             emit_move_insn (reg, new);
@@ -1996,28 +2494,26 @@ legitimize_pic_address (orig, reg)
       if (GET_CODE (addr) == PLUS)
        {
          rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1);
-         /* Check first to see if this is a constant offset 
+         /* Check first to see if this is a constant offset
              from a local symbol reference.  */
          if ((GET_CODE (op0) == LABEL_REF
-               || (GET_CODE (op0) == SYMBOL_REF
-                   && (SYMBOL_REF_FLAG (op0)
-                        || CONSTANT_POOL_ADDRESS_P (op0))))
+               || (GET_CODE (op0) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (op0)))
              && GET_CODE (op1) == CONST_INT)
            {
-              if (TARGET_64BIT)
+              if (TARGET_CPU_ZARCH && larl_operand (op0, VOIDmode))
                 {
                   if (INTVAL (op1) & 1)
                     {
-                      /* LARL can't handle odd offsets, so emit a 
+                      /* LARL can't handle odd offsets, so emit a
                          pair of LARL and LA.  */
                       rtx temp = reg? reg : gen_reg_rtx (Pmode);
 
-                      if (INTVAL (op1) < 0 || INTVAL (op1) >= 4096)
+                      if (!DISP_IN_RANGE (INTVAL (op1)))
                         {
                           int even = INTVAL (op1) - 1;
                           op0 = gen_rtx_PLUS (Pmode, op0, GEN_INT (even));
                          op0 = gen_rtx_CONST (Pmode, op0);
-                          op1 = GEN_INT (1);
+                          op1 = const1_rtx;
                         }
 
                       emit_move_insn (temp, op0);
@@ -2037,20 +2533,21 @@ legitimize_pic_address (orig, reg)
                 }
               else
                 {
-                  /* Access local symbols relative to the literal pool.  */
+                  /* Access local symbols relative to the GOT.  */
 
                   rtx temp = reg? reg : gen_reg_rtx (Pmode);
 
-                  addr = gen_rtx_UNSPEC (SImode, gen_rtvec (1, op0), 100);
-                  addr = gen_rtx_PLUS (SImode, addr, op1);
-                  addr = gen_rtx_CONST (SImode, addr);
-                  addr = force_const_mem (SImode, addr);
-                 emit_move_insn (temp, addr);
+                 if (reload_in_progress || reload_completed)
+                   regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
 
-                  base = gen_rtx_REG (Pmode, BASE_REGISTER);
-                  base = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, base), 101);
-                  new = gen_rtx_PLUS (Pmode, base, temp);
+                  addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0),
+                                        UNSPEC_GOTOFF);
+                  addr = gen_rtx_PLUS (Pmode, addr, op1);
+                  addr = gen_rtx_CONST (Pmode, addr);
+                  addr = force_const_mem (Pmode, addr);
+         emit_move_insn (temp, addr);
 
+                  new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
                   if (reg != 0)
                     {
                       emit_move_insn (reg, new);
@@ -2059,7 +2556,7 @@ legitimize_pic_address (orig, reg)
                 }
            }
 
-          /* Now, check whether it is an LT-relative symbol plus offset
+          /* Now, check whether it is a GOT relative symbol plus offset
              that was pulled out of the literal pool.  Force it back in.  */
 
          else if (GET_CODE (op0) == UNSPEC
@@ -2067,10 +2564,10 @@ legitimize_pic_address (orig, reg)
             {
              if (XVECLEN (op0, 0) != 1)
                 abort ();
-              if (XINT (op0, 1) != 100)
+              if (XINT (op0, 1) != UNSPEC_GOTOFF)
                 abort ();
 
-              new = force_const_mem (SImode, orig);
+              new = force_const_mem (Pmode, orig);
             }
 
           /* Otherwise, compute the sum.  */
@@ -2100,18 +2597,234 @@ legitimize_pic_address (orig, reg)
   return new;
 }
 
+/* Load the thread pointer into a register.  */
+
+static rtx
+get_thread_pointer (void)
+{
+  rtx tp;
+
+  tp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TP);
+  tp = force_reg (Pmode, tp);
+  mark_reg_pointer (tp, BITS_PER_WORD);
+
+  return tp;
+}
+
+/* Construct the SYMBOL_REF for the tls_get_offset function.  */
+
+static GTY(()) rtx s390_tls_symbol;
+rtx
+s390_tls_get_offset (void)
+{
+  if (!s390_tls_symbol)
+    s390_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_offset");
+
+  return s390_tls_symbol;
+}
+
+/* ADDR contains a thread-local SYMBOL_REF.  Generate code to compute
+   this (thread-local) address.  REG may be used as temporary.  */
+
+static rtx
+legitimize_tls_address (rtx addr, rtx reg)
+{
+  rtx new, tls_call, temp, base, r2, insn;
+
+  if (GET_CODE (addr) == SYMBOL_REF)
+    switch (tls_symbolic_operand (addr))
+      {
+      case TLS_MODEL_GLOBAL_DYNAMIC:
+       start_sequence ();
+       r2 = gen_rtx_REG (Pmode, 2);
+       tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_TLSGD);
+       new = gen_rtx_CONST (Pmode, tls_call);
+       new = force_const_mem (Pmode, new);
+       emit_move_insn (r2, new);
+       emit_call_insn (gen_call_value_tls (r2, tls_call));
+       insn = get_insns ();
+       end_sequence ();
+
+       new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF);
+       temp = gen_reg_rtx (Pmode);
+       emit_libcall_block (insn, temp, r2, new);
+
+       new = gen_rtx_PLUS (Pmode, get_thread_pointer (), temp);
+       if (reg != 0)
+         {
+           s390_load_address (reg, new);
+           new = reg;
+         }
+       break;
+
+      case TLS_MODEL_LOCAL_DYNAMIC:
+       start_sequence ();
+       r2 = gen_rtx_REG (Pmode, 2);
+       tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM);
+       new = gen_rtx_CONST (Pmode, tls_call);
+       new = force_const_mem (Pmode, new);
+       emit_move_insn (r2, new);
+       emit_call_insn (gen_call_value_tls (r2, tls_call));
+       insn = get_insns ();
+       end_sequence ();
+
+       new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM_NTPOFF);
+       temp = gen_reg_rtx (Pmode);
+       emit_libcall_block (insn, temp, r2, new);
+
+       new = gen_rtx_PLUS (Pmode, get_thread_pointer (), temp);
+       base = gen_reg_rtx (Pmode);
+       s390_load_address (base, new);
+
+       new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_DTPOFF);
+       new = gen_rtx_CONST (Pmode, new);
+       new = force_const_mem (Pmode, new);
+       temp = gen_reg_rtx (Pmode);
+       emit_move_insn (temp, new);
+
+       new = gen_rtx_PLUS (Pmode, base, temp);
+       if (reg != 0)
+         {
+           s390_load_address (reg, new);
+           new = reg;
+         }
+       break;
+
+      case TLS_MODEL_INITIAL_EXEC:
+       if (flag_pic == 1)
+         {
+           /* Assume GOT offset < 4k.  This is handled the same way
+              in both 31- and 64-bit code.  */
+
+           if (reload_in_progress || reload_completed)
+             regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
+
+           new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF);
+           new = gen_rtx_CONST (Pmode, new);
+           new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new);
+           new = gen_rtx_MEM (Pmode, new);
+           RTX_UNCHANGING_P (new) = 1;
+           temp = gen_reg_rtx (Pmode);
+           emit_move_insn (temp, new);
+         }
+       else if (TARGET_CPU_ZARCH)
+         {
+           /* If the GOT offset might be >= 4k, we determine the position
+              of the GOT entry via a PC-relative LARL.  */
+
+           new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF);
+           new = gen_rtx_CONST (Pmode, new);
+           temp = gen_reg_rtx (Pmode);
+           emit_move_insn (temp, new);
+
+           new = gen_rtx_MEM (Pmode, temp);
+           RTX_UNCHANGING_P (new) = 1;
+           temp = gen_reg_rtx (Pmode);
+           emit_move_insn (temp, new);
+         }
+       else if (flag_pic)
+         {
+           /* If the GOT offset might be >= 4k, we have to load it
+              from the literal pool.  */
+
+           if (reload_in_progress || reload_completed)
+             regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
+
+           new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF);
+           new = gen_rtx_CONST (Pmode, new);
+           new = force_const_mem (Pmode, new);
+           temp = gen_reg_rtx (Pmode);
+           emit_move_insn (temp, new);
+
+            new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
+           new = gen_rtx_MEM (Pmode, new);
+           RTX_UNCHANGING_P (new) = 1;
+
+           new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD);
+           temp = gen_reg_rtx (Pmode);
+           emit_insn (gen_rtx_SET (Pmode, temp, new));
+         }
+       else
+         {
+           /* In position-dependent code, load the absolute address of
+              the GOT entry from the literal pool.  */
+
+           new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF);
+           new = gen_rtx_CONST (Pmode, new);
+           new = force_const_mem (Pmode, new);
+           temp = gen_reg_rtx (Pmode);
+           emit_move_insn (temp, new);
+
+           new = temp;
+           new = gen_rtx_MEM (Pmode, new);
+           RTX_UNCHANGING_P (new) = 1;
+
+           new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD);
+           temp = gen_reg_rtx (Pmode);
+           emit_insn (gen_rtx_SET (Pmode, temp, new));
+         }
+
+       new = gen_rtx_PLUS (Pmode, get_thread_pointer (), temp);
+       if (reg != 0)
+         {
+           s390_load_address (reg, new);
+           new = reg;
+         }
+       break;
+
+      case TLS_MODEL_LOCAL_EXEC:
+       new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF);
+       new = gen_rtx_CONST (Pmode, new);
+       new = force_const_mem (Pmode, new);
+        temp = gen_reg_rtx (Pmode);
+       emit_move_insn (temp, new);
+
+       new = gen_rtx_PLUS (Pmode, get_thread_pointer (), temp);
+       if (reg != 0)
+         {
+           s390_load_address (reg, new);
+           new = reg;
+         }
+       break;
+
+      default:
+       abort ();
+      }
+
+  else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == UNSPEC)
+    {
+      switch (XINT (XEXP (addr, 0), 1))
+       {
+       case UNSPEC_INDNTPOFF:
+         if (TARGET_CPU_ZARCH)
+           new = addr;
+         else
+           abort ();
+         break;
+
+       default:
+         abort ();
+       }
+    }
+
+  else
+    abort ();  /* for now ... */
+
+  return new;
+}
+
 /* Emit insns to move operands[1] into operands[0].  */
 
 void
-emit_pic_move (operands, mode)
-     rtx *operands;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+emit_symbolic_move (rtx *operands)
 {
   rtx temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
 
-  if (GET_CODE (operands[0]) == MEM && SYMBOLIC_CONST (operands[1]))
+  if (GET_CODE (operands[0]) == MEM)
     operands[1] = force_reg (Pmode, operands[1]);
-  else
+  else if (TLS_SYMBOLIC_CONST (operands[1]))
+    operands[1] = legitimize_tls_address (operands[1], temp);
+  else if (flag_pic)
     operands[1] = legitimize_pic_address (operands[1], temp);
 }
 
@@ -2127,18 +2840,23 @@ emit_pic_move (operands, mode)
    See comments by legitimize_pic_address for details.  */
 
 rtx
-legitimize_address (x, oldx, mode)
-     register rtx x;
-     register rtx oldx ATTRIBUTE_UNUSED;
-     enum machine_mode mode ATTRIBUTE_UNUSED;
+legitimize_address (register rtx x, register rtx oldx ATTRIBUTE_UNUSED,
+                   enum machine_mode mode ATTRIBUTE_UNUSED)
 {
   rtx constant_term = const0_rtx;
 
-  if (flag_pic)
+  if (TLS_SYMBOLIC_CONST (x))
+    {
+      x = legitimize_tls_address (x, 0);
+
+      if (legitimate_address_p (mode, x, FALSE))
+       return x;
+    }
+  else if (flag_pic)
     {
       if (SYMBOLIC_CONST (x)
-          || (GET_CODE (x) == PLUS 
-              && (SYMBOLIC_CONST (XEXP (x, 0)) 
+          || (GET_CODE (x) == PLUS
+              && (SYMBOLIC_CONST (XEXP (x, 0))
                   || SYMBOLIC_CONST (XEXP (x, 1)))))
          x = legitimize_pic_address (x, 0);
 
@@ -2150,15 +2868,15 @@ legitimize_address (x, oldx, mode)
 
   /* Optimize loading of large displacements by splitting them
      into the multiple of 4K and the rest; this allows the
-     former to be CSE'd if possible. 
+     former to be CSE'd if possible.
 
      Don't do this if the displacement is added to a register
      pointing into the stack frame, as the offsets will
      change later anyway.  */
 
   if (GET_CODE (constant_term) == CONST_INT
-      && (INTVAL (constant_term) < 0
-          || INTVAL (constant_term) >= 4096)
+      && !TARGET_LONG_DISPLACEMENT
+      && !DISP_IN_RANGE (INTVAL (constant_term))
       && !(REG_P (x) && REGNO_PTR_FRAME_P (REGNO (x))))
     {
       HOST_WIDE_INT lower = INTVAL (constant_term) & 0xfff;
@@ -2205,39 +2923,17 @@ legitimize_address (x, oldx, mode)
 /* Emit code to move LEN bytes from DST to SRC.  */
 
 void
-s390_expand_movstr (dst, src, len)
-     rtx dst;
-     rtx src;
-     rtx len;
+s390_expand_movstr (rtx dst, rtx src, rtx len)
 {
-  rtx (*gen_short) PARAMS ((rtx, rtx, rtx)) = 
-    TARGET_64BIT ? gen_movstr_short_64 : gen_movstr_short_31;
-  rtx (*gen_long) PARAMS ((rtx, rtx, rtx, rtx)) = 
-    TARGET_64BIT ? gen_movstr_long_64 : gen_movstr_long_31;
-
-
   if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256)
     {
       if (INTVAL (len) > 0)
-        emit_insn ((*gen_short) (dst, src, GEN_INT (INTVAL (len) - 1)));
+        emit_insn (gen_movstr_short (dst, src, GEN_INT (INTVAL (len) - 1)));
     }
 
   else if (TARGET_MVCLE)
     {
-      enum machine_mode double_mode = TARGET_64BIT ? TImode : DImode;
-      enum machine_mode single_mode = TARGET_64BIT ? DImode : SImode;
-      rtx reg0 = gen_reg_rtx (double_mode);
-      rtx reg1 = gen_reg_rtx (double_mode);
-
-      emit_move_insn (gen_highpart (single_mode, reg0), 
-                     force_operand (XEXP (dst, 0), NULL_RTX));
-      emit_move_insn (gen_highpart (single_mode, reg1), 
-                     force_operand (XEXP (src, 0), NULL_RTX));
-
-      convert_move (gen_lowpart (single_mode, reg0), len, 1);
-      convert_move (gen_lowpart (single_mode, reg1), len, 1);
-
-      emit_insn ((*gen_long) (reg0, reg1, reg0, reg1));
+      emit_insn (gen_movstr_long (dst, src, convert_to_mode (Pmode, len, 1)));
     }
 
   else
@@ -2249,9 +2945,9 @@ s390_expand_movstr (dst, src, len)
 
       mode = GET_MODE (len);
       if (mode == VOIDmode)
-        mode = word_mode;
+        mode = Pmode;
 
-      type = (*lang_hooks.types.type_for_mode) (mode, 1);
+      type = lang_hooks.types.type_for_mode (mode, 1);
       if (!type)
         abort ();
 
@@ -2261,14 +2957,14 @@ s390_expand_movstr (dst, src, len)
       blocks = gen_reg_rtx (mode);
 
       convert_move (count, len, 1);
-      emit_cmp_and_jump_insns (count, const0_rtx, 
+      emit_cmp_and_jump_insns (count, const0_rtx,
                               EQ, NULL_RTX, mode, 1, end_label);
 
       emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX));
       emit_move_insn (src_addr, force_operand (XEXP (src, 0), NULL_RTX));
       dst = change_address (dst, VOIDmode, dst_addr);
       src = change_address (src, VOIDmode, src_addr);
-     
+
       temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
       if (temp != count)
         emit_move_insn (count, temp);
@@ -2282,19 +2978,20 @@ s390_expand_movstr (dst, src, len)
                                           make_tree (type, blocks),
                                           make_tree (type, const0_rtx)));
 
-      emit_insn ((*gen_short) (dst, src, GEN_INT (255)));
-      s390_load_address (dst_addr, 
+      emit_insn (gen_movstr_short (dst, src, GEN_INT (255)));
+      s390_load_address (dst_addr,
                         gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256)));
-      s390_load_address (src_addr, 
+      s390_load_address (src_addr,
                         gen_rtx_PLUS (Pmode, src_addr, GEN_INT (256)));
-      
+
       temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
       if (temp != blocks)
         emit_move_insn (blocks, temp);
 
       expand_end_loop ();
 
-      emit_insn ((*gen_short) (dst, src, convert_to_mode (word_mode, count, 1)));
+      emit_insn (gen_movstr_short (dst, src, 
+                                  convert_to_mode (Pmode, count, 1)));
       emit_label (end_label);
     }
 }
@@ -2302,37 +2999,17 @@ s390_expand_movstr (dst, src, len)
 /* Emit code to clear LEN bytes at DST.  */
 
 void
-s390_expand_clrstr (dst, len)
-     rtx dst;
-     rtx len;
+s390_expand_clrstr (rtx dst, rtx len)
 {
-  rtx (*gen_short) PARAMS ((rtx, rtx)) = 
-    TARGET_64BIT ? gen_clrstr_short_64 : gen_clrstr_short_31;
-  rtx (*gen_long) PARAMS ((rtx, rtx, rtx)) = 
-    TARGET_64BIT ? gen_clrstr_long_64 : gen_clrstr_long_31;
-
-
   if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256)
     {
       if (INTVAL (len) > 0)
-        emit_insn ((*gen_short) (dst, GEN_INT (INTVAL (len) - 1)));
+        emit_insn (gen_clrstr_short (dst, GEN_INT (INTVAL (len) - 1)));
     }
 
   else if (TARGET_MVCLE)
     {
-      enum machine_mode double_mode = TARGET_64BIT ? TImode : DImode;
-      enum machine_mode single_mode = TARGET_64BIT ? DImode : SImode;
-      rtx reg0 = gen_reg_rtx (double_mode);
-      rtx reg1 = gen_reg_rtx (double_mode);
-
-      emit_move_insn (gen_highpart (single_mode, reg0), 
-                     force_operand (XEXP (dst, 0), NULL_RTX));
-      convert_move (gen_lowpart (single_mode, reg0), len, 1);
-
-      emit_move_insn (gen_highpart (single_mode, reg1), const0_rtx);
-      emit_move_insn (gen_lowpart (single_mode, reg1), const0_rtx);
-
-      emit_insn ((*gen_long) (reg0, reg1, reg0));
+      emit_insn (gen_clrstr_long (dst, convert_to_mode (Pmode, len, 1)));
     }
 
   else
@@ -2344,9 +3021,9 @@ s390_expand_clrstr (dst, len)
 
       mode = GET_MODE (len);
       if (mode == VOIDmode)
-        mode = word_mode;
+        mode = Pmode;
 
-      type = (*lang_hooks.types.type_for_mode) (mode, 1);
+      type = lang_hooks.types.type_for_mode (mode, 1);
       if (!type)
         abort ();
 
@@ -2356,12 +3033,12 @@ s390_expand_clrstr (dst, len)
       blocks = gen_reg_rtx (mode);
 
       convert_move (count, len, 1);
-      emit_cmp_and_jump_insns (count, const0_rtx, 
+      emit_cmp_and_jump_insns (count, const0_rtx,
                               EQ, NULL_RTX, mode, 1, end_label);
 
       emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX));
       dst = change_address (dst, VOIDmode, dst_addr);
-     
+
       temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
       if (temp != count)
         emit_move_insn (count, temp);
@@ -2375,17 +3052,17 @@ s390_expand_clrstr (dst, len)
                                           make_tree (type, blocks),
                                           make_tree (type, const0_rtx)));
 
-      emit_insn ((*gen_short) (dst, GEN_INT (255)));
-      s390_load_address (dst_addr, 
+      emit_insn (gen_clrstr_short (dst, GEN_INT (255)));
+      s390_load_address (dst_addr,
                         gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256)));
-      
+
       temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
       if (temp != blocks)
         emit_move_insn (blocks, temp);
 
       expand_end_loop ();
 
-      emit_insn ((*gen_short) (dst, convert_to_mode (word_mode, count, 1)));
+      emit_insn (gen_clrstr_short (dst, convert_to_mode (Pmode, count, 1)));
       emit_label (end_label);
     }
 }
@@ -2394,17 +3071,9 @@ s390_expand_clrstr (dst, len)
    and return the result in TARGET.  */
 
 void
-s390_expand_cmpstr (target, op0, op1, len)
-     rtx target;
-     rtx op0;
-     rtx op1;
-     rtx len;
-{
-  rtx (*gen_short) PARAMS ((rtx, rtx, rtx)) = 
-    TARGET_64BIT ? gen_cmpstr_short_64 : gen_cmpstr_short_31;
-  rtx (*gen_long) PARAMS ((rtx, rtx, rtx, rtx)) = 
-    TARGET_64BIT ? gen_cmpstr_long_64 : gen_cmpstr_long_31;
-  rtx (*gen_result) PARAMS ((rtx)) =
+s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
+{
+  rtx (*gen_result) (rtx) =
     GET_MODE (target) == DImode ? gen_cmpint_di : gen_cmpint_si;
 
   op0 = protect_from_queue (op0, 0);
@@ -2415,32 +3084,22 @@ s390_expand_cmpstr (target, op0, op1, len)
     {
       if (INTVAL (len) > 0)
         {
-          emit_insn ((*gen_short) (op0, op1, GEN_INT (INTVAL (len) - 1)));
-          emit_insn ((*gen_result) (target));
+          emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (INTVAL (len) - 1)));
+          emit_insn (gen_result (target));
         }
       else
         emit_move_insn (target, const0_rtx);
     }
 
-  else if (TARGET_MVCLE)
+  else /* if (TARGET_MVCLE) */
     {
-      enum machine_mode double_mode = TARGET_64BIT ? TImode : DImode;
-      enum machine_mode single_mode = TARGET_64BIT ? DImode : SImode;
-      rtx reg0 = gen_reg_rtx (double_mode);
-      rtx reg1 = gen_reg_rtx (double_mode);
-
-      emit_move_insn (gen_highpart (single_mode, reg0), 
-                     force_operand (XEXP (op0, 0), NULL_RTX));
-      emit_move_insn (gen_highpart (single_mode, reg1), 
-                     force_operand (XEXP (op1, 0), NULL_RTX));
-
-      convert_move (gen_lowpart (single_mode, reg0), len, 1);
-      convert_move (gen_lowpart (single_mode, reg1), len, 1);
-
-      emit_insn ((*gen_long) (reg0, reg1, reg0, reg1));
-      emit_insn ((*gen_result) (target));
+      emit_insn (gen_cmpmem_long (op0, op1, convert_to_mode (Pmode, len, 1)));
+      emit_insn (gen_result (target));
     }
 
+#if 0
+  /* Deactivate for now as profile code cannot cope with
+     CC being live across basic block boundaries.  */
   else
     {
       rtx addr0, addr1, count, blocks, temp;
@@ -2450,9 +3109,9 @@ s390_expand_cmpstr (target, op0, op1, len)
 
       mode = GET_MODE (len);
       if (mode == VOIDmode)
-        mode = word_mode;
+        mode = Pmode;
 
-      type = (*lang_hooks.types.type_for_mode) (mode, 1);
+      type = lang_hooks.types.type_for_mode (mode, 1);
       if (!type)
         abort ();
 
@@ -2462,14 +3121,14 @@ s390_expand_cmpstr (target, op0, op1, len)
       blocks = gen_reg_rtx (mode);
 
       convert_move (count, len, 1);
-      emit_cmp_and_jump_insns (count, const0_rtx, 
+      emit_cmp_and_jump_insns (count, const0_rtx,
                               EQ, NULL_RTX, mode, 1, end_label);
 
       emit_move_insn (addr0, force_operand (XEXP (op0, 0), NULL_RTX));
       emit_move_insn (addr1, force_operand (XEXP (op1, 0), NULL_RTX));
       op0 = change_address (op0, VOIDmode, addr0);
       op1 = change_address (op1, VOIDmode, addr1);
-     
+
       temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
       if (temp != count)
         emit_move_insn (count, temp);
@@ -2483,38 +3142,60 @@ s390_expand_cmpstr (target, op0, op1, len)
                                           make_tree (type, blocks),
                                           make_tree (type, const0_rtx)));
 
-      emit_insn ((*gen_short) (op0, op1, GEN_INT (255)));
+      emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (255)));
       temp = gen_rtx_NE (VOIDmode, gen_rtx_REG (CCSmode, 33), const0_rtx);
-      temp = gen_rtx_IF_THEN_ELSE (VOIDmode, temp, 
+      temp = gen_rtx_IF_THEN_ELSE (VOIDmode, temp,
                        gen_rtx_LABEL_REF (VOIDmode, end_label), pc_rtx);
       temp = gen_rtx_SET (VOIDmode, pc_rtx, temp);
       emit_jump_insn (temp);
 
-      s390_load_address (addr0, 
+      s390_load_address (addr0,
                         gen_rtx_PLUS (Pmode, addr0, GEN_INT (256)));
-      s390_load_address (addr1, 
+      s390_load_address (addr1,
                         gen_rtx_PLUS (Pmode, addr1, GEN_INT (256)));
-      
+
       temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
       if (temp != blocks)
         emit_move_insn (blocks, temp);
 
       expand_end_loop ();
 
-      emit_insn ((*gen_short) (op0, op1, convert_to_mode (word_mode, count, 1)));
+      emit_insn (gen_cmpmem_short (op0, op1, 
+                                  convert_to_mode (Pmode, count, 1)));
       emit_label (end_label);
 
-      emit_insn ((*gen_result) (target));
+      emit_insn (gen_result (target));
+    }
+#endif
+}
+
+/* This is called from dwarf2out.c via ASM_OUTPUT_DWARF_DTPREL.
+   We need to emit DTP-relative relocations.  */
+
+void
+s390_output_dwarf_dtprel (FILE *file, int size, rtx x)
+{
+  switch (size)
+    {
+    case 4:
+      fputs ("\t.long\t", file);
+      break;
+    case 8:
+      fputs ("\t.quad\t", file);
+      break;
+    default:
+      abort ();
     }
+  output_addr_const (file, x);
+  fputs ("@DTPOFF", file);
 }
 
 /* In the name of slightly smaller debug output, and to cater to
    general assembler losage, recognize various UNSPEC sequences
    and turn them back into a direct symbol reference.  */
 
-rtx
-s390_simplify_dwarf_addr (orig_x)
-     rtx orig_x;
+static rtx
+s390_delegitimize_address (rtx orig_x)
 {
   rtx x = orig_x, y;
 
@@ -2529,7 +3210,7 @@ s390_simplify_dwarf_addr (orig_x)
     {
       y = XEXP (XEXP (x, 1), 0);
       if (GET_CODE (y) == UNSPEC
-         && XINT (y, 1) == 110)
+         && XINT (y, 1) == UNSPEC_GOT)
        return XVECEXP (y, 0, 0);
       return orig_x;
     }
@@ -2538,105 +3219,152 @@ s390_simplify_dwarf_addr (orig_x)
     {
       y = XEXP (x, 0);
       if (GET_CODE (y) == UNSPEC
-         && XINT (y, 1) == 111)
+         && XINT (y, 1) == UNSPEC_GOTENT)
        return XVECEXP (y, 0, 0);
       return orig_x;
     }
 
-  return orig_x;      
+  return orig_x;
 }
 
-/* Output symbolic constant X in assembler syntax to 
-   stdio stream FILE.  */
+/* Output shift count operand OP to stdio stream FILE.  */
 
-void
-s390_output_symbolic_const (file, x)
-     FILE *file;
-     rtx x;
+static void
+print_shift_count_operand (FILE *file, rtx op)
 {
-  switch (GET_CODE (x))
+  HOST_WIDE_INT offset = 0;
+
+  /* We can have an integer constant, an address register,
+     or a sum of the two.  */
+  if (GET_CODE (op) == CONST_INT)
     {
-    case CONST:
-    case ZERO_EXTEND:
-    case SIGN_EXTEND:
-      s390_output_symbolic_const (file, XEXP (x, 0));
-      break;
+      offset = INTVAL (op);
+      op = NULL_RTX;
+    }
+  if (op && GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT)
+    {
+      offset = INTVAL (XEXP (op, 1));
+      op = XEXP (op, 0);
+    }
+  while (op && GET_CODE (op) == SUBREG)
+    op = SUBREG_REG (op);
 
-    case PLUS:
-      s390_output_symbolic_const (file, XEXP (x, 0));
-      fprintf (file, "+");
-      s390_output_symbolic_const (file, XEXP (x, 1));
-      break;
+  /* Sanity check.  */
+  if (op && (GET_CODE (op) != REG
+            || REGNO (op) >= FIRST_PSEUDO_REGISTER
+            || REGNO_REG_CLASS (REGNO (op)) != ADDR_REGS))
+    abort ();
 
-    case MINUS:
-      s390_output_symbolic_const (file, XEXP (x, 0));
-      fprintf (file, "-");
-      s390_output_symbolic_const (file, XEXP (x, 1));
-      break;
+  /* Shift counts are truncated to the low six bits anyway.  */
+  fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset & 63);
+  if (op)
+    fprintf (file, "(%s)", reg_names[REGNO (op)]);
+}
 
-    case CONST_INT:
-    case LABEL_REF:
-    case CODE_LABEL:
-    case SYMBOL_REF:
-      output_addr_const (file, x);
-      break;
+/* Locate some local-dynamic symbol still in use by this function
+   so that we can print its name in local-dynamic base patterns.  */
 
-    case UNSPEC:
-      if (XVECLEN (x, 0) != 1)
-        output_operand_lossage ("invalid UNSPEC as operand (1)");
-      switch (XINT (x, 1))
-        {
-        case 100:
-        case 104:
-         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
-          fprintf (file, "-"); 
-         s390_output_symbolic_const (file, cfun->machine->literal_pool_label);
-         break;
-        case 105:
-         s390_output_symbolic_const (file, cfun->machine->literal_pool_label);
-          fprintf (file, "-");
-         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
-         break;
-       case 110:
-         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
-         fprintf (file, "@GOT12");
-         break;
-       case 111:
-         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
-         fprintf (file, "@GOTENT");
-         break;
-       case 112:
-         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
-         fprintf (file, "@GOT");
-         break;
-       case 113:
-         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
-         fprintf (file, "@PLT");
-         break;
-       case 114:
-         s390_output_symbolic_const (file, XVECEXP (x, 0, 0));
-          fprintf (file, "@PLT-");
-         s390_output_symbolic_const (file, cfun->machine->literal_pool_label);
-         break;
-       default:
-         output_operand_lossage ("invalid UNSPEC as operand (2)");
-         break;
-        }
-      break;
+static const char *
+get_some_local_dynamic_name (void)
+{
+  rtx insn;
 
-    default:
-      fatal_insn ("UNKNOWN in s390_output_symbolic_const !?", x);
-      break;
+  if (cfun->machine->some_ld_name)
+    return cfun->machine->some_ld_name;
+
+  for (insn = get_insns (); insn ; insn = NEXT_INSN (insn))
+    if (INSN_P (insn)
+        && for_each_rtx (&PATTERN (insn), get_some_local_dynamic_name_1, 0))
+      return cfun->machine->some_ld_name;
+
+  abort ();
+}
+
+static int
+get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
+{
+  rtx x = *px;
+
+  if (GET_CODE (x) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (x))
+    {
+      x = get_pool_constant (x);
+      return for_each_rtx (&x, get_some_local_dynamic_name_1, 0);
+    }
+
+  if (GET_CODE (x) == SYMBOL_REF
+      && tls_symbolic_operand (x) == TLS_MODEL_LOCAL_DYNAMIC)
+    {
+      cfun->machine->some_ld_name = XSTR (x, 0);
+      return 1;
     }
+
+  return 0;
+}
+
+/* Output machine-dependent UNSPECs occurring in address constant X 
+   in assembler syntax to stdio stream FILE.  Returns true if the
+   constant X could be recognized, false otherwise.  */
+
+bool
+s390_output_addr_const_extra (FILE *file, rtx x)
+{
+  if (GET_CODE (x) == UNSPEC && XVECLEN (x, 0) == 1)
+    switch (XINT (x, 1))
+      {
+      case UNSPEC_GOTENT:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@GOTENT");
+       return true;
+      case UNSPEC_GOT:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@GOT");
+       return true;
+      case UNSPEC_GOTOFF:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@GOTOFF");
+       return true;
+      case UNSPEC_PLT:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@PLT");
+       return true;
+      case UNSPEC_PLTOFF:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@PLTOFF");
+       return true;
+      case UNSPEC_TLSGD:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@TLSGD");
+       return true;
+      case UNSPEC_TLSLDM:
+       assemble_name (file, get_some_local_dynamic_name ());
+       fprintf (file, "@TLSLDM");
+       return true;
+      case UNSPEC_DTPOFF:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@DTPOFF");
+       return true;
+      case UNSPEC_NTPOFF:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@NTPOFF");
+       return true;
+      case UNSPEC_GOTNTPOFF:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@GOTNTPOFF");
+       return true;
+      case UNSPEC_INDNTPOFF:
+       output_addr_const (file, XVECEXP (x, 0, 0));
+       fprintf (file, "@INDNTPOFF");
+       return true;
+      }
+
+  return false;
 }
 
-/* Output address operand ADDR in assembler syntax to 
+/* Output address operand ADDR in assembler syntax to
    stdio stream FILE.  */
 
 void
-print_operand_address (file, addr)
-     FILE *file;
-     rtx addr;
+print_operand_address (FILE *file, rtx addr)
 {
   struct s390_address ad;
 
@@ -2644,9 +3372,9 @@ print_operand_address (file, addr)
       || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base))
       || (ad.indx && !REG_OK_FOR_INDEX_STRICT_P (ad.indx)))
     output_operand_lossage ("Cannot decompose address.");
+
   if (ad.disp)
-    s390_output_symbolic_const (file, ad.disp);
+    output_addr_const (file, ad.disp);
   else
     fprintf (file, "0");
 
@@ -2657,26 +3385,27 @@ print_operand_address (file, addr)
     fprintf (file, "(%s)", reg_names[REGNO (ad.base)]);
 }
 
-/* Output operand X in assembler syntax to stdio stream FILE.  
-   CODE specified the format flag.  The following format flags 
+/* Output operand X in assembler syntax to stdio stream FILE.
+   CODE specified the format flag.  The following format flags
    are recognized:
 
     'C': print opcode suffix for branch condition.
     'D': print opcode suffix for inverse branch condition.
+    'J': print tls_load/tls_gdcall/tls_ldcall suffix
     'O': print only the displacement of a memory reference.
     'R': print only the base register of a memory reference.
     'N': print the second word of a DImode operand.
     'M': print the second word of a TImode operand.
+    'Y': print shift count operand.
 
     'b': print integer X as if it's an unsigned byte.
     'x': print integer X as if it's an unsigned word.
-    'h': print integer X as if it's a signed word.  */
+    'h': print integer X as if it's a signed word.
+    'i': print the first nonzero HImode part of X.
+    'j': print the first HImode part unequal to 0xffff of X.  */
 
 void
-print_operand (file, x, code)
-     FILE *file;
-     rtx x;
-     int code;
+print_operand (FILE *file, rtx x, int code)
 {
   switch (code)
     {
@@ -2688,6 +3417,26 @@ print_operand (file, x, code)
       fprintf (file, s390_branch_condition_mnemonic (x, TRUE));
       return;
 
+    case 'J':
+      if (GET_CODE (x) == SYMBOL_REF)
+       {
+         fprintf (file, "%s", ":tls_load:");
+         output_addr_const (file, x);
+       }
+      else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSGD)
+       {
+         fprintf (file, "%s", ":tls_gdcall:");
+         output_addr_const (file, XVECEXP (x, 0, 0));
+       }
+      else if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_TLSLDM)
+       {
+         fprintf (file, "%s", ":tls_ldcall:");
+         assemble_name (file, get_some_local_dynamic_name ());
+       }
+      else
+       abort ();
+      return;
+
     case 'O':
       {
         struct s390_address ad;
@@ -2699,7 +3448,7 @@ print_operand (file, x, code)
           abort ();
 
         if (ad.disp)
-          s390_output_symbolic_const (file, ad.disp);
+          output_addr_const (file, ad.disp);
         else
           fprintf (file, "0");
       }
@@ -2739,6 +3488,10 @@ print_operand (file, x, code)
       else
         abort ();
       break;
+
+    case 'Y':
+      print_shift_count_operand (file, x);
+      return;
     }
 
   switch (GET_CODE (x))
@@ -2755,7 +3508,7 @@ print_operand (file, x, code)
     case CODE_LABEL:
     case LABEL_REF:
     case SYMBOL_REF:
-      s390_output_symbolic_const (file, x);
+      output_addr_const (file, x);
       break;
 
     case CONST_INT:
@@ -2765,6 +3518,12 @@ print_operand (file, x, code)
         fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffff);
       else if (code == 'h')
         fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((INTVAL (x) & 0xffff) ^ 0x8000) - 0x8000);
+      else if (code == 'i')
+       fprintf (file, HOST_WIDE_INT_PRINT_DEC, 
+                s390_extract_part (x, HImode, 0));
+      else if (code == 'j')
+       fprintf (file, HOST_WIDE_INT_PRINT_DEC, 
+                s390_extract_part (x, HImode, -1));    
       else
         fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
       break;
@@ -2793,44 +3552,35 @@ print_operand (file, x, code)
    handle values smaller than INT_MIN when printed in decimal.  */
 
 static bool
-s390_assemble_integer (x, size, aligned_p)
-     rtx x;
-     unsigned int size;
-     int aligned_p;
+s390_assemble_integer (rtx x, unsigned int size, int aligned_p)
 {
   if (size == 8 && aligned_p
       && GET_CODE (x) == CONST_INT && INTVAL (x) < INT_MIN)
     {
-      fputs ("\t.quad\t", asm_out_file);
-      fprintf (asm_out_file, HOST_WIDE_INT_PRINT_HEX, INTVAL (x));
-      putc ('\n', asm_out_file);
+      fprintf (asm_out_file, "\t.quad\t" HOST_WIDE_INT_PRINT_HEX "\n",
+              INTVAL (x));
       return true;
     }
   return default_assemble_integer (x, size, aligned_p);
 }
 
-
-#define DEBUG_SCHED 0
-
-/* Returns true if register REGNO is used  for forming 
+/* Returns true if register REGNO is used  for forming
    a memory address in expression X.  */
 
 static int
-reg_used_in_mem_p (regno, x)
-     int regno;
-     rtx x;
+reg_used_in_mem_p (int regno, rtx x)
 {
   enum rtx_code code = GET_CODE (x);
   int i, j;
   const char *fmt;
-  
+
   if (code == MEM)
     {
       if (refers_to_regno_p (regno, regno+1,
                             XEXP (x, 0), 0))
        return 1;
     }
-  else if (code == SET 
+  else if (code == SET
           && GET_CODE (SET_DEST (x)) == PC)
     {
       if (refers_to_regno_p (regno, regno+1,
@@ -2844,7 +3594,7 @@ reg_used_in_mem_p (regno, x)
       if (fmt[i] == 'e'
          && reg_used_in_mem_p (regno, XEXP (x, i)))
        return 1;
-      
+
       else if (fmt[i] == 'E')
        for (j = 0; j < XVECLEN (x, i); j++)
          if (reg_used_in_mem_p (regno, XVECEXP (x, i, j)))
@@ -2856,13 +3606,14 @@ reg_used_in_mem_p (regno, x)
 /* Returns true if expression DEP_RTX sets an address register
    used by instruction INSN to address memory.  */
 
-static int 
-addr_generation_dependency_p (dep_rtx, insn)
-     rtx dep_rtx; 
-     rtx insn;
+static int
+addr_generation_dependency_p (rtx dep_rtx, rtx insn)
 {
   rtx target, pat;
 
+  if (GET_CODE (dep_rtx) == INSN)
+      dep_rtx = PATTERN (dep_rtx);
+
   if (GET_CODE (dep_rtx) == SET)
     {
       target = SET_DEST (dep_rtx);
@@ -2875,7 +3626,7 @@ addr_generation_dependency_p (dep_rtx, insn)
        {
          int regno = REGNO (target);
 
-         if (get_attr_type (insn) == TYPE_LA)
+         if (s390_safe_attr_type (insn) == TYPE_LA)
            {
              pat = PATTERN (insn);
              if (GET_CODE (pat) == PARALLEL)
@@ -2889,31 +3640,48 @@ addr_generation_dependency_p (dep_rtx, insn)
              else
                abort();
            }
-         else if (get_attr_atype (insn) == ATYPE_MEM)
+         else if (get_attr_atype (insn) == ATYPE_AGEN)
            return reg_used_in_mem_p (regno, PATTERN (insn));
        }
     }
   return 0;
 }
 
+/* Return 1, if dep_insn sets register used in insn in the agen unit.  */
+
+int
+s390_agen_dep_p (rtx dep_insn, rtx insn)
+{
+  rtx dep_rtx = PATTERN (dep_insn);
+  int i;
+
+  if (GET_CODE (dep_rtx) == SET
+      && addr_generation_dependency_p (dep_rtx, insn))
+    return 1;
+  else if (GET_CODE (dep_rtx) == PARALLEL)
+    {
+      for (i = 0; i < XVECLEN (dep_rtx, 0); i++)
+       {
+         if (addr_generation_dependency_p (XVECEXP (dep_rtx, 0, i), insn))
+           return 1;
+       }
+    }
+  return 0;
+}
 
 /* Return the modified cost of the dependency of instruction INSN
-   on instruction DEP_INSN through the link LINK.  COST is the 
+   on instruction DEP_INSN through the link LINK.  COST is the
    default cost of that dependency.
 
    Data dependencies are all handled without delay.  However, if a
-   register is modified and subsequently used as base or index 
+   register is modified and subsequently used as base or index
    register of a memory reference, at least 4 cycles need to pass
-   between setting and using the register to avoid pipeline stalls.  
+   between setting and using the register to avoid pipeline stalls.
    An exception is the LA instruction. An address generated by LA can
    be used by introducing only a one cycle stall on the pipeline.  */
 
 static int
-s390_adjust_cost (insn, link, dep_insn, cost)
-     rtx insn;
-     rtx link;
-     rtx dep_insn;
-     int cost;
+s390_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
 {
   rtx dep_rtx;
   int i;
@@ -2929,100 +3697,104 @@ s390_adjust_cost (insn, link, dep_insn, cost)
   if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0)
     return cost;
 
+  /* DFA based scheduling checks address dependency in md file.  */
+  if (s390_use_dfa_pipeline_interface ())
+  {
+    /* Operand forward in case of lr, load and la.  */
+    if (s390_tune == PROCESSOR_2084_Z990
+        && cost == 1
+       && (s390_safe_attr_type (dep_insn) == TYPE_LA
+           || s390_safe_attr_type (dep_insn) == TYPE_LR
+           || s390_safe_attr_type (dep_insn) == TYPE_LOAD))
+      return 0;
+    return cost;
+  }
+
   dep_rtx = PATTERN (dep_insn);
 
-  if (GET_CODE (dep_rtx) == SET)
-    {
-      if (addr_generation_dependency_p (dep_rtx, insn))
-       {
-         cost += (get_attr_type (dep_insn) == TYPE_LA) ? 1 : 4;  
-         if (DEBUG_SCHED)
-           {
-             fprintf (stderr, "\n\nAddress dependency detected: cost %d\n",
-                      cost);
-             debug_rtx (dep_insn);
-             debug_rtx (insn);
-           }
-       }
-    }
+  if (GET_CODE (dep_rtx) == SET
+      && addr_generation_dependency_p (dep_rtx, insn))
+    cost += (s390_safe_attr_type (dep_insn) == TYPE_LA) ? 1 : 4;
   else if (GET_CODE (dep_rtx) == PARALLEL)
     {
       for (i = 0; i < XVECLEN (dep_rtx, 0); i++)
        {
-         if (addr_generation_dependency_p (XVECEXP (dep_rtx, 0, i),
-                                           insn))
-           {
-             cost += (get_attr_type (dep_insn) == TYPE_LA) ? 1 : 4;  
-             if (DEBUG_SCHED)
-               {
-                 fprintf (stderr, "\n\nAddress dependency detected: cost %d\n"
-                          ,cost);
-                 debug_rtx (dep_insn);
-                 debug_rtx (insn);
-               }
-           }
+         if (addr_generation_dependency_p (XVECEXP (dep_rtx, 0, i), insn))
+           cost += (s390_safe_attr_type (dep_insn) == TYPE_LA) ? 1 : 4;
        }
     }
 
   return cost;
 }
-
-
 /* A C statement (sans semicolon) to update the integer scheduling priority
-   INSN_PRIORITY (INSN).  Reduce the priority to execute the INSN earlier,
-   increase the priority to execute INSN later.  Do not define this macro if
-   you do not need to adjust the scheduling priorities of insns. 
+   INSN_PRIORITY (INSN).  Increase the priority to execute the INSN earlier,
+   reduce the priority to execute INSN later.  Do not define this macro if
+   you do not need to adjust the scheduling priorities of insns.
 
-   A LA instruction maybe scheduled later, since the pipeline bypasses the
-   calculated value.  */
+   A STD instruction should be scheduled earlier,
+   in order to use the bypass.  */
 
 static int
-s390_adjust_priority (insn, priority)
-     rtx insn ATTRIBUTE_UNUSED;
-     int priority;
+s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
 {
   if (! INSN_P (insn))
     return priority;
 
-  if (GET_CODE (PATTERN (insn)) == USE 
-      || GET_CODE (PATTERN (insn)) == CLOBBER)
+  if (s390_tune != PROCESSOR_2084_Z990)
     return priority;
-  
-  switch (get_attr_type (insn))
+
+  switch (s390_safe_attr_type (insn))
     {
-    default:
-      break;
-      
-    case TYPE_LA:
-      if (priority >= 0 && priority < 0x01000000)
-       priority <<= 3;
-      break;
-    case TYPE_LM:
-      /* LM in epilogue should never be scheduled. This
-        is due to literal access done in function body.
-        The usage of register 13 is not mentioned explicitly,
-        leading to scheduling 'LM' accross this instructions.  
-      */ 
-      priority = 0x7fffffff;
-      break;
-    }
-  
+      case TYPE_FSTORED:
+      case TYPE_FSTORES:
+       priority = priority << 3;
+       break;
+      case TYPE_STORE:
+       priority = priority << 1;
+       break;
+      default:
+        break;
+    }
   return priority;
 }
 
+/* The number of instructions that can be issued per cycle.  */
+
+static int
+s390_issue_rate (void)
+{
+  if (s390_tune == PROCESSOR_2084_Z990)
+    return 3;
+  return 1;
+}
+
+/* If the following function returns TRUE, we will use the the DFA
+   insn scheduler.  */
+
+static int
+s390_use_dfa_pipeline_interface (void)
+{
+  if (s390_tune == PROCESSOR_2064_Z900
+      || s390_tune == PROCESSOR_2084_Z990)
+    return 1;
+
+  return 0;
+}
+
+static int
+s390_first_cycle_multipass_dfa_lookahead (void)
+{
+  return s390_use_dfa_pipeline_interface () ? 4 : 0;
+}
 
-/* Split all branches that exceed the maximum distance.  
-   Returns true if this created a new literal pool entry.  
 
-   Code generated by this routine is allowed to use
-   TEMP_REG as temporary scratch register.  If this is
-   done, TEMP_USED is set to true.  */
+/* Split all branches that exceed the maximum distance.
+   Returns true if this created a new literal pool entry.  */
 
-static int 
-s390_split_branches (temp_reg, temp_used)
-     rtx temp_reg;
-     bool *temp_used;
+static int
+s390_split_branches (void)
 {
+  rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
   int new_literal = 0;
   rtx insn, pat, tmp, target;
   rtx *label;
@@ -3044,15 +3816,15 @@ s390_split_branches (temp_reg, temp_used)
       if (GET_CODE (pat) != SET || SET_DEST (pat) != pc_rtx)
        continue;
 
-      if (GET_CODE (SET_SRC (pat)) == LABEL_REF) 
+      if (GET_CODE (SET_SRC (pat)) == LABEL_REF)
        {
          label = &SET_SRC (pat);
-       } 
-      else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE) 
+       }
+      else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
        {
-         if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF) 
+         if (GET_CODE (XEXP (SET_SRC (pat), 1)) == LABEL_REF)
            label = &XEXP (SET_SRC (pat), 1);
-          else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF) 
+          else if (GET_CODE (XEXP (SET_SRC (pat), 2)) == LABEL_REF)
             label = &XEXP (SET_SRC (pat), 2);
          else
            continue;
@@ -3060,19 +3832,14 @@ s390_split_branches (temp_reg, temp_used)
       else
        continue;
 
-      if (get_attr_length (insn) <= (TARGET_64BIT ? 6 : 4))
+      if (get_attr_length (insn) <= 4)
        continue;
 
-      *temp_used = 1;
+      /* We are going to use the return register as scratch register,
+        make sure it will be saved/restored by the prologue/epilogue.  */
+      cfun->machine->save_return_addr_p = 1;
 
-      if (TARGET_64BIT)
-       {
-         tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, *label), insn);
-         INSN_ADDRESSES_NEW (tmp, -1);
-
-         target = temp_reg;
-       }
-      else if (!flag_pic)
+      if (!flag_pic)
        {
          new_literal = 1;
          tmp = force_const_mem (Pmode, *label);
@@ -3084,14 +3851,16 @@ s390_split_branches (temp_reg, temp_used)
       else
        {
          new_literal = 1;
-         tmp = gen_rtx_UNSPEC (SImode, gen_rtvec (1, *label), 104);
-         tmp = gen_rtx_CONST (SImode, tmp);
-         tmp = force_const_mem (SImode, tmp);
-         tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn);
+         target = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, *label),
+                                  UNSPEC_LTREL_OFFSET);
+         target = gen_rtx_CONST (Pmode, target);
+         target = force_const_mem (Pmode, target);
+         tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, target), insn);
          INSN_ADDRESSES_NEW (tmp, -1);
 
-         target = gen_rtx_REG (Pmode, BASE_REGISTER);
-         target = gen_rtx_PLUS (Pmode, target, temp_reg);
+          target = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, XEXP (target, 0)),
+                                  UNSPEC_LTREL_BASE);
+         target = gen_rtx_PLUS (Pmode, temp_reg, target);
        }
 
       if (!validate_change (insn, label, target, 0))
@@ -3102,22 +3871,29 @@ s390_split_branches (temp_reg, temp_used)
 }
 
 
-/* Find a literal pool symbol referenced in RTX X, and store 
-   it at REF.  Will abort if X contains references to more than 
+/* Find a literal pool symbol referenced in RTX X, and store
+   it at REF.  Will abort if X contains references to more than
    one such pool symbol; multiple references to the same symbol
-   are allowed, however. 
+   are allowed, however.
 
-   The rtx pointed to by REF must be initialized to NULL_RTX 
+   The rtx pointed to by REF must be initialized to NULL_RTX
    by the caller before calling this routine.  */
 
 static void
-find_constant_pool_ref (x, ref)
-     rtx x;
-     rtx *ref;
+find_constant_pool_ref (rtx x, rtx *ref)
 {
   int i, j;
   const char *fmt;
 
+  /* Ignore LTREL_BASE references.  */
+  if (GET_CODE (x) == UNSPEC
+      && XINT (x, 1) == UNSPEC_LTREL_BASE)
+    return;
+  /* Likewise POOL_ENTRY insns.  */
+  if (GET_CODE (x) == UNSPEC_VOLATILE
+      && XINT (x, 1) == UNSPECV_POOL_ENTRY)
+    return;
+
   if (GET_CODE (x) == SYMBOL_REF
       && CONSTANT_POOL_ADDRESS_P (x))
     {
@@ -3146,10 +3922,7 @@ find_constant_pool_ref (x, ref)
    in X by the address ADDR.  Fix up MEMs as required.  */
 
 static void
-replace_constant_pool_ref (x, ref, addr)
-     rtx *x;
-     rtx ref;
-     rtx addr;
+replace_constant_pool_ref (rtx *x, rtx ref, rtx addr)
 {
   int i, j;
   const char *fmt;
@@ -3216,135 +3989,55 @@ replace_constant_pool_ref (x, ref, addr)
     }
 }
 
-/* Check whether ADDR is an address that uses the base register, 
-   without actually constituting a literal pool access.  (This happens
-   in 31-bit PIC mode, where the base register is used as anchor for
-   relative addressing of local symbols.) 
-
-   Returns 1 if the base register occupies the base slot,
-   returns 2 if the base register occupies the index slot,
-   returns 0 if the address is not of this form.  */
-
-static int
-find_base_register_in_addr (addr)
-     struct s390_address *addr;
-{
-  /* If DISP is complex, we might have a literal pool reference.  */
-  if (addr->disp && GET_CODE (addr->disp) != CONST_INT)
-    return 0;
-
-  if (addr->base && REG_P (addr->base) && REGNO (addr->base) == BASE_REGISTER)
-    return 1;
-
-  if (addr->indx && REG_P (addr->indx) && REGNO (addr->indx) == BASE_REGISTER)
-    return 2;
-
-  return 0;
-}
-
-/* Return true if X contains an address that uses the base register, 
-   without actually constituting a literal pool access.  */
+/* Check whether X contains an UNSPEC_LTREL_BASE.
+   Return its constant pool symbol if found, NULL_RTX otherwise.  */
 
-static bool
-find_base_register_ref (x)
-     rtx x;
+static rtx
+find_ltrel_base (rtx x)
 {
-  bool retv = FALSE;
-  struct s390_address addr;
   int i, j;
   const char *fmt;
 
-  /* Addresses can only occur inside a MEM ...  */
-  if (GET_CODE (x) == MEM)
-    {
-      if (s390_decompose_address (XEXP (x, 0), &addr)
-         && find_base_register_in_addr (&addr))
-       return TRUE;
-    }
-
-  /* ... or a load-address type pattern.  */
-  if (GET_CODE (x) == SET && GET_CODE (SET_DEST (x)) == REG)
-    {
-      if (s390_decompose_address (SET_SRC (x), &addr)
-         && find_base_register_in_addr (&addr))
-       return TRUE;
-    }
+  if (GET_CODE (x) == UNSPEC
+      && XINT (x, 1) == UNSPEC_LTREL_BASE)
+    return XVECEXP (x, 0, 0);
 
   fmt = GET_RTX_FORMAT (GET_CODE (x));
   for (i = GET_RTX_LENGTH (GET_CODE (x)) - 1; i >= 0; i--)
     {
       if (fmt[i] == 'e')
         {
-          retv |= find_base_register_ref (XEXP (x, i));
+          rtx fnd = find_ltrel_base (XEXP (x, i));
+         if (fnd)
+           return fnd;
         }
       else if (fmt[i] == 'E')
         {
           for (j = 0; j < XVECLEN (x, i); j++)
-            retv |= find_base_register_ref (XVECEXP (x, i, j));
+           {
+              rtx fnd = find_ltrel_base (XVECEXP (x, i, j));
+             if (fnd)
+               return fnd;
+           }
         }
     }
 
-  return retv;
+  return NULL_RTX;
 }
 
-/* If X contains an address that uses the base register,
-   without actually constituting a literal pool access,
-   replace the base register with REPL in all such cases.
-
-   Handles both MEMs and load address patterns.  */
+/* Replace any occurrence of UNSPEC_LTREL_BASE in X with BASE.  */
 
 static void
-replace_base_register_ref (x, repl)
-     rtx *x;
-     rtx repl;
+replace_ltrel_base (rtx *x, rtx base)
 {
-  struct s390_address addr;
-  rtx new_addr;
-  int i, j, pos;
+  int i, j;
   const char *fmt;
 
-  /* Addresses can only occur inside a MEM ...  */
-  if (GET_CODE (*x) == MEM)
+  if (GET_CODE (*x) == UNSPEC
+      && XINT (*x, 1) == UNSPEC_LTREL_BASE)
     {
-      if (s390_decompose_address (XEXP (*x, 0), &addr)
-         && (pos = find_base_register_in_addr (&addr)))
-       {
-         if (pos == 1)
-           addr.base = repl;
-         else
-           addr.indx = repl;
-
-         new_addr = addr.base;
-         if (addr.indx)
-           new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.indx);
-         if (addr.disp)
-           new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.disp);
-
-         *x = replace_equiv_address (*x, new_addr);
-         return;
-       }
-    }
-
-  /* ... or a load-address type pattern.  */
-  if (GET_CODE (*x) == SET && GET_CODE (SET_DEST (*x)) == REG)
-    {
-      if (s390_decompose_address (SET_SRC (*x), &addr)
-         && (pos = find_base_register_in_addr (&addr)))
-       {
-         if (pos == 1)
-           addr.base = repl;
-         else
-           addr.indx = repl;
-
-         new_addr = addr.base;
-         if (addr.indx)
-           new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.indx);
-         if (addr.disp)
-           new_addr = gen_rtx_PLUS (Pmode, new_addr, addr.disp);
-
-         SET_SRC (*x) = new_addr;
-         return;
-       }
+      *x = base;
+      return;
     }
 
   fmt = GET_RTX_FORMAT (GET_CODE (*x));
@@ -3352,37 +4045,30 @@ replace_base_register_ref (x, repl)
     {
       if (fmt[i] == 'e')
         {
-          replace_base_register_ref (&XEXP (*x, i), repl);
+          replace_ltrel_base (&XEXP (*x, i), base);
         }
       else if (fmt[i] == 'E')
         {
           for (j = 0; j < XVECLEN (*x, i); j++)
-            replace_base_register_ref (&XVECEXP (*x, i, j), repl);
+            replace_ltrel_base (&XVECEXP (*x, i, j), base);
         }
     }
 }
 
 
-/* We keep a list of constants we which we have to add to internal
+/* We keep a list of constants which we have to add to internal
    constant tables in the middle of large functions.  */
 
-#define NR_C_MODES 6
-enum machine_mode constant_modes[NR_C_MODES] = 
+#define NR_C_MODES 7
+enum machine_mode constant_modes[NR_C_MODES] =
 {
+  TImode,
   DFmode, DImode,
   SFmode, SImode,
   HImode,
   QImode
 };
 
-rtx (*gen_consttable[NR_C_MODES])(rtx) =
-{
-  gen_consttable_df, gen_consttable_di,
-  gen_consttable_sf, gen_consttable_si,
-  gen_consttable_hi,
-  gen_consttable_qi
-};
-
 struct constant
 {
   struct constant *next;
@@ -3400,45 +4086,36 @@ struct constant_pool
   struct constant *constants[NR_C_MODES];
   rtx label;
   int size;
-  bool anchor;
 };
 
-static struct constant_pool * s390_chunkify_start PARAMS ((rtx, bool *));
-static void s390_chunkify_finish PARAMS ((struct constant_pool *, rtx));
-static void s390_chunkify_cancel PARAMS ((struct constant_pool *));
+static struct constant_pool * s390_mainpool_start (void);
+static void s390_mainpool_finish (struct constant_pool *, rtx base_reg);
+static void s390_mainpool_cancel (struct constant_pool *);
+
+static struct constant_pool * s390_chunkify_start (rtx base_reg);
+static void s390_chunkify_finish (struct constant_pool *, rtx base_reg);
+static void s390_chunkify_cancel (struct constant_pool *);
 
-static struct constant_pool *s390_start_pool PARAMS ((struct constant_pool **, rtx));
-static void s390_end_pool PARAMS ((struct constant_pool *, rtx));
-static void s390_add_pool_insn PARAMS ((struct constant_pool *, rtx));
-static struct constant_pool *s390_find_pool PARAMS ((struct constant_pool *, rtx));
-static void s390_add_constant PARAMS ((struct constant_pool *, rtx, enum machine_mode));
-static rtx s390_find_constant PARAMS ((struct constant_pool *, rtx, enum machine_mode));
-static void s390_add_anchor PARAMS ((struct constant_pool *));
-static rtx s390_dump_pool PARAMS ((struct constant_pool *));
-static void s390_free_pool PARAMS ((struct constant_pool *));
+static struct constant_pool *s390_start_pool (struct constant_pool **, rtx);
+static void s390_end_pool (struct constant_pool *, rtx);
+static void s390_add_pool_insn (struct constant_pool *, rtx);
+static struct constant_pool *s390_find_pool (struct constant_pool *, rtx);
+static void s390_add_constant (struct constant_pool *, rtx, enum machine_mode);
+static rtx s390_find_constant (struct constant_pool *, rtx, enum machine_mode);
+static rtx s390_dump_pool (struct constant_pool *, bool);
+static struct constant_pool *s390_alloc_pool (void);
+static void s390_free_pool (struct constant_pool *);
 
 /* Create new constant pool covering instructions starting at INSN
    and chain it to the end of POOL_LIST.  */
 
 static struct constant_pool *
-s390_start_pool (pool_list, insn)
-     struct constant_pool **pool_list;
-     rtx insn;
+s390_start_pool (struct constant_pool **pool_list, rtx insn)
 {
   struct constant_pool *pool, **prev;
-  int i;
-
-  pool = (struct constant_pool *) xmalloc (sizeof *pool);
-  pool->next = NULL;
-  for (i = 0; i < NR_C_MODES; i++)
-    pool->constants[i] = NULL;
 
-  pool->label = gen_label_rtx ();
+  pool = s390_alloc_pool ();
   pool->first_insn = insn;
-  pool->pool_insn = NULL_RTX;
-  pool->insns = BITMAP_XMALLOC ();
-  pool->size = 0;
-  pool->anchor = FALSE;
 
   for (prev = pool_list; *prev; prev = &(*prev)->next)
     ;
@@ -3451,9 +4128,7 @@ s390_start_pool (pool_list, insn)
    placeholder insn representing the pool.  */
 
 static void
-s390_end_pool (pool, insn)
-     struct constant_pool *pool;
-     rtx insn;
+s390_end_pool (struct constant_pool *pool, rtx insn)
 {
   rtx pool_size = GEN_INT (pool->size + 8 /* alignment slop */);
 
@@ -3467,9 +4142,7 @@ s390_end_pool (pool, insn)
 /* Add INSN to the list of insns covered by POOL.  */
 
 static void
-s390_add_pool_insn (pool, insn)
-     struct constant_pool *pool;
-     rtx insn;
+s390_add_pool_insn (struct constant_pool *pool, rtx insn)
 {
   bitmap_set_bit (pool->insns, INSN_UID (insn));
 }
@@ -3477,9 +4150,7 @@ s390_add_pool_insn (pool, insn)
 /* Return pool out of POOL_LIST that covers INSN.  */
 
 static struct constant_pool *
-s390_find_pool (pool_list, insn)
-     struct constant_pool *pool_list;
-     rtx insn;
+s390_find_pool (struct constant_pool *pool_list, rtx insn)
 {
   struct constant_pool *pool;
 
@@ -3493,10 +4164,7 @@ s390_find_pool (pool_list, insn)
 /* Add constant VAL of mode MODE to the constant pool POOL.  */
 
 static void
-s390_add_constant (pool, val, mode)
-     struct constant_pool *pool;
-     rtx val;
-     enum machine_mode mode;
+s390_add_constant (struct constant_pool *pool, rtx val, enum machine_mode mode)
 {
   struct constant *c;
   int i;
@@ -3525,77 +4193,55 @@ s390_add_constant (pool, val, mode)
 /* Find constant VAL of mode MODE in the constant pool POOL.
    Return an RTX describing the distance from the start of
    the pool to the location of the new constant.  */
+
 static rtx
-s390_find_constant (pool, val, mode)
-     struct constant_pool *pool;
-     rtx val;
-     enum machine_mode mode;
+s390_find_constant (struct constant_pool *pool, rtx val,
+                   enum machine_mode mode)
 {
   struct constant *c;
   rtx offset;
   int i;
+
   for (i = 0; i < NR_C_MODES; i++)
     if (constant_modes[i] == mode)
       break;
   if (i == NR_C_MODES)
     abort ();
+
   for (c = pool->constants[i]; c != NULL; c = c->next)
     if (rtx_equal_p (val, c->value))
       break;
+
   if (c == NULL)
     abort ();
+
   offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label),
                                  gen_rtx_LABEL_REF (Pmode, pool->label));
   offset = gen_rtx_CONST (Pmode, offset);
   return offset;
 }
 
-/* Set 'anchor' flag in POOL.  */
-
-static void
-s390_add_anchor (pool)
-     struct constant_pool *pool;
-{
-  if (!pool->anchor)
-    {
-      pool->anchor = TRUE;
-      pool->size += 4;
-    }
-}
-
-/* Dump out the constants in POOL.  */
+/* Dump out the constants in POOL.  If REMOTE_LABEL is true,
+   do not emit the pool base label.  */
 
 static rtx
-s390_dump_pool (pool)
-     struct constant_pool *pool;
+s390_dump_pool (struct constant_pool *pool, bool remote_label)
 {
   struct constant *c;
   rtx insn;
   int i;
 
-  /* Pool start insn switches to proper section 
+  /* Pool start insn switches to proper section
      and guarantees necessary alignment.  */
-  if (TARGET_64BIT)
+  if (TARGET_CPU_ZARCH)
     insn = emit_insn_after (gen_pool_start_64 (), pool->pool_insn);
   else
     insn = emit_insn_after (gen_pool_start_31 (), pool->pool_insn);
   INSN_ADDRESSES_NEW (insn, -1);
 
-  insn = emit_label_after (pool->label, insn);
-  INSN_ADDRESSES_NEW (insn, -1);
-
-  /* Emit anchor if we need one.  */
-  if (pool->anchor)
+  if (!remote_label)
     {
-      rtx anchor = gen_rtx_LABEL_REF (VOIDmode, pool->label);
-      anchor = gen_rtx_UNSPEC (VOIDmode, gen_rtvec (1, anchor), 105);
-      anchor = gen_rtx_CONST (VOIDmode, anchor);
-      insn = emit_insn_after (gen_consttable_si (anchor), insn);
+      insn = emit_label_after (pool->label, insn);
       INSN_ADDRESSES_NEW (insn, -1);
     }
 
@@ -3604,27 +4250,31 @@ s390_dump_pool (pool)
   for (i = 0; i < NR_C_MODES; i++)
     for (c = pool->constants[i]; c; c = c->next)
       {
-       /* Convert 104 unspecs to pool-relative references.  */
+       /* Convert UNSPEC_LTREL_OFFSET unspecs to pool-relative references.  */
        rtx value = c->value;
        if (GET_CODE (value) == CONST
            && GET_CODE (XEXP (value, 0)) == UNSPEC
-           && XINT (XEXP (value, 0), 1) == 104
+           && XINT (XEXP (value, 0), 1) == UNSPEC_LTREL_OFFSET
            && XVECLEN (XEXP (value, 0), 0) == 1)
          {
            value = gen_rtx_MINUS (Pmode, XVECEXP (XEXP (value, 0), 0, 0),
-                                  gen_rtx_LABEL_REF (VOIDmode, pool->label));
+                                  gen_rtx_LABEL_REF (VOIDmode, pool->label));
            value = gen_rtx_CONST (VOIDmode, value);
          }
 
        insn = emit_label_after (c->label, insn);
        INSN_ADDRESSES_NEW (insn, -1);
-       insn = emit_insn_after (gen_consttable[i] (value), insn);
+
+       value = gen_rtx_UNSPEC_VOLATILE (constant_modes[i], 
+                                        gen_rtvec (1, value),
+                                        UNSPECV_POOL_ENTRY);
+       insn = emit_insn_after (value, insn);
        INSN_ADDRESSES_NEW (insn, -1);
       }
 
-  /* Pool end insn switches back to previous section 
+  /* Pool end insn switches back to previous section
      and guarantees necessary alignment.  */
-  if (TARGET_64BIT)
+  if (TARGET_CPU_ZARCH)
     insn = emit_insn_after (gen_pool_end_64 (), insn);
   else
     insn = emit_insn_after (gen_pool_end_31 (), insn);
@@ -3639,11 +4289,32 @@ s390_dump_pool (pool)
   return insn;
 }
 
+/* Allocate new constant_pool structure.  */
+
+static struct constant_pool *
+s390_alloc_pool (void)
+{
+  struct constant_pool *pool;
+  int i;
+
+  pool = (struct constant_pool *) xmalloc (sizeof *pool);
+  pool->next = NULL;
+  for (i = 0; i < NR_C_MODES; i++)
+    pool->constants[i] = NULL;
+
+  pool->label = gen_label_rtx ();
+  pool->first_insn = NULL_RTX;
+  pool->pool_insn = NULL_RTX;
+  pool->insns = BITMAP_XMALLOC ();
+  pool->size = 0;
+
+  return pool;
+}
+
 /* Free all memory used by POOL.  */
 
 static void
-s390_free_pool (pool)
-     struct constant_pool *pool;
+s390_free_pool (struct constant_pool *pool)
 {
   int i;
 
@@ -3660,90 +4331,263 @@ s390_free_pool (pool)
 
   BITMAP_XFREE (pool->insns);
   free (pool);
-} 
+}
+
+
+/* Collect main literal pool.  Return NULL on overflow.  */
+
+static struct constant_pool *
+s390_mainpool_start (void)
+{
+  struct constant_pool *pool;
+  rtx insn;
+
+  pool = s390_alloc_pool ();
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      if (GET_CODE (insn) == INSN
+         && GET_CODE (PATTERN (insn)) == UNSPEC_VOLATILE
+         && XINT (PATTERN (insn), 1) == UNSPECV_MAIN_POOL)
+       {
+         if (pool->pool_insn)
+           abort ();
+         pool->pool_insn = insn;
+       }
+
+      if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+       {
+         rtx pool_ref = NULL_RTX;
+         find_constant_pool_ref (PATTERN (insn), &pool_ref);
+         if (pool_ref)
+           {
+             rtx constant = get_pool_constant (pool_ref);
+             enum machine_mode mode = get_pool_mode (pool_ref);
+             s390_add_constant (pool, constant, mode);
+           }
+       }
+    }
+
+  if (!pool->pool_insn)
+    abort ();
+
+  if (pool->size >= 4096)
+    {
+      /* We're going to chunkify the pool, so remove the main
+        pool placeholder insn.  */
+      remove_insn (pool->pool_insn);
+
+      s390_free_pool (pool);
+      pool = NULL;
+    }
+
+  return pool;
+}
+
+/* POOL holds the main literal pool as collected by s390_mainpool_start.
+   Modify the current function to output the pool constants as well as
+   the pool register setup instruction.  BASE_REG is the register to
+   be used as pool base register.  */
+
+static void
+s390_mainpool_finish (struct constant_pool *pool, rtx base_reg)
+{
+  rtx insn;
+
+  /* If the pool is empty, we're done.  */
+  if (pool->size == 0)
+    {
+      remove_insn (pool->pool_insn);
+      s390_free_pool (pool);
+      return;
+    }
+
+  /* We need correct insn addresses.  */
+  shorten_branches (get_insns ());
+
+  /* On zSeries, we use a LARL to load the pool register.  The pool is
+     located in the .rodata section, so we emit it after the function.  */
+  if (TARGET_CPU_ZARCH)
+    {
+      insn = gen_main_base_64 (base_reg, pool->label);
+      insn = emit_insn_after (insn, pool->pool_insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+      remove_insn (pool->pool_insn);
+     
+      insn = get_last_insn (); 
+      pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
+      INSN_ADDRESSES_NEW (pool->pool_insn, -1);
+
+      s390_dump_pool (pool, 0);
+    }
+
+  /* On S/390, if the total size of the function's code plus literal pool
+     does not exceed 4096 bytes, we use BASR to set up a function base
+     pointer, and emit the literal pool at the end of the function.  */
+  else if (INSN_ADDRESSES (INSN_UID (get_last_insn ()))
+          + pool->size + 8 /* alignment slop */ < 4096)
+    {
+      insn = gen_main_base_31_small (base_reg, pool->label);
+      insn = emit_insn_after (insn, pool->pool_insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+      remove_insn (pool->pool_insn);
+
+      insn = emit_label_after (pool->label, insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+
+      insn = get_last_insn ();
+      pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
+      INSN_ADDRESSES_NEW (pool->pool_insn, -1);
+
+      s390_dump_pool (pool, 1);
+    }
+
+  /* Otherwise, we emit an inline literal pool and use BASR to branch
+     over it, setting up the pool register at the same time.  */
+  else
+    {
+      rtx pool_end = gen_label_rtx ();
+
+      insn = gen_main_base_31_large (base_reg, pool->label, pool_end);
+      insn = emit_insn_after (insn, pool->pool_insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+      remove_insn (pool->pool_insn);
+
+      insn = emit_label_after (pool->label, insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+
+      pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
+      INSN_ADDRESSES_NEW (pool->pool_insn, -1);
+
+      insn = emit_label_after (pool_end, pool->pool_insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+
+      s390_dump_pool (pool, 1);
+    }
 
 
-/* Chunkify the literal pool if required.
+  /* Replace all literal pool references.  */
 
-   Code generated by this routine is allowed to use
-   TEMP_REG as temporary scratch register.  If this is
-   done, TEMP_USED is set to true.  */
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      if (INSN_P (insn))
+       replace_ltrel_base (&PATTERN (insn), base_reg);
+
+      if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+        {
+          rtx addr, pool_ref = NULL_RTX;
+          find_constant_pool_ref (PATTERN (insn), &pool_ref);
+          if (pool_ref)
+            {
+              addr = s390_find_constant (pool, get_pool_constant (pool_ref),
+                                               get_pool_mode (pool_ref));
+              addr = gen_rtx_PLUS (Pmode, base_reg, addr);
+              replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
+              INSN_CODE (insn) = -1;
+            }
+        }
+    }
+
+
+  /* Free the pool.  */
+  s390_free_pool (pool);
+}
+
+/* POOL holds the main literal pool as collected by s390_mainpool_start.
+   We have decided we cannot use this pool, so revert all changes
+   to the current function that were done by s390_mainpool_start.  */
+static void
+s390_mainpool_cancel (struct constant_pool *pool)
+{
+  /* We didn't actually change the instruction stream, so simply
+     free the pool memory.  */
+  s390_free_pool (pool);
+}
+
+
+/* Chunkify the literal pool.  BASE_REG is to be used as pool
+   register.  */
 
 #define S390_POOL_CHUNK_MIN    0xc00
 #define S390_POOL_CHUNK_MAX    0xe00
 
-static struct constant_pool * 
-s390_chunkify_start (temp_reg, temp_used)
-     rtx temp_reg;
-     bool *temp_used;
+static struct constant_pool *
+s390_chunkify_start (rtx base_reg)
 {
-  rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER);
-
   struct constant_pool *curr_pool = NULL, *pool_list = NULL;
   int extra_size = 0;
   bitmap far_labels;
+  rtx pending_ltrel = NULL_RTX;
   rtx insn;
 
-  rtx (*gen_reload_base) PARAMS ((rtx, rtx)) =
-    TARGET_64BIT? gen_reload_base_64 : gen_reload_base_31;
+  rtx (*gen_reload_base) (rtx, rtx) =
+    TARGET_CPU_ZARCH? gen_reload_base_64 : gen_reload_base_31;
 
 
-  /* Do we need to chunkify the literal pool?  */
-
-  if (get_pool_size () < S390_POOL_CHUNK_MAX)
-    return NULL;
-
   /* We need correct insn addresses.  */
 
   shorten_branches (get_insns ());
 
-  /* Scan all insns and move literals to pool chunks.
-     Also, emit anchor reload insns before every insn that uses 
-     the literal pool base register as anchor pointer.  */
+  /* Scan all insns and move literals to pool chunks.  */
 
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
+      /* Check for pending LTREL_BASE.  */
+      if (INSN_P (insn))
+       {
+         rtx ltrel_base = find_ltrel_base (PATTERN (insn));
+         if (ltrel_base)
+           {
+             if (ltrel_base == pending_ltrel)
+               pending_ltrel = NULL_RTX;
+             else
+               abort ();
+           }
+       }
+
       if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
        {
          rtx pool_ref = NULL_RTX;
          find_constant_pool_ref (PATTERN (insn), &pool_ref);
          if (pool_ref)
            {
+             rtx constant = get_pool_constant (pool_ref);
+             enum machine_mode mode = get_pool_mode (pool_ref);
+
              if (!curr_pool)
                curr_pool = s390_start_pool (&pool_list, insn);
 
-             s390_add_constant (curr_pool, get_pool_constant (pool_ref), 
-                                           get_pool_mode (pool_ref));
+             s390_add_constant (curr_pool, constant, mode);
              s390_add_pool_insn (curr_pool, insn);
-           }
-
-         else if (!TARGET_64BIT && flag_pic
-                   && find_base_register_ref (PATTERN (insn)))
-           {
-             rtx new = gen_reload_anchor (temp_reg, base_reg);
-             new = emit_insn_before (new, insn);
-             INSN_ADDRESSES_NEW (new, INSN_ADDRESSES (INSN_UID (insn)));
-             extra_size += 8;
-             *temp_used = 1;
-             
-             if (!curr_pool)
-               curr_pool = s390_start_pool (&pool_list, new);
 
-             s390_add_anchor (curr_pool);
-             s390_add_pool_insn (curr_pool, insn);
+             /* Don't split the pool chunk between a LTREL_OFFSET load
+                and the corresponding LTREL_BASE.  */
+             if (GET_CODE (constant) == CONST
+                 && GET_CODE (XEXP (constant, 0)) == UNSPEC
+                 && XINT (XEXP (constant, 0), 1) == UNSPEC_LTREL_OFFSET)
+               {
+                 if (pending_ltrel)
+                   abort ();
+                 pending_ltrel = pool_ref;
+               }
            }
        }
 
       if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CODE_LABEL)
-       if (curr_pool)
-         s390_add_pool_insn (curr_pool, insn);
+       {
+         if (curr_pool)
+           s390_add_pool_insn (curr_pool, insn);
+         /* An LTREL_BASE must follow within the same basic block.  */
+         if (pending_ltrel)
+           abort ();
+       }
 
-      if (!curr_pool 
+      if (!curr_pool
          || INSN_ADDRESSES_SIZE () <= (size_t) INSN_UID (insn)
           || INSN_ADDRESSES (INSN_UID (insn)) == -1)
        continue;
 
-      if (TARGET_64BIT)
+      if (TARGET_CPU_ZARCH)
        {
          if (curr_pool->size < S390_POOL_CHUNK_MAX)
            continue;
@@ -3754,7 +4598,7 @@ s390_chunkify_start (temp_reg, temp_used)
       else
        {
           int chunk_size = INSN_ADDRESSES (INSN_UID (insn))
-                          - INSN_ADDRESSES (INSN_UID (curr_pool->first_insn))
+                          - INSN_ADDRESSES (INSN_UID (curr_pool->first_insn))
                         + extra_size;
 
          /* We will later have to insert base register reload insns.
@@ -3788,13 +4632,12 @@ s390_chunkify_start (temp_reg, temp_used)
              if (get_attr_length (insn) == 0)
                continue;
 
-             /* Don't separate insns created by s390_split_branches.  */
-             if (GET_CODE (insn) == INSN 
-                 && GET_CODE (PATTERN (insn)) == SET
-                 && rtx_equal_p (SET_DEST (PATTERN (insn)), temp_reg))
+             /* Don't separate LTREL_BASE from the corresponding
+                LTREL_OFFSET load.  */
+             if (pending_ltrel)
                continue;
 
-             label = gen_label_rtx ();
+             label = gen_label_rtx ();
              jump = emit_jump_insn_after (gen_jump (label), insn);
              barrier = emit_barrier_after (jump);
              insn = emit_label_after (label, barrier);
@@ -3814,9 +4657,11 @@ s390_chunkify_start (temp_reg, temp_used)
 
   if (curr_pool)
     s390_end_pool (curr_pool, NULL_RTX);
+  if (pending_ltrel)
+    abort ();
 
 
-  /* Find all labels that are branched into 
+  /* Find all labels that are branched into
      from an insn belonging to a different chunk.  */
 
   far_labels = BITMAP_XMALLOC ();
@@ -3830,11 +4675,11 @@ s390_chunkify_start (temp_reg, temp_used)
         Don't do that, however, if it is the label before
         a jump table.  */
 
-      if (GET_CODE (insn) == CODE_LABEL 
+      if (GET_CODE (insn) == CODE_LABEL
          && (LABEL_PRESERVE_P (insn) || LABEL_NAME (insn)))
        {
          rtx vec_insn = next_real_insn (insn);
-         rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ? 
+         rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ?
                        PATTERN (vec_insn) : NULL_RTX;
          if (!vec_pat
              || !(GET_CODE (vec_pat) == ADDR_VEC
@@ -3844,22 +4689,22 @@ s390_chunkify_start (temp_reg, temp_used)
 
       /* If we have a direct jump (conditional or unconditional)
         or a casesi jump, check all potential targets.  */
-      else if (GET_CODE (insn) == JUMP_INSN) 
+      else if (GET_CODE (insn) == JUMP_INSN)
        {
           rtx pat = PATTERN (insn);
          if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2)
            pat = XVECEXP (pat, 0, 0);
 
-          if (GET_CODE (pat) == SET) 
+          if (GET_CODE (pat) == SET)
             {
              rtx label = JUMP_LABEL (insn);
              if (label)
                {
-                 if (s390_find_pool (pool_list, label) 
+                 if (s390_find_pool (pool_list, label)
                      != s390_find_pool (pool_list, insn))
                    bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label));
                }
-            } 
+            }
          else if (GET_CODE (pat) == PARALLEL
                   && XVECLEN (pat, 0) == 2
                   && GET_CODE (XVECEXP (pat, 0, 0)) == SET
@@ -3869,7 +4714,7 @@ s390_chunkify_start (temp_reg, temp_used)
              /* Find the jump table used by this casesi jump.  */
              rtx vec_label = XEXP (XEXP (XVECEXP (pat, 0, 1), 0), 0);
              rtx vec_insn = next_real_insn (vec_label);
-             rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ? 
+             rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ?
                            PATTERN (vec_insn) : NULL_RTX;
              if (vec_pat
                  && (GET_CODE (vec_pat) == ADDR_VEC
@@ -3881,7 +4726,7 @@ s390_chunkify_start (temp_reg, temp_used)
                    {
                      rtx label = XEXP (XVECEXP (vec_pat, diff_p, i), 0);
 
-                     if (s390_find_pool (pool_list, label) 
+                     if (s390_find_pool (pool_list, label)
                          != s390_find_pool (pool_list, insn))
                        bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label));
                    }
@@ -3902,7 +4747,7 @@ s390_chunkify_start (temp_reg, temp_used)
   /* Insert base register reload insns at every far label.  */
 
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-    if (GET_CODE (insn) == CODE_LABEL 
+    if (GET_CODE (insn) == CODE_LABEL
         && bitmap_bit_p (far_labels, CODE_LABEL_NUMBER (insn)))
       {
        struct constant_pool *pool = s390_find_pool (pool_list, insn);
@@ -3926,26 +4771,24 @@ s390_chunkify_start (temp_reg, temp_used)
 }
 
 /* POOL_LIST is a chunk list as prepared by s390_chunkify_start.
-   After we have decided to use this list, finish implementing 
-   all changes to the current function as required.
+   After we have decided to use this list, finish implementing
+   all changes to the current function as required.  BASE_REG is
+   to be used as pool base register.  */
 
-   Code generated by this routine is allowed to use
-   TEMP_REG as temporary scratch register.  */
 static void
-s390_chunkify_finish (pool_list, temp_reg)
-     struct constant_pool *pool_list;
-     rtx temp_reg;
+s390_chunkify_finish (struct constant_pool *pool_list, rtx base_reg)
 {
-  rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER);
   struct constant_pool *curr_pool = NULL;
   rtx insn;
+
+
   /* Replace all literal pool references.  */
 
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn)) 
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
+      if (INSN_P (insn))
+       replace_ltrel_base (&PATTERN (insn), base_reg);
+
       curr_pool = s390_find_pool (pool_list, insn);
       if (!curr_pool)
        continue;
@@ -3962,20 +4805,14 @@ s390_chunkify_finish (pool_list, temp_reg)
               replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
               INSN_CODE (insn) = -1;
             }
-
-         else if (!TARGET_64BIT && flag_pic
-                   && find_base_register_ref (PATTERN (insn)))
-           {
-             replace_base_register_ref (&PATTERN (insn), temp_reg);
-           }
         }
     }
 
   /* Dump out all literal pools.  */
+
   for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
-    s390_dump_pool (curr_pool);
+    s390_dump_pool (curr_pool, 0);
+
   /* Free pool list.  */
 
   while (pool_list)
@@ -3989,10 +4826,9 @@ s390_chunkify_finish (pool_list, temp_reg)
 /* POOL_LIST is a chunk list as prepared by s390_chunkify_start.
    We have decided we cannot use this list, so revert all changes
    to the current function that were done by s390_chunkify_start.  */
+
 static void
-s390_chunkify_cancel (pool_list)
-     struct constant_pool *pool_list;
+s390_chunkify_cancel (struct constant_pool *pool_list)
 {
   struct constant_pool *curr_pool = NULL;
   rtx insn;
@@ -4022,7 +4858,7 @@ s390_chunkify_cancel (pool_list)
       remove_insn (curr_pool->pool_insn);
     }
 
-  /* Remove all base/anchor register reload insns.  */
+  /* Remove all base register reload insns.  */
 
   for (insn = get_insns (); insn; )
     {
@@ -4031,8 +4867,7 @@ s390_chunkify_cancel (pool_list)
       if (GET_CODE (insn) == INSN
          && GET_CODE (PATTERN (insn)) == SET
          && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
-         && (XINT (SET_SRC (PATTERN (insn)), 1) == 210
-             || XINT (SET_SRC (PATTERN (insn)), 1) == 211))
+         && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_RELOAD_BASE)
        remove_insn (insn);
 
       insn = next_insn;
@@ -4049,91 +4884,69 @@ s390_chunkify_cancel (pool_list)
 }
 
 
-/* Index of constant pool chunk that is currently being processed.
-   Set to -1 before function output has started.  */
-int s390_pool_count = -1;
-
-/* Number of elements of current constant pool.  */
-int s390_nr_constants;
-
-/* Output main constant pool to stdio stream FILE.  */ 
+/* Output the constant pool entry EXP in mode MODE with alignment ALIGN.  */
 
 void
-s390_output_constant_pool (start_label, end_label)
-     rtx start_label;
-     rtx end_label;
+s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align)
 {
-  if (TARGET_64BIT) 
-    {
-      readonly_data_section ();
-      ASM_OUTPUT_ALIGN (asm_out_file, 3);
-      (*targetm.asm_out.internal_label) (asm_out_file, "L", 
-                                        CODE_LABEL_NUMBER (start_label));
-    } 
-  else 
-    {
-      (*targetm.asm_out.internal_label) (asm_out_file, "L",
-                                        CODE_LABEL_NUMBER (start_label));
-      ASM_OUTPUT_ALIGN (asm_out_file, 2);      
-    }
+  REAL_VALUE_TYPE r;
 
-  s390_pool_count = 0;
-  output_constant_pool (current_function_name, current_function_decl);
-  s390_pool_count = -1;
-  if (TARGET_64BIT)
-    function_section (current_function_decl);
-  else
+  switch (GET_MODE_CLASS (mode))
     {
-      ASM_OUTPUT_ALIGN (asm_out_file, 1);
-      (*targetm.asm_out.internal_label) (asm_out_file, "L", 
-                                        CODE_LABEL_NUMBER (end_label));
+    case MODE_FLOAT:
+      if (GET_CODE (exp) != CONST_DOUBLE)
+       abort ();
+
+      REAL_VALUE_FROM_CONST_DOUBLE (r, exp);
+      assemble_real (r, mode, align);
+      break;
+
+    case MODE_INT:
+      assemble_integer (exp, GET_MODE_SIZE (mode), align, 1);
+      break;
+
+    default:
+      abort ();
     }
 }
 
+
 /* Rework the prolog/epilog to avoid saving/restoring
-   registers unnecessarily.  If TEMP_REGNO is nonnegative,
-   it specifies the number of a caller-saved register used 
-   as temporary scratch register by code emitted during 
-   machine dependent reorg.  */
+   registers unnecessarily.  BASE_USED specifies whether
+   the literal pool base register needs to be saved.  */
 
 static void
-s390_optimize_prolog (temp_regno)
-     int temp_regno;
+s390_optimize_prolog (bool base_used)
 {
   int save_first, save_last, restore_first, restore_last;
   int i, j;
   rtx insn, new_insn, next_insn;
 
   /* Recompute regs_ever_live data for special registers.  */
-  regs_ever_live[BASE_REGISTER] = 0;
-  regs_ever_live[RETURN_REGNUM] = 0;
+  regs_ever_live[BASE_REGISTER] = base_used;
+  regs_ever_live[RETURN_REGNUM] = cfun->machine->save_return_addr_p;
   regs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
 
-  /* If there is (possibly) any pool entry, we need to
-     load the base register.  
-     ??? FIXME: this should be more precise.  */
-  if (get_pool_size ())
-    regs_ever_live[BASE_REGISTER] = 1;
-
-  /* In non-leaf functions, the prolog/epilog code relies 
-     on RETURN_REGNUM being saved in any case.  */
-  if (!current_function_is_leaf)
-    regs_ever_live[RETURN_REGNUM] = 1;
-
-  /* We need to save/restore the temporary register.  */
-  if (temp_regno >= 0)
-    regs_ever_live[temp_regno] = 1;
-
 
   /* Find first and last gpr to be saved.  */
-  
+
   for (i = 6; i < 16; i++)
     if (regs_ever_live[i])
-      break;
+      if (!global_regs[i]
+         || i == STACK_POINTER_REGNUM
+          || i == RETURN_REGNUM
+          || i == BASE_REGISTER
+          || (flag_pic && i == (int)PIC_OFFSET_TABLE_REGNUM))
+       break;
 
   for (j = 15; j > i; j--)
     if (regs_ever_live[j])
-      break;
+      if (!global_regs[j]
+         || j == STACK_POINTER_REGNUM
+          || j == RETURN_REGNUM
+          || j == BASE_REGISTER
+          || (flag_pic && j == (int)PIC_OFFSET_TABLE_REGNUM))
+       break;
 
   if (i == 16)
     {
@@ -4160,7 +4973,7 @@ s390_optimize_prolog (temp_regno)
   /* If all special registers are in fact used, there's nothing we
      can do, so no point in walking the insn list.  */
   if (i <= BASE_REGISTER && j >= BASE_REGISTER
-      && i <= RETURN_REGNUM && j >= RETURN_REGNUM)
+      && (TARGET_CPU_ZARCH || (i <= RETURN_REGNUM && j >= RETURN_REGNUM)))
     return;
 
 
@@ -4175,10 +4988,9 @@ s390_optimize_prolog (temp_regno)
 
       if (GET_CODE (insn) != INSN)
        continue;
-      if (GET_CODE (PATTERN (insn)) != PARALLEL)
-       continue;
 
-      if (store_multiple_operation (PATTERN (insn), VOIDmode))
+      if (GET_CODE (PATTERN (insn)) == PARALLEL
+         && store_multiple_operation (PATTERN (insn), VOIDmode))
        {
          set = XVECEXP (PATTERN (insn), 0, 0);
          first = REGNO (SET_SRC (set));
@@ -4189,9 +5001,31 @@ s390_optimize_prolog (temp_regno)
 
          if (GET_CODE (base) != REG || off < 0)
            continue;
-         if (first > BASE_REGISTER && first > RETURN_REGNUM)
+         if (first > BASE_REGISTER || last < BASE_REGISTER)
            continue;
-         if (last < BASE_REGISTER && last < RETURN_REGNUM)
+
+         if (save_first != -1)
+           {
+             new_insn = save_gprs (base, off, save_first, save_last);
+             new_insn = emit_insn_before (new_insn, insn);
+             INSN_ADDRESSES_NEW (new_insn, -1);
+           }
+
+         remove_insn (insn);
+         continue;
+       }
+
+      if (GET_CODE (PATTERN (insn)) == SET
+         && GET_CODE (SET_SRC (PATTERN (insn))) == REG
+         && REGNO (SET_SRC (PATTERN (insn))) == BASE_REGISTER
+         && GET_CODE (SET_DEST (PATTERN (insn))) == MEM)
+       {
+         set = PATTERN (insn);
+         offset = const0_rtx;
+         base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
+         off = INTVAL (offset) - BASE_REGISTER * UNITS_PER_WORD;
+
+         if (GET_CODE (base) != REG || off < 0)
            continue;
 
          if (save_first != -1)
@@ -4202,9 +5036,11 @@ s390_optimize_prolog (temp_regno)
            }
 
          remove_insn (insn);
+         continue;
        }
 
-      if (load_multiple_operation (PATTERN (insn), VOIDmode))
+      if (GET_CODE (PATTERN (insn)) == PARALLEL
+         && load_multiple_operation (PATTERN (insn), VOIDmode))
        {
          set = XVECEXP (PATTERN (insn), 0, 0);
          first = REGNO (SET_DEST (set));
@@ -4215,9 +5051,7 @@ s390_optimize_prolog (temp_regno)
 
          if (GET_CODE (base) != REG || off < 0)
            continue;
-         if (first > BASE_REGISTER && first > RETURN_REGNUM)
-           continue;
-         if (last < BASE_REGISTER && last < RETURN_REGNUM)
+         if (first > BASE_REGISTER || last < BASE_REGISTER)
            continue;
 
          if (restore_first != -1)
@@ -4228,97 +5062,80 @@ s390_optimize_prolog (temp_regno)
            }
 
          remove_insn (insn);
+         continue;
        }
-    }
-}
-
-/* Check whether any insn in the function makes use of the original
-   value of RETURN_REG (e.g. for __builtin_return_address).
-   If so, insert an insn reloading that value.
-
-   Return true if any such insn was found.  */
 
-static bool
-s390_fixup_clobbered_return_reg (return_reg)
-    rtx return_reg;
-{
-  bool replacement_done = 0;
-  rtx insn;
-
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-    {
-      rtx reg, off, new_insn;
+      if (GET_CODE (PATTERN (insn)) == SET
+         && GET_CODE (SET_DEST (PATTERN (insn))) == REG
+         && REGNO (SET_DEST (PATTERN (insn))) == BASE_REGISTER
+         && GET_CODE (SET_SRC (PATTERN (insn))) == MEM)
+       {
+         set = PATTERN (insn);
+         offset = const0_rtx;
+         base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
+         off = INTVAL (offset) - BASE_REGISTER * UNITS_PER_WORD;
 
-      if (GET_CODE (insn) != INSN)
-       continue;
-      if (!reg_referenced_p (return_reg, PATTERN (insn)))
-       continue;
-      if (GET_CODE (PATTERN (insn)) == PARALLEL
-         && store_multiple_operation (PATTERN (insn), VOIDmode))
-       continue;
+         if (GET_CODE (base) != REG || off < 0)
+           continue;
 
-      if (frame_pointer_needed)
-       reg = hard_frame_pointer_rtx;
-      else
-       reg = stack_pointer_rtx;
+         if (restore_first != -1)
+           {
+             new_insn = restore_gprs (base, off, restore_first, restore_last);
+             new_insn = emit_insn_before (new_insn, insn);
+             INSN_ADDRESSES_NEW (new_insn, -1);
+           }
 
-      off = GEN_INT (cfun->machine->frame_size + REGNO (return_reg) * UNITS_PER_WORD);
-      if (INTVAL (off) >= 4096)
-       {
-         off = force_const_mem (Pmode, off);
-         new_insn = gen_rtx_SET (Pmode, return_reg, off);
-         new_insn = emit_insn_before (new_insn, insn);
-         INSN_ADDRESSES_NEW (new_insn, -1);
-         off = return_reg;
+         remove_insn (insn);
+         continue;
        }
-
-      new_insn = gen_rtx_MEM (Pmode, gen_rtx_PLUS (Pmode, reg, off));
-      new_insn = gen_rtx_SET (Pmode, return_reg, new_insn);
-      new_insn = emit_insn_before (new_insn, insn);
-      INSN_ADDRESSES_NEW (new_insn, -1);
-
-      replacement_done = 1;
     }
-
-  return replacement_done;
 }
 
 /* Perform machine-dependent processing.  */
 
-void
-s390_machine_dependent_reorg (first)
-     rtx first ATTRIBUTE_UNUSED;
+static void
+s390_reorg (void)
 {
-  bool fixed_up_clobbered_return_reg = 0;
-  rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
-  bool temp_used = 0;
+  rtx base_reg = gen_rtx_REG (Pmode, BASE_REGISTER);
+  bool base_used = false;
+  bool pool_overflow = false;
 
   /* Make sure all splits have been performed; splits after
      machine_dependent_reorg might confuse insn length counts.  */
   split_all_insns_noflow ();
 
 
-  /* There are two problematic situations we need to correct:
+  /* In small leaf functions, try to use an unused call-clobbered
+     register as base register to avoid save/restore overhead.  */
+  if (current_function_is_leaf && !regs_ever_live[5])
+    base_reg = gen_rtx_REG (Pmode, 5);
+
+
+  /* Install the main literal pool and the associated base
+     register load insns.
+
+     In addition, there are two problematic situations we need 
+     to correct:
+
      - the literal pool might be > 4096 bytes in size, so that
        some of its elements cannot be directly accessed
+
      - a branch target might be > 64K away from the branch, so that
        it is not possible to use a PC-relative instruction.
+
      To fix those, we split the single literal pool into multiple
      pool chunks, reloading the pool base register at various
      points throughout the function to ensure it always points to
      the pool chunk the following code expects, and / or replace
      PC-relative branches by absolute branches.
+
      However, the two problems are interdependent: splitting the
      literal pool can move a branch further away from its target,
      causing the 64K limit to overflow, and on the other hand,
      replacing a PC-relative branch by an absolute branch means
      we need to put the branch target address into the literal
      pool, possibly causing it to overflow.
+
      So, we loop trying to fix up both problems until we manage
      to satisfy both conditions at the same time.  Note that the
      loop is guaranteed to terminate as every pass of the loop
@@ -4326,49 +5143,52 @@ s390_machine_dependent_reorg (first)
      in the function.  (This is not completely true as there
      might be branch-over-pool insns introduced by chunkify_start.
      Those never need to be split however.)  */
+
   for (;;)
     {
-      struct constant_pool *pool_list;
-      /* Try to chunkify the literal pool.  */
-      pool_list = s390_chunkify_start (temp_reg, &temp_used);
+      struct constant_pool *pool = NULL;
+
+      /* Collect the literal pool.  */
+      if (!pool_overflow)
+       {
+         pool = s390_mainpool_start ();
+         if (!pool)
+           pool_overflow = true;
+       }
+
+      /* If literal pool overflowed, start to chunkify it.  */
+      if (pool_overflow)
+        pool = s390_chunkify_start (base_reg);
 
       /* Split out-of-range branches.  If this has created new
         literal pool entries, cancel current chunk list and
-        recompute it.  */
-      if (s390_split_branches (temp_reg, &temp_used))
+        recompute it.  zSeries machines have large branch
+        instructions, so we never need to split a branch.  */
+      if (!TARGET_CPU_ZARCH && s390_split_branches ())
         {
-          if (pool_list)
-            s390_chunkify_cancel (pool_list);
+          if (pool_overflow)
+            s390_chunkify_cancel (pool);
+         else
+            s390_mainpool_cancel (pool);
+
           continue;
         }
 
-      /* Check whether we have clobbered a use of the return
-        register (e.g. for __builtin_return_address).  If so,
-        add insns reloading the register where necessary.  */
-      if (temp_used && !fixed_up_clobbered_return_reg
-         && s390_fixup_clobbered_return_reg (temp_reg))
-       {
-         fixed_up_clobbered_return_reg = 1;
+      /* If we made it up to here, both conditions are satisfied.
+        Finish up literal pool related changes.  */
+      if ((pool_overflow || pool->size > 0)
+          && REGNO (base_reg) == BASE_REGISTER)
+       base_used = true;
 
-         /* The fixup insns might have caused a jump to overflow.  */
-         if (pool_list)
-           s390_chunkify_cancel (pool_list);
+      if (pool_overflow)
+       s390_chunkify_finish (pool, base_reg);
+      else
+       s390_mainpool_finish (pool, base_reg);
 
-         continue;
-       }
-      /* If we made it up to here, both conditions are satisfied.
-        Finish up pool chunkification if required.  */
-      if (pool_list)
-       s390_chunkify_finish (pool_list, temp_reg);
       break;
     }
-  s390_optimize_prolog (temp_used? RETURN_REGNUM : -1);
+
+  s390_optimize_prolog (base_used);
 }
 
 
@@ -4377,32 +5197,35 @@ s390_machine_dependent_reorg (first)
    frame pointer of that frame.  */
 
 rtx
-s390_return_addr_rtx (count, frame)
-     int count;
-     rtx frame;
+s390_return_addr_rtx (int count, rtx frame)
 {
   rtx addr;
 
-  /* For the current frame, we use the initial value of RETURN_REGNUM.
-     This works both in leaf and non-leaf functions.  */
+  /* Without backchain, we fail for all but the current frame.  */
+
+  if (!TARGET_BACKCHAIN && count > 0)
+    return NULL_RTX;
+
+  /* For the current frame, we need to make sure the initial
+     value of RETURN_REGNUM is actually saved.  */
 
   if (count == 0)
-    return get_hard_reg_initial_val (Pmode, RETURN_REGNUM);
+    cfun->machine->save_return_addr_p = true;
 
-  /* For frames farther back, we read the stack slot where the
+  /* To retrieve the return address we read the stack slot where the
      corresponding RETURN_REGNUM value was saved.  */
 
   addr = plus_constant (frame, RETURN_REGNUM * UNITS_PER_WORD);
   addr = memory_address (Pmode, addr);
   return gen_rtx_MEM (Pmode, addr);
-} 
+}
 
 /* Find first call clobbered register unsused in a function.
    This could be used as base register in a leaf function
    or for holding the return address before epilogue.  */
 
 static int
-find_unused_clobbered_reg ()
+find_unused_clobbered_reg (void)
 {
   int i;
   for (i = 0; i < 6; i++)
@@ -4414,20 +5237,19 @@ find_unused_clobbered_reg ()
 /* Fill FRAME with info about frame of current function.  */
 
 static void
-s390_frame_info ()
+s390_frame_info (void)
 {
-  char gprs_ever_live[16];
   int i, j;
   HOST_WIDE_INT fsize = get_frame_size ();
 
-  if (fsize > 0x7fff0000)
+  if (!TARGET_64BIT && fsize > 0x7fff0000)
     fatal_error ("Total size of local variables exceeds architecture limit.");
 
   /* fprs 8 - 15 are caller saved for 64 Bit ABI.  */
   cfun->machine->save_fprs_p = 0;
   if (TARGET_64BIT)
-    for (i = 24; i < 32; i++) 
-      if (regs_ever_live[i])
+    for (i = 24; i < 32; i++)
+      if (regs_ever_live[i] && !global_regs[i])
        {
           cfun->machine->save_fprs_p = 1;
          break;
@@ -4436,35 +5258,49 @@ s390_frame_info ()
   cfun->machine->frame_size = fsize + cfun->machine->save_fprs_p * 64;
 
   /* Does function need to setup frame and save area.  */
-  
+
   if (! current_function_is_leaf
       || cfun->machine->frame_size > 0
-      || current_function_calls_alloca 
+      || current_function_calls_alloca
       || current_function_stdarg)
     cfun->machine->frame_size += STARTING_FRAME_OFFSET;
 
+  /* If we use the return register, we'll need to make sure
+     it is going to be saved/restored.  */
+
+  if (!current_function_is_leaf
+      || regs_ever_live[RETURN_REGNUM])
+    cfun->machine->save_return_addr_p = 1;
+
   /* Find first and last gpr to be saved.  Note that at this point,
-     we assume the return register and the base register always
-     need to be saved.  This is done because the usage of these
+     we assume the base register and -on S/390- the return register
+     always need to be saved.  This is done because the usage of these
      register might change even after the prolog was emitted.
      If it turns out later that we really don't need them, the
      prolog/epilog code is modified again.  */
 
-  for (i = 0; i < 16; i++)
-    gprs_ever_live[i] = regs_ever_live[i];
+  regs_ever_live[BASE_REGISTER] = 1;
+  if (!TARGET_CPU_ZARCH || cfun->machine->save_return_addr_p)
+    regs_ever_live[RETURN_REGNUM] = 1;
+  regs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
 
-  gprs_ever_live[BASE_REGISTER] = 1;
-  gprs_ever_live[RETURN_REGNUM] = 1;
-  gprs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
-  
   for (i = 6; i < 16; i++)
-    if (gprs_ever_live[i])
-      break;
+    if (regs_ever_live[i])
+      if (!global_regs[i]
+         || i == STACK_POINTER_REGNUM
+          || i == RETURN_REGNUM
+          || i == BASE_REGISTER
+          || (flag_pic && i == (int)PIC_OFFSET_TABLE_REGNUM))
+       break;
 
   for (j = 15; j > i; j--)
-    if (gprs_ever_live[j])
-      break;
-
+    if (regs_ever_live[j])
+      if (!global_regs[j]
+         || j == STACK_POINTER_REGNUM
+          || j == RETURN_REGNUM
+          || j == BASE_REGISTER
+          || (flag_pic && j == (int)PIC_OFFSET_TABLE_REGNUM))
+       break;
 
   /* Save / Restore from gpr i to j.  */
   cfun->machine->first_save_gpr = i;
@@ -4476,11 +5312,11 @@ s390_frame_info ()
     cfun->machine->first_save_gpr = 2;
 }
 
-/* Return offset between argument pointer and frame pointer 
+/* Return offset between argument pointer and frame pointer
    initially after prologue.  */
 
-int 
-s390_arg_frame_offset ()
+HOST_WIDE_INT
+s390_arg_frame_offset (void)
 {
   HOST_WIDE_INT fsize = get_frame_size ();
   int save_fprs_p, i;
@@ -4488,8 +5324,8 @@ s390_arg_frame_offset ()
   /* fprs 8 - 15 are caller saved for 64 Bit ABI.  */
   save_fprs_p = 0;
   if (TARGET_64BIT)
-    for (i = 24; i < 32; i++) 
-      if (regs_ever_live[i])
+    for (i = 24; i < 32; i++)
+      if (regs_ever_live[i] && !global_regs[i])
        {
           save_fprs_p = 1;
          break;
@@ -4498,23 +5334,20 @@ s390_arg_frame_offset ()
   fsize = fsize + save_fprs_p * 64;
 
   /* Does function need to setup frame and save area.  */
-  
+
   if (! current_function_is_leaf
       || fsize > 0
-      || current_function_calls_alloca 
+      || current_function_calls_alloca
       || current_function_stdarg)
     fsize += STARTING_FRAME_OFFSET;
   return fsize + STACK_POINTER_OFFSET;
 }
 
 /* Emit insn to save fpr REGNUM at offset OFFSET relative
-   to register BASE.  Return generated insn.  */ 
+   to register BASE.  Return generated insn.  */
 
 static rtx
-save_fpr (base, offset, regnum)
-     rtx base;
-     int offset;
-     int regnum;     
+save_fpr (rtx base, int offset, int regnum)
 {
   rtx addr;
   addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
@@ -4524,13 +5357,10 @@ save_fpr (base, offset, regnum)
 }
 
 /* Emit insn to restore fpr REGNUM from offset OFFSET relative
-   to register BASE.  Return generated insn.  */ 
+   to register BASE.  Return generated insn.  */
 
 static rtx
-restore_fpr (base, offset, regnum)
-     rtx base;
-     int offset;
-     int regnum;
+restore_fpr (rtx base, int offset, int regnum)
 {
   rtx addr;
   addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
@@ -4540,15 +5370,11 @@ restore_fpr (base, offset, regnum)
 }
 
 /* Generate insn to save registers FIRST to LAST into
-   the register save area located at offset OFFSET 
+   the register save area located at offset OFFSET
    relative to register BASE.  */
 
 static rtx
-save_gprs (base, offset, first, last)
-     rtx base;
-     int offset;
-     int first;
-     int last;
+save_gprs (rtx base, int offset, int first, int last)
 {
   rtx addr, insn, note;
   int i;
@@ -4579,7 +5405,7 @@ save_gprs (base, offset, first, last)
      inside the store-multiple pattern.
 
      However, we must not emit DWARF records for registers 2..5
-     if they are stored for use by variable arguments ...  
+     if they are stored for use by variable arguments ...
 
      ??? Unfortunately, it is not enough to simply not the the
      FRAME_RELATED flags for those SETs, because the first SET
@@ -4600,13 +5426,13 @@ save_gprs (base, offset, first, last)
   else if (last >= 6)
     {
       addr = plus_constant (base, offset + 6 * UNITS_PER_WORD);
-      note = gen_store_multiple (gen_rtx_MEM (Pmode, addr), 
+      note = gen_store_multiple (gen_rtx_MEM (Pmode, addr),
                                 gen_rtx_REG (Pmode, 6),
                                 GEN_INT (last - 6 + 1));
       note = PATTERN (note);
 
       REG_NOTES (insn) =
-       gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR, 
+       gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
                           note, REG_NOTES (insn));
 
       for (i = 0; i < XVECLEN (note, 0); i++)
@@ -4620,15 +5446,11 @@ save_gprs (base, offset, first, last)
 }
 
 /* Generate insn to restore registers FIRST to LAST from
-   the register save area located at offset OFFSET 
+   the register save area located at offset OFFSET
    relative to register BASE.  */
 
 static rtx
-restore_gprs (base, offset, first, last)
-     rtx base;
-     int offset;
-     int first;
-     int last;
+restore_gprs (rtx base, int offset, int first, int last)
 {
   rtx addr, insn;
 
@@ -4653,82 +5475,98 @@ restore_gprs (base, offset, first, last)
   return insn;
 }
 
+/* Emit code to load the GOT register.  If MAYBE_DEAD is true,
+   annotate generated insns with REG_MAYBE_DEAD notes.  */
+
+static GTY(()) rtx got_symbol;
+void
+s390_load_got (int maybe_dead)
+{
+  if (!got_symbol)
+    {
+      got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
+      SYMBOL_REF_FLAGS (got_symbol) = SYMBOL_FLAG_LOCAL;
+    }
+
+  if (TARGET_CPU_ZARCH)
+    {
+      rtx insn = emit_move_insn (pic_offset_table_rtx, got_symbol);
+      if (maybe_dead)
+        REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
+                                             REG_NOTES (insn));
+    }
+  else
+    {
+      rtx offset, insn;
+
+      offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, got_symbol),
+                              UNSPEC_LTREL_OFFSET);
+      offset = gen_rtx_CONST (Pmode, offset);
+      offset = force_const_mem (Pmode, offset);
+
+      insn = emit_move_insn (pic_offset_table_rtx, offset);
+      if (maybe_dead)
+       REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
+                                            REG_NOTES (insn));
+
+      offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, XEXP (offset, 0)),
+                              UNSPEC_LTREL_BASE);
+      offset = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, offset);
+
+      insn = emit_move_insn (pic_offset_table_rtx, offset);
+      if (maybe_dead)
+       REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
+                                            REG_NOTES (insn));
+    }
+}
+
 /* Expand the prologue into a bunch of separate insns.  */
 
 void
-s390_emit_prologue ()
+s390_emit_prologue (void)
 {
   rtx insn, addr;
   rtx temp_reg;
-  rtx pool_start_label, pool_end_label;
   int i;
 
   /* Compute frame_info.  */
 
   s390_frame_info ();
 
-  /* Choose best register to use for temp use within prologue.  */
-  
+  /* Choose best register to use for temp use within prologue.
+     See below for why TPF must use the register 1.  */
+
   if (!current_function_is_leaf
-      && !has_hard_reg_initial_val (Pmode, RETURN_REGNUM)
-      && get_pool_size () < S390_POOL_CHUNK_MAX / 2)
+      && !TARGET_TPF)
     temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
   else
     temp_reg = gen_rtx_REG (Pmode, 1);
 
   /* Save call saved gprs.  */
 
-  insn = save_gprs (stack_pointer_rtx, 0, 
+  insn = save_gprs (stack_pointer_rtx, 0,
                    cfun->machine->first_save_gpr, cfun->machine->last_save_gpr);
   emit_insn (insn);
 
-  /* Dump constant pool and set constant pool register.  */
+  /* Dummy insn to mark literal pool slot.  */
+
+  emit_insn (gen_main_pool ());
 
-  pool_start_label = gen_label_rtx();
-  pool_end_label = gen_label_rtx();
-  cfun->machine->literal_pool_label = pool_start_label;
-  
-  if (TARGET_64BIT)
-    insn = emit_insn (gen_literal_pool_64 (gen_rtx_REG (Pmode, BASE_REGISTER),
-                                          pool_start_label, pool_end_label));
-  else
-    insn = emit_insn (gen_literal_pool_31 (gen_rtx_REG (Pmode, BASE_REGISTER),
-                                            pool_start_label, pool_end_label));  
-  
   /* Save fprs for variable args.  */
 
   if (current_function_stdarg)
-    {
-      /* Save fpr 0 and 2.  */ 
-
-      save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 32, 16); 
-      save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 24, 17); 
-      
-      if (TARGET_64BIT)
-       {
-         /* Save fpr 4 and 6.  */
-         save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 16, 18); 
-         save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 8, 19); 
-       }
-    }
+    for (i = 16; i < (TARGET_64BIT ? 20 : 18); i++)
+      save_fpr (stack_pointer_rtx, 16*UNITS_PER_WORD + 8*(i-16), i);
 
   /* Save fprs 4 and 6 if used (31 bit ABI).  */
 
   if (!TARGET_64BIT)
-    {
-      /* Save fpr 4 and 6.  */
-      if (regs_ever_live[18])
-       {
-         insn = save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 16, 18);
-         RTX_FRAME_RELATED_P (insn) = 1;
-       }
-      if (regs_ever_live[19]) 
+    for (i = 18; i < 20; i++)
+      if (regs_ever_live[i] && !global_regs[i])
        {
-         insn = save_fpr (stack_pointer_rtx, STACK_POINTER_OFFSET - 8, 19); 
+         insn = save_fpr (stack_pointer_rtx, 16*UNITS_PER_WORD + 8*(i-16), i);
          RTX_FRAME_RELATED_P (insn) = 1;
        }
-    }
 
   /* Decrement stack pointer.  */
 
@@ -4737,21 +5575,31 @@ s390_emit_prologue ()
       rtx frame_off = GEN_INT (-cfun->machine->frame_size);
 
       /* Save incoming stack pointer into temp reg.  */
-      
+
       if (TARGET_BACKCHAIN || cfun->machine->save_fprs_p)
        {
          insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx));
        }
-      
-      /* Substract frame size from stack pointer.  */
 
-      frame_off = GEN_INT (-cfun->machine->frame_size);
-      if (!CONST_OK_FOR_LETTER_P (-cfun->machine->frame_size, 'K'))
-       frame_off = force_const_mem (Pmode, frame_off);
+      /* Subtract frame size from stack pointer.  */
+
+      if (DISP_IN_RANGE (INTVAL (frame_off)))
+       {
+         insn = gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+                             gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+                                           frame_off));
+         insn = emit_insn (insn);
+       }
+      else
+       {
+         if (!CONST_OK_FOR_CONSTRAINT_P (INTVAL (frame_off), 'K', "K"))
+           frame_off = force_const_mem (Pmode, frame_off);
+
+          insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off));
+       }
 
-      insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off));
       RTX_FRAME_RELATED_P (insn) = 1;
-      REG_NOTES (insn) = 
+      REG_NOTES (insn) =
        gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
                           gen_rtx_SET (VOIDmode, stack_pointer_rtx,
                                   gen_rtx_PLUS (Pmode, stack_pointer_rtx,
@@ -4759,40 +5607,50 @@ s390_emit_prologue ()
                           REG_NOTES (insn));
 
       /* Set backchain.  */
-      
+
       if (TARGET_BACKCHAIN)
        {
          addr = gen_rtx_MEM (Pmode, stack_pointer_rtx);
          set_mem_alias_set (addr, s390_sr_alias_set);
          insn = emit_insn (gen_move_insn (addr, temp_reg));
        }
+
+      /* If we support asynchronous exceptions (e.g. for Java),
+        we need to make sure the backchain pointer is set up
+        before any possibly trapping memory access.  */
+
+      if (TARGET_BACKCHAIN && flag_non_call_exceptions)
+       {
+         addr = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode));
+         emit_insn (gen_rtx_CLOBBER (VOIDmode, addr));
+       }
     }
 
   /* Save fprs 8 - 15 (64 bit ABI).  */
-  
+
   if (cfun->machine->save_fprs_p)
     {
       insn = emit_insn (gen_add2_insn (temp_reg, GEN_INT(-64)));
 
       for (i = 24; i < 32; i++)
-       if (regs_ever_live[i])
+       if (regs_ever_live[i] && !global_regs[i])
          {
-           rtx addr = plus_constant (stack_pointer_rtx, 
+           rtx addr = plus_constant (stack_pointer_rtx,
                                      cfun->machine->frame_size - 64 + (i-24)*8);
 
            insn = save_fpr (temp_reg, (i-24)*8, i);
            RTX_FRAME_RELATED_P (insn) = 1;
-           REG_NOTES (insn) = 
+           REG_NOTES (insn) =
              gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-               gen_rtx_SET (VOIDmode, 
+               gen_rtx_SET (VOIDmode,
                             gen_rtx_MEM (DFmode, addr),
                             gen_rtx_REG (DFmode, i)),
                REG_NOTES (insn));
          }
     }
-           
+
   /* Set frame pointer, if needed.  */
-  
+
   if (frame_pointer_needed)
     {
       insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
@@ -4800,52 +5658,70 @@ s390_emit_prologue ()
     }
 
   /* Set up got pointer, if needed.  */
-  
+
   if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
+    s390_load_got(true);
+
+  if (TARGET_TPF)
     {
-      rtx got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
-      SYMBOL_REF_FLAG (got_symbol) = 1;
+      /* Generate a BAS instruction to serve as a function
+        entry intercept to facilitate the use of tracing
+        algorithms located at the branch target.
 
-      if (TARGET_64BIT)
-       {
-         insn = emit_insn (gen_movdi (pic_offset_table_rtx,
-                                      got_symbol));             
+        This must use register 1.  */
+      rtx addr;
+      rtx unkn;
+      rtx link;
 
-          /* It can happen that the GOT pointer isn't really needed ...  */
-          REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
-                                               REG_NOTES (insn));
-       }
-      else
-       {
-          got_symbol = gen_rtx_UNSPEC (VOIDmode, 
-                                      gen_rtvec (1, got_symbol), 100);
-          got_symbol = gen_rtx_CONST (VOIDmode, got_symbol);
-         got_symbol = force_const_mem (Pmode, got_symbol);
-         insn = emit_move_insn (pic_offset_table_rtx,
-                                got_symbol);            
-          REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
-                                               REG_NOTES (insn));
-
-         insn = emit_insn (gen_add2_insn (pic_offset_table_rtx,
-                                          gen_rtx_REG (Pmode, BASE_REGISTER)));
-          REG_NOTES(insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
-                                               REG_NOTES (insn));
-       }
-    }      
+      addr = GEN_INT (0xfe0);
+      unkn = CONST0_RTX (SImode);
+      link = gen_rtx_REG (Pmode, 1);
+
+      emit_call_insn (gen_call_exp (gen_rtx_MEM (QImode, addr), unkn, link));
+
+      /* Emit a blockage here so that all code
+        lies between the profiling mechanisms.  */
+      emit_insn (gen_blockage ());
+    }
 }
 
 /* Expand the epilogue into a bunch of separate insns.  */
 
 void
-s390_emit_epilogue ()
+s390_emit_epilogue (void)
 {
   rtx frame_pointer, return_reg;
   int area_bottom, area_top, offset = 0;
   rtvec p;
+  int i;
+
+  if (TARGET_TPF)
+    {
+
+      /* Generate a BAS instruction to serve as a function
+        entry intercept to facilitate the use of tracing
+        algorithms located at the branch target.
+
+        This must use register 1.  */
+
+      rtx addr;
+      rtx unkn;
+      rtx link;
+
+      addr = GEN_INT (0xfe6);
+      unkn = CONST0_RTX (SImode);
+      link = gen_rtx_REG (Pmode, 1);
+
+      /* Emit a blockage here so that all code
+         lies between the profiling mechanisms.  */
+      emit_insn (gen_blockage ());
+
+      emit_call_insn (gen_call_exp (gen_rtx_MEM (QImode, addr), unkn, link));
+    }
 
   /* Check whether to use frame or stack pointer for restore.  */
 
-  frame_pointer = frame_pointer_needed ? 
+  frame_pointer = frame_pointer_needed ?
     hard_frame_pointer_rtx : stack_pointer_rtx;
 
   /* Compute which parts of the save area we need to access.  */
@@ -4873,31 +5749,25 @@ s390_emit_epilogue ()
     }
   else
     {
-      if (regs_ever_live[18])
-       {
-         if (area_bottom > STACK_POINTER_OFFSET - 16)
-           area_bottom = STACK_POINTER_OFFSET - 16;
-         if (area_top < STACK_POINTER_OFFSET - 8)
-           area_top = STACK_POINTER_OFFSET - 8;
-       }
-      if (regs_ever_live[19])
-       {
-         if (area_bottom > STACK_POINTER_OFFSET - 8)
-           area_bottom = STACK_POINTER_OFFSET - 8;
-         if (area_top < STACK_POINTER_OFFSET)
-           area_top = STACK_POINTER_OFFSET;
-       }
+      for (i = 18; i < 20; i++)
+       if (regs_ever_live[i] && !global_regs[i])
+         {
+           if (area_bottom > 16*UNITS_PER_WORD + 8*(i-16))
+             area_bottom = 16*UNITS_PER_WORD + 8*(i-16);
+           if (area_top < 16*UNITS_PER_WORD + 8*(i-16) + 8)
+             area_top = 16*UNITS_PER_WORD + 8*(i-16) + 8;
+         } 
     }
 
-  /* Check whether we can access the register save area.  
+  /* Check whether we can access the register save area.
      If not, increment the frame pointer as required.  */
 
   if (area_top <= area_bottom)
     {
       /* Nothing to restore.  */
     }
-  else if (cfun->machine->frame_size + area_bottom >= 0
-           && cfun->machine->frame_size + area_top <= 4096)
+  else if (DISP_IN_RANGE (cfun->machine->frame_size + area_bottom)
+           && DISP_IN_RANGE (cfun->machine->frame_size + area_top-1))
     {
       /* Area is in range.  */
       offset = cfun->machine->frame_size;
@@ -4906,38 +5776,45 @@ s390_emit_epilogue ()
     {
       rtx insn, frame_off;
 
-      offset = area_bottom < 0 ? -area_bottom : 0; 
+      offset = area_bottom < 0 ? -area_bottom : 0;
       frame_off = GEN_INT (cfun->machine->frame_size - offset);
 
-      if (!CONST_OK_FOR_LETTER_P (INTVAL (frame_off), 'K'))
-       frame_off = force_const_mem (Pmode, frame_off);
+      if (DISP_IN_RANGE (INTVAL (frame_off)))
+       {
+         insn = gen_rtx_SET (VOIDmode, frame_pointer,
+                             gen_rtx_PLUS (Pmode, frame_pointer, frame_off));
+         insn = emit_insn (insn);
+       }
+      else
+       {
+         if (!CONST_OK_FOR_CONSTRAINT_P (INTVAL (frame_off), 'K', "K"))
+           frame_off = force_const_mem (Pmode, frame_off);
 
-      insn = emit_insn (gen_add2_insn (frame_pointer, frame_off));
+         insn = emit_insn (gen_add2_insn (frame_pointer, frame_off));
+       }
     }
 
   /* Restore call saved fprs.  */
 
   if (TARGET_64BIT)
     {
-      int i;
-
       if (cfun->machine->save_fprs_p)
        for (i = 24; i < 32; i++)
          if (regs_ever_live[i] && !global_regs[i])
-           restore_fpr (frame_pointer, 
+           restore_fpr (frame_pointer,
                         offset - 64 + (i-24) * 8, i);
     }
   else
     {
-      if (regs_ever_live[18] && !global_regs[18])
-       restore_fpr (frame_pointer, offset + STACK_POINTER_OFFSET - 16, 18);
-      if (regs_ever_live[19] && !global_regs[19])
-       restore_fpr (frame_pointer, offset + STACK_POINTER_OFFSET - 8, 19);
+      for (i = 18; i < 20; i++)
+       if (regs_ever_live[i] && !global_regs[i])
+         restore_fpr (frame_pointer, 
+                      offset + 16*UNITS_PER_WORD + 8*(i-16), i);
     }
 
   /* Return register.  */
 
-  return_reg = gen_rtx_REG (Pmode, RETURN_REGNUM); 
+  return_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
 
   /* Restore call saved gprs.  */
 
@@ -4946,43 +5823,45 @@ s390_emit_epilogue ()
       rtx insn, addr;
       int i;
 
-      /* Check for global register and save them 
+      /* Check for global register and save them
         to stack location from where they get restored.  */
 
-      for (i = cfun->machine->first_restore_gpr; 
+      for (i = cfun->machine->first_restore_gpr;
           i <= cfun->machine->last_save_gpr;
           i++)
        {
-         /* These registers are special and need to be 
+         /* These registers are special and need to be
             restored in any case.  */
-         if (i == STACK_POINTER_REGNUM 
+         if (i == STACK_POINTER_REGNUM
               || i == RETURN_REGNUM
-              || i == BASE_REGISTER 
+              || i == BASE_REGISTER
               || (flag_pic && i == (int)PIC_OFFSET_TABLE_REGNUM))
            continue;
 
          if (global_regs[i])
            {
-             addr = plus_constant (frame_pointer, 
+             addr = plus_constant (frame_pointer,
                     offset + i * UNITS_PER_WORD);
              addr = gen_rtx_MEM (Pmode, addr);
              set_mem_alias_set (addr, s390_sr_alias_set);
              emit_move_insn (addr, gen_rtx_REG (Pmode, i));
-           }  
+           }
        }
 
       /* Fetch return address from stack before load multiple,
         this will do good for scheduling.  */
 
-      if (!current_function_is_leaf)
+      if (cfun->machine->save_return_addr_p
+         || (cfun->machine->first_restore_gpr < BASE_REGISTER
+             && cfun->machine->last_save_gpr > RETURN_REGNUM))
        {
          int return_regnum = find_unused_clobbered_reg();
          if (!return_regnum)
            return_regnum = 4;
          return_reg = gen_rtx_REG (Pmode, return_regnum);
-       
-         addr = plus_constant (frame_pointer, 
-                               offset + RETURN_REGNUM * UNITS_PER_WORD); 
+
+         addr = plus_constant (frame_pointer,
+                               offset + RETURN_REGNUM * UNITS_PER_WORD);
          addr = gen_rtx_MEM (Pmode, addr);
          set_mem_alias_set (addr, s390_sr_alias_set);
          emit_move_insn (return_reg, addr);
@@ -4992,10 +5871,10 @@ s390_emit_epilogue ()
         explicit in insn RTX code, we have to add a barrier here
         to prevent incorrect scheduling.  */
 
-      emit_insn (gen_blockage());      
+      emit_insn (gen_blockage());
 
-      insn = restore_gprs (frame_pointer, offset, 
-                          cfun->machine->first_restore_gpr, 
+      insn = restore_gprs (frame_pointer, offset,
+                          cfun->machine->first_restore_gpr,
                           cfun->machine->last_save_gpr);
       emit_insn (insn);
     }
@@ -5003,21 +5882,19 @@ s390_emit_epilogue ()
   /* Return to caller.  */
 
   p = rtvec_alloc (2);
-  
+
   RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode);
   RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg);
   emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
 }
 
 
-/* Return the size in bytes of a function argument of 
+/* Return the size in bytes of a function argument of
    type TYPE and/or mode MODE.  At least one of TYPE or
    MODE must be specified.  */
 
 static int
-s390_function_arg_size (mode, type)
-     enum machine_mode mode;
-     tree type;
+s390_function_arg_size (enum machine_mode mode, tree type)
 {
   if (type)
     return int_size_in_bytes (type);
@@ -5030,6 +5907,83 @@ s390_function_arg_size (mode, type)
   abort ();
 }
 
+/* Return true if a function argument of type TYPE and mode MODE
+   is to be passed in a floating-point register, if available.  */
+
+static bool
+s390_function_arg_float (enum machine_mode mode, tree type)
+{
+  int size = s390_function_arg_size (mode, type);
+  if (size > 8)
+    return false;
+
+  /* Soft-float changes the ABI: no floating-point registers are used.  */
+  if (TARGET_SOFT_FLOAT)
+    return false;
+
+  /* No type info available for some library calls ...  */
+  if (!type)
+    return mode == SFmode || mode == DFmode;
+
+  /* The ABI says that record types with a single member are treated
+     just like that member would be.  */
+  while (TREE_CODE (type) == RECORD_TYPE)
+    {
+      tree field, single = NULL_TREE;
+
+      for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+       {
+         if (TREE_CODE (field) != FIELD_DECL)
+           continue;
+
+         if (single == NULL_TREE)
+           single = TREE_TYPE (field);
+         else
+           return false;
+       }
+
+      if (single == NULL_TREE)
+       return false;
+      else
+       type = single;
+    }
+
+  return TREE_CODE (type) == REAL_TYPE;
+}
+
+/* Return true if a function argument of type TYPE and mode MODE
+   is to be passed in an integer register, or a pair of integer
+   registers, if available.  */
+
+static bool
+s390_function_arg_integer (enum machine_mode mode, tree type)
+{
+  int size = s390_function_arg_size (mode, type);
+  if (size > 8)
+    return false;
+
+  /* No type info available for some library calls ...  */
+  if (!type)
+    return GET_MODE_CLASS (mode) == MODE_INT
+          || (TARGET_SOFT_FLOAT &&  GET_MODE_CLASS (mode) == MODE_FLOAT);
+
+  /* We accept small integral (and similar) types.  */
+  if (INTEGRAL_TYPE_P (type)
+      || POINTER_TYPE_P (type) 
+      || TREE_CODE (type) == OFFSET_TYPE
+      || (TARGET_SOFT_FLOAT && TREE_CODE (type) == REAL_TYPE))
+    return true;
+
+  /* We also accept structs of size 1, 2, 4, 8 that are not
+     passed in floating-point registers.  */  
+  if (AGGREGATE_TYPE_P (type)
+      && exact_log2 (size) >= 0
+      && !s390_function_arg_float (mode, type))
+    return true;
+
+  return false;
+}
+
 /* Return 1 if a function argument of type TYPE and mode MODE
    is to be passed by reference.  The ABI specifies that only
    structures of size 1, 2, 4, or 8 bytes are passed by value,
@@ -5037,23 +5991,23 @@ s390_function_arg_size (mode, type)
    reference.  */
 
 int
-s390_function_arg_pass_by_reference (mode, type)
-     enum machine_mode mode;
-     tree type;
+s390_function_arg_pass_by_reference (enum machine_mode mode, tree type)
 {
   int size = s390_function_arg_size (mode, type);
+  if (size > 8)
+    return true;
 
   if (type)
     {
-      if (AGGREGATE_TYPE_P (type) &&
-          size != 1 && size != 2 && size != 4 && size != 8)
+      if (AGGREGATE_TYPE_P (type) && exact_log2 (size) < 0)
         return 1;
 
-      if (TREE_CODE (type) == COMPLEX_TYPE)
+      if (TREE_CODE (type) == COMPLEX_TYPE
+         || TREE_CODE (type) == VECTOR_TYPE)
         return 1;
     }
-  return 0;
 
+  return 0;
 }
 
 /* Update the data in CUM to advance over an argument of mode MODE and
@@ -5063,25 +6017,24 @@ s390_function_arg_pass_by_reference (mode, type)
    matching an ellipsis).  */
 
 void
-s390_function_arg_advance (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum;
-     enum machine_mode mode;
-     tree type;
-     int named ATTRIBUTE_UNUSED;
+s390_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                          tree type, int named ATTRIBUTE_UNUSED)
 {
-  if (! TARGET_SOFT_FLOAT && (mode == DFmode || mode == SFmode))
+  if (s390_function_arg_pass_by_reference (mode, type))
     {
-      cum->fprs++;
+      cum->gprs += 1;
     }
-  else if (s390_function_arg_pass_by_reference (mode, type))
+  else if (s390_function_arg_float (mode, type))
     {
-      cum->gprs += 1;
+      cum->fprs += 1;
     }
-  else
+  else if (s390_function_arg_integer (mode, type))
     {
       int size = s390_function_arg_size (mode, type);
       cum->gprs += ((size + UNITS_PER_WORD-1) / UNITS_PER_WORD);
     }
+  else
+    abort ();
 }
 
 /* Define where to put the arguments to a function.
@@ -5095,7 +6048,7 @@ s390_function_arg_advance (cum, mode, type, named)
    CUM is a variable of type CUMULATIVE_ARGS which gives info about
     the preceding args and about the function being called.
    NAMED is nonzero if this argument is a named parameter
-    (otherwise it is an extra parameter matching an ellipsis).  
+    (otherwise it is an extra parameter matching an ellipsis).
 
    On S/390, we use general purpose registers 2 through 6 to
    pass integer, pointer, and certain structure arguments, and
@@ -5104,23 +6057,20 @@ s390_function_arg_advance (cum, mode, type, named)
    are pushed to the stack.  */
 
 rtx
-s390_function_arg (cum, mode, type, named)
-     CUMULATIVE_ARGS *cum;
-     enum machine_mode mode;
-     tree type;
-     int named ATTRIBUTE_UNUSED;
+s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
+                  int named ATTRIBUTE_UNUSED)
 {
   if (s390_function_arg_pass_by_reference (mode, type))
       return 0;
 
-  if (! TARGET_SOFT_FLOAT && (mode == DFmode || mode == SFmode))
+  if (s390_function_arg_float (mode, type))
     {
       if (cum->fprs + 1 > (TARGET_64BIT? 4 : 2))
        return 0;
       else
-       return gen_rtx (REG, mode, cum->fprs + 16);
+       return gen_rtx_REG (mode, cum->fprs + 16);
     }
-  else
+  else if (s390_function_arg_integer (mode, type))
     {
       int size = s390_function_arg_size (mode, type);
       int n_gprs = (size + UNITS_PER_WORD-1) / UNITS_PER_WORD;
@@ -5128,8 +6078,70 @@ s390_function_arg (cum, mode, type, named)
       if (cum->gprs + n_gprs > 5)
        return 0;
       else
-       return gen_rtx (REG, mode, cum->gprs + 2);
+       return gen_rtx_REG (mode, cum->gprs + 2);
+    }
+
+  /* After the real arguments, expand_call calls us once again
+     with a void_type_node type.  Whatever we return here is
+     passed as operand 2 to the call expanders.
+
+     We don't need this feature ...  */
+  else if (type == void_type_node)
+    return const0_rtx;
+
+  abort ();
+}
+
+/* Return true if return values of type TYPE should be returned
+   in a memory buffer whose address is passed by the caller as
+   hidden first argument.  */
+
+static bool
+s390_return_in_memory (tree type, tree fundecl ATTRIBUTE_UNUSED)
+{
+  /* We accept small integral (and similar) types.  */
+  if (INTEGRAL_TYPE_P (type)
+      || POINTER_TYPE_P (type) 
+      || TREE_CODE (type) == OFFSET_TYPE
+      || TREE_CODE (type) == REAL_TYPE)
+    return int_size_in_bytes (type) > 8;
+
+  /* Aggregates and similar constructs are always returned
+     in memory.  */
+  if (AGGREGATE_TYPE_P (type)
+      || TREE_CODE (type) == COMPLEX_TYPE
+      || TREE_CODE (type) == VECTOR_TYPE)
+    return true;
+
+  /* ??? We get called on all sorts of random stuff from
+     aggregate_value_p.  We can't abort, but it's not clear
+     what's safe to return.  Pretend it's a struct I guess.  */
+  return true;
+}
+
+/* Define where to return a (scalar) value of type TYPE.
+   If TYPE is null, define where to return a (scalar)
+   value of mode MODE from a libcall.  */
+
+rtx
+s390_function_value (tree type, enum machine_mode mode)
+{
+  if (type)
+    {
+      int unsignedp = TREE_UNSIGNED (type);
+      mode = promote_mode (type, TYPE_MODE (type), &unsignedp, 1);
     }
+
+  if (GET_MODE_CLASS (mode) != MODE_INT 
+      && GET_MODE_CLASS (mode) != MODE_FLOAT)
+    abort ();
+  if (GET_MODE_SIZE (mode) > 8)
+    abort ();
+
+  if (TARGET_HARD_FLOAT && GET_MODE_CLASS (mode) == MODE_FLOAT)
+    return gen_rtx_REG (mode, 16);
+  else
+    return gen_rtx_REG (mode, 2);
 }
 
 
@@ -5143,31 +6155,30 @@ s390_function_arg (cum, mode, type, named)
             long __fpr;
             void *__overflow_arg_area;
             void *__reg_save_area;
-            
         } va_list[1];
 
    where __gpr and __fpr hold the number of general purpose
    or floating point arguments used up to now, respectively,
-   __overflow_arg_area points to the stack location of the 
+   __overflow_arg_area points to the stack location of the
    next argument passed on the stack, and __reg_save_area
    always points to the start of the register area in the
    call frame of the current function.  The function prologue
    saves all registers used for argument passing into this
    area if the function uses variable arguments.  */
 
-tree
-s390_build_va_list ()
+static tree
+s390_build_builtin_va_list (void)
 {
   tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl;
 
-  record = (*lang_hooks.types.make_type) (RECORD_TYPE);
+  record = lang_hooks.types.make_type (RECORD_TYPE);
 
   type_decl =
     build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
 
-  f_gpr = build_decl (FIELD_DECL, get_identifier ("__gpr"), 
+  f_gpr = build_decl (FIELD_DECL, get_identifier ("__gpr"),
                      long_integer_type_node);
-  f_fpr = build_decl (FIELD_DECL, get_identifier ("__fpr"), 
+  f_fpr = build_decl (FIELD_DECL, get_identifier ("__fpr"),
                      long_integer_type_node);
   f_ovf = build_decl (FIELD_DECL, get_identifier ("__overflow_arg_area"),
                      ptr_type_node);
@@ -5206,9 +6217,7 @@ s390_build_va_list ()
        (relative to the virtual arg pointer).  */
 
 void
-s390_va_start (valist, nextarg)
-     tree valist;
-     rtx nextarg ATTRIBUTE_UNUSED;
+s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
 {
   HOST_WIDE_INT n_gpr, n_fpr;
   int off;
@@ -5263,15 +6272,15 @@ s390_va_start (valist, nextarg)
   expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
 }
 
-/* Implement va_arg by updating the va_list structure 
+/* Implement va_arg by updating the va_list structure
    VALIST as required to retrieve an argument of type
-   TYPE, and returning that argument. 
-   
+   TYPE, and returning that argument.
+
    Generates code equivalent to:
-   
+
    if (integral value) {
      if (size  <= 4 && args.gpr < 5 ||
-         size  > 4 && args.gpr < 4 ) 
+         size  > 4 && args.gpr < 4 )
        ret = args.reg_save_area[args.gpr+8]
      else
        ret = *args.overflow_arg_area++;
@@ -5288,9 +6297,7 @@ s390_va_start (valist, nextarg)
    } */
 
 rtx
-s390_va_arg (valist, type)
-     tree valist;
-     tree type;
+s390_va_arg (tree valist, tree type)
 {
   tree f_gpr, f_fpr, f_ovf, f_sav;
   tree gpr, fpr, ovf, sav, reg, t, u;
@@ -5327,7 +6334,7 @@ s390_va_arg (valist, type)
       size = UNITS_PER_WORD;
       max_reg = 4;
     }
-  else if (FLOAT_TYPE_P (type) && ! TARGET_SOFT_FLOAT)
+  else if (s390_function_arg_float (TYPE_MODE (type), type))
     {
       if (TARGET_DEBUG_ARG)
        {
@@ -5357,13 +6364,9 @@ s390_va_arg (valist, type)
       reg = gpr;
       n_reg = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
       sav_ofs = 2 * UNITS_PER_WORD;
-      if (TARGET_64BIT)
-       sav_ofs += TYPE_MODE (type) == SImode ? 4 : 
-                  TYPE_MODE (type) == HImode ? 6 : 
-                  TYPE_MODE (type) == QImode ? 7 : 0;
-      else
-       sav_ofs += TYPE_MODE (type) == HImode ? 2 : 
-                  TYPE_MODE (type) == QImode ? 3 : 0;
+
+      if (size < UNITS_PER_WORD)
+       sav_ofs += UNITS_PER_WORD - size;
 
       sav_scale = UNITS_PER_WORD;
       if (n_reg > 1)
@@ -5430,10 +6433,10 @@ s390_va_arg (valist, type)
 
   emit_label (lab_over);
 
-  /* If less than max_regs a registers are retrieved out 
+  /* If less than max_regs a registers are retrieved out
      of register save area, increment.  */
 
-  u = build (PREINCREMENT_EXPR, TREE_TYPE (reg), reg, 
+  u = build (PREINCREMENT_EXPR, TREE_TYPE (reg), reg,
             build_int_2 (n_reg, 0));
   TREE_SIDE_EFFECTS (u) = 1;
   expand_expr (u, const0_rtx, VOIDmode, EXPAND_NORMAL);
@@ -5450,6 +6453,131 @@ s390_va_arg (valist, type)
 }
 
 
+/* Builtins.  */
+
+enum s390_builtin
+{
+  S390_BUILTIN_THREAD_POINTER,
+  S390_BUILTIN_SET_THREAD_POINTER,
+
+  S390_BUILTIN_max
+};
+
+static unsigned int const code_for_builtin_64[S390_BUILTIN_max] = {
+  CODE_FOR_get_tp_64,
+  CODE_FOR_set_tp_64
+};
+
+static unsigned int const code_for_builtin_31[S390_BUILTIN_max] = {
+  CODE_FOR_get_tp_31,
+  CODE_FOR_set_tp_31
+};
+
+static void
+s390_init_builtins (void)
+{
+  tree ftype;
+
+  ftype = build_function_type (ptr_type_node, void_list_node);
+  builtin_function ("__builtin_thread_pointer", ftype,
+                   S390_BUILTIN_THREAD_POINTER, BUILT_IN_MD,
+                   NULL, NULL_TREE);
+
+  ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
+  builtin_function ("__builtin_set_thread_pointer", ftype,
+                   S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD,
+                   NULL, NULL_TREE);
+}
+
+/* Expand an expression EXP that calls a built-in function,
+   with result going to TARGET if that's convenient
+   (and in mode MODE if that's convenient).
+   SUBTARGET may be used as the target for computing one of EXP's operands.
+   IGNORE is nonzero if the value is to be ignored.  */
+
+static rtx
+s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
+                    enum machine_mode mode ATTRIBUTE_UNUSED,
+                    int ignore ATTRIBUTE_UNUSED)
+{
+#define MAX_ARGS 2
+
+  unsigned int const *code_for_builtin =
+    TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31;
+
+  tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+  unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
+  tree arglist = TREE_OPERAND (exp, 1);
+  enum insn_code icode;
+  rtx op[MAX_ARGS], pat;
+  int arity;
+  bool nonvoid;
+
+  if (fcode >= S390_BUILTIN_max)
+    internal_error ("bad builtin fcode");
+  icode = code_for_builtin[fcode];
+  if (icode == 0)
+    internal_error ("bad builtin fcode");
+
+  nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node;
+
+  for (arglist = TREE_OPERAND (exp, 1), arity = 0;
+       arglist;
+       arglist = TREE_CHAIN (arglist), arity++)
+    {
+      const struct insn_operand_data *insn_op;
+
+      tree arg = TREE_VALUE (arglist);
+      if (arg == error_mark_node)
+       return NULL_RTX;
+      if (arity > MAX_ARGS)
+       return NULL_RTX;
+
+      insn_op = &insn_data[icode].operand[arity + nonvoid];
+
+      op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0);
+
+      if (!(*insn_op->predicate) (op[arity], insn_op->mode))
+       op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]);
+    }
+
+  if (nonvoid)
+    {
+      enum machine_mode tmode = insn_data[icode].operand[0].mode;
+      if (!target
+         || GET_MODE (target) != tmode
+         || !(*insn_data[icode].operand[0].predicate) (target, tmode))
+       target = gen_reg_rtx (tmode);
+    }
+
+  switch (arity)
+    {
+    case 0:
+      pat = GEN_FCN (icode) (target);
+      break;
+    case 1:
+      if (nonvoid)
+        pat = GEN_FCN (icode) (target, op[0]);
+      else
+       pat = GEN_FCN (icode) (op[0]);
+      break;
+    case 2:
+      pat = GEN_FCN (icode) (target, op[0], op[1]);
+      break;
+    default:
+      abort ();
+    }
+  if (!pat)
+    return NULL_RTX;
+  emit_insn (pat);
+
+  if (nonvoid)
+    return target;
+  else
+    return const0_rtx;
+}
+
+
 /* Output assembly code for the trampoline template to
    stdio stream FILE.
 
@@ -5457,8 +6585,7 @@ s390_va_arg (valist, type)
    gpr 0 is used to hold the static chain.  */
 
 void
-s390_trampoline_template (file)
-     FILE *file;
+s390_trampoline_template (FILE *file)
 {
   if (TARGET_64BIT)
     {
@@ -5485,18 +6612,13 @@ s390_trampoline_template (file)
    CXT is an RTX for the static chain value for the function.  */
 
 void
-s390_initialize_trampoline (addr, fnaddr, cxt)
-     rtx addr;
-     rtx fnaddr;
-     rtx cxt;
-{
-  emit_move_insn (gen_rtx 
-                 (MEM, Pmode,
-                  memory_address (Pmode, 
+s390_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt)
+{
+  emit_move_insn (gen_rtx_MEM (Pmode,
+                  memory_address (Pmode,
                   plus_constant (addr, (TARGET_64BIT ? 20 : 12) ))), cxt);
-  emit_move_insn (gen_rtx
-                 (MEM, Pmode,
-                  memory_address (Pmode, 
+  emit_move_insn (gen_rtx_MEM (Pmode,
+                  memory_address (Pmode,
                   plus_constant (addr, (TARGET_64BIT ? 28 : 16) ))), fnaddr);
 }
 
@@ -5504,16 +6626,14 @@ s390_initialize_trampoline (addr, fnaddr, cxt)
    LOW and HIGH, independent of the host word size.  */
 
 rtx
-s390_gen_rtx_const_DI (high, low)
-     int high;
-     int low;
+s390_gen_rtx_const_DI (int high, int low)
 {
 #if HOST_BITS_PER_WIDE_INT >= 64
   HOST_WIDE_INT val;
   val = (HOST_WIDE_INT)high;
   val <<= 32;
   val |= (HOST_WIDE_INT)low;
-  
+
   return GEN_INT (val);
 #else
 #if HOST_BITS_PER_WIDE_INT >= 32
@@ -5522,15 +6642,13 @@ s390_gen_rtx_const_DI (high, low)
   abort ();
 #endif
 #endif
-} 
+}
 
 /* Output assembler code to FILE to increment profiler label # LABELNO
    for profiling a function entry.  */
 
 void
-s390_function_profiler (file, labelno)
-     FILE *file;
-     int labelno;
+s390_function_profiler (FILE *file, int labelno)
 {
   rtx op[7];
 
@@ -5545,12 +6663,12 @@ s390_function_profiler (file, labelno)
 
   op[2] = gen_rtx_REG (Pmode, 1);
   op[3] = gen_rtx_SYMBOL_REF (Pmode, label);
-  SYMBOL_REF_FLAG (op[3]) = 1;
+  SYMBOL_REF_FLAGS (op[3]) = SYMBOL_FLAG_LOCAL;
 
   op[4] = gen_rtx_SYMBOL_REF (Pmode, "_mcount");
   if (flag_pic)
     {
-      op[4] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[4]), 113);
+      op[4] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[4]), UNSPEC_PLT);
       op[4] = gen_rtx_CONST (Pmode, op[4]);
     }
 
@@ -5569,7 +6687,7 @@ s390_function_profiler (file, labelno)
       output_asm_insn ("bras\t%2,%l6", op);
       output_asm_insn (".long\t%4", op);
       output_asm_insn (".long\t%3", op);
-      (*targetm.asm_out.internal_label) (file, "L", CODE_LABEL_NUMBER (op[6]));
+      targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6]));
       output_asm_insn ("l\t%0,0(%2)", op);
       output_asm_insn ("l\t%2,4(%2)", op);
       output_asm_insn ("basr\t%0,%0", op);
@@ -5582,10 +6700,10 @@ s390_function_profiler (file, labelno)
 
       output_asm_insn ("st\t%0,%1", op);
       output_asm_insn ("bras\t%2,%l6", op);
-      (*targetm.asm_out.internal_label) (file, "L", CODE_LABEL_NUMBER (op[5]));
+      targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[5]));
       output_asm_insn (".long\t%4-%l5", op);
       output_asm_insn (".long\t%3-%l5", op);
-      (*targetm.asm_out.internal_label) (file, "L", CODE_LABEL_NUMBER (op[6]));
+      targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[6]));
       output_asm_insn ("lr\t%0,%2", op);
       output_asm_insn ("a\t%0,0(%2)", op);
       output_asm_insn ("a\t%2,4(%2)", op);
@@ -5598,65 +6716,57 @@ s390_function_profiler (file, labelno)
    constants go in the function section; in 64-bit mode in .rodata.  */
 
 static void
-s390_select_rtx_section (mode, x, align)
-     enum machine_mode mode ATTRIBUTE_UNUSED;
-     rtx x ATTRIBUTE_UNUSED;
-     unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED;
+s390_select_rtx_section (enum machine_mode mode ATTRIBUTE_UNUSED,
+                        rtx x ATTRIBUTE_UNUSED,
+                        unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
 {
-  if (TARGET_64BIT)
+  if (TARGET_CPU_ZARCH)
     readonly_data_section ();
   else
     function_section (current_function_decl);
 }
 
-/* If using PIC, mark a SYMBOL_REF for a non-global symbol so that we
-   may access it directly in the GOT.  */
+/* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF
+   into its SYMBOL_REF_FLAGS.  */
 
 static void
-s390_encode_section_info (decl, first)
-     tree decl;
-     int first ATTRIBUTE_UNUSED;
+s390_encode_section_info (tree decl, rtx rtl, int first)
 {
-  if (flag_pic)
-    {
-      rtx rtl = (TREE_CODE_CLASS (TREE_CODE (decl)) != 'd'
-                ? TREE_CST_RTL (decl) : DECL_RTL (decl));
+  default_encode_section_info (decl, rtl, first);
 
-      if (GET_CODE (rtl) == MEM)
-       {
-         SYMBOL_REF_FLAG (XEXP (rtl, 0))
-           = (TREE_CODE_CLASS (TREE_CODE (decl)) != 'd'
-              || ! TREE_PUBLIC (decl));
-       }
-    }
+  /* If a variable has a forced alignment to < 2 bytes, mark it with
+     SYMBOL_FLAG_ALIGN1 to prevent it from being used as LARL operand.  */
+  if (TREE_CODE (decl) == VAR_DECL
+      && DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) < 16)
+    SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_ALIGN1;
 }
 
 /* Output thunk to FILE that implements a C++ virtual function call (with
-   multiple inheritance) to FUNCTION.  The thunk adjusts the this pointer 
+   multiple inheritance) to FUNCTION.  The thunk adjusts the this pointer
    by DELTA, and unless VCALL_OFFSET is zero, applies an additional adjustment
    stored at VCALL_OFFSET in the vtable whose address is located at offset 0
    relative to the resulting this pointer.  */
 
 static void
-s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
-     FILE *file;
-     tree thunk ATTRIBUTE_UNUSED;
-     HOST_WIDE_INT delta;
-     HOST_WIDE_INT vcall_offset;
-     tree function;
+s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
+                     HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
+                     tree function)
 {
-  rtx op[9];
+  rtx op[10];
+  int nonlocal = 0;
 
   /* Operand 0 is the target function.  */
   op[0] = XEXP (DECL_RTL (function), 0);
-  if (flag_pic && !SYMBOL_REF_FLAG (op[0]))
+  if (flag_pic && !SYMBOL_REF_LOCAL_P (op[0]))
     {
-      op[0] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[0]), 113);
+      nonlocal = 1;
+      op[0] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[0]),
+                             TARGET_64BIT ? UNSPEC_PLT : UNSPEC_GOT);
       op[0] = gen_rtx_CONST (Pmode, op[0]);
     }
 
   /* Operand 1 is the 'this' pointer.  */
-  if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function))))
+  if (aggregate_value_p (TREE_TYPE (TREE_TYPE (function)), function))
     op[1] = gen_rtx_REG (Pmode, 3);
   else
     op[1] = gen_rtx_REG (Pmode, 2);
@@ -5676,12 +6786,17 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
   op[7] = NULL_RTX;
   op[8] = NULL_RTX;
 
+  /* Operand 9 can be used for temporary register.  */
+  op[9] = NULL_RTX;
+
   /* Generate code.  */
   if (TARGET_64BIT)
     {
       /* Setup literal pool pointer if required.  */
-      if (!CONST_OK_FOR_LETTER_P (delta, 'K')
-         || !CONST_OK_FOR_LETTER_P (vcall_offset, 'K'))
+      if ((!DISP_IN_RANGE (delta)
+          && !CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K"))
+         || (!DISP_IN_RANGE (vcall_offset)
+             && !CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K")))
        {
          op[5] = gen_label_rtx ();
          output_asm_insn ("larl\t%4,%5", op);
@@ -5690,9 +6805,11 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
       /* Add DELTA to this pointer.  */
       if (delta)
        {
-         if (CONST_OK_FOR_LETTER_P (delta, 'J'))
+         if (CONST_OK_FOR_CONSTRAINT_P (delta, 'J', "J"))
            output_asm_insn ("la\t%1,%2(%1)", op);
-         else if (CONST_OK_FOR_LETTER_P (delta, 'K'))
+         else if (DISP_IN_RANGE (delta))
+           output_asm_insn ("lay\t%1,%2(%1)", op);
+         else if (CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K"))
            output_asm_insn ("aghi\t%1,%2", op);
          else
            {
@@ -5704,12 +6821,12 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
       /* Perform vcall adjustment.  */
       if (vcall_offset)
        {
-         if (CONST_OK_FOR_LETTER_P (vcall_offset, 'J'))
+         if (DISP_IN_RANGE (vcall_offset))
            {
              output_asm_insn ("lg\t%4,0(%1)", op);
              output_asm_insn ("ag\t%1,%3(%4)", op);
            }
-         else if (CONST_OK_FOR_LETTER_P (vcall_offset, 'K'))
+         else if (CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K"))
            {
              output_asm_insn ("lghi\t%4,%3", op);
              output_asm_insn ("ag\t%4,0(%1)", op);
@@ -5723,7 +6840,7 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
              output_asm_insn ("ag\t%1,0(%4)", op);
            }
        }
-        
+
       /* Jump to target.  */
       output_asm_insn ("jg\t%0", op);
 
@@ -5731,19 +6848,19 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
       if (op[5])
        {
          output_asm_insn (".align\t4", op);
-         (*targetm.asm_out.internal_label) (file, "L",
-                                            CODE_LABEL_NUMBER (op[5]));
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[5]));
        }
       if (op[6])
        {
-         (*targetm.asm_out.internal_label) (file, "L",
-                                            CODE_LABEL_NUMBER (op[6]));
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[6]));
          output_asm_insn (".long\t%2", op);
        }
       if (op[7])
        {
-         (*targetm.asm_out.internal_label) (file, "L",
-                                            CODE_LABEL_NUMBER (op[7]));
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[7]));
          output_asm_insn (".long\t%3", op);
        }
     }
@@ -5751,21 +6868,25 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
     {
       /* Setup base pointer if required.  */
       if (!vcall_offset
-         || !CONST_OK_FOR_LETTER_P (delta, 'K')
-         || !CONST_OK_FOR_LETTER_P (vcall_offset, 'K'))
+         || (!DISP_IN_RANGE (delta)
+              && !CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K"))
+         || (!DISP_IN_RANGE (delta)
+              && !CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K")))
        {
          op[5] = gen_label_rtx ();
          output_asm_insn ("basr\t%4,0", op);
-         (*targetm.asm_out.internal_label) (file, "L",
-                                            CODE_LABEL_NUMBER (op[5]));
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[5]));
        }
 
       /* Add DELTA to this pointer.  */
       if (delta)
        {
-         if (CONST_OK_FOR_LETTER_P (delta, 'J'))
+         if (CONST_OK_FOR_CONSTRAINT_P (delta, 'J', "J"))
            output_asm_insn ("la\t%1,%2(%1)", op);
-         else if (CONST_OK_FOR_LETTER_P (delta, 'K'))
+         else if (DISP_IN_RANGE (delta))
+           output_asm_insn ("lay\t%1,%2(%1)", op);
+         else if (CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K"))
            output_asm_insn ("ahi\t%1,%2", op);
          else
            {
@@ -5777,12 +6898,17 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
       /* Perform vcall adjustment.  */
       if (vcall_offset)
         {
-         if (CONST_OK_FOR_LETTER_P (vcall_offset, 'J'))
+         if (CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'J', "J"))
            {
              output_asm_insn ("lg\t%4,0(%1)", op);
              output_asm_insn ("a\t%1,%3(%4)", op);
            }
-         else if (CONST_OK_FOR_LETTER_P (vcall_offset, 'K'))
+         else if (DISP_IN_RANGE (vcall_offset))
+           {
+             output_asm_insn ("lg\t%4,0(%1)", op);
+             output_asm_insn ("ay\t%1,%3(%4)", op);
+           }
+         else if (CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K"))
            {
              output_asm_insn ("lhi\t%4,%3", op);
              output_asm_insn ("a\t%4,0(%1)", op);
@@ -5800,21 +6926,46 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
             Re-setup the base pointer (with a different base).  */
          op[5] = gen_label_rtx ();
          output_asm_insn ("basr\t%4,0", op);
-         (*targetm.asm_out.internal_label) (file, "L",
-                                            CODE_LABEL_NUMBER (op[5]));
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[5]));
        }
 
       /* Jump to target.  */
       op[8] = gen_label_rtx ();
+
       if (!flag_pic)
        output_asm_insn ("l\t%4,%8-%5(%4)", op);
-      else
+      else if (!nonlocal)
        output_asm_insn ("a\t%4,%8-%5(%4)", op);
+      /* We cannot call through .plt, since .plt requires %r12 loaded.  */
+      else if (flag_pic == 1)
+       {
+         output_asm_insn ("a\t%4,%8-%5(%4)", op);
+         output_asm_insn ("l\t%4,%0(%4)", op);
+       }
+      else if (flag_pic == 2)
+       {
+         op[9] = gen_rtx_REG (Pmode, 0);
+         output_asm_insn ("l\t%9,%8-4-%5(%4)", op);
+         output_asm_insn ("a\t%4,%8-%5(%4)", op);
+         output_asm_insn ("ar\t%4,%9", op);
+         output_asm_insn ("l\t%4,0(%4)", op);
+       }
+
       output_asm_insn ("br\t%4", op);
 
       /* Output literal pool.  */
       output_asm_insn (".align\t4", op);
-      (*targetm.asm_out.internal_label) (file, "L", CODE_LABEL_NUMBER (op[8]));
+
+      if (nonlocal && flag_pic == 2)
+       output_asm_insn (".long\t%0", op);
+      if (nonlocal)
+       {
+         op[0] = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
+         SYMBOL_REF_FLAGS (op[0]) = SYMBOL_FLAG_LOCAL;
+       }
+
+      targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[8]));
       if (!flag_pic)
        output_asm_insn (".long\t%0", op);
       else
@@ -5822,23 +6973,29 @@ s390_output_mi_thunk (file, thunk, delta, vcall_offset, function)
 
       if (op[6])
        {
-         (*targetm.asm_out.internal_label) (file, "L",
-                                            CODE_LABEL_NUMBER (op[6]));
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[6]));
          output_asm_insn (".long\t%2", op);
        }
       if (op[7])
        {
-         (*targetm.asm_out.internal_label) (file, "L",
-                                            CODE_LABEL_NUMBER (op[7]));
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[7]));
          output_asm_insn (".long\t%3", op);
        }
     }
 }
 
+bool
+s390_valid_pointer_mode (enum machine_mode mode)
+{
+  return (mode == SImode || (TARGET_64BIT && mode == DImode));
+}
+
 /* How to allocate a 'struct machine_function'.  */
 
 static struct machine_function *
-s390_init_machine_status ()
+s390_init_machine_status (void)
 {
   return ggc_alloc_cleared (sizeof (struct machine_function));
 }