OSDN Git Service

2005-12-02 Richard Guenther <rguenther@suse.de>
[pf3gnuchains/gcc-fork.git] / gcc / config / s390 / s390.c
index 41faff3..d9dc288 100644 (file)
@@ -18,8 +18,8 @@ for more details.
 
 You should have received a copy of the GNU General Public License
 along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 59 Temple Place - Suite 330, Boston, MA
-02111-1307, USA.  */
+Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
+02110-1301, USA.  */
 
 #include "config.h"
 #include "system.h"
@@ -52,39 +52,6 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "optabs.h"
 #include "tree-gimple.h"
 
-/* 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_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_priority (rtx, int);
-static int s390_issue_rate (void);
-static int s390_first_cycle_multipass_dfa_lookahead (void);
-static bool s390_cannot_copy_insn_p (rtx);
-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);
-static tree s390_gimplify_va_arg (tree, tree, tree *, tree *);
-static bool s390_function_ok_for_sibcall (tree, tree);
-static bool s390_call_saved_register_used (tree);
-static bool s390_pass_by_reference (CUMULATIVE_ARGS *, enum machine_mode mode,
-                                   tree, bool);
-static bool s390_fixed_condition_code_regs (unsigned int *, unsigned int *);
-static enum machine_mode s390_cc_modes_compatible (enum machine_mode,
-                                                  enum machine_mode);
-
 
 /* Define the specific costs for a given cpu.  */
 
@@ -186,98 +153,47 @@ struct processor_costs z990_cost =
   COSTS_N_INSNS (31),    /* DSGR */
 };
 
-
-#undef  TARGET_ASM_ALIGNED_HI_OP
-#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
-#undef  TARGET_ASM_ALIGNED_DI_OP
-#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
-#undef  TARGET_ASM_INTEGER
-#define TARGET_ASM_INTEGER s390_assemble_integer
-
-#undef  TARGET_ASM_OPEN_PAREN
-#define TARGET_ASM_OPEN_PAREN ""
-
-#undef  TARGET_ASM_CLOSE_PAREN
-#define TARGET_ASM_CLOSE_PAREN ""
-
-#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_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_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
-#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD s390_first_cycle_multipass_dfa_lookahead
-
-#undef TARGET_CANNOT_COPY_INSN_P
-#define TARGET_CANNOT_COPY_INSN_P s390_cannot_copy_insn_p
-#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_GIMPLIFY_VA_ARG_EXPR
-#define TARGET_GIMPLIFY_VA_ARG_EXPR s390_gimplify_va_arg
-
-#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
-#undef TARGET_PASS_BY_REFERENCE
-#define TARGET_PASS_BY_REFERENCE s390_pass_by_reference
-
-#undef TARGET_FUNCTION_OK_FOR_SIBCALL
-#define TARGET_FUNCTION_OK_FOR_SIBCALL s390_function_ok_for_sibcall
-
-#undef TARGET_FIXED_CONDITION_CODE_REGS
-#define TARGET_FIXED_CONDITION_CODE_REGS s390_fixed_condition_code_regs
-
-#undef TARGET_CC_MODES_COMPATIBLE
-#define TARGET_CC_MODES_COMPATIBLE s390_cc_modes_compatible
-
-struct gcc_target targetm = TARGET_INITIALIZER;
+static const
+struct processor_costs z9_109_cost = 
+{
+  COSTS_N_INSNS (4),     /* M     */
+  COSTS_N_INSNS (2),     /* MGHI  */
+  COSTS_N_INSNS (2),     /* MH    */
+  COSTS_N_INSNS (2),     /* MHI   */
+  COSTS_N_INSNS (4),     /* ML    */
+  COSTS_N_INSNS (4),     /* MR    */
+  COSTS_N_INSNS (5),     /* MS    */
+  COSTS_N_INSNS (6),     /* MSG   */
+  COSTS_N_INSNS (4),     /* MSGF  */
+  COSTS_N_INSNS (4),     /* MSGFR */
+  COSTS_N_INSNS (4),     /* MSGR  */
+  COSTS_N_INSNS (4),     /* MSR   */
+  COSTS_N_INSNS (1),     /* multiplication in DFmode */
+  COSTS_N_INSNS (66),    /* SQDBR */
+  COSTS_N_INSNS (38),    /* SQEBR */
+  COSTS_N_INSNS (1),     /* MADBR */
+  COSTS_N_INSNS (1),     /* MAEBR */
+  COSTS_N_INSNS (40),    /* DDBR */
+  COSTS_N_INSNS (37),    /* DDR  */
+  COSTS_N_INSNS (26),    /* DDBR */
+  COSTS_N_INSNS (28),    /* DER  */
+  COSTS_N_INSNS (30),    /* DLGR */
+  COSTS_N_INSNS (23),    /* DLR */
+  COSTS_N_INSNS (23),    /* DR */
+  COSTS_N_INSNS (24),    /* DSGFR */
+  COSTS_N_INSNS (24),    /* DSGR */
+};
 
 extern int reload_completed;
 
-/* The alias set for prologue/epilogue register save/restore.  */
-static int s390_sr_alias_set = 0;
-
 /* Save information from a "cmpxx" operation until the branch or scc is
    emitted.  */
 rtx s390_compare_op0, s390_compare_op1;
 
+/* Save the result of a compare_and_swap  until the branch or scc is
+   emitted.  */
+rtx s390_compare_emitted = NULL_RTX;
+
 /* Structure used to hold the components of a S/390 memory
    address.  A legitimate address on S/390 is of the general
    form
@@ -292,27 +208,17 @@ struct s390_address
   rtx base;
   rtx indx;
   rtx disp;
-  int pointer;
+  bool pointer;
 };
 
 /* Which cpu are we tuning for.  */
-enum processor_type s390_tune;
+enum processor_type s390_tune = PROCESSOR_max;
 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> */
-
-const char *s390_warn_framesize_string;
-const char *s390_warn_dynamicstack_string;
-const char *s390_stack_size_string;
-const char *s390_stack_guard_string;
-
 HOST_WIDE_INT s390_warn_framesize = 0;
-bool s390_warn_dynamicstack_p = 0;
 HOST_WIDE_INT s390_stack_size = 0;
 HOST_WIDE_INT s390_stack_guard = 0;
 
@@ -327,7 +233,12 @@ struct s390_frame_layout GTY (())
   HOST_WIDE_INT f4_offset;
   HOST_WIDE_INT f8_offset;
   HOST_WIDE_INT backchain_offset;
-  
+
+  /* Number of first and last gpr where slots in the register
+     save area are reserved for.  */
+  int first_save_gpr_slot;
+  int last_save_gpr_slot;
+
   /* Number of first and last gpr to be saved, restored.  */
   int first_save_gpr;
   int first_restore_gpr;
@@ -345,7 +256,10 @@ struct s390_frame_layout GTY (())
   /* Number of floating point registers f8-f15 which must be saved.  */
   int high_fprs;
 
-  /* Set if return address needs to be saved.  */
+  /* Set if return address needs to be saved.
+     This flag is set by s390_return_addr_rtx if it could not use
+     the initial value of r14 and therefore depends on r14 saved
+     to the stack.  */
   bool save_return_addr_p;
 
   /* Size of stack frame.  */
@@ -366,69 +280,90 @@ struct machine_function GTY(())
 
   /* Some local-dynamic TLS symbol name.  */
   const char *some_ld_name;
+
+  bool has_landing_pad_p;
 };
 
 /* Few accessor macros for struct cfun->machine->s390_frame_layout.  */
 
 #define cfun_frame_layout (cfun->machine->frame_layout)
 #define cfun_save_high_fprs_p (!!cfun_frame_layout.high_fprs)
-#define cfun_gprs_save_area_size ((cfun_frame_layout.last_save_gpr -           \
-  cfun_frame_layout.first_save_gpr + 1) * UNITS_PER_WORD)
+#define cfun_gprs_save_area_size ((cfun_frame_layout.last_save_gpr_slot -           \
+  cfun_frame_layout.first_save_gpr_slot + 1) * UNITS_PER_WORD)
 #define cfun_set_fpr_bit(BITNUM) (cfun->machine->frame_layout.fpr_bitmap |=    \
   (1 << (BITNUM)))
 #define cfun_fpr_bit_p(BITNUM) (!!(cfun->machine->frame_layout.fpr_bitmap &    \
   (1 << (BITNUM))))
 
-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 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 annotate_constant_pool_refs (rtx *x);
-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 *);
-static void s390_optimize_prologue (void);
-static int find_unused_clobbered_reg (void);
-static void s390_frame_area (int *, int *);
-static void s390_register_info (int []);
-static void s390_frame_info (void);
-static void s390_init_frame_layout (void);
-static void s390_update_frame_layout (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))
+/* Number of GPRs and FPRs used for argument passing.  */
+#define GP_ARG_NUM_REG 5
+#define FP_ARG_NUM_REG (TARGET_64BIT? 4 : 2)
+
+/* A couple of shortcuts.  */
+#define CONST_OK_FOR_J(x) \
+       CONST_OK_FOR_CONSTRAINT_P((x), 'J', "J")
+#define CONST_OK_FOR_K(x) \
+       CONST_OK_FOR_CONSTRAINT_P((x), 'K', "K")
+#define CONST_OK_FOR_Os(x) \
+        CONST_OK_FOR_CONSTRAINT_P((x), 'O', "Os")
+#define CONST_OK_FOR_Op(x) \
+        CONST_OK_FOR_CONSTRAINT_P((x), 'O', "Op")
+#define CONST_OK_FOR_On(x) \
+        CONST_OK_FOR_CONSTRAINT_P((x), 'O', "On")
+
+/* Set the has_landing_pad_p flag in struct machine_function to VALUE.  */
+
+void
+s390_set_has_landing_pad_p (bool value)
+{
+  cfun->machine->has_landing_pad_p = value;
+}
+
+/* If two condition code modes are compatible, return a condition code
+   mode which is compatible with both.  Otherwise, return
+   VOIDmode.  */
+
+static enum machine_mode
+s390_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2)
+{
+  if (m1 == m2)
+    return m1;
+
+  switch (m1)
+    {
+    case CCZmode:
+      if (m2 == CCUmode || m2 == CCTmode || m2 == CCZ1mode
+         || m2 == CCSmode || m2 == CCSRmode || m2 == CCURmode)
+        return m2;
+      return VOIDmode;
+
+    case CCSmode:
+    case CCUmode:
+    case CCTmode:
+    case CCSRmode:
+    case CCURmode:
+    case CCZ1mode:
+      if (m2 == CCZmode)
+       return m1;
+      
+      return VOIDmode;
+
+    default:
+      return VOIDmode;
+    }
+  return VOIDmode;
+}
 
 /* Return true if SET either doesn't set the CC register, or else
    the source and destination have matching CC modes and that
    CC mode is at least as constrained as REQ_MODE.  */
 
-static int
+static bool
 s390_match_ccmode_set (rtx set, enum machine_mode req_mode)
 {
   enum machine_mode set_mode;
 
-  if (GET_CODE (set) != SET)
-    abort ();
+  gcc_assert (GET_CODE (set) == SET);
 
   if (GET_CODE (SET_DEST (set)) != REG || !CC_REGNO_P (REGNO (SET_DEST (set))))
     return 1;
@@ -464,7 +399,7 @@ s390_match_ccmode_set (rtx set, enum machine_mode req_mode)
       break;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 
   return (GET_MODE (SET_SRC (set)) == set_mode);
@@ -475,14 +410,14 @@ s390_match_ccmode_set (rtx set, enum machine_mode req_mode)
    CC mode is at least as constrained as REQ_MODE.
    If REQ_MODE is VOIDmode, always return false.  */
 
-int
+bool
 s390_match_ccmode (rtx insn, enum machine_mode req_mode)
 {
   int i;
 
   /* s390_tm_ccmode returns VOIDmode to indicate failure.  */
   if (req_mode == VOIDmode)
-    return 0;
+    return false;
 
   if (GET_CODE (PATTERN (insn)) == SET)
     return s390_match_ccmode_set (PATTERN (insn), req_mode);
@@ -493,10 +428,10 @@ s390_match_ccmode (rtx insn, enum machine_mode req_mode)
           rtx set = XVECEXP (PATTERN (insn), 0, i);
           if (GET_CODE (set) == SET)
             if (!s390_match_ccmode_set (set, req_mode))
-              return 0;
+              return false;
         }
 
-  return 1;
+  return true;
 }
 
 /* If a test-under-mask instruction can be used to implement
@@ -507,7 +442,7 @@ s390_match_ccmode (rtx insn, enum machine_mode req_mode)
    if the instruction cannot (TM).  */
 
 enum machine_mode
-s390_tm_ccmode (rtx op1, rtx op2, int mixed)
+s390_tm_ccmode (rtx op1, rtx op2, bool mixed)
 {
   int bit0, bit1;
 
@@ -555,7 +490,7 @@ s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
            && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
          return CCAPmode;
        if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT
-           && CONST_OK_FOR_CONSTRAINT_P (INTVAL (XEXP (op0, 1)), 'K', "K"))
+           && CONST_OK_FOR_K (INTVAL (XEXP (op0, 1))))
          return CCAPmode;
        if ((GET_CODE (op0) == PLUS || GET_CODE (op0) == MINUS
             || GET_CODE (op1) == NEG)
@@ -600,11 +535,11 @@ s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
 
        /* If constants are involved in an add instruction it is possible to use
           the resulting cc for comparisons with zero. Knowing the sign of the
-          constant the overflow behaviour gets predictable. e.g.:
+          constant the overflow behavior gets predictable. e.g.:
             int a, b; if ((b = a + c) > 0)  
           with c as a constant value: c < 0 -> CCAN and c >= 0 -> CCAP  */
        if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT
-           && CONST_OK_FOR_CONSTRAINT_P (INTVAL (XEXP (op0, 1)), 'K', "K"))
+           && CONST_OK_FOR_K (INTVAL (XEXP (op0, 1))))
          {
            if (INTVAL (XEXP((op0), 1)) < 0)
              return CCANmode;
@@ -648,7 +583,7 @@ s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
        return CCUmode;
 
       default:
-       abort ();
+       gcc_unreachable ();
     }
 }
 
@@ -759,6 +694,30 @@ s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1)
          *code = new_code;
        }
     }
+
+  /* Simplify cascaded EQ, NE with const0_rtx.  */
+  if ((*code == NE || *code == EQ)
+      && (GET_CODE (*op0) == EQ || GET_CODE (*op0) == NE)
+      && GET_MODE (*op0) == SImode
+      && GET_MODE (XEXP (*op0, 0)) == CCZ1mode
+      && REG_P (XEXP (*op0, 0))
+      && XEXP (*op0, 1) == const0_rtx
+      && *op1 == const0_rtx)
+    {
+      if ((*code == EQ && GET_CODE (*op0) == NE)
+          || (*code == NE && GET_CODE (*op0) == EQ))
+       *code = EQ;
+      else
+       *code = NE;
+      *op0 = XEXP (*op0, 0);
+    }
+
+  /* Prefer register over memory as first operand.  */
+  if (MEM_P (*op0) && REG_P (*op1))
+    {
+      rtx tem = *op0; *op0 = *op1; *op1 = tem;
+      *code = swap_condition (*code);
+    }
 }
 
 /* Emit a compare instruction suitable to implement the comparison
@@ -769,10 +728,23 @@ rtx
 s390_emit_compare (enum rtx_code code, rtx op0, rtx op1)
 {
   enum machine_mode mode = s390_select_ccmode (code, op0, op1);
-  rtx cc = gen_rtx_REG (mode, CC_REGNUM);
-
-  emit_insn (gen_rtx_SET (VOIDmode, cc, gen_rtx_COMPARE (mode, op0, op1)));
-  return gen_rtx_fmt_ee (code, VOIDmode, cc, const0_rtx);
+  rtx ret = NULL_RTX;
+
+  /* Do not output a redundant compare instruction if a compare_and_swap
+     pattern already computed the result and the machine modes are compatible.  */
+  if (s390_compare_emitted 
+      && (s390_cc_modes_compatible (GET_MODE (s390_compare_emitted), mode)
+         == GET_MODE (s390_compare_emitted)))
+    ret = gen_rtx_fmt_ee (code, VOIDmode, s390_compare_emitted, const0_rtx); 
+  else
+    {
+      rtx cc = gen_rtx_REG (mode, CC_REGNUM);
+      
+      emit_insn (gen_rtx_SET (VOIDmode, cc, gen_rtx_COMPARE (mode, op0, op1)));
+      ret = gen_rtx_fmt_ee (code, VOIDmode, cc, const0_rtx); 
+    }
+  s390_compare_emitted = NULL_RTX;
+  return ret;
 }
 
 /* Emit a jump instruction to TARGET.  If COND is NULL_RTX, emit an
@@ -791,126 +763,10 @@ s390_emit_jump (rtx target, rtx cond)
   emit_jump_insn (insn);
 }
 
-/* Return nonzero if OP is a valid comparison operator
-   for a branch condition in mode MODE.  */
-
-int
-s390_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;
-
-  return s390_branch_condition_mask (op) >= 0;
-}
-
-/* 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;
-
-  while (GET_CODE (op) == ZERO_EXTEND || GET_CODE (op) == SIGN_EXTEND)
-    op = XEXP (op, 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 CCL3mode:
-      return GET_CODE (op) == GEU;
-
-    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;
-
-  while (GET_CODE (op) == ZERO_EXTEND || GET_CODE (op) == SIGN_EXTEND)
-    op = XEXP (op, 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 CCL3mode:
-      return GET_CODE (op) == LTU;
-
-    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.  Return -1 for invalid comparisons.  */
 
-static int
+int
 s390_branch_condition_mask (rtx code)
 {
   const int CC0 = 1 << 3;
@@ -918,14 +774,14 @@ s390_branch_condition_mask (rtx code)
   const int CC2 = 1 << 1;
   const int CC3 = 1 << 0;
 
-  if (GET_CODE (XEXP (code, 0)) != REG
-      || REGNO (XEXP (code, 0)) != CC_REGNUM
-      || XEXP (code, 1) != const0_rtx)
-    abort ();
+  gcc_assert (GET_CODE (XEXP (code, 0)) == REG);
+  gcc_assert (REGNO (XEXP (code, 0)) == CC_REGNUM);
+  gcc_assert (XEXP (code, 1) == const0_rtx);
 
   switch (GET_MODE (XEXP (code, 0)))
     {
     case CCZmode:
+    case CCZ1mode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC0;
@@ -1120,8 +976,7 @@ s390_branch_condition_mnemonic (rtx code, int inv)
   if (inv)
     mask ^= 15;
 
-  if (mask < 1 || mask > 14)
-    abort ();
+  gcc_assert (mask >= 1 && mask <= 14);
 
   return mnemonic[mask];
 }
@@ -1137,7 +992,8 @@ s390_extract_part (rtx op, enum machine_mode mode, int def)
   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;
+  unsigned HOST_WIDE_INT part_mask
+    = ((unsigned HOST_WIDE_INT)1 << part_bits) - 1;
   int i;
 
   for (i = 0; i < max_parts; i++)
@@ -1151,7 +1007,7 @@ s390_extract_part (rtx op, enum machine_mode mode, int def)
        return value & part_mask;
     }
 
-  abort ();
+  gcc_unreachable ();
 }
 
 /* If OP is an integer constant of mode MODE with exactly one
@@ -1166,7 +1022,8 @@ s390_single_part (rtx op,
 {
   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;
+  unsigned HOST_WIDE_INT part_mask
+    = ((unsigned HOST_WIDE_INT)1 << GET_MODE_BITSIZE (part_mode)) - 1;
   int i, part = -1;
 
   if (GET_CODE (op) != CONST_INT)
@@ -1222,23 +1079,65 @@ s390_split_ok_p (rtx dst, rtx src, enum machine_mode mode, int first_subword)
   return true;
 }
 
-/* Check whether the address of memory reference MEM2 equals exactly
-   the address of memory reference MEM1 plus DELTA.  Return true if
-   we can prove this to be the case, false otherwise.  */
+/* Return true if it can be proven that [MEM1, MEM1 + SIZE]
+   and [MEM2, MEM2 + SIZE] do overlap and false
+   otherwise.  */
 
 bool
-s390_offset_p (rtx mem1, rtx mem2, rtx delta)
+s390_overlap_p (rtx mem1, rtx mem2, HOST_WIDE_INT size)
 {
   rtx addr1, addr2, addr_delta;
+  HOST_WIDE_INT delta;
 
   if (GET_CODE (mem1) != MEM || GET_CODE (mem2) != MEM)
+    return true;
+
+  if (size == 0)
     return false;
 
   addr1 = XEXP (mem1, 0);
   addr2 = XEXP (mem2, 0);
 
   addr_delta = simplify_binary_operation (MINUS, Pmode, addr2, addr1);
-  if (!addr_delta || !rtx_equal_p (addr_delta, delta))
+
+  /* This overlapping check is used by peepholes merging memory block operations.
+     Overlapping operations would otherwise be recognized by the S/390 hardware
+     and would fall back to a slower implementation. Allowing overlapping 
+     operations would lead to slow code but not to wrong code. Therefore we are
+     somewhat optimistic if we cannot prove that the memory blocks are 
+     overlapping.
+     That's why we return false here although this may accept operations on
+     overlapping memory areas.  */
+  if (!addr_delta || GET_CODE (addr_delta) != CONST_INT)
+    return false;
+
+  delta = INTVAL (addr_delta);
+
+  if (delta == 0
+      || (delta > 0 && delta < size)
+      || (delta < 0 && -delta < size))
+    return true;
+
+  return false;
+}
+
+/* Check whether the address of memory reference MEM2 equals exactly
+   the address of memory reference MEM1 plus DELTA.  Return true if
+   we can prove this to be the case, false otherwise.  */
+
+bool
+s390_offset_p (rtx mem1, rtx mem2, rtx delta)
+{
+  rtx addr1, addr2, addr_delta;
+
+  if (GET_CODE (mem1) != MEM || GET_CODE (mem2) != MEM)
+    return false;
+
+  addr1 = XEXP (mem1, 0);
+  addr2 = XEXP (mem2, 0);
+
+  addr_delta = simplify_binary_operation (MINUS, Pmode, addr2, addr1);
+  if (!addr_delta || !rtx_equal_p (addr_delta, delta))
     return false;
 
   return true;
@@ -1336,6 +1235,14 @@ s390_narrow_logical_operator (enum rtx_code code, rtx *memop, rtx *immop)
 }
 
 
+/* How to allocate a 'struct machine_function'.  */
+
+static struct machine_function *
+s390_init_machine_status (void)
+{
+  return ggc_alloc_cleared (sizeof (struct machine_function));
+}
+
 /* Change optimizations to be performed, depending on the
    optimization level.
 
@@ -1353,12 +1260,20 @@ optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
   /* By default, always emit DWARF-2 unwind info.  This allows debugging
      without maintaining a stack frame back-chain.  */
   flag_asynchronous_unwind_tables = 1;
+
+  /* Use MVCLE instructions to decrease code size if requested.  */
+  if (size != 0)
+    target_flags |= MASK_MVCLE;
 }
 
-void
-override_options (void)
+/* Return true if ARG is the name of a processor.  Set *TYPE and *FLAGS
+   to the associated processor_type and processor_flags if so.  */
+
+static bool
+s390_handle_arch_option (const char *arg,
+                        enum processor_type *type,
+                        enum processor_flags *flags)
 {
-  int i;
   static struct pta
     {
       const char *const name;          /* processor name or nickname.  */
@@ -1372,13 +1287,59 @@ override_options (void)
       {"z900", PROCESSOR_2064_Z900, PF_IEEE_FLOAT | PF_ZARCH},
       {"z990", PROCESSOR_2084_Z990, PF_IEEE_FLOAT | PF_ZARCH
                                    | PF_LONG_DISPLACEMENT},
+      {"z9-109", PROCESSOR_2094_Z9_109, PF_IEEE_FLOAT | PF_ZARCH
+                                       | PF_LONG_DISPLACEMENT | PF_EXTIMM},
     };
+  size_t i;
+
+  for (i = 0; i < ARRAY_SIZE (processor_alias_table); i++)
+    if (strcmp (arg, processor_alias_table[i].name) == 0)
+      {
+       *type = processor_alias_table[i].processor;
+       *flags = processor_alias_table[i].flags;
+       return true;
+      }
+  return false;
+}
+
+/* Implement TARGET_HANDLE_OPTION.  */
+
+static bool
+s390_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
+{
+  switch (code)
+    {
+    case OPT_march_:
+      return s390_handle_arch_option (arg, &s390_arch, &s390_arch_flags);
+
+    case OPT_mstack_guard_:
+      if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_guard) != 1)
+       return false;
+      if (exact_log2 (s390_stack_guard) == -1)
+       error ("stack guard value must be an exact power of 2");
+      return true;
+
+    case OPT_mstack_size_:
+      if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_size) != 1)
+       return false;
+      if (exact_log2 (s390_stack_size) == -1)
+       error ("stack size must be an exact power of 2");
+      return true;
 
-  int const pta_size = ARRAY_SIZE (processor_alias_table);
+    case OPT_mtune_:
+      return s390_handle_arch_option (arg, &s390_tune, &s390_tune_flags);
 
-  /* Acquire a unique set number for our register saves and restores.  */
-  s390_sr_alias_set = new_alias_set ();
+    case OPT_mwarn_framesize_:
+      return sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_warn_framesize) == 1;
+
+    default:
+      return true;
+    }
+}
 
+void
+override_options (void)
+{
   /* Set up function hooks.  */
   init_machine_status = s390_init_machine_status;
 
@@ -1393,92 +1354,46 @@ override_options (void)
 
   /* 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);
+    {
+      s390_arch_string = TARGET_ZARCH? "z900" : "g5";
+      s390_handle_arch_option (s390_arch_string, &s390_arch, &s390_arch_flags);
+    }
 
   /* Determine processor to tune for.  */
-  if (!s390_tune_string)
+  if (s390_tune == PROCESSOR_max)
     {
       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);
+    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.");
-
+    error ("64-bit ABI not supported in ESA/390 mode");
 
   /* Set processor cost function.  */
-  if (s390_tune == PROCESSOR_2084_Z990) 
+  if (s390_tune == PROCESSOR_2094_Z9_109)
+    s390_cost = &z9_109_cost;
+  else if (s390_tune == PROCESSOR_2084_Z990)
     s390_cost = &z990_cost;
   else
     s390_cost = &z900_cost;
-
-
+  
   if (TARGET_BACKCHAIN && TARGET_PACKED_STACK && TARGET_HARD_FLOAT)
     error ("-mbackchain -mpacked-stack -mhard-float are not supported "
-          "in combination.");
+          "in combination");
 
-  if (s390_warn_framesize_string)
-    {
-      if (sscanf (s390_warn_framesize_string, HOST_WIDE_INT_PRINT_DEC,
-                 &s390_warn_framesize) != 1)
-       error ("invalid value for -mwarn-framesize");
-    }
-
-  if (s390_warn_dynamicstack_string)
-    s390_warn_dynamicstack_p = 1;
-  
-  if (s390_stack_size_string)
+  if (s390_stack_size)
     {
-      if (sscanf (s390_stack_size_string, HOST_WIDE_INT_PRINT_DEC, 
-                 &s390_stack_size) != 1)
-       error ("invalid value for -mstack-size");
-      
-      if (exact_log2 (s390_stack_size) == -1)
-       error ("stack size must be an exact power of 2");
-      
-      if (s390_stack_guard_string)
-       {
-         if (sscanf (s390_stack_guard_string, HOST_WIDE_INT_PRINT_DEC, 
-                     &s390_stack_guard) != 1)
-           error ("invalid value for -mstack-guard");
-         
-         if (s390_stack_guard >= s390_stack_size)
-           error ("stack size must be greater than the stack guard value");
-         if (exact_log2 (s390_stack_guard) == -1)
-           error ("stack guard value must be an exact power of 2");
-       }
-      else
+      if (!s390_stack_guard)
        error ("-mstack-size implies use of -mstack-guard");
+      else if (s390_stack_guard >= s390_stack_size)
+       error ("stack size must be greater than the stack guard value");
+      else if (s390_stack_size > 1 << 16)
+       error ("stack size must not be greater than 64k");
     }
-  
-  if (s390_stack_guard_string && !s390_stack_size_string)
+  else if (s390_stack_guard)
     error ("-mstack-guard implies use of -mstack-size"); 
 }
 
@@ -1508,180 +1423,14 @@ s390_safe_attr_type (rtx insn)
     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 (register rtx op, enum machine_mode mode)
-{
-  return op == CONST0_RTX (mode);
-}
-
-/* Return true if OP is constant.
-   OP is the current operation.
-   MODE is the current operation mode.  */
-
-int
-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.  */
-
-static int
-check_mode (register rtx op, enum machine_mode *mode)
-{
-  if (*mode == VOIDmode)
-      *mode = GET_MODE (op);
-  else
-  {
-    if (GET_MODE (op) != VOIDmode && GET_MODE (op) != *mode)
-       return 0;
-  }
-  return 1;
-}
-
-/* Return true if OP a valid operand for the LARL instruction.
-   OP is the current operation.
-   MODE is the current operation mode.  */
-
-int
-larl_operand (register rtx op, enum machine_mode mode)
-{
-  if (! check_mode (op, &mode))
-    return 0;
-
-  /* Allow labels and local symbols.  */
-  if (GET_CODE (op) == LABEL_REF)
-    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* 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)
-    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
-     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) == UNSPEC_PLT)
-    return 1;
-  if (GET_CODE (op) == UNSPEC
-      && XINT (op, 1) == UNSPEC_INDNTPOFF)
-    return 1;
-
-  return 0;
-}
-
-/* Return true if OP is a valid S-type operand.
-   OP is the current operation.
-   MODE is the current operation mode.  */
-
-int
-s_operand (rtx op, enum machine_mode mode)
-{
-  struct s390_address addr;
-
-  /* Call general_operand first, so that we don't have to
-     check for many special cases.  */
-  if (!general_operand (op, mode))
-    return 0;
-
-  /* Just like memory_operand, allow (subreg (mem ...))
-     after reload.  */
-  if (reload_completed
-      && GET_CODE (op) == SUBREG
-      && GET_CODE (SUBREG_REG (op)) == MEM)
-    op = SUBREG_REG (op);
-
-  if (GET_CODE (op) != MEM)
-    return 0;
-  if (!s390_decompose_address (XEXP (op, 0), &addr))
-    return 0;
-  if (addr.indx)
-    return 0;
-
-  return 1;
-}
-
-/* Return true if OP a valid shift count operand.
-   OP is the current operation.
-   MODE is the current operation mode.  */
-
-int
-shift_count_operand (rtx op, enum machine_mode mode)
-{
-  HOST_WIDE_INT offset = 0;
-
-  if (! check_mode (op, &mode))
-    return 0;
-
-  /* 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;
-
-  /* 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 true if DISP is a valid short displacement.  */
 
-static int
+static bool
 s390_short_displacement (rtx disp)
 {
   /* No displacement is OK.  */
   if (!disp)
-    return 1;
+    return true;
 
   /* Integer displacement in range.  */
   if (GET_CODE (disp) == CONST_INT)
@@ -1692,233 +1441,624 @@ s390_short_displacement (rtx disp)
       && GET_CODE (XEXP (disp, 0)) == UNSPEC
       && (XINT (XEXP (disp, 0), 1) == UNSPEC_GOT
           || XINT (XEXP (disp, 0), 1) == UNSPEC_GOTNTPOFF))
-    return 0;
+    return false;
 
   /* 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;
+    return true;
 
-  return 0;
+  return false;
 }
 
-/* Return true if OP is a valid operand for a C constraint.  */
+/* Decompose a RTL expression ADDR for a memory address into
+   its components, returned in OUT.
 
-int
-s390_extra_constraint_str (rtx op, int c, const char * str)
-{
-  struct s390_address addr;
+   Returns false if ADDR is not a valid memory address, true
+   otherwise.  If OUT is NULL, don't return the components,
+   but check for validity only.
 
-  if (c != str[0])
-    abort ();
+   Note: Only addresses in canonical form are recognized.
+   LEGITIMIZE_ADDRESS should convert non-canonical forms to the
+   canonical form so that they will be recognized.  */
 
-  /* Check for offsettable variants of memory constraints.  */
-  if (c == 'A')
-    {
-      /* Only accept non-volatile MEMs.  */
-      if (!MEM_P (op) || MEM_VOLATILE_P (op))
-       return 0;
+static int
+s390_decompose_address (rtx addr, struct s390_address *out)
+{
+  HOST_WIDE_INT offset = 0;
+  rtx base = NULL_RTX;
+  rtx indx = NULL_RTX;
+  rtx disp = NULL_RTX;
+  rtx orig_disp;
+  bool pointer = false;
+  bool base_ptr = false;
+  bool indx_ptr = false;
 
-      if ((reload_completed || reload_in_progress)
-         ? !offsettable_memref_p (op)
-         : !offsettable_nonstrict_memref_p (op))
-       return 0;
+  /* Decompose address into base + index + displacement.  */
 
-      c = str[1];
-    }
+  if (GET_CODE (addr) == REG || GET_CODE (addr) == UNSPEC)
+    base = addr;
 
-  /* Check for non-literal-pool variants of memory constraints.  */
-  else if (c == 'B')
+  else if (GET_CODE (addr) == PLUS)
     {
-      if (GET_CODE (op) != MEM)
-       return 0;
-      if (!s390_decompose_address (XEXP (op, 0), &addr))
-       return 0;
-      if (addr.base && REG_P (addr.base) && REGNO (addr.base) == BASE_REGNUM)
-       return 0;
-      if (addr.indx && REG_P (addr.indx) && REGNO (addr.indx) == BASE_REGNUM)
-       return 0;
+      rtx op0 = XEXP (addr, 0);
+      rtx op1 = XEXP (addr, 1);
+      enum rtx_code code0 = GET_CODE (op0);
+      enum rtx_code code1 = GET_CODE (op1);
 
-      c = str[1];
-    }
+      if (code0 == REG || code0 == UNSPEC)
+       {
+         if (code1 == REG || code1 == UNSPEC)
+           {
+             indx = op0;       /* index + base */
+             base = op1;
+           }
 
-  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;
+         else
+           {
+             base = op0;       /* base + displacement */
+             disp = op1;
+           }
+       }
 
-      if (TARGET_LONG_DISPLACEMENT)
+      else if (code0 == PLUS)
        {
-         if (!s390_short_displacement (addr.disp))
-           return 0;
+         indx = XEXP (op0, 0); /* index + base + disp */
+         base = XEXP (op0, 1);
+         disp = op1;
        }
-      break;
-
-    case 'R':
-      if (GET_CODE (op) != MEM)
-       return 0;
 
-      if (TARGET_LONG_DISPLACEMENT)
+      else
        {
-         if (!s390_decompose_address (XEXP (op, 0), &addr))
-           return 0;
-         if (!s390_short_displacement (addr.disp))
-           return 0;
+         return false;
        }
-      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;
+  else
+    disp = addr;               /* displacement */
 
-    case 'U':
-      if (TARGET_LONG_DISPLACEMENT)
+  /* Extract integer part of displacement.  */
+  orig_disp = disp;
+  if (disp)
+    {
+      if (GET_CODE (disp) == CONST_INT)
        {
-         if (!s390_decompose_address (op, &addr))
-           return 0;
-         if (!s390_short_displacement (addr.disp))
-           return 0;
+         offset = INTVAL (disp);
+         disp = NULL_RTX;
+       }
+      else if (GET_CODE (disp) == CONST
+              && GET_CODE (XEXP (disp, 0)) == PLUS
+              && GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT)
+       {
+         offset = INTVAL (XEXP (XEXP (disp, 0), 1));
+         disp = XEXP (XEXP (disp, 0), 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.  */
+  /* Strip off CONST here to avoid special case tests later.  */
+  if (disp && GET_CODE (disp) == CONST)
+    disp = XEXP (disp, 0);
 
-int
-s390_const_ok_for_constraint_p (HOST_WIDE_INT value,
-                               int c,
-                               const char * str)
-{
-  enum machine_mode mode, part_mode;
-  int def;
-  int part, part_goal;
+  /* We can convert literal pool addresses to
+     displacements by basing them off the base register.  */
+  if (disp && GET_CODE (disp) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (disp))
+    {
+      /* Either base or index must be free to hold the base register.  */
+      if (!base)
+        base = gen_rtx_REG (Pmode, BASE_REGNUM);
+      else if (!indx)
+        indx = gen_rtx_REG (Pmode, BASE_REGNUM);
+      else
+        return false;
 
-  if (c != str[0])
-    abort ();
+      /* Mark up the displacement.  */
+      disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp),
+                            UNSPEC_LTREL_OFFSET);
+    }
 
-  switch (str[0])
+  /* Validate base register.  */
+  if (base)
     {
-    case 'I':
-      return (unsigned int)value < 256;
+      if (GET_CODE (base) == UNSPEC)
+       switch (XINT (base, 1))
+         {
+         case UNSPEC_LTREF:
+           if (!disp)
+             disp = gen_rtx_UNSPEC (Pmode, 
+                                    gen_rtvec (1, XVECEXP (base, 0, 0)),
+                                    UNSPEC_LTREL_OFFSET);
+           else
+             return false;
 
-    case 'J':
-      return (unsigned int)value < 4096;
+           base = gen_rtx_REG (Pmode, BASE_REGNUM);
+           break;
 
-    case 'K':
-      return value >= -32768 && value < 32768;
+         case UNSPEC_LTREL_BASE:
+           base = gen_rtx_REG (Pmode, BASE_REGNUM);
+           break;
 
-    case 'L':
-      return (TARGET_LONG_DISPLACEMENT ?
-             (value >= -524288 && value <= 524287)
-             : (value >= 0 && value <= 4095));
-    case 'M':
-      return value == 2147483647;
+         default:
+           return false;
+         }
 
-    case 'N':
-      if (str[1] == 'x')
-       part_goal = -1;
-      else
-       part_goal = str[1] - '0';
+      if (GET_CODE (base) != REG || GET_MODE (base) != Pmode)
+       return false;
 
-      switch (str[2])
-       {
-       case 'H': part_mode = HImode; break;
-       case 'Q': part_mode = QImode; break;
-       default:  return 0;
-       }
+      if (REGNO (base) == BASE_REGNUM
+         || REGNO (base) == STACK_POINTER_REGNUM
+         || REGNO (base) == FRAME_POINTER_REGNUM
+         || ((reload_completed || reload_in_progress)
+             && frame_pointer_needed
+             && REGNO (base) == HARD_FRAME_POINTER_REGNUM)
+         || REGNO (base) == ARG_POINTER_REGNUM
+          || (flag_pic
+              && REGNO (base) == PIC_OFFSET_TABLE_REGNUM))
+        pointer = base_ptr = true;
+    }
 
-      switch (str[3])
-       {
-       case 'H': mode = HImode; break;
-       case 'S': mode = SImode; break;
-       case 'D': mode = DImode; break;
-       default: return 0;
-       }
+  /* Validate index register.  */
+  if (indx)
+    {
+      if (GET_CODE (indx) == UNSPEC)
+       switch (XINT (indx, 1))
+         {
+         case UNSPEC_LTREF:
+           if (!disp)
+             disp = gen_rtx_UNSPEC (Pmode, 
+                                    gen_rtvec (1, XVECEXP (indx, 0, 0)),
+                                    UNSPEC_LTREL_OFFSET);
+           else
+             return false;
 
-      switch (str[4])
-       {
-       case '0': def = 0;  break;
-       case 'F': def = -1; break;
-       default: return 0;
-       }
+           indx = gen_rtx_REG (Pmode, BASE_REGNUM);
+           break;
 
-      if (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (part_mode))
-       return 0;
+         case UNSPEC_LTREL_BASE:
+           indx = gen_rtx_REG (Pmode, BASE_REGNUM);
+           break;
 
-      part = s390_single_part (GEN_INT (value), mode, part_mode, def);
-      if (part < 0)
-       return 0;
-      if (part_goal != -1 && part_goal != part)
-       return 0;
+         default:
+           return false;
+         }
 
-      break;
+      if (GET_CODE (indx) != REG || GET_MODE (indx) != Pmode)
+       return false;
 
-    default:
-      return 0;
+      if (REGNO (indx) == BASE_REGNUM
+         || REGNO (indx) == STACK_POINTER_REGNUM
+         || REGNO (indx) == FRAME_POINTER_REGNUM
+         || ((reload_completed || reload_in_progress)
+             && frame_pointer_needed
+             && REGNO (indx) == HARD_FRAME_POINTER_REGNUM)
+         || REGNO (indx) == ARG_POINTER_REGNUM
+          || (flag_pic
+              && REGNO (indx) == PIC_OFFSET_TABLE_REGNUM))
+        pointer = indx_ptr = true;
     }
 
-  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.  
-   CODE contains GET_CODE (x), OUTER_CODE contains the code 
-   of the superexpression of x.  */
+  /* 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;
+    }
 
-static bool
-s390_rtx_costs (rtx x, int code, int outer_code, int *total)
-{
-  switch (code)
+  /* Validate displacement.  */
+  if (!disp)
     {
-    case CONST:
-    case CONST_INT:
-    case LABEL_REF:
+      /* If virtual registers are involved, the displacement will change later 
+        anyway as the virtual registers get eliminated.  This could make a 
+        valid displacement invalid, but it is more likely to make an invalid 
+        displacement valid, because we sometimes access the register save area 
+        via negative offsets to one of those registers.
+        Thus we don't check the displacement for validity here.  If after
+        elimination the displacement turns out to be invalid after all,
+        this is fixed up by reload in any case.  */
+      if (base != arg_pointer_rtx 
+         && indx != arg_pointer_rtx 
+         && base != return_address_pointer_rtx 
+         && indx != return_address_pointer_rtx
+         && base != frame_pointer_rtx 
+         && indx != frame_pointer_rtx
+         && base != virtual_stack_vars_rtx 
+         && indx != virtual_stack_vars_rtx)
+       if (!DISP_IN_RANGE (offset))
+         return false;
+    }
+  else
+    {
+      /* All the special cases are pointers.  */
+      pointer = true;
+
+      /* In the small-PIC case, the linker converts @GOT
+         and @GOTNTPOFF offsets to possible displacements.  */
+      if (GET_CODE (disp) == UNSPEC
+          && (XINT (disp, 1) == UNSPEC_GOT
+             || XINT (disp, 1) == UNSPEC_GOTNTPOFF)
+         && offset == 0
+         && flag_pic == 1)
+        {
+         ;
+        }
+
+      /* Accept chunkified literal pool symbol references.  */
+      else if (GET_CODE (disp) == MINUS
+               && GET_CODE (XEXP (disp, 0)) == LABEL_REF
+               && GET_CODE (XEXP (disp, 1)) == LABEL_REF)
+        {
+         ;
+        }
+
+      /* Accept literal pool references.  */
+      else if (GET_CODE (disp) == UNSPEC
+              && XINT (disp, 1) == UNSPEC_LTREL_OFFSET)
+        {
+         orig_disp = gen_rtx_CONST (Pmode, disp);
+         if (offset)
+           {
+             /* If we have an offset, make sure it does not
+                exceed the size of the constant pool entry.  */
+             rtx sym = XVECEXP (disp, 0, 0);
+             if (offset >= GET_MODE_SIZE (get_pool_mode (sym)))
+               return false;
+
+              orig_disp = plus_constant (orig_disp, offset);
+           }
+        }
+
+      else
+       return false;
+    }
+
+  if (!base && !indx)
+    pointer = true;
+
+  if (out)
+    {
+      out->base = base;
+      out->indx = indx;
+      out->disp = orig_disp;
+      out->pointer = pointer;
+    }
+
+  return true;
+}
+
+/* Decompose a RTL expression OP for a shift count into its components,
+   and return the base register in BASE and the offset in OFFSET.
+
+   If BITS is non-zero, the expression is used in a context where only
+   that number to low-order bits is significant.  We then allow OP to
+   contain and outer AND that does not affect significant bits.  If BITS
+   is zero, we allow OP to contain any outer AND with a constant.
+
+   Return true if OP is a valid shift count, false if not.  */
+
+bool
+s390_decompose_shift_count (rtx op, rtx *base, HOST_WIDE_INT *offset, int bits)
+{
+  HOST_WIDE_INT off = 0;
+
+  /* Drop outer ANDs.  */
+  if (GET_CODE (op) == AND && GET_CODE (XEXP (op, 1)) == CONST_INT)
+    {
+      HOST_WIDE_INT mask = ((HOST_WIDE_INT)1 << bits) - 1;
+      if ((INTVAL (XEXP (op, 1)) & mask) != mask)
+       return false;
+
+      op = XEXP (op, 0);
+    }
+
+  /* We can have an integer constant, an address register,
+     or a sum of the two.  */
+  if (GET_CODE (op) == CONST_INT)
+    {
+      off = INTVAL (op);
+      op = NULL_RTX;
+    }
+  if (op && GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT)
+    {
+      off = 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 false;
+
+  if (offset)
+    *offset = off;
+  if (base)
+    *base = op;
+
+   return true;
+}
+
+
+/* Return true if CODE is a valid address without index.  */
+
+bool
+s390_legitimate_address_without_index_p (rtx op)
+{
+  struct s390_address addr;
+
+  if (!s390_decompose_address (XEXP (op, 0), &addr))
+    return false;
+  if (addr.indx)
+    return false;
+
+  return true;
+}
+
+/* Return 1 if OP is a valid operand for a C constraint, 0 else.  */
+
+int
+s390_extra_constraint_str (rtx op, int c, const char * str)
+{
+  struct s390_address addr;
+
+  gcc_assert (c == str[0]);
+
+  /* Check for offsettable variants of memory constraints.  */
+  if (c == 'A')
+    {
+      /* Only accept non-volatile MEMs.  */
+      if (!MEM_P (op) || MEM_VOLATILE_P (op))
+       return 0;
+
+      if ((reload_completed || reload_in_progress)
+         ? !offsettable_memref_p (op)
+         : !offsettable_nonstrict_memref_p (op))
+       return 0;
+
+      c = str[1];
+    }
+
+  /* Check for non-literal-pool variants of memory constraints.  */
+  else if (c == 'B')
+    {
+      if (GET_CODE (op) != MEM)
+       return 0;
+      if (!s390_decompose_address (XEXP (op, 0), &addr))
+       return 0;
+      if (addr.base && REG_P (addr.base) && REGNO (addr.base) == BASE_REGNUM)
+       return 0;
+      if (addr.indx && REG_P (addr.indx) && REGNO (addr.indx) == BASE_REGNUM)
+       return 0;
+
+      c = str[1];
+    }
+
+  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':
+      /* Simply check for the basic form of a shift count.  Reload will
+        take care of making sure we have a proper base register.  */
+      if (!s390_decompose_shift_count (op, NULL, NULL, 0))
+       return 0;
+      break;
+
+    default:
+      return 0;
+    }
+
+  return 1;
+}
+
+/* Return true if VALUE matches the constraint STR.  */
+
+int
+s390_const_double_ok_for_constraint_p (rtx value,
+                                      int c,
+                                      const char * str)
+{
+  gcc_assert (c == str[0]);
+
+  switch (str[0])
+    {
+    case 'G':
+      /* The floating point zero constant.  */
+      return (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT
+              && value == CONST0_RTX (GET_MODE (value)));
+      
+    default:
+      return 0;
+    }
+}
+
+/* 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;
+  int part, part_goal;
+
+  gcc_assert (c == str[0]);
+
+  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':
+      if (str[1] == 'x')
+       part_goal = -1;
+      else
+       part_goal = str[1] - '0';
+
+      switch (str[2])
+       {
+       case 'Q': part_mode = QImode; break;
+       case 'H': part_mode = HImode; break;
+       case 'S': part_mode = SImode; 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;
+
+      part = s390_single_part (GEN_INT (value), mode, part_mode, def);
+      if (part < 0)
+       return 0;
+      if (part_goal != -1 && part_goal != part)
+       return 0;
+
+      break;
+
+    case 'O':
+      if (!TARGET_EXTIMM)
+       return 0;
+      
+      switch (str[1])
+       {
+       case 's':
+         return trunc_int_for_mode (value, SImode) == value;
+         
+       case 'p':
+         return value == 0
+           || s390_single_part (GEN_INT (value), DImode, SImode, 0) == 1;
+         
+       case 'n':
+         return value == -1
+           || s390_single_part (GEN_INT (value), DImode, SImode, -1) == 1;
+         
+       default:
+         gcc_unreachable ();
+       }
+      break;
+
+    case 'P':
+      return legitimate_reload_constant_p (GEN_INT (value));
+
+    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.  
+   CODE contains GET_CODE (x), OUTER_CODE contains the code 
+   of the superexpression of x.  */
+
+static bool
+s390_rtx_costs (rtx x, int code, int outer_code, int *total)
+{
+  switch (code)
+    {
+    case CONST:
+    case CONST_INT:
+    case LABEL_REF:
     case SYMBOL_REF:
     case CONST_DOUBLE:
     case MEM:
@@ -1966,7 +2106,7 @@ s390_rtx_costs (rtx x, int code, int outer_code, int *total)
            rtx left = XEXP (x, 0);
            rtx right = XEXP (x, 1);
            if (GET_CODE (right) == CONST_INT
-               && CONST_OK_FOR_CONSTRAINT_P (INTVAL (right), 'K', "K"))
+               && CONST_OK_FOR_K (INTVAL (right)))
              *total = s390_cost->mhi;
            else if (GET_CODE (left) == SIGN_EXTEND)
              *total = s390_cost->mh;
@@ -1981,7 +2121,7 @@ s390_rtx_costs (rtx x, int code, int outer_code, int *total)
            if (TARGET_64BIT)
              {
                if (GET_CODE (right) == CONST_INT
-                   && CONST_OK_FOR_CONSTRAINT_P (INTVAL (right), 'K', "K"))
+                   && CONST_OK_FOR_K (INTVAL (right)))
                  *total = s390_cost->mghi;
                else if (GET_CODE (left) == SIGN_EXTEND)
                  *total = s390_cost->msgf;
@@ -2112,158 +2252,17 @@ s390_address_cost (rtx addr)
   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)
+tls_symbolic_operand (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.  */
-  if (count <= 1
-      || GET_CODE (XVECEXP (op, 0, 0)) != SET
-      || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != REG
-      || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != MEM)
-    return 0;
-
-  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, 1)) == CONST_INT)
-    {
-      off = INTVAL (XEXP (src_addr, 1));
-      src_addr = XEXP (src_addr, 0);
-    }
-  else
-    return 0;
-
-  for (i = 1; i < count; i++)
-    {
-      rtx elt = XVECEXP (op, 0, i);
-
-      if (GET_CODE (elt) != SET
-         || GET_CODE (SET_DEST (elt)) != REG
-         || 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)) != 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 * GET_MODE_SIZE (elt_mode))
-       return 0;
-    }
-
-  return 1;
-}
-
-/* Return true if OP is a store 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
-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;
-  int i, off;
-
-  /* Perform a quick check so we don't blow up below.  */
-  if (count <= 1
-      || GET_CODE (XVECEXP (op, 0, 0)) != SET
-      || GET_CODE (SET_DEST (XVECEXP (op, 0, 0))) != MEM
-      || GET_CODE (SET_SRC (XVECEXP (op, 0, 0))) != REG)
-    return 0;
-
-  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, 1)) == CONST_INT)
-    {
-      off = INTVAL (XEXP (dest_addr, 1));
-      dest_addr = XEXP (dest_addr, 0);
-    }
-  else
-    return 0;
-
-  for (i = 1; i < count; i++)
-    {
-      rtx elt = XVECEXP (op, 0, i);
-
-      if (GET_CODE (elt) != SET
-         || GET_CODE (SET_SRC (elt)) != REG
-         || 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)) != 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 * GET_MODE_SIZE (elt_mode))
-       return 0;
-    }
-  return 1;
-}
-
 /* Split DImode access register reference REG (on 64-bit) into its constituent
    low and high parts, and store them into LO and HI.  Note that gen_lowpart/
    gen_highpart cannot be used as they assume all registers are word-sized,
@@ -2283,11 +2282,11 @@ s390_split_access_reg (rtx reg, rtx *lo, rtx *hi)
 
 /* Return true if OP contains a symbol reference */
 
-int
+bool
 symbolic_reference_mentioned_p (rtx op)
 {
-  register const char *fmt;
-  register int i;
+  const char *fmt;
+  int i;
 
   if (GET_CODE (op) == SYMBOL_REF || GET_CODE (op) == LABEL_REF)
     return 1;
@@ -2297,7 +2296,7 @@ symbolic_reference_mentioned_p (rtx op)
     {
       if (fmt[i] == 'E')
        {
-         register int j;
+         int j;
 
          for (j = XVECLEN (op, i) - 1; j >= 0; j--)
            if (symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
@@ -2313,11 +2312,11 @@ symbolic_reference_mentioned_p (rtx op)
 
 /* Return true if OP contains a reference to a thread-local symbol.  */
 
-int
+bool
 tls_symbolic_reference_mentioned_p (rtx op)
 {
-  register const char *fmt;
-  register int i;
+  const char *fmt;
+  int i;
 
   if (GET_CODE (op) == SYMBOL_REF)
     return tls_symbolic_operand (op);
@@ -2327,18 +2326,18 @@ tls_symbolic_reference_mentioned_p (rtx op)
     {
       if (fmt[i] == 'E')
        {
-         register int j;
+         int j;
 
          for (j = XVECLEN (op, i) - 1; j >= 0; j--)
            if (tls_symbolic_reference_mentioned_p (XVECEXP (op, i, j)))
-             return 1;
+             return true;
        }
 
       else if (fmt[i] == 'e' && tls_symbolic_reference_mentioned_p (XEXP (op, i)))
-       return 1;
+       return true;
     }
 
-  return 0;
+  return false;
 }
 
 
@@ -2347,7 +2346,7 @@ tls_symbolic_reference_mentioned_p (rtx op)
    and that OP satisfies CONSTANT_P or is a CONST_DOUBLE.  */
 
 int
-legitimate_pic_operand_p (register rtx op)
+legitimate_pic_operand_p (rtx op)
 {
   /* Accept all non-symbolic constants.  */
   if (!SYMBOLIC_CONST (op))
@@ -2362,7 +2361,7 @@ legitimate_pic_operand_p (register rtx op)
    It is given that OP satisfies CONSTANT_P or is a CONST_DOUBLE.  */
 
 int
-legitimate_constant_p (register rtx op)
+legitimate_constant_p (rtx op)
 {
   /* Accept all non-symbolic constants.  */
   if (!SYMBOLIC_CONST (op))
@@ -2449,7 +2448,7 @@ s390_cannot_force_const_mem (rtx x)
       break;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 
@@ -2459,31 +2458,55 @@ s390_cannot_force_const_mem (rtx x)
    a constant that would need to be forced to the literal pool
    before it can be used as operand.  */
 
-int
-legitimate_reload_constant_p (register rtx op)
+bool
+legitimate_reload_constant_p (rtx op)
 {
   /* Accept la(y) operands.  */
   if (GET_CODE (op) == CONST_INT
       && DISP_IN_RANGE (INTVAL (op)))
-    return 1;
+    return true;
 
-  /* Accept l(g)hi operands.  */
+  /* Accept l(g)hi/l(g)fi operands.  */
   if (GET_CODE (op) == CONST_INT
-      && CONST_OK_FOR_CONSTRAINT_P (INTVAL (op), 'K', "K"))
-    return 1;
+      && (CONST_OK_FOR_K (INTVAL (op)) || CONST_OK_FOR_Os (INTVAL (op))))
+    return true;
 
   /* Accept lliXX operands.  */
   if (TARGET_ZARCH
-      && s390_single_part (op, DImode, HImode, 0) >= 0)
-  return 1;
+      && GET_CODE (op) == CONST_INT
+      && trunc_int_for_mode (INTVAL (op), word_mode) == INTVAL (op)
+      && s390_single_part (op, word_mode, HImode, 0) >= 0)
+  return true;
+
+  if (TARGET_EXTIMM
+      && GET_CODE (op) == CONST_INT
+      && trunc_int_for_mode (INTVAL (op), word_mode) == INTVAL (op)
+      && s390_single_part (op, word_mode, SImode, 0) >= 0)
+    return true;
 
   /* Accept larl operands.  */
   if (TARGET_CPU_ZARCH
       && larl_operand (op, VOIDmode))
-    return 1;
+    return true;
+
+  /* Accept lzXX operands.  */
+  if (GET_CODE (op) == CONST_DOUBLE
+      && CONST_DOUBLE_OK_FOR_CONSTRAINT_P (op, 'G', "G"))
+    return true;
+
+  /* Accept double-word operands that can be split.  */
+  if (GET_CODE (op) == CONST_INT
+      && trunc_int_for_mode (INTVAL (op), word_mode) != INTVAL (op))
+    {
+      enum machine_mode dword_mode = word_mode == SImode ? DImode : TImode;
+      rtx hi = operand_subword (op, 0, 0, dword_mode);
+      rtx lo = operand_subword (op, 1, 0, dword_mode);
+      return legitimate_reload_constant_p (hi)
+            && legitimate_reload_constant_p (lo);
+    }
 
   /* Everything else cannot be handled without reload.  */
-  return 0;
+  return false;
 }
 
 /* Given an rtx OP being reloaded into a reg required to be in class CLASS,
@@ -2530,445 +2553,153 @@ s390_preferred_reload_class (rtx op, enum reg_class class)
    We need a temporary when loading a PLUS expression which
    is not a legitimate operand of the LOAD ADDRESS instruction.  */
 
-enum reg_class
-s390_secondary_input_reload_class (enum reg_class class,
-                                  enum machine_mode mode, rtx in)
-{
-  if (s390_plus_operand (in, mode))
-    return ADDR_REGS;
-
-  if (reg_classes_intersect_p (CC_REGS, class))
-    return GENERAL_REGS;
-
-  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;
-
-  if (reg_classes_intersect_p (CC_REGS, class))
-    return GENERAL_REGS;
-
-  return NO_REGS;
-}
-
-/* Return true if OP is a PLUS that is not a legitimate
-   operand for the LA instruction.
-   OP is the current operation.
-   MODE is the current operation mode.  */
-
-int
-s390_plus_operand (register rtx op, enum machine_mode mode)
-{
-  if (!check_mode (op, &mode) || mode != Pmode)
-    return FALSE;
-
-  if (GET_CODE (op) != PLUS)
-    return FALSE;
-
-  if (legitimate_la_operand_p (op))
-    return FALSE;
-
-  return TRUE;
-}
-
-/* Generate code to load SRC, which is PLUS that is not a
-   legitimate operand for the LA instruction, into TARGET.
-   SCRATCH may be used as scratch register.  */
-
-void
-s390_expand_plus_operand (register rtx target, register rtx src,
-                         register rtx scratch)
-{
-  rtx sum1, sum2;
-  struct s390_address ad;
-
-  /* src must be a PLUS; get its two operands.  */
-  if (GET_CODE (src) != PLUS || GET_MODE (src) != Pmode)
-    abort ();
-
-  /* Check if any of the two operands is already scheduled
-     for replacement by reload.  This can happen e.g. when
-     float registers occur in an address.  */
-  sum1 = find_replacement (&XEXP (src, 0));
-  sum2 = find_replacement (&XEXP (src, 1));
-  src = gen_rtx_PLUS (Pmode, sum1, sum2);
-
-  /* If the address is already strictly valid, there's nothing to do.  */
-  if (!s390_decompose_address (src, &ad)
-      || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base))
-      || (ad.indx && !REG_OK_FOR_INDEX_STRICT_P (ad.indx)))
-    {
-      /* Otherwise, one of the operands cannot be an address register;
-         we reload its value into the scratch register.  */
-      if (true_regnum (sum1) < 1 || true_regnum (sum1) > 15)
-       {
-         emit_move_insn (scratch, sum1);
-         sum1 = scratch;
-       }
-      if (true_regnum (sum2) < 1 || true_regnum (sum2) > 15)
-       {
-         emit_move_insn (scratch, sum2);
-         sum2 = scratch;
-       }
-
-      /* According to the way these invalid addresses are generated
-         in reload.c, it should never happen (at least on s390) that
-         *neither* of the PLUS components, after find_replacements
-         was applied, is an address register.  */
-      if (sum1 == scratch && sum2 == scratch)
-       {
-         debug_rtx (src);
-         abort ();
-       }
-
-      src = gen_rtx_PLUS (Pmode, sum1, sum2);
-    }
-
-  /* Emit the LOAD ADDRESS pattern.  Note that reload of PLUS
-     is only ever performed on addresses, so we can mark the
-     sum as legitimate for LA in any case.  */
-  s390_load_address (target, src);
-}
-
-
-/* Decompose a RTL expression ADDR for a memory address into
-   its components, returned in OUT.
-
-   Returns 0 if ADDR is not a valid memory address, nonzero
-   otherwise.  If OUT is NULL, don't return the components,
-   but check for validity only.
-
-   Note: Only addresses in canonical form are recognized.
-   LEGITIMIZE_ADDRESS should convert non-canonical forms to the
-   canonical form so that they will be recognized.  */
-
-static int
-s390_decompose_address (register rtx addr, struct s390_address *out)
-{
-  HOST_WIDE_INT offset = 0;
-  rtx base = NULL_RTX;
-  rtx indx = NULL_RTX;
-  rtx disp = NULL_RTX;
-  rtx orig_disp;
-  int pointer = FALSE;
-  int base_ptr = FALSE;
-  int indx_ptr = FALSE;
-
-  /* Decompose address into base + index + displacement.  */
-
-  if (GET_CODE (addr) == REG || GET_CODE (addr) == UNSPEC)
-    base = addr;
-
-  else if (GET_CODE (addr) == PLUS)
-    {
-      rtx op0 = XEXP (addr, 0);
-      rtx op1 = XEXP (addr, 1);
-      enum rtx_code code0 = GET_CODE (op0);
-      enum rtx_code code1 = GET_CODE (op1);
-
-      if (code0 == REG || code0 == UNSPEC)
-       {
-         if (code1 == REG || code1 == UNSPEC)
-           {
-             indx = op0;       /* index + base */
-             base = op1;
-           }
-
-         else
-           {
-             base = op0;       /* base + displacement */
-             disp = op1;
-           }
-       }
-
-      else if (code0 == PLUS)
-       {
-         indx = XEXP (op0, 0); /* index + base + disp */
-         base = XEXP (op0, 1);
-         disp = op1;
-       }
-
-      else
-       {
-         return FALSE;
-       }
-    }
-
-  else
-    disp = addr;               /* displacement */
-
-  /* Extract integer part of displacement.  */
-  orig_disp = disp;
-  if (disp)
-    {
-      if (GET_CODE (disp) == CONST_INT)
-       {
-         offset = INTVAL (disp);
-         disp = NULL_RTX;
-       }
-      else if (GET_CODE (disp) == CONST
-              && GET_CODE (XEXP (disp, 0)) == PLUS
-              && GET_CODE (XEXP (XEXP (disp, 0), 1)) == CONST_INT)
-       {
-         offset = INTVAL (XEXP (XEXP (disp, 0), 1));
-         disp = XEXP (XEXP (disp, 0), 0);
-       }
-    }
-
-  /* Strip off CONST here to avoid special case tests later.  */
-  if (disp && GET_CODE (disp) == CONST)
-    disp = XEXP (disp, 0);
-
-  /* We can convert literal pool addresses to
-     displacements by basing them off the base register.  */
-  if (disp && GET_CODE (disp) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (disp))
-    {
-      /* Either base or index must be free to hold the base register.  */
-      if (!base)
-        base = gen_rtx_REG (Pmode, BASE_REGNUM);
-      else if (!indx)
-        indx = gen_rtx_REG (Pmode, BASE_REGNUM);
-      else
-        return FALSE;
-
-      /* Mark up the displacement.  */
-      disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp),
-                            UNSPEC_LTREL_OFFSET);
-    }
-
-  /* Validate base register.  */
-  if (base)
-    {
-      if (GET_CODE (base) == UNSPEC)
-       switch (XINT (base, 1))
-         {
-         case UNSPEC_LTREF:
-           if (!disp)
-             disp = gen_rtx_UNSPEC (Pmode, 
-                                    gen_rtvec (1, XVECEXP (base, 0, 0)),
-                                    UNSPEC_LTREL_OFFSET);
-           else
-             return FALSE;
-
-           base = gen_rtx_REG (Pmode, BASE_REGNUM);
-           break;
-
-         case UNSPEC_LTREL_BASE:
-           base = gen_rtx_REG (Pmode, BASE_REGNUM);
-           break;
-
-         default:
-           return FALSE;
-         }
-
-      if (GET_CODE (base) != REG || GET_MODE (base) != Pmode)
-       return FALSE;
-
-      if (REGNO (base) == BASE_REGNUM
-         || REGNO (base) == STACK_POINTER_REGNUM
-         || REGNO (base) == FRAME_POINTER_REGNUM
-         || ((reload_completed || reload_in_progress)
-             && frame_pointer_needed
-             && REGNO (base) == HARD_FRAME_POINTER_REGNUM)
-         || REGNO (base) == ARG_POINTER_REGNUM
-          || (flag_pic
-              && REGNO (base) == PIC_OFFSET_TABLE_REGNUM))
-        pointer = base_ptr = TRUE;
-    }
-
-  /* Validate index register.  */
-  if (indx)
-    {
-      if (GET_CODE (indx) == UNSPEC)
-       switch (XINT (indx, 1))
-         {
-         case UNSPEC_LTREF:
-           if (!disp)
-             disp = gen_rtx_UNSPEC (Pmode, 
-                                    gen_rtvec (1, XVECEXP (indx, 0, 0)),
-                                    UNSPEC_LTREL_OFFSET);
-           else
-             return FALSE;
-
-           indx = gen_rtx_REG (Pmode, BASE_REGNUM);
-           break;
-
-         case UNSPEC_LTREL_BASE:
-           indx = gen_rtx_REG (Pmode, BASE_REGNUM);
-           break;
+enum reg_class
+s390_secondary_input_reload_class (enum reg_class class,
+                                  enum machine_mode mode, rtx in)
+{
+  if (s390_plus_operand (in, mode))
+    return ADDR_REGS;
 
-         default:
-           return FALSE;
-         }
+  if (reg_classes_intersect_p (CC_REGS, class))
+    return GENERAL_REGS;
 
-      if (GET_CODE (indx) != REG || GET_MODE (indx) != Pmode)
-       return FALSE;
+  return NO_REGS;
+}
 
-      if (REGNO (indx) == BASE_REGNUM
-         || REGNO (indx) == STACK_POINTER_REGNUM
-         || REGNO (indx) == FRAME_POINTER_REGNUM
-         || ((reload_completed || reload_in_progress)
-             && frame_pointer_needed
-             && REGNO (indx) == HARD_FRAME_POINTER_REGNUM)
-         || REGNO (indx) == ARG_POINTER_REGNUM
-          || (flag_pic
-              && REGNO (indx) == PIC_OFFSET_TABLE_REGNUM))
-        pointer = indx_ptr = TRUE;
-    }
+/* Return the register class of a scratch register needed to
+   store a register of class CLASS in MODE into OUT:
 
-  /* 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;
-    }
+   We need a temporary when storing a double-word to a
+   non-offsettable memory address.  */
 
-  /* Validate displacement.  */
-  if (!disp)
-    {
-      /* If the argument pointer or the return address pointer are involved,
-        the displacement will change later anyway as the virtual registers get
-        eliminated.  This could make a valid displacement invalid, but it is 
-        more likely to make an invalid displacement valid, because we sometimes
-        access the register save area via negative offsets to one of those 
-        registers.
-        Thus we don't check the displacement for validity here.  If after
-        elimination the displacement turns out to be invalid after all,
-        this is fixed up by reload in any case.  */
-      if (base != arg_pointer_rtx 
-         && indx != arg_pointer_rtx 
-         && base != return_address_pointer_rtx 
-         && indx != return_address_pointer_rtx)
-       if (!DISP_IN_RANGE (offset))
-         return FALSE;
-    }
-  else
-    {
-      /* All the special cases are pointers.  */
-      pointer = TRUE;
+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
+      && GET_CODE (XEXP (out, 0)) == PLUS
+      && GET_CODE (XEXP (XEXP (out, 0), 0)) == PLUS
+      && GET_CODE (XEXP (XEXP (out, 0), 1)) == CONST_INT
+      && !DISP_IN_RANGE (INTVAL (XEXP (XEXP (out, 0), 1))
+                        + GET_MODE_SIZE (mode) - 1))
+    return ADDR_REGS;
 
-      /* In the small-PIC case, the linker converts @GOT
-         and @GOTNTPOFF offsets to possible displacements.  */
-      if (GET_CODE (disp) == UNSPEC
-          && (XINT (disp, 1) == UNSPEC_GOT
-             || XINT (disp, 1) == UNSPEC_GOTNTPOFF)
-         && offset == 0
-         && flag_pic == 1)
-        {
-         ;
-        }
+  if (reg_classes_intersect_p (CC_REGS, class))
+    return GENERAL_REGS;
 
-      /* Accept chunkified literal pool symbol references.  */
-      else if (GET_CODE (disp) == MINUS
-               && GET_CODE (XEXP (disp, 0)) == LABEL_REF
-               && GET_CODE (XEXP (disp, 1)) == LABEL_REF)
-        {
-         ;
-        }
+  return NO_REGS;
+}
 
-      /* Accept literal pool references.  */
-      else if (GET_CODE (disp) == UNSPEC
-              && XINT (disp, 1) == UNSPEC_LTREL_OFFSET)
-        {
-         orig_disp = gen_rtx_CONST (Pmode, disp);
-         if (offset)
-           {
-             /* If we have an offset, make sure it does not
-                exceed the size of the constant pool entry.  */
-             rtx sym = XVECEXP (disp, 0, 0);
-             if (offset >= GET_MODE_SIZE (get_pool_mode (sym)))
-               return FALSE;
+/* Generate code to load SRC, which is PLUS that is not a
+   legitimate operand for the LA instruction, into TARGET.
+   SCRATCH may be used as scratch register.  */
 
-              orig_disp = plus_constant (orig_disp, offset);
-           }
-        }
+void
+s390_expand_plus_operand (rtx target, rtx src,
+                         rtx scratch)
+{
+  rtx sum1, sum2;
+  struct s390_address ad;
 
-      else
-       return FALSE;
-    }
+  /* src must be a PLUS; get its two operands.  */
+  gcc_assert (GET_CODE (src) == PLUS);
+  gcc_assert (GET_MODE (src) == Pmode);
 
-  if (!base && !indx)
-    pointer = TRUE;
+  /* Check if any of the two operands is already scheduled
+     for replacement by reload.  This can happen e.g. when
+     float registers occur in an address.  */
+  sum1 = find_replacement (&XEXP (src, 0));
+  sum2 = find_replacement (&XEXP (src, 1));
+  src = gen_rtx_PLUS (Pmode, sum1, sum2);
 
-  if (out)
+  /* If the address is already strictly valid, there's nothing to do.  */
+  if (!s390_decompose_address (src, &ad)
+      || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base))
+      || (ad.indx && !REG_OK_FOR_INDEX_STRICT_P (ad.indx)))
     {
-      out->base = base;
-      out->indx = indx;
-      out->disp = orig_disp;
-      out->pointer = pointer;
+      /* Otherwise, one of the operands cannot be an address register;
+         we reload its value into the scratch register.  */
+      if (true_regnum (sum1) < 1 || true_regnum (sum1) > 15)
+       {
+         emit_move_insn (scratch, sum1);
+         sum1 = scratch;
+       }
+      if (true_regnum (sum2) < 1 || true_regnum (sum2) > 15)
+       {
+         emit_move_insn (scratch, sum2);
+         sum2 = scratch;
+       }
+
+      /* According to the way these invalid addresses are generated
+         in reload.c, it should never happen (at least on s390) that
+         *neither* of the PLUS components, after find_replacements
+         was applied, is an address register.  */
+      if (sum1 == scratch && sum2 == scratch)
+       {
+         debug_rtx (src);
+         gcc_unreachable ();
+       }
+
+      src = gen_rtx_PLUS (Pmode, sum1, sum2);
     }
 
-  return TRUE;
+  /* Emit the LOAD ADDRESS pattern.  Note that reload of PLUS
+     is only ever performed on addresses, so we can mark the
+     sum as legitimate for LA in any case.  */
+  s390_load_address (target, src);
 }
 
-/* Return nonzero if ADDR is a valid memory address.
+
+/* Return true if ADDR is a valid memory address.
    STRICT specifies whether strict register checking applies.  */
 
-int
+bool
 legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
-                     register rtx addr, int strict)
+                     rtx addr, int strict)
 {
   struct s390_address ad;
   if (!s390_decompose_address (addr, &ad))
-    return FALSE;
+    return false;
 
   if (strict)
     {
       if (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base))
-       return FALSE;
+       return false;
       if (ad.indx && !REG_OK_FOR_INDEX_STRICT_P (ad.indx))
-       return FALSE;
+       return false;
     }
   else
     {
       if (ad.base && !REG_OK_FOR_BASE_NONSTRICT_P (ad.base))
-       return FALSE;
+       return false;
       if (ad.indx && !REG_OK_FOR_INDEX_NONSTRICT_P (ad.indx))
-       return FALSE;
+       return false;
     }
 
-  return TRUE;
+  return true;
 }
 
-/* Return 1 if OP is a valid operand for the LA instruction.
+/* Return true if OP is a valid operand for the LA instruction.
    In 31-bit, we need to prove that the result is used as an
    address, as LA performs only a 31-bit addition.  */
 
-int
-legitimate_la_operand_p (register rtx op)
+bool
+legitimate_la_operand_p (rtx op)
 {
   struct s390_address addr;
   if (!s390_decompose_address (op, &addr))
-    return FALSE;
-
-  if (TARGET_64BIT || addr.pointer)
-    return TRUE;
+    return false;
 
-  return FALSE;
+  return (TARGET_64BIT || addr.pointer);
 }
 
-/* Return 1 if it is valid *and* preferable to use LA to
+/* Return true if it is valid *and* preferable to use LA to
    compute the sum of OP1 and OP2.  */
 
-int
+bool
 preferred_la_operand_p (rtx op1, rtx op2)
 {
   struct s390_address addr;
@@ -2977,23 +2708,23 @@ preferred_la_operand_p (rtx op1, rtx op2)
     op1 = gen_rtx_PLUS (Pmode, op1, op2);
 
   if (!s390_decompose_address (op1, &addr))
-    return FALSE;
+    return false;
   if (addr.base && !REG_OK_FOR_BASE_STRICT_P (addr.base))
-    return FALSE;
+    return false;
   if (addr.indx && !REG_OK_FOR_INDEX_STRICT_P (addr.indx))
-    return FALSE;
+    return false;
 
   if (!TARGET_64BIT && !addr.pointer)
-    return FALSE;
+    return false;
 
   if (addr.pointer)
-    return TRUE;
+    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 true;
 
-  return FALSE;
+  return false;
 }
 
 /* Emit a forced load-address operation to load SRC into DST.
@@ -3129,8 +2860,7 @@ legitimize_pic_address (rtx orig, rtx reg)
          addr = XEXP (addr, 0);
          if (GET_CODE (addr) == UNSPEC)
            {
-             if (XVECLEN (addr, 0) != 1)
-                abort ();
+             gcc_assert (XVECLEN (addr, 0) == 1);
               switch (XINT (addr, 1))
                 {
                   /* If someone moved a GOT-relative UNSPEC
@@ -3178,11 +2908,11 @@ legitimize_pic_address (rtx orig, rtx reg)
 
                   /* Everything else cannot happen.  */
                   default:
-                    abort ();
+                    gcc_unreachable ();
                 }
            }
-         else if (GET_CODE (addr) != PLUS)
-           abort ();
+         else 
+           gcc_assert (GET_CODE (addr) == PLUS);
        }
       if (GET_CODE (addr) == PLUS)
        {
@@ -3256,8 +2986,7 @@ legitimize_pic_address (rtx orig, rtx reg)
                   && GET_CODE (op1) == CONST_INT
                   && XINT (op0, 1) == UNSPEC_GOTOFF)
             {
-             if (XVECLEN (op0, 0) != 1)
-                abort ();
+             gcc_assert (XVECLEN (op0, 0) == 1);
 
               new = force_const_mem (Pmode, orig);
             }
@@ -3291,8 +3020,8 @@ legitimize_pic_address (rtx orig, rtx reg)
 
 /* Load the thread pointer into a register.  */
 
-static rtx
-get_thread_pointer (void)
+rtx
+s390_get_thread_pointer (void)
 {
   rtx tp = gen_reg_rtx (Pmode);
 
@@ -3314,8 +3043,7 @@ s390_emit_tls_call_insn (rtx result_reg, rtx tls_call)
 {
   rtx insn;
 
-  if (!flag_pic)
-    abort ();
+  gcc_assert (flag_pic);
 
   if (!s390_tls_symbol)
     s390_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_offset");
@@ -3353,7 +3081,7 @@ legitimize_tls_address (rtx addr, rtx reg)
        temp = gen_reg_rtx (Pmode);
        emit_libcall_block (insn, temp, r2, new);
 
-       new = gen_rtx_PLUS (Pmode, get_thread_pointer (), temp);
+       new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
        if (reg != 0)
          {
            s390_load_address (reg, new);
@@ -3376,7 +3104,7 @@ legitimize_tls_address (rtx addr, rtx reg)
        temp = gen_reg_rtx (Pmode);
        emit_libcall_block (insn, temp, r2, new);
 
-       new = gen_rtx_PLUS (Pmode, get_thread_pointer (), temp);
+       new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
        base = gen_reg_rtx (Pmode);
        s390_load_address (base, new);
 
@@ -3463,7 +3191,7 @@ legitimize_tls_address (rtx addr, rtx reg)
            emit_insn (gen_rtx_SET (Pmode, temp, new));
          }
 
-       new = gen_rtx_PLUS (Pmode, get_thread_pointer (), temp);
+       new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
        if (reg != 0)
          {
            s390_load_address (reg, new);
@@ -3478,7 +3206,7 @@ legitimize_tls_address (rtx addr, rtx reg)
         temp = gen_reg_rtx (Pmode);
        emit_move_insn (temp, new);
 
-       new = gen_rtx_PLUS (Pmode, get_thread_pointer (), temp);
+       new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
        if (reg != 0)
          {
            s390_load_address (reg, new);
@@ -3487,7 +3215,7 @@ legitimize_tls_address (rtx addr, rtx reg)
        break;
 
       default:
-       abort ();
+       gcc_unreachable ();
       }
 
   else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == UNSPEC)
@@ -3495,14 +3223,12 @@ legitimize_tls_address (rtx addr, rtx reg)
       switch (XINT (XEXP (addr, 0), 1))
        {
        case UNSPEC_INDNTPOFF:
-         if (TARGET_CPU_ZARCH)
-           new = addr;
-         else
-           abort ();
+         gcc_assert (TARGET_CPU_ZARCH);
+         new = addr;
          break;
 
        default:
-         abort ();
+         gcc_unreachable ();
        }
     }
 
@@ -3519,7 +3245,7 @@ legitimize_tls_address (rtx addr, rtx reg)
     }
 
   else
-    abort ();  /* for now ... */
+    gcc_unreachable ();  /* for now ... */
 
   return new;
 }
@@ -3551,7 +3277,7 @@ emit_symbolic_move (rtx *operands)
    See comments by legitimize_pic_address for details.  */
 
 rtx
-legitimize_address (register rtx x, register rtx oldx ATTRIBUTE_UNUSED,
+legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
                    enum machine_mode mode ATTRIBUTE_UNUSED)
 {
   rtx constant_term = const0_rtx;
@@ -3606,8 +3332,8 @@ legitimize_address (register rtx x, register rtx oldx ATTRIBUTE_UNUSED,
     {
       if (GET_CODE (XEXP (x, 0)) == REG)
        {
-         register rtx temp = gen_reg_rtx (Pmode);
-         register rtx val  = force_operand (XEXP (x, 1), temp);
+         rtx temp = gen_reg_rtx (Pmode);
+         rtx val  = force_operand (XEXP (x, 1), temp);
          if (val != temp)
            emit_move_insn (temp, val);
 
@@ -3616,8 +3342,8 @@ legitimize_address (register rtx x, register rtx oldx ATTRIBUTE_UNUSED,
 
       else if (GET_CODE (XEXP (x, 1)) == REG)
        {
-         register rtx temp = gen_reg_rtx (Pmode);
-         register rtx val  = force_operand (XEXP (x, 0), temp);
+         rtx temp = gen_reg_rtx (Pmode);
+         rtx val  = force_operand (XEXP (x, 0), temp);
          if (val != temp)
            emit_move_insn (temp, val);
 
@@ -3754,25 +3480,49 @@ s390_expand_movmem (rtx dst, rtx src, rtx len)
     }
 }
 
-/* Emit code to clear LEN bytes at DST.  */
+/* Emit code to set LEN bytes at DST to VAL.
+   Make use of clrmem if VAL is zero.  */
 
 void
-s390_expand_clrmem (rtx dst, rtx len)
+s390_expand_setmem (rtx dst, rtx len, rtx val)
 {
-  if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256)
+  gcc_assert (GET_CODE (len) != CONST_INT || INTVAL (len) > 0);
+  gcc_assert (GET_CODE (val) == CONST_INT || GET_MODE (val) == QImode);
+  
+  if (GET_CODE (len) == CONST_INT && INTVAL (len) <= 257)
     {
-      if (INTVAL (len) > 0)
+      if (val == const0_rtx && INTVAL (len) <= 256)
         emit_insn (gen_clrmem_short (dst, GEN_INT (INTVAL (len) - 1)));
+      else
+       {
+         /* Initialize memory by storing the first byte.  */
+         emit_move_insn (adjust_address (dst, QImode, 0), val);
+         
+         if (INTVAL (len) > 1)
+           {
+             /* Initiate 1 byte overlap move.
+                The first byte of DST is propagated through DSTP1.
+                Prepare a movmem for:  DST+1 = DST (length = LEN - 1).
+                DST is set to size 1 so the rest of the memory location
+                does not count as source operand.  */
+             rtx dstp1 = adjust_address (dst, VOIDmode, 1);
+             set_mem_size (dst, const1_rtx);
+
+             emit_insn (gen_movmem_short (dstp1, dst, 
+                                          GEN_INT (INTVAL (len) - 2)));
+           }
+       }
     }
 
   else if (TARGET_MVCLE)
     {
-      emit_insn (gen_clrmem_long (dst, convert_to_mode (Pmode, len, 1)));
+      val = force_not_mem (convert_modes (Pmode, QImode, val, 1));
+      emit_insn (gen_setmem_long (dst, convert_to_mode (Pmode, len, 1), val));
     }
 
   else
     {
-      rtx dst_addr, src_addr, count, blocks, temp;
+      rtx dst_addr, src_addr, count, blocks, temp, dstp1 = NULL_RTX;
       rtx loop_start_label = gen_label_rtx ();
       rtx loop_end_label = gen_label_rtx ();
       rtx end_label = gen_label_rtx ();
@@ -3794,7 +3544,22 @@ s390_expand_clrmem (rtx dst, rtx len)
       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 (val == const0_rtx)
+        temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
+      else
+       {
+         dstp1 = adjust_address (dst, VOIDmode, 1);
+         set_mem_size (dst, const1_rtx);
+
+         /* Initialize memory by storing the first byte.  */
+         emit_move_insn (adjust_address (dst, QImode, 0), val);
+         
+         /* If count is 1 we are done.  */
+         emit_cmp_and_jump_insns (count, const1_rtx,
+                                  EQ, NULL_RTX, mode, 1, end_label);
+
+         temp = expand_binop (mode, add_optab, count, GEN_INT (-2), count, 1, 0);
+       }
       if (temp != count)
         emit_move_insn (count, temp);
 
@@ -3807,7 +3572,10 @@ s390_expand_clrmem (rtx dst, rtx len)
 
       emit_label (loop_start_label);
 
-      emit_insn (gen_clrmem_short (dst, GEN_INT (255)));
+      if (val == const0_rtx)
+       emit_insn (gen_clrmem_short (dst, GEN_INT (255)));
+      else
+       emit_insn (gen_movmem_short (dstp1, dst, GEN_INT (255)));
       s390_load_address (dst_addr,
                         gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256)));
 
@@ -3821,7 +3589,10 @@ s390_expand_clrmem (rtx dst, rtx len)
       emit_jump (loop_start_label);
       emit_label (loop_end_label);
 
-      emit_insn (gen_clrmem_short (dst, convert_to_mode (Pmode, count, 1)));
+      if (val == const0_rtx)
+        emit_insn (gen_clrmem_short (dst, convert_to_mode (Pmode, count, 1)));
+      else
+        emit_insn (gen_movmem_short (dstp1, dst, convert_to_mode (Pmode, count, 1)));
       emit_label (end_label);
     }
 }
@@ -3952,6 +3723,7 @@ s390_expand_addcc (enum rtx_code cmp_code, rtx cmp_op0, rtx cmp_op1,
   rtx op_res;
   rtx insn;
   rtvec p;
+  int ret;
 
   if ((GET_MODE (cmp_op0) == SImode || GET_MODE (cmp_op0) == VOIDmode)
       && (GET_MODE (cmp_op1) == SImode || GET_MODE (cmp_op1) == VOIDmode))
@@ -4007,8 +3779,8 @@ s390_expand_addcc (enum rtx_code cmp_code, rtx cmp_op0, rtx cmp_op1,
       insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (cc_mode, CC_REGNUM),
                          gen_rtx_COMPARE (cc_mode, cmp_op0, cmp_op1));
       /* We use insn_invalid_p here to add clobbers if required.  */
-      if (insn_invalid_p (emit_insn (insn)))
-       abort ();
+      ret = insn_invalid_p (emit_insn (insn));
+      gcc_assert (!ret);
 
       /* Emit ALC instruction pattern.  */
       op_res = gen_rtx_fmt_ee (cmp_code, GET_MODE (dst),
@@ -4079,8 +3851,8 @@ s390_expand_addcc (enum rtx_code cmp_code, rtx cmp_op0, rtx cmp_op1,
       insn = gen_rtx_SET (VOIDmode, gen_rtx_REG (cc_mode, CC_REGNUM),
                          gen_rtx_COMPARE (cc_mode, cmp_op0, cmp_op1));
       /* We use insn_invalid_p here to add clobbers if required.  */
-      if (insn_invalid_p (emit_insn (insn)))
-       abort ();
+      ret = insn_invalid_p (emit_insn (insn));
+      gcc_assert (!ret);
 
       /* Emit SLB instruction pattern.  */
       if (!register_operand (src, GET_MODE (dst)))
@@ -4104,11 +3876,108 @@ s390_expand_addcc (enum rtx_code cmp_code, rtx cmp_op0, rtx cmp_op1,
   return false;
 }
 
+/* Expand code for the insv template. Return true if successful, false else.  */
+
+bool 
+s390_expand_insv (rtx dest, rtx op1, rtx op2, rtx src)
+{
+  int bitsize = INTVAL (op1);
+  int bitpos = INTVAL (op2);
+
+  /* We need byte alignment.  */
+  if (bitsize % BITS_PER_UNIT)
+    return false;
+
+  if (bitpos == 0
+      && memory_operand (dest, VOIDmode)
+      && (register_operand (src, word_mode)
+         || const_int_operand (src, VOIDmode)))
+    {
+      /* Emit standard pattern if possible.  */
+      enum machine_mode mode = smallest_mode_for_size (bitsize, MODE_INT);
+      if (GET_MODE_BITSIZE (mode) == bitsize)
+       emit_move_insn (adjust_address (dest, mode, 0), gen_lowpart (mode, src));
+
+      /* (set (ze (mem)) (const_int)).  */
+      else if (const_int_operand (src, VOIDmode))
+       {
+         int size = bitsize / BITS_PER_UNIT;
+         rtx src_mem = adjust_address (force_const_mem (word_mode, src), BLKmode,
+                                       GET_MODE_SIZE (word_mode) - size);
+
+         dest = adjust_address (dest, BLKmode, 0);
+         set_mem_size (dest, GEN_INT (size));
+         s390_expand_movmem (dest, src_mem, GEN_INT (size));
+       }
+         
+      /* (set (ze (mem)) (reg)).  */
+      else if (register_operand (src, word_mode))
+       {
+         if (bitsize <= GET_MODE_BITSIZE (SImode))
+           emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, op1,
+                                                 const0_rtx), src);
+         else
+           {
+             /* Emit st,stcmh sequence.  */
+             int stcmh_width = bitsize - GET_MODE_BITSIZE (SImode);
+             int size = stcmh_width / BITS_PER_UNIT;
+
+             emit_move_insn (adjust_address (dest, SImode, size), 
+                             gen_lowpart (SImode, src));
+             set_mem_size (dest, GEN_INT (size));
+             emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, GEN_INT
+                                                   (stcmh_width), const0_rtx),
+                             gen_rtx_LSHIFTRT (word_mode, src, GEN_INT
+                                               (GET_MODE_BITSIZE (SImode))));
+           }
+       }
+      else
+       return false;
+
+      return true;
+    }
+
+  /* (set (ze (reg)) (const_int)).  */
+  if (TARGET_ZARCH
+      && register_operand (dest, word_mode) 
+      && (bitpos % 16) == 0
+      && (bitsize % 16) == 0
+      && const_int_operand (src, VOIDmode))
+    {
+      HOST_WIDE_INT val = INTVAL (src);
+      int regpos = bitpos + bitsize;
+
+      while (regpos > bitpos)
+       {
+         enum machine_mode putmode;
+         int putsize;
+
+         if (TARGET_EXTIMM && (regpos % 32 == 0) && (regpos >= bitpos + 32))
+           putmode = SImode;
+         else
+           putmode = HImode;
+
+         putsize = GET_MODE_BITSIZE (putmode);
+         regpos -= putsize;
+         emit_move_insn (gen_rtx_ZERO_EXTRACT (word_mode, dest, 
+                                               GEN_INT (putsize),
+                                               GEN_INT (regpos)), 
+                         gen_int_mode (val, putmode));
+         val >>= putsize;
+       }
+      gcc_assert (regpos == bitpos);
+      return true;
+    }
+
+  return false;
+}
 
-/* This is called from dwarf2out.c via ASM_OUTPUT_DWARF_DTPREL.
+/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
    We need to emit DTP-relative relocations.  */
 
-void
+static void s390_output_dwarf_dtprel (FILE *, int, rtx) ATTRIBUTE_UNUSED;
+
+static void
 s390_output_dwarf_dtprel (FILE *file, int size, rtx x)
 {
   switch (size)
@@ -4120,14 +3989,14 @@ s390_output_dwarf_dtprel (FILE *file, int size, rtx x)
       fputs ("\t.quad\t", file);
       break;
     default:
-      abort ();
+      gcc_unreachable ();
     }
   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
+   general assembler lossage, recognize various UNSPEC sequences
    and turn them back into a direct symbol reference.  */
 
 static rtx
@@ -4163,59 +4032,36 @@ s390_delegitimize_address (rtx orig_x)
   return orig_x;
 }
 
-/* Output shift count operand OP to stdio stream FILE.  */
+/* Output operand OP to stdio stream FILE.
+   OP is an address (register + offset) which is not used to address data;
+   instead the rightmost bits are interpreted as the value.  */
 
 static void
 print_shift_count_operand (FILE *file, rtx op)
 {
-  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)
-    {
-      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);
-
-  /* Sanity check.  */
-  if (op && (GET_CODE (op) != REG
-            || REGNO (op) >= FIRST_PSEUDO_REGISTER
-            || REGNO_REG_CLASS (REGNO (op)) != ADDR_REGS))
-    abort ();
-
-  /* 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)]);
-}
-
-/* Locate some local-dynamic symbol still in use by this function
-   so that we can print its name in local-dynamic base patterns.  */
-
-static const char *
-get_some_local_dynamic_name (void)
-{
-  rtx insn;
+  HOST_WIDE_INT offset;
+  rtx base;
 
-  if (cfun->machine->some_ld_name)
-    return cfun->machine->some_ld_name;
+  /* Extract base register and offset.  */
+  if (!s390_decompose_shift_count (op, &base, &offset, 0))
+    gcc_unreachable ();
 
-  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;
+  /* Sanity check.  */
+  if (base)
+    {
+      gcc_assert (GET_CODE (base) == REG);
+      gcc_assert (REGNO (base) < FIRST_PSEUDO_REGISTER);
+      gcc_assert (REGNO_REG_CLASS (REGNO (base)) == ADDR_REGS);
+    }
 
-  abort ();
+  /* Offsets are constricted to twelve bits.  */
+  fprintf (file, HOST_WIDE_INT_PRINT_DEC, offset & ((1 << 12) - 1));
+  if (base)
+    fprintf (file, "(%s)", reg_names[REGNO (base)]);
 }
 
+/* See 'get_some_local_dynamic_name'.  */
+
 static int
 get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
 {
@@ -4237,6 +4083,25 @@ get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
   return 0;
 }
 
+/* Locate some local-dynamic symbol still in use by this function
+   so that we can print its name in local-dynamic base patterns.  */
+
+static const char *
+get_some_local_dynamic_name (void)
+{
+  rtx insn;
+
+  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;
+
+  gcc_unreachable ();
+}
+
 /* 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.  */
@@ -4307,7 +4172,7 @@ print_operand_address (FILE *file, rtx addr)
   if (!s390_decompose_address (addr, &ad)
       || (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.");
+    output_operand_lossage ("cannot decompose address");
 
   if (ad.disp)
     output_addr_const (file, ad.disp);
@@ -4328,6 +4193,7 @@ print_operand_address (FILE *file, rtx addr)
     'C': print opcode suffix for branch condition.
     'D': print opcode suffix for inverse branch condition.
     'J': print tls_load/tls_gdcall/tls_ldcall suffix
+    'G': print the size of the operand in bytes.
     'O': print only the displacement of a memory reference.
     'R': print only the base register of a memory reference.
     'S': print S-type memory reference (base+displacement).
@@ -4336,10 +4202,13 @@ print_operand_address (FILE *file, rtx addr)
     '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.
+    'x': print integer X as if it's an unsigned halfword.
+    'h': print integer X as if it's a signed halfword.
     'i': print the first nonzero HImode part of X.
-    'j': print the first HImode part unequal to 0xffff of X.  */
+    'j': print the first HImode part unequal to -1 of X.
+    'k': print the first nonzero SImode part of X.
+    'm': print the first SImode part unequal to -1 of X.
+    'o': print integer X as if it's an unsigned 32bit word.  */
 
 void
 print_operand (FILE *file, rtx x, int code)
@@ -4371,18 +4240,23 @@ print_operand (FILE *file, rtx x, int code)
          assemble_name (file, get_some_local_dynamic_name ());
        }
       else
-       abort ();
+       gcc_unreachable ();
+      return;
+
+    case 'G':
+      fprintf (file, "%u", GET_MODE_SIZE (GET_MODE (x)));
       return;
 
     case 'O':
       {
         struct s390_address ad;
+       int ret;
 
-        if (GET_CODE (x) != MEM
-            || !s390_decompose_address (XEXP (x, 0), &ad)
-           || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base))
-            || ad.indx)
-          abort ();
+        gcc_assert (GET_CODE (x) == MEM);
+       ret = s390_decompose_address (XEXP (x, 0), &ad);
+       gcc_assert (ret);
+       gcc_assert (!ad.base || REG_OK_FOR_BASE_STRICT_P (ad.base));
+       gcc_assert (!ad.indx);
 
         if (ad.disp)
           output_addr_const (file, ad.disp);
@@ -4394,12 +4268,13 @@ print_operand (FILE *file, rtx x, int code)
     case 'R':
       {
         struct s390_address ad;
+       int ret;
 
-        if (GET_CODE (x) != MEM
-            || !s390_decompose_address (XEXP (x, 0), &ad)
-           || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base))
-            || ad.indx)
-          abort ();
+        gcc_assert (GET_CODE (x) == MEM);
+       ret = s390_decompose_address (XEXP (x, 0), &ad);
+       gcc_assert (ret);
+       gcc_assert (!ad.base || REG_OK_FOR_BASE_STRICT_P (ad.base));
+       gcc_assert (!ad.indx);
 
         if (ad.base)
           fprintf (file, "%s", reg_names[REGNO (ad.base)]);
@@ -4411,12 +4286,13 @@ print_operand (FILE *file, rtx x, int code)
     case 'S':
       {
        struct s390_address ad;
+       int ret;
 
-       if (GET_CODE (x) != MEM
-           || !s390_decompose_address (XEXP (x, 0), &ad)
-           || (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base))
-           || ad.indx)
-         abort ();
+        gcc_assert (GET_CODE (x) == MEM);
+       ret = s390_decompose_address (XEXP (x, 0), &ad);
+       gcc_assert (ret);
+       gcc_assert (!ad.base || REG_OK_FOR_BASE_STRICT_P (ad.base));
+       gcc_assert (!ad.indx);
 
        if (ad.disp)
          output_addr_const (file, ad.disp);
@@ -4434,7 +4310,7 @@ print_operand (FILE *file, rtx x, int code)
       else if (GET_CODE (x) == MEM)
        x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 4));
       else
-        abort ();
+        gcc_unreachable ();
       break;
 
     case 'M':
@@ -4443,7 +4319,7 @@ print_operand (FILE *file, rtx x, int code)
       else if (GET_CODE (x) == MEM)
        x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 8));
       else
-        abort ();
+        gcc_unreachable ();
       break;
 
     case 'Y':
@@ -4481,13 +4357,20 @@ print_operand (FILE *file, rtx x, int code)
       else if (code == 'j')
        fprintf (file, HOST_WIDE_INT_PRINT_DEC,
                 s390_extract_part (x, HImode, -1));
+      else if (code == 'k')
+       fprintf (file, HOST_WIDE_INT_PRINT_DEC,
+                s390_extract_part (x, SImode, 0));
+      else if (code == 'm')
+       fprintf (file, HOST_WIDE_INT_PRINT_DEC,
+                s390_extract_part (x, SImode, -1));
+      else if (code == 'o')
+       fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffffffff);
       else
         fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x));
       break;
 
     case CONST_DOUBLE:
-      if (GET_MODE (x) != VOIDmode)
-        abort ();
+      gcc_assert (GET_MODE (x) == VOIDmode);
       if (code == 'b')
         fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xff);
       else if (code == 'x')
@@ -4495,7 +4378,7 @@ print_operand (FILE *file, rtx x, int code)
       else if (code == 'h')
         fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((CONST_DOUBLE_LOW (x) & 0xffff) ^ 0x8000) - 0x8000);
       else
-        abort ();
+        gcc_unreachable ();
       break;
 
     default:
@@ -4524,7 +4407,7 @@ s390_assemble_integer (rtx x, unsigned int size, int aligned_p)
 /* Returns true if register REGNO is used  for forming
    a memory address in expression X.  */
 
-static int
+static bool
 reg_used_in_mem_p (int regno, rtx x)
 {
   enum rtx_code code = GET_CODE (x);
@@ -4535,14 +4418,14 @@ reg_used_in_mem_p (int regno, rtx x)
     {
       if (refers_to_regno_p (regno, regno+1,
                             XEXP (x, 0), 0))
-       return 1;
+       return true;
     }
   else if (code == SET
           && GET_CODE (SET_DEST (x)) == PC)
     {
       if (refers_to_regno_p (regno, regno+1,
                             SET_SRC (x), 0))
-       return 1;
+       return true;
     }
 
   fmt = GET_RTX_FORMAT (code);
@@ -4550,20 +4433,20 @@ reg_used_in_mem_p (int regno, rtx x)
     {
       if (fmt[i] == 'e'
          && reg_used_in_mem_p (regno, XEXP (x, i)))
-       return 1;
+       return true;
 
       else if (fmt[i] == 'E')
        for (j = 0; j < XVECLEN (x, i); j++)
          if (reg_used_in_mem_p (regno, XVECEXP (x, i, j)))
-           return 1;
+           return true;
     }
-  return 0;
+  return false;
 }
 
 /* Returns true if expression DEP_RTX sets an address register
    used by instruction INSN to address memory.  */
 
-static int
+static bool
 addr_generation_dependency_p (rtx dep_rtx, rtx insn)
 {
   rtx target, pat;
@@ -4588,20 +4471,17 @@ addr_generation_dependency_p (rtx dep_rtx, rtx insn)
              pat = PATTERN (insn);
              if (GET_CODE (pat) == PARALLEL)
                {
-                 if (XVECLEN (pat, 0) != 2)
-                   abort();
+                 gcc_assert (XVECLEN (pat, 0) == 2);
                  pat = XVECEXP (pat, 0, 0);
                }
-             if (GET_CODE (pat) == SET)
-               return refers_to_regno_p (regno, regno+1, SET_SRC (pat), 0);
-             else
-               abort();
+             gcc_assert (GET_CODE (pat) == SET);
+             return refers_to_regno_p (regno, regno+1, SET_SRC (pat), 0);
            }
          else if (get_attr_atype (insn) == ATYPE_AGEN)
            return reg_used_in_mem_p (regno, PATTERN (insn));
        }
     }
-  return 0;
+  return false;
 }
 
 /* Return 1, if dep_insn sets register used in insn in the agen unit.  */
@@ -4640,13 +4520,14 @@ s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
   if (! INSN_P (insn))
     return priority;
 
-  if (s390_tune != PROCESSOR_2084_Z990)
+  if (s390_tune != PROCESSOR_2084_Z990
+      && s390_tune != PROCESSOR_2094_Z9_109)
     return priority;
 
   switch (s390_safe_attr_type (insn))
     {
-      case TYPE_FSTORED:
-      case TYPE_FSTORES:
+      case TYPE_FSTOREDF:
+      case TYPE_FSTORESF:
        priority = priority << 3;
        break;
       case TYPE_STORE:
@@ -4664,7 +4545,8 @@ s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
 static int
 s390_issue_rate (void)
 {
-  if (s390_tune == PROCESSOR_2084_Z990)
+  if (s390_tune == PROCESSOR_2084_Z990
+      || s390_tune == PROCESSOR_2094_Z9_109)
     return 3;
   return 1;
 }
@@ -4676,91 +4558,6 @@ s390_first_cycle_multipass_dfa_lookahead (void)
 }
 
 
-/* Split all branches that exceed the maximum distance.
-   Returns true if this created a new literal pool entry.  */
-
-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;
-
-  /* We need correct insn addresses.  */
-
-  shorten_branches (get_insns ());
-
-  /* Find all branches that exceed 64KB, and split them.  */
-
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-    {
-      if (GET_CODE (insn) != JUMP_INSN)
-       continue;
-
-      pat = PATTERN (insn);
-      if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2)
-       pat = XVECEXP (pat, 0, 0);
-      if (GET_CODE (pat) != SET || SET_DEST (pat) != pc_rtx)
-       continue;
-
-      if (GET_CODE (SET_SRC (pat)) == LABEL_REF)
-       {
-         label = &SET_SRC (pat);
-       }
-      else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
-       {
-         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)
-            label = &XEXP (SET_SRC (pat), 2);
-         else
-           continue;
-        }
-      else
-       continue;
-
-      if (get_attr_length (insn) <= 4)
-       continue;
-
-      /* We are going to use the return register as scratch register,
-        make sure it will be saved/restored by the prologue/epilogue.  */
-      cfun_frame_layout.save_return_addr_p = 1;
-
-      if (!flag_pic)
-       {
-         new_literal = 1;
-         tmp = force_const_mem (Pmode, *label);
-         tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn);
-         INSN_ADDRESSES_NEW (tmp, -1);
-         annotate_constant_pool_refs (&PATTERN (tmp));
-
-         target = temp_reg;
-       }
-      else
-       {
-         new_literal = 1;
-         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);
-         annotate_constant_pool_refs (&PATTERN (tmp));
-
-          target = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XEXP (target, 0),
-                                                       cfun->machine->base_reg),
-                                  UNSPEC_LTREL_BASE);
-         target = gen_rtx_PLUS (Pmode, temp_reg, target);
-       }
-
-      if (!validate_change (insn, label, target, 0))
-       abort ();
-    }
-
-  return new_literal;
-}
-
 /* Annotate every literal pool reference in X by an UNSPEC_LTREF expression.
    Fix up MEMs as required.  */
 
@@ -4770,9 +4567,8 @@ annotate_constant_pool_refs (rtx *x)
   int i, j;
   const char *fmt;
 
-  if (GET_CODE (*x) == SYMBOL_REF
-      && CONSTANT_POOL_ADDRESS_P (*x))
-    abort ();
+  gcc_assert (GET_CODE (*x) != SYMBOL_REF
+             || !CONSTANT_POOL_ADDRESS_P (*x));
 
   /* Literal pool references can only occur inside a MEM ...  */
   if (GET_CODE (*x) == MEM)
@@ -4865,6 +4661,91 @@ annotate_constant_pool_refs (rtx *x)
     }
 }
 
+/* Split all branches that exceed the maximum distance.
+   Returns true if this created a new literal pool entry.  */
+
+static int
+s390_split_branches (void)
+{
+  rtx temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
+  int new_literal = 0, ret;
+  rtx insn, pat, tmp, target;
+  rtx *label;
+
+  /* We need correct insn addresses.  */
+
+  shorten_branches (get_insns ());
+
+  /* Find all branches that exceed 64KB, and split them.  */
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    {
+      if (GET_CODE (insn) != JUMP_INSN)
+       continue;
+
+      pat = PATTERN (insn);
+      if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2)
+       pat = XVECEXP (pat, 0, 0);
+      if (GET_CODE (pat) != SET || SET_DEST (pat) != pc_rtx)
+       continue;
+
+      if (GET_CODE (SET_SRC (pat)) == LABEL_REF)
+       {
+         label = &SET_SRC (pat);
+       }
+      else if (GET_CODE (SET_SRC (pat)) == IF_THEN_ELSE)
+       {
+         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)
+            label = &XEXP (SET_SRC (pat), 2);
+         else
+           continue;
+        }
+      else
+       continue;
+
+      if (get_attr_length (insn) <= 4)
+       continue;
+
+      /* We are going to use the return register as scratch register,
+        make sure it will be saved/restored by the prologue/epilogue.  */
+      cfun_frame_layout.save_return_addr_p = 1;
+
+      if (!flag_pic)
+       {
+         new_literal = 1;
+         tmp = force_const_mem (Pmode, *label);
+         tmp = emit_insn_before (gen_rtx_SET (Pmode, temp_reg, tmp), insn);
+         INSN_ADDRESSES_NEW (tmp, -1);
+         annotate_constant_pool_refs (&PATTERN (tmp));
+
+         target = temp_reg;
+       }
+      else
+       {
+         new_literal = 1;
+         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);
+         annotate_constant_pool_refs (&PATTERN (tmp));
+
+          target = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, XEXP (target, 0),
+                                                       cfun->machine->base_reg),
+                                  UNSPEC_LTREL_BASE);
+         target = gen_rtx_PLUS (Pmode, temp_reg, target);
+       }
+
+      ret = validate_change (insn, label, target, 0);
+      gcc_assert (ret);
+    }
+
+  return new_literal;
+}
+
 
 /* Find an annotated literal pool symbol referenced in RTX X, 
    and store it at REF.  Will abort if X contains references to 
@@ -4889,21 +4770,19 @@ find_constant_pool_ref (rtx x, rtx *ref)
       && XINT (x, 1) == UNSPECV_POOL_ENTRY)
     return;
 
-  if (GET_CODE (x) == SYMBOL_REF
-      && CONSTANT_POOL_ADDRESS_P (x))
-    abort ();
+  gcc_assert (GET_CODE (x) != SYMBOL_REF
+              || !CONSTANT_POOL_ADDRESS_P (x));
 
   if (GET_CODE (x) == UNSPEC && XINT (x, 1) == UNSPEC_LTREF)
     {
       rtx sym = XVECEXP (x, 0, 0);
-      if (GET_CODE (sym) != SYMBOL_REF
-         || !CONSTANT_POOL_ADDRESS_P (sym))
-       abort ();
+      gcc_assert (GET_CODE (sym) == SYMBOL_REF
+                 && CONSTANT_POOL_ADDRESS_P (sym));
 
       if (*ref == NULL_RTX)
        *ref = sym;
-      else if (*ref != sym)
-       abort ();
+      else 
+       gcc_assert (*ref == sym);
 
       return;
     }
@@ -4932,8 +4811,7 @@ replace_constant_pool_ref (rtx *x, rtx ref, rtx offset)
   int i, j;
   const char *fmt;
 
-  if (*x == ref)
-    abort ();
+  gcc_assert (*x != ref);
 
   if (GET_CODE (*x) == UNSPEC
       && XINT (*x, 1) == UNSPEC_LTREF
@@ -5069,28 +4947,28 @@ struct constant_pool
   int size;
 };
 
-static struct constant_pool * s390_mainpool_start (void);
-static void s390_mainpool_finish (struct constant_pool *);
-static void s390_mainpool_cancel (struct constant_pool *);
-
-static struct constant_pool * s390_chunkify_start (void);
-static void s390_chunkify_finish (struct constant_pool *);
-static void s390_chunkify_cancel (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 void s390_add_execute (struct constant_pool *, rtx);
-static rtx s390_find_execute (struct constant_pool *, rtx);
-static rtx s390_execute_label (rtx);
-static rtx s390_execute_target (rtx);
-static void s390_dump_pool (struct constant_pool *, bool);
-static void s390_dump_execute (struct constant_pool *);
-static struct constant_pool *s390_alloc_pool (void);
-static void s390_free_pool (struct constant_pool *);
+/* 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->execute = NULL;
+  pool->label = gen_label_rtx ();
+  pool->first_insn = NULL_RTX;
+  pool->pool_insn = NULL_RTX;
+  pool->insns = BITMAP_ALLOC (NULL);
+  pool->size = 0;
+
+  return pool;
+}
 
 /* Create new constant pool covering instructions starting at INSN
    and chain it to the end of POOL_LIST.  */
@@ -5158,8 +5036,7 @@ s390_add_constant (struct constant_pool *pool, rtx val, enum machine_mode mode)
   for (i = 0; i < NR_C_MODES; i++)
     if (constant_modes[i] == mode)
       break;
-  if (i == NR_C_MODES)
-    abort ();
+  gcc_assert (i != NR_C_MODES);
 
   for (c = pool->constants[i]; c != NULL; c = c->next)
     if (rtx_equal_p (val, c->value))
@@ -5191,15 +5068,13 @@ s390_find_constant (struct constant_pool *pool, rtx val,
   for (i = 0; i < NR_C_MODES; i++)
     if (constant_modes[i] == mode)
       break;
-  if (i == NR_C_MODES)
-    abort ();
+  gcc_assert (i != NR_C_MODES);
 
   for (c = pool->constants[i]; c != NULL; c = c->next)
     if (rtx_equal_p (val, c->value))
       break;
 
-  if (c == NULL)
-    abort ();
+  gcc_assert (c);
 
   offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label),
                                  gen_rtx_LABEL_REF (Pmode, pool->label));
@@ -5207,6 +5082,21 @@ s390_find_constant (struct constant_pool *pool, rtx val,
   return offset;
 }
 
+/* Check whether INSN is an execute.  Return the label_ref to its
+   execute target template if so, NULL_RTX otherwise.  */
+
+static rtx
+s390_execute_label (rtx insn)
+{
+  if (GET_CODE (insn) == INSN
+      && GET_CODE (PATTERN (insn)) == PARALLEL
+      && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC
+      && XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE)
+    return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2);
+
+  return NULL_RTX;
+}
+
 /* Add execute target for INSN to the constant pool POOL.  */
 
 static void
@@ -5220,15 +5110,12 @@ s390_add_execute (struct constant_pool *pool, rtx insn)
 
   if (c == NULL)
     {
-      rtx label = s390_execute_label (insn);
-      gcc_assert (label);
-
       c = (struct constant *) xmalloc (sizeof *c);
       c->value = insn;
-      c->label = label == const0_rtx ? gen_label_rtx () : XEXP (label, 0);
+      c->label = gen_label_rtx ();
       c->next = pool->execute;
       pool->execute = c;
-      pool->size += label == const0_rtx ? 6 : 0;
+      pool->size += 6;
     }
 }
 
@@ -5246,8 +5133,7 @@ s390_find_execute (struct constant_pool *pool, rtx insn)
     if (INSN_UID (insn) == INSN_UID (c->value))
       break;
 
-  if (c == NULL)
-    abort ();
+  gcc_assert (c);
 
   offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label),
                                 gen_rtx_LABEL_REF (Pmode, pool->label));
@@ -5255,21 +5141,6 @@ s390_find_execute (struct constant_pool *pool, rtx insn)
   return offset;
 }
 
-/* Check whether INSN is an execute.  Return the label_ref to its
-   execute target template if so, NULL_RTX otherwise.  */
-
-static rtx
-s390_execute_label (rtx insn)
-{
-  if (GET_CODE (insn) == INSN
-      && GET_CODE (PATTERN (insn)) == PARALLEL
-      && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC
-      && XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE)
-    return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2);
-
-  return NULL_RTX;
-}
-
 /* For an execute INSN, extract the execute target template.  */
 
 static rtx
@@ -5371,9 +5242,6 @@ s390_dump_pool (struct constant_pool *pool, bool remote_label)
   /* Output in-pool execute template insns.  */
   for (c = pool->execute; c; c = c->next)
     {
-      if (s390_execute_label (c->value) != const0_rtx)
-       continue;
-
       insn = emit_label_after (c->label, insn);
       INSN_ADDRESSES_NEW (insn, -1);
 
@@ -5393,54 +5261,6 @@ s390_dump_pool (struct constant_pool *pool, bool remote_label)
 
   /* Remove placeholder insn.  */
   remove_insn (pool->pool_insn);
-
-  /* Output out-of-pool execute template isns.  */
-  s390_dump_execute (pool);
-}
-
-/* Dump out the out-of-pool execute template insns in POOL
-   at the end of the instruction stream.  */
-
-static void
-s390_dump_execute (struct constant_pool *pool)
-{
-  struct constant *c;
-  rtx insn;
-
-  for (c = pool->execute; c; c = c->next)
-    {
-      if (s390_execute_label (c->value) == const0_rtx)
-       continue;
-
-      insn = emit_label (c->label);
-      INSN_ADDRESSES_NEW (insn, -1);
-
-      insn = emit_insn (s390_execute_target (c->value));
-      INSN_ADDRESSES_NEW (insn, -1);
-    }
-}
-
-/* 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->execute = 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.  */
@@ -5464,7 +5284,7 @@ s390_free_pool (struct constant_pool *pool)
       free (c);
     }
 
-  BITMAP_XFREE (pool->insns);
+  BITMAP_FREE (pool->insns);
   free (pool);
 }
 
@@ -5486,12 +5306,11 @@ s390_mainpool_start (void)
          && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC_VOLATILE
          && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPECV_MAIN_POOL)
        {
-         if (pool->pool_insn)
-           abort ();
+         gcc_assert (!pool->pool_insn);
          pool->pool_insn = insn;
        }
 
-      if (s390_execute_label (insn))
+      if (!TARGET_CPU_ZARCH && s390_execute_label (insn))
        {
          s390_add_execute (pool, insn);
        }
@@ -5508,8 +5327,7 @@ s390_mainpool_start (void)
        }
     }
 
-  if (!pool->pool_insn && pool->size > 0)
-    abort ();
+  gcc_assert (pool->pool_insn || pool->size == 0);
 
   if (pool->size >= 4096)
     {
@@ -5537,9 +5355,6 @@ s390_mainpool_finish (struct constant_pool *pool)
   /* If the pool is empty, we're done.  */
   if (pool->size == 0)
     {
-      /* However, we may have out-of-pool execute templates.  */
-      s390_dump_execute (pool);
-
       /* We don't actually need a base register after all.  */
       cfun->machine->base_reg = NULL_RTX;
 
@@ -5687,14 +5502,12 @@ s390_chunkify_start (void)
          rtx ltrel_base = find_ltrel_base (PATTERN (insn));
          if (ltrel_base)
            {
-             if (ltrel_base == pending_ltrel)
-               pending_ltrel = NULL_RTX;
-             else
-               abort ();
+             gcc_assert (ltrel_base == pending_ltrel);
+             pending_ltrel = NULL_RTX;
            }
        }
 
-      if (s390_execute_label (insn))
+      if (!TARGET_CPU_ZARCH && s390_execute_label (insn))
        {
          if (!curr_pool)
            curr_pool = s390_start_pool (&pool_list, insn);
@@ -5723,8 +5536,7 @@ s390_chunkify_start (void)
                  && GET_CODE (XEXP (constant, 0)) == UNSPEC
                  && XINT (XEXP (constant, 0), 1) == UNSPEC_LTREL_OFFSET)
                {
-                 if (pending_ltrel)
-                   abort ();
+                 gcc_assert (!pending_ltrel);
                  pending_ltrel = pool_ref;
                }
            }
@@ -5735,8 +5547,7 @@ s390_chunkify_start (void)
          if (curr_pool)
            s390_add_pool_insn (curr_pool, insn);
          /* An LTREL_BASE must follow within the same basic block.  */
-         if (pending_ltrel)
-           abort ();
+         gcc_assert (!pending_ltrel);
        }
 
       if (!curr_pool
@@ -5814,14 +5625,12 @@ s390_chunkify_start (void)
 
   if (curr_pool)
     s390_end_pool (curr_pool, NULL_RTX);
-  if (pending_ltrel)
-    abort ();
-
+  gcc_assert (!pending_ltrel);
 
   /* Find all labels that are branched into
      from an insn belonging to a different chunk.  */
 
-  far_labels = BITMAP_XMALLOC ();
+  far_labels = BITMAP_ALLOC (NULL);
 
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
@@ -5918,7 +5727,7 @@ s390_chunkify_start (void)
       }
 
 
-  BITMAP_XFREE (far_labels);
+  BITMAP_FREE (far_labels);
 
 
   /* Recompute insn addresses.  */
@@ -5970,2809 +5779,3112 @@ s390_chunkify_finish (struct constant_pool *pool_list)
         }
     }
 
-  /* Dump out all literal pools.  */
+  /* Dump out all literal pools.  */
+
+  for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
+    s390_dump_pool (curr_pool, 0);
+
+  /* Free pool list.  */
+
+  while (pool_list)
+    {
+      struct constant_pool *next = pool_list->next;
+      s390_free_pool (pool_list);
+      pool_list = next;
+    }
+}
+
+/* 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 (struct constant_pool *pool_list)
+{
+  struct constant_pool *curr_pool = NULL;
+  rtx insn;
+
+  /* Remove all pool placeholder insns.  */
+
+  for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
+    {
+      /* Did we insert an extra barrier?  Remove it.  */
+      rtx barrier = PREV_INSN (curr_pool->pool_insn);
+      rtx jump = barrier? PREV_INSN (barrier) : NULL_RTX;
+      rtx label = NEXT_INSN (curr_pool->pool_insn);
+
+      if (jump && GET_CODE (jump) == JUMP_INSN
+         && barrier && GET_CODE (barrier) == BARRIER
+         && label && GET_CODE (label) == CODE_LABEL
+         && GET_CODE (PATTERN (jump)) == SET
+         && SET_DEST (PATTERN (jump)) == pc_rtx
+         && GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF
+         && XEXP (SET_SRC (PATTERN (jump)), 0) == label)
+       {
+         remove_insn (jump);
+         remove_insn (barrier);
+         remove_insn (label);
+       }
+
+      remove_insn (curr_pool->pool_insn);
+    }
+
+  /* Remove all base register reload insns.  */
+
+  for (insn = get_insns (); insn; )
+    {
+      rtx next_insn = NEXT_INSN (insn);
+
+      if (GET_CODE (insn) == INSN
+         && GET_CODE (PATTERN (insn)) == SET
+         && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
+         && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_RELOAD_BASE)
+       remove_insn (insn);
+
+      insn = next_insn;
+    }
+
+  /* Free pool list.  */
+
+  while (pool_list)
+    {
+      struct constant_pool *next = pool_list->next;
+      s390_free_pool (pool_list);
+      pool_list = next;
+    }
+}
+
+
+/* Output the constant pool entry EXP in mode MODE with alignment ALIGN.  */
+
+void
+s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align)
+{
+  REAL_VALUE_TYPE r;
+
+  switch (GET_MODE_CLASS (mode))
+    {
+    case MODE_FLOAT:
+      gcc_assert (GET_CODE (exp) == CONST_DOUBLE);
+
+      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:
+      gcc_unreachable ();
+    }
+}
+
+
+/* Return an RTL expression representing the value of the return address
+   for the frame COUNT steps up from the current frame.  FRAME is the
+   frame pointer of that frame.  */
+
+rtx
+s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED)
+{
+  int offset;
+  rtx addr;
+
+  /* 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)
+    {
+      /* On non-z architectures branch splitting could overwrite r14.  */
+      if (TARGET_CPU_ZARCH)
+       return get_hard_reg_initial_val (Pmode, RETURN_REGNUM);
+      else
+       {
+         cfun_frame_layout.save_return_addr_p = true;
+         return gen_rtx_MEM (Pmode, return_address_pointer_rtx);
+       }
+    }
+
+  if (TARGET_PACKED_STACK)
+    offset = -2 * UNITS_PER_WORD;
+  else
+    offset = RETURN_REGNUM * UNITS_PER_WORD;
+
+  addr = plus_constant (frame, offset);
+  addr = memory_address (Pmode, addr);
+  return gen_rtx_MEM (Pmode, addr);
+}
+
+/* Return an RTL expression representing the back chain stored in
+   the current stack frame.  */
+
+rtx
+s390_back_chain_rtx (void)
+{
+  rtx chain;
+
+  gcc_assert (TARGET_BACKCHAIN);
+
+  if (TARGET_PACKED_STACK)
+    chain = plus_constant (stack_pointer_rtx,
+                          STACK_POINTER_OFFSET - UNITS_PER_WORD);
+  else
+    chain = stack_pointer_rtx;
+
+  chain = gen_rtx_MEM (Pmode, chain);
+  return chain;
+}
+
+/* Find first call clobbered register unused 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 (void)
+{
+  int i;
+  for (i = 0; i < 6; i++)
+    if (!regs_ever_live[i])
+      return i;
+  return 0;
+}
+
+
+/* Helper function for s390_regs_ever_clobbered.  Sets the fields in DATA for all 
+   clobbered hard regs in SETREG.  */
+
+static void
+s390_reg_clobbered_rtx (rtx setreg, rtx set_insn ATTRIBUTE_UNUSED, void *data)
+{
+  int *regs_ever_clobbered = (int *)data;
+  unsigned int i, regno;
+  enum machine_mode mode = GET_MODE (setreg);
+
+  if (GET_CODE (setreg) == SUBREG)
+    {
+      rtx inner = SUBREG_REG (setreg);
+      if (!GENERAL_REG_P (inner))
+       return;
+      regno = subreg_regno (setreg);
+    }
+  else if (GENERAL_REG_P (setreg))
+    regno = REGNO (setreg);
+  else
+    return;
+
+  for (i = regno;
+       i < regno + HARD_REGNO_NREGS (regno, mode);
+       i++)
+    regs_ever_clobbered[i] = 1;
+}
+
+/* Walks through all basic blocks of the current function looking
+   for clobbered hard regs using s390_reg_clobbered_rtx.  The fields
+   of the passed integer array REGS_EVER_CLOBBERED are set to one for
+   each of those regs.  */
+
+static void
+s390_regs_ever_clobbered (int *regs_ever_clobbered)
+{
+  basic_block cur_bb;
+  rtx cur_insn;
+  unsigned int i;
+
+  memset (regs_ever_clobbered, 0, 16 * sizeof (int));
+
+  /* For non-leaf functions we have to consider all call clobbered regs to be
+     clobbered.  */
+  if (!current_function_is_leaf)
+    {
+      for (i = 0; i < 16; i++)
+       regs_ever_clobbered[i] = call_really_used_regs[i];
+    }
+
+  /* Make the "magic" eh_return registers live if necessary.  For regs_ever_live
+     this work is done by liveness analysis (mark_regs_live_at_end).
+     Special care is needed for functions containing landing pads.  Landing pads
+     may use the eh registers, but the code which sets these registers is not
+     contained in that function.  Hence s390_regs_ever_clobbered is not able to
+     deal with this automatically.  */
+  if (current_function_calls_eh_return || cfun->machine->has_landing_pad_p)
+    for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM ; i++)
+      if (current_function_calls_eh_return 
+         || (cfun->machine->has_landing_pad_p 
+             && regs_ever_live [EH_RETURN_DATA_REGNO (i)]))
+       regs_ever_clobbered[EH_RETURN_DATA_REGNO (i)] = 1;
+
+  /* For nonlocal gotos all call-saved registers have to be saved.
+     This flag is also set for the unwinding code in libgcc.
+     See expand_builtin_unwind_init.  For regs_ever_live this is done by
+     reload.  */
+  if (current_function_has_nonlocal_label)
+    for (i = 0; i < 16; i++)
+      if (!call_really_used_regs[i])
+       regs_ever_clobbered[i] = 1;
+
+  FOR_EACH_BB (cur_bb)
+    {
+      FOR_BB_INSNS (cur_bb, cur_insn)
+       {
+         if (INSN_P (cur_insn))
+           note_stores (PATTERN (cur_insn),
+                        s390_reg_clobbered_rtx, 
+                        regs_ever_clobbered);
+       }
+    }
+}
+
+/* Determine the frame area which actually has to be accessed 
+   in the function epilogue. The values are stored at the 
+   given pointers AREA_BOTTOM (address of the lowest used stack
+   address) and AREA_TOP (address of the first item which does 
+   not belong to the stack frame).  */
+
+static void
+s390_frame_area (int *area_bottom, int *area_top)
+{
+  int b, t;
+  int i;
 
-  for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
-    s390_dump_pool (curr_pool, 0);
+  b = INT_MAX;
+  t = INT_MIN;
 
-  /* Free pool list.  */
+  if (cfun_frame_layout.first_restore_gpr != -1)
+    {
+      b = (cfun_frame_layout.gprs_offset
+          + cfun_frame_layout.first_restore_gpr * UNITS_PER_WORD);
+      t = b + (cfun_frame_layout.last_restore_gpr
+              - cfun_frame_layout.first_restore_gpr + 1) * UNITS_PER_WORD;
+    }
 
-  while (pool_list)
+  if (TARGET_64BIT && cfun_save_high_fprs_p)
     {
-      struct constant_pool *next = pool_list->next;
-      s390_free_pool (pool_list);
-      pool_list = next;
+      b = MIN (b, cfun_frame_layout.f8_offset);
+      t = MAX (t, (cfun_frame_layout.f8_offset
+                  + cfun_frame_layout.high_fprs * 8));
     }
+
+  if (!TARGET_64BIT)
+    for (i = 2; i < 4; i++)
+      if (cfun_fpr_bit_p (i))
+       {
+         b = MIN (b, cfun_frame_layout.f4_offset + (i - 2) * 8);
+         t = MAX (t, cfun_frame_layout.f4_offset + (i - 1) * 8);
+       }
+  
+  *area_bottom = b;
+  *area_top = t;
 }
 
-/* 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.  */
+/* Fill cfun->machine with info about register usage of current function.
+   Return in CLOBBERED_REGS which GPRs are currently considered set.  */
 
 static void
-s390_chunkify_cancel (struct constant_pool *pool_list)
+s390_register_info (int clobbered_regs[])
 {
-  struct constant_pool *curr_pool = NULL;
-  rtx insn;
-
-  /* Remove all pool placeholder insns.  */
-
-  for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
-    {
-      /* Did we insert an extra barrier?  Remove it.  */
-      rtx barrier = PREV_INSN (curr_pool->pool_insn);
-      rtx jump = barrier? PREV_INSN (barrier) : NULL_RTX;
-      rtx label = NEXT_INSN (curr_pool->pool_insn);
+  int i, j;
 
-      if (jump && GET_CODE (jump) == JUMP_INSN
-         && barrier && GET_CODE (barrier) == BARRIER
-         && label && GET_CODE (label) == CODE_LABEL
-         && GET_CODE (PATTERN (jump)) == SET
-         && SET_DEST (PATTERN (jump)) == pc_rtx
-         && GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF
-         && XEXP (SET_SRC (PATTERN (jump)), 0) == label)
+  /* fprs 8 - 15 are call saved for 64 Bit ABI.  */
+  cfun_frame_layout.fpr_bitmap = 0;
+  cfun_frame_layout.high_fprs = 0;
+  if (TARGET_64BIT)
+    for (i = 24; i < 32; i++)
+      if (regs_ever_live[i] && !global_regs[i])
        {
-         remove_insn (jump);
-         remove_insn (barrier);
-         remove_insn (label);
+         cfun_set_fpr_bit (i - 16);
+         cfun_frame_layout.high_fprs++;
        }
 
-      remove_insn (curr_pool->pool_insn);
-    }
+  /* Find first and last gpr to be saved.  We trust regs_ever_live
+     data, except that we don't save and restore global registers.
 
-  /* Remove all base register reload insns.  */
+     Also, all registers with special meaning to the compiler need
+     to be handled extra.  */
 
-  for (insn = get_insns (); insn; )
-    {
-      rtx next_insn = NEXT_INSN (insn);
+  s390_regs_ever_clobbered (clobbered_regs);
 
-      if (GET_CODE (insn) == INSN
-         && GET_CODE (PATTERN (insn)) == SET
-         && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
-         && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_RELOAD_BASE)
-       remove_insn (insn);
+  for (i = 0; i < 16; i++)
+    clobbered_regs[i] = clobbered_regs[i] && !global_regs[i] && !fixed_regs[i];
 
-      insn = next_insn;
-    }
+  if (frame_pointer_needed)
+    clobbered_regs[HARD_FRAME_POINTER_REGNUM] = 1;
 
-  /* Free pool list.  */
+  if (flag_pic)
+    clobbered_regs[PIC_OFFSET_TABLE_REGNUM] 
+      |= regs_ever_live[PIC_OFFSET_TABLE_REGNUM];
+
+  clobbered_regs[BASE_REGNUM] 
+    |= (cfun->machine->base_reg
+        && REGNO (cfun->machine->base_reg) == BASE_REGNUM);
+
+  clobbered_regs[RETURN_REGNUM]
+    |= (!current_function_is_leaf
+       || TARGET_TPF_PROFILING
+       || cfun->machine->split_branches_pending_p
+       || cfun_frame_layout.save_return_addr_p
+       || current_function_calls_eh_return
+       || current_function_stdarg);
+
+  clobbered_regs[STACK_POINTER_REGNUM]
+    |= (!current_function_is_leaf
+       || TARGET_TPF_PROFILING
+       || cfun_save_high_fprs_p
+       || get_frame_size () > 0
+       || current_function_calls_alloca
+       || current_function_stdarg);
 
-  while (pool_list)
+  for (i = 6; i < 16; i++)
+    if (regs_ever_live[i] || clobbered_regs[i])
+      break;
+  for (j = 15; j > i; j--)
+    if (regs_ever_live[j] || clobbered_regs[j])
+      break;
+
+  if (i == 16)
     {
-      struct constant_pool *next = pool_list->next;
-      s390_free_pool (pool_list);
-      pool_list = next;
+      /* Nothing to save/restore.  */
+      cfun_frame_layout.first_save_gpr_slot = -1;
+      cfun_frame_layout.last_save_gpr_slot = -1;
+      cfun_frame_layout.first_save_gpr = -1;
+      cfun_frame_layout.first_restore_gpr = -1;
+      cfun_frame_layout.last_save_gpr = -1;
+      cfun_frame_layout.last_restore_gpr = -1;
     }
-}
-
+  else
+    {
+      /* Save slots for gprs from i to j.  */
+      cfun_frame_layout.first_save_gpr_slot = i;
+      cfun_frame_layout.last_save_gpr_slot = j;
 
-/* Output the constant pool entry EXP in mode MODE with alignment ALIGN.  */
+      for (i = cfun_frame_layout.first_save_gpr_slot; 
+          i < cfun_frame_layout.last_save_gpr_slot + 1; 
+          i++)
+       if (clobbered_regs[i])
+         break;
 
-void
-s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align)
-{
-  REAL_VALUE_TYPE r;
+      for (j = cfun_frame_layout.last_save_gpr_slot; j > i; j--)
+       if (clobbered_regs[j])
+         break;
+      
+      if (i == cfun_frame_layout.last_save_gpr_slot + 1)
+       {
+         /* Nothing to save/restore.  */
+         cfun_frame_layout.first_save_gpr = -1;
+         cfun_frame_layout.first_restore_gpr = -1;
+         cfun_frame_layout.last_save_gpr = -1;
+         cfun_frame_layout.last_restore_gpr = -1;
+       }
+      else
+       {
+         /* Save / Restore from gpr i to j.  */
+         cfun_frame_layout.first_save_gpr = i;
+         cfun_frame_layout.first_restore_gpr = i;
+         cfun_frame_layout.last_save_gpr = j;
+         cfun_frame_layout.last_restore_gpr = j;
+       }
+    }
 
-  switch (GET_MODE_CLASS (mode))
+  if (current_function_stdarg)
     {
-    case MODE_FLOAT:
-      if (GET_CODE (exp) != CONST_DOUBLE)
-       abort ();
+      /* Varargs functions need to save gprs 2 to 6.  */
+      if (cfun->va_list_gpr_size
+         && current_function_args_info.gprs < GP_ARG_NUM_REG)
+       {
+         int min_gpr = current_function_args_info.gprs;
+         int max_gpr = min_gpr + cfun->va_list_gpr_size;
+         if (max_gpr > GP_ARG_NUM_REG)
+           max_gpr = GP_ARG_NUM_REG;
 
-      REAL_VALUE_FROM_CONST_DOUBLE (r, exp);
-      assemble_real (r, mode, align);
-      break;
+         if (cfun_frame_layout.first_save_gpr == -1
+             || cfun_frame_layout.first_save_gpr > 2 + min_gpr)
+           {
+             cfun_frame_layout.first_save_gpr = 2 + min_gpr;
+             cfun_frame_layout.first_save_gpr_slot = 2 + min_gpr;
+           }
 
-    case MODE_INT:
-      assemble_integer (exp, GET_MODE_SIZE (mode), align, 1);
-      break;
+         if (cfun_frame_layout.last_save_gpr == -1
+             || cfun_frame_layout.last_save_gpr < 2 + max_gpr - 1)
+           {
+             cfun_frame_layout.last_save_gpr = 2 + max_gpr - 1;
+             cfun_frame_layout.last_save_gpr_slot = 2 + max_gpr - 1;
+           }
+       }
 
-    default:
-      abort ();
+      /* Mark f0, f2 for 31 bit and f0-f4 for 64 bit to be saved.  */
+      if (TARGET_HARD_FLOAT && cfun->va_list_fpr_size
+         && current_function_args_info.fprs < FP_ARG_NUM_REG)
+       {
+         int min_fpr = current_function_args_info.fprs;
+         int max_fpr = min_fpr + cfun->va_list_fpr_size;
+         if (max_fpr > FP_ARG_NUM_REG)
+           max_fpr = FP_ARG_NUM_REG;
+
+         /* ??? This is currently required to ensure proper location
+            of the fpr save slots within the va_list save area.  */
+         if (TARGET_PACKED_STACK)
+           min_fpr = 0;
+
+         for (i = min_fpr; i < max_fpr; i++)
+           cfun_set_fpr_bit (i);
+       }
     }
-}
 
+  if (!TARGET_64BIT)
+    for (i = 2; i < 4; i++)
+      if (regs_ever_live[i + 16] && !global_regs[i + 16])
+       cfun_set_fpr_bit (i);
+}
 
-/* Rework the prologue/epilogue to avoid saving/restoring
-   registers unnecessarily.  */
+/* Fill cfun->machine with info about frame of current function.  */
 
 static void
-s390_optimize_prologue (void)
+s390_frame_info (void)
 {
-  rtx insn, new_insn, next_insn;
-
-  /* Do a final recompute of the frame-related data.  */
-
-  s390_update_frame_layout ();
-
-  /* If all special registers are in fact used, there's nothing we
-     can do, so no point in walking the insn list.  */
-
-  if (cfun_frame_layout.first_save_gpr <= BASE_REGNUM 
-      && cfun_frame_layout.last_save_gpr >= BASE_REGNUM
-      && (TARGET_CPU_ZARCH 
-          || (cfun_frame_layout.first_save_gpr <= RETURN_REGNUM 
-              && cfun_frame_layout.last_save_gpr >= RETURN_REGNUM)))
-    return;
-
-  /* Search for prologue/epilogue insns and replace them.  */
+  int i;
 
-  for (insn = get_insns (); insn; insn = next_insn)
+  cfun_frame_layout.frame_size = get_frame_size ();
+  if (!TARGET_64BIT && cfun_frame_layout.frame_size > 0x7fff0000)
+    fatal_error ("total size of local variables exceeds architecture limit");
+  
+  if (!TARGET_PACKED_STACK)
     {
-      int first, last, off;
-      rtx set, base, offset;
-
-      next_insn = NEXT_INSN (insn);
-
-      if (GET_CODE (insn) != INSN)
-       continue;
-
-      if (GET_CODE (PATTERN (insn)) == PARALLEL
-         && store_multiple_operation (PATTERN (insn), VOIDmode))
+      cfun_frame_layout.backchain_offset = 0;
+      cfun_frame_layout.f0_offset = 16 * UNITS_PER_WORD;
+      cfun_frame_layout.f4_offset = cfun_frame_layout.f0_offset + 2 * 8;
+      cfun_frame_layout.f8_offset = -cfun_frame_layout.high_fprs * 8;
+      cfun_frame_layout.gprs_offset = (cfun_frame_layout.first_save_gpr_slot
+                                      * UNITS_PER_WORD);
+    }
+  else if (TARGET_BACKCHAIN) /* kernel stack layout */
+    {
+      cfun_frame_layout.backchain_offset = (STACK_POINTER_OFFSET
+                                           - UNITS_PER_WORD);
+      cfun_frame_layout.gprs_offset 
+       = (cfun_frame_layout.backchain_offset 
+          - (STACK_POINTER_REGNUM - cfun_frame_layout.first_save_gpr_slot + 1)
+          * UNITS_PER_WORD);
+         
+      if (TARGET_64BIT)
        {
-         set = XVECEXP (PATTERN (insn), 0, 0);
-         first = REGNO (SET_SRC (set));
-         last = first + XVECLEN (PATTERN (insn), 0) - 1;
-         offset = const0_rtx;
-         base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
-         off = INTVAL (offset);
+         cfun_frame_layout.f4_offset 
+           = (cfun_frame_layout.gprs_offset
+              - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
+         
+         cfun_frame_layout.f0_offset 
+           = (cfun_frame_layout.f4_offset 
+              - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
+       }
+      else
+       {
+         /* On 31 bit we have to care about alignment of the
+            floating point regs to provide fastest access.  */
+         cfun_frame_layout.f0_offset 
+           = ((cfun_frame_layout.gprs_offset 
+               & ~(STACK_BOUNDARY / BITS_PER_UNIT - 1))
+              - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
+         
+         cfun_frame_layout.f4_offset 
+           = (cfun_frame_layout.f0_offset
+              - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
+       }
+    }
+  else /* no backchain */
+    {
+      cfun_frame_layout.f4_offset 
+       = (STACK_POINTER_OFFSET
+          - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
+      
+      cfun_frame_layout.f0_offset 
+       = (cfun_frame_layout.f4_offset
+          - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
+      
+      cfun_frame_layout.gprs_offset 
+       = cfun_frame_layout.f0_offset - cfun_gprs_save_area_size;
+    }
 
-         if (GET_CODE (base) != REG || off < 0)
-           continue;
-         if (REGNO (base) != STACK_POINTER_REGNUM
-             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
-           continue;
-         if (first > BASE_REGNUM || last < BASE_REGNUM)
-           continue;
+  if (current_function_is_leaf
+      && !TARGET_TPF_PROFILING
+      && cfun_frame_layout.frame_size == 0
+      && !cfun_save_high_fprs_p
+      && !current_function_calls_alloca
+      && !current_function_stdarg)
+    return;
 
-         if (cfun_frame_layout.first_save_gpr != -1)
-           {
-             new_insn  = save_gprs (base, 
-                                    off + (cfun_frame_layout.first_save_gpr
-                                           - first) * UNITS_PER_WORD, 
-                                    cfun_frame_layout.first_save_gpr,
-                                    cfun_frame_layout.last_save_gpr);
-             new_insn = emit_insn_before (new_insn, insn);
-             INSN_ADDRESSES_NEW (new_insn, -1);
-           }
+  if (!TARGET_PACKED_STACK)
+    cfun_frame_layout.frame_size += (STACK_POINTER_OFFSET
+                                    + current_function_outgoing_args_size
+                                    + cfun_frame_layout.high_fprs * 8);
+  else
+    {
+      if (TARGET_BACKCHAIN)
+       cfun_frame_layout.frame_size += UNITS_PER_WORD;
 
-         remove_insn (insn);
-         continue;
-       }
+      /* No alignment trouble here because f8-f15 are only saved under 
+        64 bit.  */
+      cfun_frame_layout.f8_offset = (MIN (MIN (cfun_frame_layout.f0_offset,
+                                              cfun_frame_layout.f4_offset),
+                                         cfun_frame_layout.gprs_offset)
+                                    - cfun_frame_layout.high_fprs * 8);
 
-      if (GET_CODE (PATTERN (insn)) == SET
-         && GET_CODE (SET_SRC (PATTERN (insn))) == REG
-         && (REGNO (SET_SRC (PATTERN (insn))) == BASE_REGNUM
-             || (!TARGET_CPU_ZARCH
-                 && REGNO (SET_SRC (PATTERN (insn))) == RETURN_REGNUM))
-         && GET_CODE (SET_DEST (PATTERN (insn))) == MEM)
-       {
-         set = PATTERN (insn);
-         first = REGNO (SET_SRC (set));
-         offset = const0_rtx;
-         base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
-         off = INTVAL (offset);
+      cfun_frame_layout.frame_size += cfun_frame_layout.high_fprs * 8;
 
-         if (GET_CODE (base) != REG || off < 0)
-           continue;
-         if (REGNO (base) != STACK_POINTER_REGNUM
-             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
-           continue;
-         if (cfun_frame_layout.first_save_gpr != -1)
-           {
-             new_insn = save_gprs (base, 
-                                   off + (cfun_frame_layout.first_save_gpr 
-                                          - first) * UNITS_PER_WORD, 
-                                   cfun_frame_layout.first_save_gpr,
-                                   cfun_frame_layout.last_save_gpr);
-             new_insn = emit_insn_before (new_insn, insn);
-             INSN_ADDRESSES_NEW (new_insn, -1);
-           }
+      for (i = 0; i < 8; i++)
+       if (cfun_fpr_bit_p (i))
+         cfun_frame_layout.frame_size += 8;
+      
+      cfun_frame_layout.frame_size += cfun_gprs_save_area_size;
+      
+      /* If under 31 bit an odd number of gprs has to be saved we have to adjust
+        the frame size to sustain 8 byte alignment of stack frames.  */
+      cfun_frame_layout.frame_size = ((cfun_frame_layout.frame_size +
+                                      STACK_BOUNDARY / BITS_PER_UNIT - 1)
+                                     & ~(STACK_BOUNDARY / BITS_PER_UNIT - 1));
 
-         remove_insn (insn);
-         continue;
-       }
+      cfun_frame_layout.frame_size += current_function_outgoing_args_size;
+    }
+}
 
-      if (GET_CODE (PATTERN (insn)) == PARALLEL
-         && load_multiple_operation (PATTERN (insn), VOIDmode))
-       {
-         set = XVECEXP (PATTERN (insn), 0, 0);
-         first = REGNO (SET_DEST (set));
-         last = first + XVECLEN (PATTERN (insn), 0) - 1;
-         offset = const0_rtx;
-         base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
-         off = INTVAL (offset);
+/* Generate frame layout.  Fills in register and frame data for the current
+   function in cfun->machine.  This routine can be called multiple times;
+   it will re-do the complete frame layout every time.  */
 
-         if (GET_CODE (base) != REG || off < 0)
-           continue;
-         if (REGNO (base) != STACK_POINTER_REGNUM
-             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
-           continue;
-         if (first > BASE_REGNUM || last < BASE_REGNUM)
-           continue;
+static void
+s390_init_frame_layout (void)
+{
+  HOST_WIDE_INT frame_size;
+  int base_used;
+  int clobbered_regs[16];
 
-         if (cfun_frame_layout.first_restore_gpr != -1)
-           {
-             new_insn = restore_gprs (base, 
-                                      off + (cfun_frame_layout.first_restore_gpr
-                                             - first) * UNITS_PER_WORD, 
-                                      cfun_frame_layout.first_restore_gpr,
-                                      cfun_frame_layout.last_restore_gpr);
-             new_insn = emit_insn_before (new_insn, insn);
-             INSN_ADDRESSES_NEW (new_insn, -1);
-           }
+  /* On S/390 machines, we may need to perform branch splitting, which
+     will require both base and return address register.  We have no
+     choice but to assume we're going to need them until right at the
+     end of the machine dependent reorg phase.  */
+  if (!TARGET_CPU_ZARCH)
+    cfun->machine->split_branches_pending_p = true;
 
-         remove_insn (insn);
-         continue;
-       }
+  do
+    {
+      frame_size = cfun_frame_layout.frame_size;
 
-      if (GET_CODE (PATTERN (insn)) == SET
-         && GET_CODE (SET_DEST (PATTERN (insn))) == REG
-         && (REGNO (SET_DEST (PATTERN (insn))) == BASE_REGNUM
-             || (!TARGET_CPU_ZARCH
-                 && REGNO (SET_DEST (PATTERN (insn))) == RETURN_REGNUM))
-         && GET_CODE (SET_SRC (PATTERN (insn))) == MEM)
-       {
-         set = PATTERN (insn);
-         first = REGNO (SET_DEST (set));
-         offset = const0_rtx;
-         base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
-         off = INTVAL (offset);
+      /* Try to predict whether we'll need the base register.  */
+      base_used = cfun->machine->split_branches_pending_p
+                 || current_function_uses_const_pool
+                 || (!DISP_IN_RANGE (-frame_size)
+                     && !CONST_OK_FOR_K (-frame_size));
 
-         if (GET_CODE (base) != REG || off < 0)
-           continue;
-         if (REGNO (base) != STACK_POINTER_REGNUM
-             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
-           continue;
-         if (cfun_frame_layout.first_restore_gpr != -1)
-           {
-             new_insn = restore_gprs (base, 
-                                      off + (cfun_frame_layout.first_restore_gpr 
-                                             - first) * UNITS_PER_WORD,
-                                      cfun_frame_layout.first_restore_gpr,
-                                      cfun_frame_layout.last_restore_gpr);
-             new_insn = emit_insn_before (new_insn, insn);
-             INSN_ADDRESSES_NEW (new_insn, -1);
-           }
+      /* Decide which register to use as literal pool base.  In small
+        leaf functions, try to use an unused call-clobbered register
+        as base register to avoid save/restore overhead.  */
+      if (!base_used)
+       cfun->machine->base_reg = NULL_RTX;
+      else if (current_function_is_leaf && !regs_ever_live[5])
+       cfun->machine->base_reg = gen_rtx_REG (Pmode, 5);
+      else
+       cfun->machine->base_reg = gen_rtx_REG (Pmode, BASE_REGNUM);
 
-         remove_insn (insn);
-         continue;
-       }
+      s390_register_info (clobbered_regs);
+      s390_frame_info ();
     }
+  while (frame_size != cfun_frame_layout.frame_size);
 }
 
-/* Perform machine-dependent processing.  */
+/* Update frame layout.  Recompute actual register save data based on
+   current info and update regs_ever_live for the special registers.
+   May be called multiple times, but may never cause *more* registers
+   to be saved than s390_init_frame_layout allocated room for.  */
 
 static void
-s390_reorg (void)
+s390_update_frame_layout (void)
 {
-  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 ();
-
+  int clobbered_regs[16];
 
-  /* Install the main literal pool and the associated base
-     register load insns.
+  s390_register_info (clobbered_regs);
 
-     In addition, there are two problematic situations we need
-     to correct:
+  regs_ever_live[BASE_REGNUM] = clobbered_regs[BASE_REGNUM];
+  regs_ever_live[RETURN_REGNUM] = clobbered_regs[RETURN_REGNUM];
+  regs_ever_live[STACK_POINTER_REGNUM] = clobbered_regs[STACK_POINTER_REGNUM];
 
-     - the literal pool might be > 4096 bytes in size, so that
-       some of its elements cannot be directly accessed
+  if (cfun->machine->base_reg)
+    regs_ever_live[REGNO (cfun->machine->base_reg)] = 1;
+}
 
-     - a branch target might be > 64K away from the branch, so that
-       it is not possible to use a PC-relative instruction.
+/* Return nonzero if register OLD_REG can be renamed to register NEW_REG.  */
 
-     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.
+bool
+s390_hard_regno_rename_ok (unsigned int old_reg, unsigned int new_reg)
+{
+   /* Once we've decided upon a register to use as base register, it must
+      no longer be used for any other purpose.  */
+  if (cfun->machine->base_reg)
+    if (REGNO (cfun->machine->base_reg) == old_reg
+       || REGNO (cfun->machine->base_reg) == new_reg)
+      return false;
 
-     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.
+  return true;
+}
 
-     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
-     strictly decreases the total number of PC-relative branches
-     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.)  */
+/* Return true if register FROM can be eliminated via register TO.  */
 
-  for (;;)
+bool
+s390_can_eliminate (int from, int to)
+{
+  /* On zSeries machines, we have not marked the base register as fixed.
+     Instead, we have an elimination rule BASE_REGNUM -> BASE_REGNUM.
+     If a function requires the base register, we say here that this
+     elimination cannot be performed.  This will cause reload to free
+     up the base register (as if it were fixed).  On the other hand,
+     if the current function does *not* require the base register, we
+     say here the elimination succeeds, which in turn allows reload
+     to allocate the base register for any other purpose.  */
+  if (from == BASE_REGNUM && to == BASE_REGNUM)
     {
-      struct constant_pool *pool = NULL;
-
-      /* Collect the literal pool.  */
-      if (!pool_overflow)
+      if (TARGET_CPU_ZARCH)
        {
-         pool = s390_mainpool_start ();
-         if (!pool)
-           pool_overflow = true;
+         s390_init_frame_layout ();
+         return cfun->machine->base_reg == NULL_RTX;
        }
 
-      /* If literal pool overflowed, start to chunkify it.  */
-      if (pool_overflow)
-        pool = s390_chunkify_start ();
-
-      /* Split out-of-range branches.  If this has created new
-        literal pool entries, cancel current chunk list and
-        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_overflow)
-            s390_chunkify_cancel (pool);
-         else
-            s390_mainpool_cancel (pool);
+      return false;
+    }
 
-          continue;
-        }
+  /* Everything else must point into the stack frame.  */
+  gcc_assert (to == STACK_POINTER_REGNUM
+             || to == HARD_FRAME_POINTER_REGNUM);
 
-      /* If we made it up to here, both conditions are satisfied.
-        Finish up literal pool related changes.  */
-      if (pool_overflow)
-       s390_chunkify_finish (pool);
-      else
-       s390_mainpool_finish (pool);
+  gcc_assert (from == FRAME_POINTER_REGNUM
+             || from == ARG_POINTER_REGNUM
+             || from == RETURN_ADDRESS_POINTER_REGNUM);
 
-      /* We're done splitting branches.  */
-      cfun->machine->split_branches_pending_p = false;
-      break;
-    }
+  /* Make sure we actually saved the return address.  */
+  if (from == RETURN_ADDRESS_POINTER_REGNUM)
+    if (!current_function_calls_eh_return
+       && !current_function_stdarg
+       && !cfun_frame_layout.save_return_addr_p)
+      return false;
 
-  s390_optimize_prologue ();
+  return true;
 }
 
+/* Return offset between register FROM and TO initially after prolog.  */
 
-/* Return an RTL expression representing the value of the return address
-   for the frame COUNT steps up from the current frame.  FRAME is the
-   frame pointer of that frame.  */
-
-rtx
-s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED)
+HOST_WIDE_INT
+s390_initial_elimination_offset (int from, int to)
 {
-  int offset;
-  rtx addr;
+  HOST_WIDE_INT offset;
+  int index;
 
-  /* Without backchain, we fail for all but the current frame.  */
+  /* ??? Why are we called for non-eliminable pairs?  */
+  if (!s390_can_eliminate (from, to))
+    return 0;
 
-  if (!TARGET_BACKCHAIN && count > 0)
-    return NULL_RTX;
+  switch (from)
+    {
+    case FRAME_POINTER_REGNUM:
+      offset = (get_frame_size() 
+               + STACK_POINTER_OFFSET
+               + current_function_outgoing_args_size);
+      break;
 
-  /* For the current frame, we need to make sure the initial
-     value of RETURN_REGNUM is actually saved.  */
+    case ARG_POINTER_REGNUM:
+      s390_init_frame_layout ();
+      offset = cfun_frame_layout.frame_size + STACK_POINTER_OFFSET;
+      break;
 
-  if (count == 0)
-    {
-      cfun_frame_layout.save_return_addr_p = true;
-      return gen_rtx_MEM (Pmode, return_address_pointer_rtx);
-    }
+    case RETURN_ADDRESS_POINTER_REGNUM:
+      s390_init_frame_layout ();
+      index = RETURN_REGNUM - cfun_frame_layout.first_save_gpr_slot;
+      gcc_assert (index >= 0);
+      offset = cfun_frame_layout.frame_size + cfun_frame_layout.gprs_offset;
+      offset += index * UNITS_PER_WORD;
+      break;
 
-  if (TARGET_PACKED_STACK)
-    offset = -2 * UNITS_PER_WORD;
-  else
-    offset = RETURN_REGNUM * UNITS_PER_WORD;
+    case BASE_REGNUM:
+      offset = 0;
+      break;
 
-  addr = plus_constant (frame, offset);
-  addr = memory_address (Pmode, addr);
-  return gen_rtx_MEM (Pmode, addr);
+    default:
+      gcc_unreachable ();
+    }
+
+  return offset;
 }
 
-/* Return an RTL expression representing the back chain stored in
-   the current stack frame.  */
+/* Emit insn to save fpr REGNUM at offset OFFSET relative
+   to register BASE.  Return generated insn.  */
 
-rtx
-s390_back_chain_rtx (void)
+static rtx
+save_fpr (rtx base, int offset, int regnum)
 {
-  rtx chain;
-
-  gcc_assert (TARGET_BACKCHAIN);
+  rtx addr;
+  addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
 
-  if (TARGET_PACKED_STACK)
-    chain = plus_constant (stack_pointer_rtx,
-                          STACK_POINTER_OFFSET - UNITS_PER_WORD);
+  if (regnum >= 16 && regnum <= (16 + FP_ARG_NUM_REG))
+    set_mem_alias_set (addr, get_varargs_alias_set ());
   else
-    chain = stack_pointer_rtx;
+    set_mem_alias_set (addr, get_frame_alias_set ());
 
-  chain = gen_rtx_MEM (Pmode, chain);
-  return chain;
+  return emit_move_insn (addr, gen_rtx_REG (DFmode, regnum));
 }
 
-/* Find first call clobbered register unused in a function.
-   This could be used as base register in a leaf function
-   or for holding the return address before epilogue.  */
+/* Emit insn to restore fpr REGNUM from offset OFFSET relative
+   to register BASE.  Return generated insn.  */
 
-static int
-find_unused_clobbered_reg (void)
+static rtx
+restore_fpr (rtx base, int offset, int regnum)
 {
-  int i;
-  for (i = 0; i < 6; i++)
-    if (!regs_ever_live[i])
-      return i;
-  return 0;
+  rtx addr;
+  addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
+  set_mem_alias_set (addr, get_frame_alias_set ());
+
+  return emit_move_insn (gen_rtx_REG (DFmode, regnum), addr);
 }
 
-/* Determine the frame area which actually has to be accessed 
-   in the function epilogue. The values are stored at the 
-   given pointers AREA_BOTTOM (address of the lowest used stack
-   address) and AREA_TOP (address of the first item which does 
-   not belong to the stack frame).  */
+/* Generate insn to save registers FIRST to LAST into
+   the register save area located at offset OFFSET
+   relative to register BASE.  */
 
-static void
-s390_frame_area (int *area_bottom, int *area_top)
+static rtx
+save_gprs (rtx base, int offset, int first, int last)
 {
-  int b, t;
+  rtx addr, insn, note;
   int i;
 
-  b = INT_MAX;
-  t = INT_MIN;
+  addr = plus_constant (base, offset);
+  addr = gen_rtx_MEM (Pmode, addr);
 
-  if (cfun_frame_layout.first_restore_gpr != -1)
+  set_mem_alias_set (addr, get_frame_alias_set ());
+
+  /* Special-case single register.  */
+  if (first == last)
     {
-      b = (cfun_frame_layout.gprs_offset
-          + cfun_frame_layout.first_restore_gpr * UNITS_PER_WORD);
-      t = b + (cfun_frame_layout.last_restore_gpr
-              - cfun_frame_layout.first_restore_gpr + 1) * UNITS_PER_WORD;
+      if (TARGET_64BIT)
+        insn = gen_movdi (addr, gen_rtx_REG (Pmode, first));
+      else
+        insn = gen_movsi (addr, gen_rtx_REG (Pmode, first));
+
+      RTX_FRAME_RELATED_P (insn) = 1;
+      return insn;
     }
 
-  if (TARGET_64BIT && cfun_save_high_fprs_p)
+
+  insn = gen_store_multiple (addr,
+                            gen_rtx_REG (Pmode, first),
+                            GEN_INT (last - first + 1));
+
+  if (first <= 6 && current_function_stdarg)
+    for (i = 0; i < XVECLEN (PATTERN (insn), 0); i++)
+      {
+       rtx mem = XEXP (XVECEXP (PATTERN (insn), 0, i), 0);
+       
+       if (first + i <= 6)
+         set_mem_alias_set (mem, get_varargs_alias_set ());
+      }
+
+  /* We need to set the FRAME_RELATED flag on all SETs
+     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 ...
+
+     ??? Unfortunately, it is not enough to simply not the
+     FRAME_RELATED flags for those SETs, because the first SET
+     of the PARALLEL is always treated as if it had the flag
+     set, even if it does not.  Therefore we emit a new pattern
+     without those registers as REG_FRAME_RELATED_EXPR note.  */
+
+  if (first >= 6)
     {
-      b = MIN (b, cfun_frame_layout.f8_offset);
-      t = MAX (t, (cfun_frame_layout.f8_offset
-                  + cfun_frame_layout.high_fprs * 8));
+      rtx pat = PATTERN (insn);
+
+      for (i = 0; i < XVECLEN (pat, 0); i++)
+       if (GET_CODE (XVECEXP (pat, 0, i)) == SET)
+         RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1;
+
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
+  else if (last >= 6)
+    {
+      addr = plus_constant (base, offset + (6 - first) * UNITS_PER_WORD);
+      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,
+                          note, REG_NOTES (insn));
+
+      for (i = 0; i < XVECLEN (note, 0); i++)
+       if (GET_CODE (XVECEXP (note, 0, i)) == SET)
+         RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1;
+
+      RTX_FRAME_RELATED_P (insn) = 1;
     }
 
-  if (!TARGET_64BIT)
-    for (i = 2; i < 4; i++)
-      if (cfun_fpr_bit_p (i))
-       {
-         b = MIN (b, cfun_frame_layout.f4_offset + (i - 2) * 8);
-         t = MAX (t, cfun_frame_layout.f4_offset + (i - 1) * 8);
-       }
-  
-  *area_bottom = b;
-  *area_top = t;
+  return insn;
 }
 
-/* Fill cfun->machine with info about register usage of current function.
-   Return in LIVE_REGS which GPRs are currently considered live.  */
+/* Generate insn to restore registers FIRST to LAST from
+   the register save area located at offset OFFSET
+   relative to register BASE.  */
 
-static void
-s390_register_info (int live_regs[])
+static rtx
+restore_gprs (rtx base, int offset, int first, int last)
 {
-  int i, j;
+  rtx addr, insn;
 
-  /* fprs 8 - 15 are call saved for 64 Bit ABI.  */
-  cfun_frame_layout.fpr_bitmap = 0;
-  cfun_frame_layout.high_fprs = 0;
-  if (TARGET_64BIT)
-    for (i = 24; i < 32; i++)
-      if (regs_ever_live[i] && !global_regs[i])
-       {
-         cfun_set_fpr_bit (i - 16);
-         cfun_frame_layout.high_fprs++;
-       }
+  addr = plus_constant (base, offset);
+  addr = gen_rtx_MEM (Pmode, addr);
+  set_mem_alias_set (addr, get_frame_alias_set ());
 
-  /* Find first and last gpr to be saved.  We trust regs_ever_live
-     data, except that we don't save and restore global registers.
+  /* Special-case single register.  */
+  if (first == last)
+    {
+      if (TARGET_64BIT)
+        insn = gen_movdi (gen_rtx_REG (Pmode, first), addr);
+      else
+        insn = gen_movsi (gen_rtx_REG (Pmode, first), addr);
 
-     Also, all registers with special meaning to the compiler need
-     to be handled extra.  */
+      return insn;
+    }
 
-  for (i = 0; i < 16; i++)
-    live_regs[i] = regs_ever_live[i] && !global_regs[i];
+  insn = gen_load_multiple (gen_rtx_REG (Pmode, first),
+                           addr,
+                           GEN_INT (last - first + 1));
+  return insn;
+}
 
-  if (flag_pic)
-    live_regs[PIC_OFFSET_TABLE_REGNUM] 
-    = regs_ever_live[PIC_OFFSET_TABLE_REGNUM];
-
-  live_regs[BASE_REGNUM] 
-    = cfun->machine->base_reg
-      && REGNO (cfun->machine->base_reg) == BASE_REGNUM;
-
-  live_regs[RETURN_REGNUM]
-    = cfun->machine->split_branches_pending_p
-      || cfun_frame_layout.save_return_addr_p;
-
-  live_regs[STACK_POINTER_REGNUM]
-    = !current_function_is_leaf
-      || TARGET_TPF_PROFILING
-      || cfun_save_high_fprs_p
-      || get_frame_size () > 0
-      || current_function_calls_alloca
-      || current_function_stdarg;
-  
-  for (i = 6; i < 16; i++)
-    if (live_regs[i])
-      break;
-  for (j = 15; j > i; j--)
-    if (live_regs[j])
-      break;
+/* Return insn sequence to load the GOT register.  */
 
-  if (i == 16)
+static GTY(()) rtx got_symbol;
+rtx
+s390_load_got (void)
+{
+  rtx insns;
+
+  if (!got_symbol)
     {
-      /* Nothing to save/restore.  */
-      cfun_frame_layout.first_save_gpr = -1;
-      cfun_frame_layout.first_restore_gpr = -1;
-      cfun_frame_layout.last_save_gpr = -1;
-      cfun_frame_layout.last_restore_gpr = -1;
+      got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
+      SYMBOL_REF_FLAGS (got_symbol) = SYMBOL_FLAG_LOCAL;
     }
-  else
+
+  start_sequence ();
+
+  if (TARGET_CPU_ZARCH)
     {
-      /* Save / Restore from gpr i to j.  */
-      cfun_frame_layout.first_save_gpr = i;
-      cfun_frame_layout.first_restore_gpr = i;
-      cfun_frame_layout.last_save_gpr = j;
-      cfun_frame_layout.last_restore_gpr = j;
+      emit_move_insn (pic_offset_table_rtx, got_symbol);
     }
-
-  if (current_function_stdarg)
+  else
     {
-      /* Varargs functions need to save gprs 2 to 6.  */
-      if (cfun_frame_layout.first_save_gpr == -1
-          || cfun_frame_layout.first_save_gpr > 2)
-        cfun_frame_layout.first_save_gpr = 2;
+      rtx offset;
+
+      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);
 
-      if (cfun_frame_layout.last_save_gpr == -1
-          || cfun_frame_layout.last_save_gpr < 6)
-        cfun_frame_layout.last_save_gpr = 6;
+      emit_move_insn (pic_offset_table_rtx, offset);
 
-      /* Mark f0, f2 for 31 bit and f0-f4 for 64 bit to be saved.  */
-      if (TARGET_HARD_FLOAT)
-       for (i = 0; i < (TARGET_64BIT ? 4 : 2); i++)
-         cfun_set_fpr_bit (i);
+      offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, XEXP (offset, 0)),
+                              UNSPEC_LTREL_BASE);
+      offset = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, offset);
+
+      emit_move_insn (pic_offset_table_rtx, offset);
     }
 
-  if (!TARGET_64BIT)
-    for (i = 2; i < 4; i++)
-      if (regs_ever_live[i + 16] && !global_regs[i + 16])
-       cfun_set_fpr_bit (i);
+  insns = get_insns ();
+  end_sequence ();
+  return insns;
 }
 
-/* Fill cfun->machine with info about frame of current function.  */
+/* Expand the prologue into a bunch of separate insns.  */
 
-static void
-s390_frame_info (void)
+void
+s390_emit_prologue (void)
 {
+  rtx insn, addr;
+  rtx temp_reg;
   int i;
+  int offset;
+  int next_fpr = 0;
 
-  cfun_frame_layout.frame_size = get_frame_size ();
-  if (!TARGET_64BIT && cfun_frame_layout.frame_size > 0x7fff0000)
-    fatal_error ("Total size of local variables exceeds architecture limit.");
-  
-  if (!TARGET_PACKED_STACK)
+  /* Complete frame layout.  */
+
+  s390_update_frame_layout ();
+
+  /* Annotate all constant pool references to let the scheduler know
+     they implicitly use the base register.  */
+
+  push_topmost_sequence ();
+
+  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+    if (INSN_P (insn))
+      annotate_constant_pool_refs (&PATTERN (insn));
+
+  pop_topmost_sequence ();
+
+  /* Choose best register to use for temp use within prologue.
+     See below for why TPF must use the register 1.  */
+
+  if (!has_hard_reg_initial_val (Pmode, RETURN_REGNUM) 
+      && !current_function_is_leaf 
+      && !TARGET_TPF_PROFILING)
+    temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
+  else
+    temp_reg = gen_rtx_REG (Pmode, 1);
+
+  /* Save call saved gprs.  */
+  if (cfun_frame_layout.first_save_gpr != -1)
     {
-      cfun_frame_layout.backchain_offset = 0;
-      cfun_frame_layout.f0_offset = 16 * UNITS_PER_WORD;
-      cfun_frame_layout.f4_offset = cfun_frame_layout.f0_offset + 2 * 8;
-      cfun_frame_layout.f8_offset = -cfun_frame_layout.high_fprs * 8;
-      cfun_frame_layout.gprs_offset = (cfun_frame_layout.first_save_gpr
-                                      * UNITS_PER_WORD);
+      insn = save_gprs (stack_pointer_rtx, 
+                       cfun_frame_layout.gprs_offset + 
+                       UNITS_PER_WORD * (cfun_frame_layout.first_save_gpr 
+                                         - cfun_frame_layout.first_save_gpr_slot),
+                       cfun_frame_layout.first_save_gpr, 
+                       cfun_frame_layout.last_save_gpr);
+      emit_insn (insn);
     }
-  else if (TARGET_BACKCHAIN) /* kernel stack layout */
+
+  /* Dummy insn to mark literal pool slot.  */
+
+  if (cfun->machine->base_reg)
+    emit_insn (gen_main_pool (cfun->machine->base_reg));
+
+  offset = cfun_frame_layout.f0_offset;
+
+  /* Save f0 and f2.  */
+  for (i = 0; i < 2; i++)
     {
-      cfun_frame_layout.backchain_offset = (STACK_POINTER_OFFSET
-                                           - UNITS_PER_WORD);
-      cfun_frame_layout.gprs_offset 
-       = (cfun_frame_layout.backchain_offset 
-          - (STACK_POINTER_REGNUM - cfun_frame_layout.first_save_gpr + 1)
-          * UNITS_PER_WORD);
-         
-      if (TARGET_64BIT)
+      if (cfun_fpr_bit_p (i))
        {
-         cfun_frame_layout.f4_offset 
-           = (cfun_frame_layout.gprs_offset
-              - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
-         
-         cfun_frame_layout.f0_offset 
-           = (cfun_frame_layout.f4_offset 
-              - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
+         save_fpr (stack_pointer_rtx, offset, i + 16);
+         offset += 8;
        }
-      else
+      else if (!TARGET_PACKED_STACK)
+         offset += 8;
+    }
+
+  /* Save f4 and f6.  */
+  offset = cfun_frame_layout.f4_offset;
+  for (i = 2; i < 4; i++)
+    {
+      if (cfun_fpr_bit_p (i))
        {
-         /* On 31 bit we have to care about alignment of the
-            floating point regs to provide fastest access.  */
-         cfun_frame_layout.f0_offset 
-           = ((cfun_frame_layout.gprs_offset 
-               & ~(STACK_BOUNDARY / BITS_PER_UNIT - 1))
-              - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
-         
-         cfun_frame_layout.f4_offset 
-           = (cfun_frame_layout.f0_offset
-              - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
+         insn = save_fpr (stack_pointer_rtx, offset, i + 16);
+         offset += 8;
+
+         /* If f4 and f6 are call clobbered they are saved due to stdargs and
+            therefore are not frame related.  */
+         if (!call_really_used_regs[i + 16])
+           RTX_FRAME_RELATED_P (insn) = 1;
        }
+      else if (!TARGET_PACKED_STACK)
+       offset += 8;
     }
-  else /* no backchain */
+
+  if (TARGET_PACKED_STACK
+      && cfun_save_high_fprs_p
+      && cfun_frame_layout.f8_offset + cfun_frame_layout.high_fprs * 8 > 0)
     {
-      cfun_frame_layout.f4_offset 
-       = (STACK_POINTER_OFFSET
-          - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
-      
-      cfun_frame_layout.f0_offset 
-       = (cfun_frame_layout.f4_offset
-          - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
-      
-      cfun_frame_layout.gprs_offset 
-       = cfun_frame_layout.f0_offset - cfun_gprs_save_area_size;
+      offset = (cfun_frame_layout.f8_offset
+               + (cfun_frame_layout.high_fprs - 1) * 8);
+
+      for (i = 15; i > 7 && offset >= 0; i--)
+       if (cfun_fpr_bit_p (i))
+         {
+           insn = save_fpr (stack_pointer_rtx, offset, i + 16);
+                      
+           RTX_FRAME_RELATED_P (insn) = 1;
+           offset -= 8;
+         }
+      if (offset >= cfun_frame_layout.f8_offset)
+       next_fpr = i + 16;
     }
+  
+  if (!TARGET_PACKED_STACK)
+    next_fpr = cfun_save_high_fprs_p ? 31 : 0;
 
-  if (current_function_is_leaf
-      && !TARGET_TPF_PROFILING
-      && cfun_frame_layout.frame_size == 0
-      && !cfun_save_high_fprs_p
-      && !current_function_calls_alloca
-      && !current_function_stdarg)
-    return;
+  /* Decrement stack pointer.  */
 
-  if (!TARGET_PACKED_STACK)
-    cfun_frame_layout.frame_size += (STARTING_FRAME_OFFSET
-                                    + cfun_frame_layout.high_fprs * 8);
-  else
+  if (cfun_frame_layout.frame_size > 0)
     {
-      if (TARGET_BACKCHAIN)
-       cfun_frame_layout.frame_size += UNITS_PER_WORD;
+      rtx frame_off = GEN_INT (-cfun_frame_layout.frame_size);
 
-      /* No alignment trouble here because f8-f15 are only saved under 
-        64 bit.  */
-      cfun_frame_layout.f8_offset = (MIN (MIN (cfun_frame_layout.f0_offset,
-                                              cfun_frame_layout.f4_offset),
-                                         cfun_frame_layout.gprs_offset)
-                                    - cfun_frame_layout.high_fprs * 8);
+      if (s390_stack_size)
+       {
+         HOST_WIDE_INT stack_check_mask = ((s390_stack_size - 1)
+                                           & ~(s390_stack_guard - 1));
+         rtx t = gen_rtx_AND (Pmode, stack_pointer_rtx,
+                              GEN_INT (stack_check_mask));
 
-      cfun_frame_layout.frame_size += cfun_frame_layout.high_fprs * 8;
+         if (TARGET_64BIT)
+           gen_cmpdi (t, const0_rtx);
+         else
+           gen_cmpsi (t, const0_rtx);
 
-      for (i = 0; i < 8; i++)
-       if (cfun_fpr_bit_p (i))
-         cfun_frame_layout.frame_size += 8;
-      
-      cfun_frame_layout.frame_size += cfun_gprs_save_area_size;
-      
-      /* If under 31 bit an odd number of gprs has to be saved we have to adjust
-        the frame size to sustain 8 byte alignment of stack frames.  */
-      cfun_frame_layout.frame_size = ((cfun_frame_layout.frame_size +
-                                      STACK_BOUNDARY / BITS_PER_UNIT - 1)
-                                     & ~(STACK_BOUNDARY / BITS_PER_UNIT - 1));
+         emit_insn (gen_conditional_trap (gen_rtx_EQ (CCmode, 
+                                                      gen_rtx_REG (CCmode, 
+                                                                   CC_REGNUM),
+                                                      const0_rtx),
+                                          const0_rtx));
+       }
 
-      cfun_frame_layout.frame_size += current_function_outgoing_args_size;
-    }
-}
+      if (s390_warn_framesize > 0 
+         && cfun_frame_layout.frame_size >= s390_warn_framesize)
+       warning (0, "frame size of %qs is " HOST_WIDE_INT_PRINT_DEC " bytes", 
+                current_function_name (), cfun_frame_layout.frame_size);
 
-/* Generate frame layout.  Fills in register and frame data for the current
-   function in cfun->machine.  This routine can be called multiple times;
-   it will re-do the complete frame layout every time.  */
+      if (s390_warn_dynamicstack_p && cfun->calls_alloca)
+       warning (0, "%qs uses dynamic stack allocation", current_function_name ());
+
+      /* Save incoming stack pointer into temp reg.  */
+      if (TARGET_BACKCHAIN || next_fpr)
+       insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx));
+
+      /* 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_K (INTVAL (frame_off)))
+           frame_off = force_const_mem (Pmode, frame_off);
+
+          insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off));
+         annotate_constant_pool_refs (&PATTERN (insn));
+       }
+
+      RTX_FRAME_RELATED_P (insn) = 1;
+      REG_NOTES (insn) =
+       gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+                          gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+                            gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+                              GEN_INT (-cfun_frame_layout.frame_size))),
+                          REG_NOTES (insn));
+
+      /* Set backchain.  */
+
+      if (TARGET_BACKCHAIN)
+       {
+         if (cfun_frame_layout.backchain_offset)
+           addr = gen_rtx_MEM (Pmode, 
+                               plus_constant (stack_pointer_rtx, 
+                                 cfun_frame_layout.backchain_offset));
+         else
+           addr = gen_rtx_MEM (Pmode, stack_pointer_rtx);  
+         set_mem_alias_set (addr, get_frame_alias_set ());
+         insn = emit_insn (gen_move_insn (addr, temp_reg));
+       }
 
-static void
-s390_init_frame_layout (void)
-{
-  HOST_WIDE_INT frame_size;
-  int base_used;
-  int live_regs[16];
+      /* 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 return address register is explicitly used, we need to save it.  */
-  if (regs_ever_live[RETURN_REGNUM]
-      || !current_function_is_leaf
-      || TARGET_TPF_PROFILING
-      || current_function_stdarg
-      || current_function_calls_eh_return)
-    cfun_frame_layout.save_return_addr_p = true;
+      if (TARGET_BACKCHAIN && flag_non_call_exceptions)
+       {
+         addr = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode));
+         emit_insn (gen_rtx_CLOBBER (VOIDmode, addr));
+       }
+    }
 
-  /* On S/390 machines, we may need to perform branch splitting, which
-     will require both base and return address register.  We have no
-     choice but to assume we're going to need them until right at the
-     end of the machine dependent reorg phase.  */
-  if (!TARGET_CPU_ZARCH)
-    cfun->machine->split_branches_pending_p = true;
+  /* Save fprs 8 - 15 (64 bit ABI).  */
 
-  do
+  if (cfun_save_high_fprs_p && next_fpr)
     {
-      frame_size = cfun_frame_layout.frame_size;
-
-      /* Try to predict whether we'll need the base register.  */
-      base_used = cfun->machine->split_branches_pending_p
-                 || current_function_uses_const_pool
-                 || (!DISP_IN_RANGE (-frame_size)
-                     && !CONST_OK_FOR_CONSTRAINT_P (-frame_size, 'K', "K"));
+      insn = emit_insn (gen_add2_insn (temp_reg, 
+                                      GEN_INT (cfun_frame_layout.f8_offset)));
 
-      /* Decide which register to use as literal pool base.  In small
-        leaf functions, try to use an unused call-clobbered register
-        as base register to avoid save/restore overhead.  */
-      if (!base_used)
-       cfun->machine->base_reg = NULL_RTX;
-      else if (current_function_is_leaf && !regs_ever_live[5])
-       cfun->machine->base_reg = gen_rtx_REG (Pmode, 5);
-      else
-       cfun->machine->base_reg = gen_rtx_REG (Pmode, BASE_REGNUM);
+      offset = 0;
 
-      s390_register_info (live_regs);
-      s390_frame_info ();
+      for (i = 24; i <= next_fpr; i++)
+       if (cfun_fpr_bit_p (i - 16))
+         {
+           rtx addr = plus_constant (stack_pointer_rtx,
+                                     cfun_frame_layout.frame_size
+                                     + cfun_frame_layout.f8_offset
+                                     + offset);
+           
+           insn = save_fpr (temp_reg, offset, i);
+           offset += 8;
+           RTX_FRAME_RELATED_P (insn) = 1;
+           REG_NOTES (insn) =
+             gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
+                                gen_rtx_SET (VOIDmode,
+                                             gen_rtx_MEM (DFmode, addr),
+                                             gen_rtx_REG (DFmode, i)),
+                                REG_NOTES (insn));
+         }
     }
-  while (frame_size != cfun_frame_layout.frame_size);
-}
-
-/* Update frame layout.  Recompute actual register save data based on
-   current info and update regs_ever_live for the special registers.
-   May be called multiple times, but may never cause *more* registers
-   to be saved than s390_init_frame_layout allocated room for.  */
 
-static void
-s390_update_frame_layout (void)
-{
-  int live_regs[16];
+  /* Set frame pointer, if needed.  */
 
-  s390_register_info (live_regs);
+  if (frame_pointer_needed)
+    {
+      insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
+      RTX_FRAME_RELATED_P (insn) = 1;
+    }
 
-  regs_ever_live[BASE_REGNUM] = live_regs[BASE_REGNUM];
-  regs_ever_live[RETURN_REGNUM] = live_regs[RETURN_REGNUM];
-  regs_ever_live[STACK_POINTER_REGNUM] = live_regs[STACK_POINTER_REGNUM];
+  /* Set up got pointer, if needed.  */
 
-  if (cfun->machine->base_reg)
-    regs_ever_live[REGNO (cfun->machine->base_reg)] = 1;
-}
+  if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
+    {
+      rtx insns = s390_load_got ();
 
-/* Return true if register FROM can be eliminated via register TO.  */
+      for (insn = insns; insn; insn = NEXT_INSN (insn))
+       {
+         annotate_constant_pool_refs (&PATTERN (insn));
 
-bool
-s390_can_eliminate (int from, int to)
-{
-  gcc_assert (to == STACK_POINTER_REGNUM
-             || to == HARD_FRAME_POINTER_REGNUM);
+         REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
+                                               REG_NOTES (insn));
+       }
 
-  gcc_assert (from == FRAME_POINTER_REGNUM
-             || from == ARG_POINTER_REGNUM
-             || from == RETURN_ADDRESS_POINTER_REGNUM);
+      emit_insn (insns);
+    }
 
-  /* Make sure we actually saved the return address.  */
-  if (from == RETURN_ADDRESS_POINTER_REGNUM)
-    if (!current_function_calls_eh_return
-       && !current_function_stdarg
-       && !cfun_frame_layout.save_return_addr_p)
-      return false;
+  if (TARGET_TPF_PROFILING)
+    {
+      /* Generate a BAS instruction to serve as a function
+        entry intercept to facilitate the use of tracing
+        algorithms located at the branch target.  */
+      emit_insn (gen_prologue_tpf ());
 
-  return true;
+      /* Emit a blockage here so that all code
+        lies between the profiling mechanisms.  */
+      emit_insn (gen_blockage ());
+    }
 }
 
-/* Return offset between register FROM and TO initially after prolog.  */
+/* Expand the epilogue into a bunch of separate insns.  */
 
-HOST_WIDE_INT
-s390_initial_elimination_offset (int from, int to)
+void
+s390_emit_epilogue (bool sibcall)
 {
-  HOST_WIDE_INT offset;
-  int index;
-
-  /* ??? Why are we called for non-eliminable pairs?  */
-  if (!s390_can_eliminate (from, to))
-    return 0;
+  rtx frame_pointer, return_reg;
+  int area_bottom, area_top, offset = 0;
+  int next_offset;
+  rtvec p;
+  int i;
 
-  switch (from)
+  if (TARGET_TPF_PROFILING)
     {
-    case FRAME_POINTER_REGNUM:
-      offset = 0;
-      break;
 
-    case ARG_POINTER_REGNUM:
-      s390_init_frame_layout ();
-      offset = cfun_frame_layout.frame_size + STACK_POINTER_OFFSET;
-      break;
+      /* Generate a BAS instruction to serve as a function
+        entry intercept to facilitate the use of tracing
+        algorithms located at the branch target.  */
 
-    case RETURN_ADDRESS_POINTER_REGNUM:
-      s390_init_frame_layout ();
-      index = RETURN_REGNUM - cfun_frame_layout.first_save_gpr;
-      gcc_assert (index >= 0);
-      offset = cfun_frame_layout.frame_size + cfun_frame_layout.gprs_offset;
-      offset += index * UNITS_PER_WORD;
-      break;
+      /* Emit a blockage here so that all code
+         lies between the profiling mechanisms.  */
+      emit_insn (gen_blockage ());
 
-    default:
-      gcc_unreachable ();
+      emit_insn (gen_epilogue_tpf ());
     }
 
-  return offset;
-}
+  /* Check whether to use frame or stack pointer for restore.  */
 
-/* Emit insn to save fpr REGNUM at offset OFFSET relative
-   to register BASE.  Return generated insn.  */
+  frame_pointer = (frame_pointer_needed 
+                  ? hard_frame_pointer_rtx : stack_pointer_rtx);
 
-static rtx
-save_fpr (rtx base, int offset, int regnum)
-{
-  rtx addr;
-  addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
-  set_mem_alias_set (addr, s390_sr_alias_set);
+  s390_frame_area (&area_bottom, &area_top);
 
-  return emit_move_insn (addr, gen_rtx_REG (DFmode, regnum));
-}
+  /* Check whether we can access the register save area.
+     If not, increment the frame pointer as required.  */
 
-/* Emit insn to restore fpr REGNUM from offset OFFSET relative
-   to register BASE.  Return generated insn.  */
+  if (area_top <= area_bottom)
+    {
+      /* Nothing to restore.  */
+    }
+  else if (DISP_IN_RANGE (cfun_frame_layout.frame_size + area_bottom)
+           && DISP_IN_RANGE (cfun_frame_layout.frame_size + area_top - 1))
+    {
+      /* Area is in range.  */
+      offset = cfun_frame_layout.frame_size;
+    }
+  else
+    {
+      rtx insn, frame_off;
 
-static rtx
-restore_fpr (rtx base, int offset, int regnum)
-{
-  rtx addr;
-  addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
-  set_mem_alias_set (addr, s390_sr_alias_set);
+      offset = area_bottom < 0 ? -area_bottom : 0;
+      frame_off = GEN_INT (cfun_frame_layout.frame_size - offset);
 
-  return emit_move_insn (gen_rtx_REG (DFmode, regnum), addr);
-}
+      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_K (INTVAL (frame_off)))
+           frame_off = force_const_mem (Pmode, frame_off);
 
-/* Generate insn to save registers FIRST to LAST into
-   the register save area located at offset OFFSET
-   relative to register BASE.  */
+         insn = emit_insn (gen_add2_insn (frame_pointer, frame_off));
+         annotate_constant_pool_refs (&PATTERN (insn));
+       }
+    }
 
-static rtx
-save_gprs (rtx base, int offset, int first, int last)
-{
-  rtx addr, insn, note;
-  int i;
+  /* Restore call saved fprs.  */
 
-  addr = plus_constant (base, offset);
-  addr = gen_rtx_MEM (Pmode, addr);
-  set_mem_alias_set (addr, s390_sr_alias_set);
+  if (TARGET_64BIT)
+    {
+      if (cfun_save_high_fprs_p)
+       {
+         next_offset = cfun_frame_layout.f8_offset;
+         for (i = 24; i < 32; i++)
+           {
+             if (cfun_fpr_bit_p (i - 16))
+               {
+                 restore_fpr (frame_pointer,
+                              offset + next_offset, i);
+                 next_offset += 8;
+               }
+           }
+       }
+             
+    }
+  else
+    {
+      next_offset = cfun_frame_layout.f4_offset;
+      for (i = 18; i < 20; i++)
+       {
+         if (cfun_fpr_bit_p (i - 16))
+           {
+             restore_fpr (frame_pointer,
+                          offset + next_offset, i);
+             next_offset += 8;
+           }
+         else if (!TARGET_PACKED_STACK)
+           next_offset += 8;
+       }
+      
+    }
 
-  /* Special-case single register.  */
-  if (first == last)
-    {
-      if (TARGET_64BIT)
-        insn = gen_movdi (addr, gen_rtx_REG (Pmode, first));
-      else
-        insn = gen_movsi (addr, gen_rtx_REG (Pmode, first));
+  /* Return register.  */
 
-      RTX_FRAME_RELATED_P (insn) = 1;
-      return insn;
-    }
+  return_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
 
+  /* Restore call saved gprs.  */
 
-  insn = gen_store_multiple (addr,
-                            gen_rtx_REG (Pmode, first),
-                            GEN_INT (last - first + 1));
+  if (cfun_frame_layout.first_restore_gpr != -1)
+    {
+      rtx insn, addr;
+      int i;
 
+      /* Check for global register and save them
+        to stack location from where they get restored.  */
 
-  /* We need to set the FRAME_RELATED flag on all SETs
-     inside the store-multiple pattern.
+      for (i = cfun_frame_layout.first_restore_gpr;
+          i <= cfun_frame_layout.last_restore_gpr;
+          i++)
+       {
+         /* These registers are special and need to be
+            restored in any case.  */
+         if (i == STACK_POINTER_REGNUM
+              || i == RETURN_REGNUM
+              || i == BASE_REGNUM
+              || (flag_pic && i == (int)PIC_OFFSET_TABLE_REGNUM))
+           continue;
 
-     However, we must not emit DWARF records for registers 2..5
-     if they are stored for use by variable arguments ...
+         if (global_regs[i])
+           {
+             addr = plus_constant (frame_pointer,
+                                   offset + cfun_frame_layout.gprs_offset 
+                                   + (i - cfun_frame_layout.first_save_gpr_slot)
+                                   * UNITS_PER_WORD);
+             addr = gen_rtx_MEM (Pmode, addr);
+             set_mem_alias_set (addr, get_frame_alias_set ());
+             emit_move_insn (addr, gen_rtx_REG (Pmode, i));
+           }
+       }
 
-     ??? Unfortunately, it is not enough to simply not the the
-     FRAME_RELATED flags for those SETs, because the first SET
-     of the PARALLEL is always treated as if it had the flag
-     set, even if it does not.  Therefore we emit a new pattern
-     without those registers as REG_FRAME_RELATED_EXPR note.  */
+      if (! sibcall)
+       {
+         /* Fetch return address from stack before load multiple,
+            this will do good for scheduling.  */
 
-  if (first >= 6)
-    {
-      rtx pat = PATTERN (insn);
+         if (cfun_frame_layout.save_return_addr_p
+             || (cfun_frame_layout.first_restore_gpr < BASE_REGNUM
+                 && cfun_frame_layout.last_restore_gpr > RETURN_REGNUM))
+           {
+             int return_regnum = find_unused_clobbered_reg();
+             if (!return_regnum)
+               return_regnum = 4;
+             return_reg = gen_rtx_REG (Pmode, return_regnum);
 
-      for (i = 0; i < XVECLEN (pat, 0); i++)
-       if (GET_CODE (XVECEXP (pat, 0, i)) == SET)
-         RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1;
+             addr = plus_constant (frame_pointer,
+                                   offset + cfun_frame_layout.gprs_offset
+                                   + (RETURN_REGNUM 
+                                      - cfun_frame_layout.first_save_gpr_slot)
+                                   * UNITS_PER_WORD);
+             addr = gen_rtx_MEM (Pmode, addr);
+             set_mem_alias_set (addr, get_frame_alias_set ());
+             emit_move_insn (return_reg, addr);
+           }
+       }
 
-      RTX_FRAME_RELATED_P (insn) = 1;
+      insn = restore_gprs (frame_pointer,
+                          offset + cfun_frame_layout.gprs_offset
+                          + (cfun_frame_layout.first_restore_gpr 
+                             - cfun_frame_layout.first_save_gpr_slot)
+                          * UNITS_PER_WORD,
+                          cfun_frame_layout.first_restore_gpr,
+                          cfun_frame_layout.last_restore_gpr);
+      emit_insn (insn);
     }
-  else if (last >= 6)
+
+  if (! sibcall)
     {
-      addr = plus_constant (base, offset + (6 - first) * UNITS_PER_WORD);
-      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,
-                          note, REG_NOTES (insn));
+      /* Return to caller.  */
 
-      for (i = 0; i < XVECLEN (note, 0); i++)
-       if (GET_CODE (XVECEXP (note, 0, i)) == SET)
-         RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1;
+      p = rtvec_alloc (2);
 
-      RTX_FRAME_RELATED_P (insn) = 1;
+      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 insn;
 }
 
-/* Generate insn to restore registers FIRST to LAST from
-   the register save area located at offset OFFSET
-   relative to register BASE.  */
-
-static rtx
-restore_gprs (rtx base, int offset, int first, int last)
-{
-  rtx addr, insn;
 
-  addr = plus_constant (base, offset);
-  addr = gen_rtx_MEM (Pmode, addr);
-  set_mem_alias_set (addr, s390_sr_alias_set);
+/* 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.  */
 
-  /* Special-case single register.  */
-  if (first == last)
-    {
-      if (TARGET_64BIT)
-        insn = gen_movdi (gen_rtx_REG (Pmode, first), addr);
-      else
-        insn = gen_movsi (gen_rtx_REG (Pmode, first), addr);
+static int
+s390_function_arg_size (enum machine_mode mode, tree type)
+{
+  if (type)
+    return int_size_in_bytes (type);
 
-      return insn;
-    }
+  /* No type info available for some library calls ...  */
+  if (mode != BLKmode)
+    return GET_MODE_SIZE (mode);
 
-  insn = gen_load_multiple (gen_rtx_REG (Pmode, first),
-                           addr,
-                           GEN_INT (last - first + 1));
-  return insn;
+  /* If we have neither type nor mode, abort */
+  gcc_unreachable ();
 }
 
-/* Return insn sequence to load the GOT register.  */
+/* Return true if a function argument of type TYPE and mode MODE
+   is to be passed in a floating-point register, if available.  */
 
-static GTY(()) rtx got_symbol;
-rtx
-s390_load_got (void)
+static bool
+s390_function_arg_float (enum machine_mode mode, tree type)
 {
-  rtx insns;
+  int size = s390_function_arg_size (mode, type);
+  if (size > 8)
+    return false;
 
-  if (!got_symbol)
-    {
-      got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
-      SYMBOL_REF_FLAGS (got_symbol) = SYMBOL_FLAG_LOCAL;
-    }
+  /* Soft-float changes the ABI: no floating-point registers are used.  */
+  if (TARGET_SOFT_FLOAT)
+    return false;
 
-  start_sequence ();
+  /* No type info available for some library calls ...  */
+  if (!type)
+    return mode == SFmode || mode == DFmode;
 
-  if (TARGET_CPU_ZARCH)
-    {
-      emit_move_insn (pic_offset_table_rtx, got_symbol);
-    }
-  else
+  /* The ABI says that record types with a single member are treated
+     just like that member would be.  */
+  while (TREE_CODE (type) == RECORD_TYPE)
     {
-      rtx offset;
-
-      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);
+      tree field, single = NULL_TREE;
 
-      emit_move_insn (pic_offset_table_rtx, offset);
+      for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+       {
+         if (TREE_CODE (field) != FIELD_DECL)
+           continue;
 
-      offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, XEXP (offset, 0)),
-                              UNSPEC_LTREL_BASE);
-      offset = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, offset);
+         if (single == NULL_TREE)
+           single = TREE_TYPE (field);
+         else
+           return false;
+       }
 
-      emit_move_insn (pic_offset_table_rtx, offset);
+      if (single == NULL_TREE)
+       return false;
+      else
+       type = single;
     }
 
-  insns = get_insns ();
-  end_sequence ();
-  return insns;
+  return TREE_CODE (type) == REAL_TYPE;
 }
 
-/* Expand the prologue into a bunch of separate insns.  */
+/* 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.  */
 
-void
-s390_emit_prologue (void)
+static bool
+s390_function_arg_integer (enum machine_mode mode, tree type)
 {
-  rtx insn, addr;
-  rtx temp_reg;
-  int i;
-  int offset;
-  int next_fpr = 0;
-
-  /* Complete frame layout.  */
-
-  s390_update_frame_layout ();
+  int size = s390_function_arg_size (mode, type);
+  if (size > 8)
+    return false;
 
-  /* Annotate all constant pool references to let the scheduler know
-     they implicitly use the base register.  */
+  /* 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);
 
-  push_topmost_sequence ();
+  /* 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;
 
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-    if (INSN_P (insn))
-      annotate_constant_pool_refs (&PATTERN (insn));
+  /* 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;
 
-  pop_topmost_sequence ();
+  return false;
+}
 
-  /* Choose best register to use for temp use within prologue.
-     See below for why TPF must use the register 1.  */
+/* 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,
+   all other structures (and complex numbers) are passed by
+   reference.  */
 
-  if (!current_function_is_leaf && !TARGET_TPF_PROFILING)
-    temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
-  else
-    temp_reg = gen_rtx_REG (Pmode, 1);
+static bool
+s390_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
+                       enum machine_mode mode, tree type,
+                       bool named ATTRIBUTE_UNUSED)
+{
+  int size = s390_function_arg_size (mode, type);
+  if (size > 8)
+    return true;
 
-  /* Save call saved gprs.  */
-  if (cfun_frame_layout.first_save_gpr != -1)
+  if (type)
     {
-      insn = save_gprs (stack_pointer_rtx, 
-                       cfun_frame_layout.gprs_offset,
-                       cfun_frame_layout.first_save_gpr, 
-                       cfun_frame_layout.last_save_gpr);
-      emit_insn (insn);
-    }
+      if (AGGREGATE_TYPE_P (type) && exact_log2 (size) < 0)
+        return 1;
 
-  /* Dummy insn to mark literal pool slot.  */
+      if (TREE_CODE (type) == COMPLEX_TYPE
+         || TREE_CODE (type) == VECTOR_TYPE)
+        return 1;
+    }
 
-  if (cfun->machine->base_reg)
-    emit_insn (gen_main_pool (cfun->machine->base_reg));
+  return 0;
+}
 
-  offset = cfun_frame_layout.f0_offset;
+/* Update the data in CUM to advance over an argument of mode MODE and
+   data type TYPE.  (TYPE is null for libcalls where that information
+   may not be available.).  The boolean NAMED specifies whether the
+   argument is a named argument (as opposed to an unnamed argument
+   matching an ellipsis).  */
 
-  /* Save f0 and f2.  */
-  for (i = 0; i < 2; i++)
+void
+s390_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
+                          tree type, int named ATTRIBUTE_UNUSED)
+{
+  if (s390_function_arg_float (mode, type))
     {
-      if (cfun_fpr_bit_p (i))
-       {
-         save_fpr (stack_pointer_rtx, offset, i + 16);
-         offset += 8;
-       }
-      else if (!TARGET_PACKED_STACK)
-         offset += 8;
+      cum->fprs += 1;
     }
-
-  /* Save f4 and f6.  */
-  offset = cfun_frame_layout.f4_offset;
-  for (i = 2; i < 4; i++)
+  else if (s390_function_arg_integer (mode, type))
     {
-      if (cfun_fpr_bit_p (i))
-       {
-         insn = save_fpr (stack_pointer_rtx, offset, i + 16);
-         offset += 8;
-
-         /* If f4 and f6 are call clobbered they are saved due to stdargs and
-            therefore are not frame related.  */
-         if (!call_really_used_regs[i + 16])
-           RTX_FRAME_RELATED_P (insn) = 1;
-       }
-      else if (!TARGET_PACKED_STACK)
-       offset += 8;
+      int size = s390_function_arg_size (mode, type);
+      cum->gprs += ((size + UNITS_PER_WORD-1) / UNITS_PER_WORD);
     }
+  else
+    gcc_unreachable ();
+}
 
-  if (TARGET_PACKED_STACK
-      && cfun_save_high_fprs_p
-      && cfun_frame_layout.f8_offset + cfun_frame_layout.high_fprs * 8 > 0)
-    {
-      offset = (cfun_frame_layout.f8_offset
-               + (cfun_frame_layout.high_fprs - 1) * 8);
+/* Define where to put the arguments to a function.
+   Value is zero to push the argument on the stack,
+   or a hard register in which to store the argument.
 
-      for (i = 15; i > 7 && offset >= 0; i--)
-       if (cfun_fpr_bit_p (i))
-         {
-           insn = save_fpr (stack_pointer_rtx, offset, i + 16);
-                      
-           RTX_FRAME_RELATED_P (insn) = 1;
-           offset -= 8;
-         }
-      if (offset >= cfun_frame_layout.f8_offset)
-       next_fpr = i + 16;
-    }
-  
-  if (!TARGET_PACKED_STACK)
-    next_fpr = cfun_save_high_fprs_p ? 31 : 0;
+   MODE is the argument's machine mode.
+   TYPE is the data type of the argument (as a tree).
+    This is null for libcalls where that information may
+    not be available.
+   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).
 
-  /* Decrement stack pointer.  */
+   On S/390, we use general purpose registers 2 through 6 to
+   pass integer, pointer, and certain structure arguments, and
+   floating point registers 0 and 2 (0, 2, 4, and 6 on 64-bit)
+   to pass floating point arguments.  All remaining arguments
+   are pushed to the stack.  */
 
-  if (cfun_frame_layout.frame_size > 0)
+rtx
+s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
+                  int named ATTRIBUTE_UNUSED)
+{
+  if (s390_function_arg_float (mode, type))
     {
-      rtx frame_off = GEN_INT (-cfun_frame_layout.frame_size);
+      if (cum->fprs + 1 > FP_ARG_NUM_REG)
+       return 0;
+      else
+       return gen_rtx_REG (mode, cum->fprs + 16);
+    }
+  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;
 
-      if (s390_stack_size)
-       {
-         HOST_WIDE_INT stack_check_mask = ((s390_stack_size - 1)
-                                           & ~(s390_stack_guard - 1));
-         rtx t = gen_rtx_AND (Pmode, stack_pointer_rtx,
-                              GEN_INT (stack_check_mask));
+      if (cum->gprs + n_gprs > GP_ARG_NUM_REG)
+       return 0;
+      else
+       return gen_rtx_REG (mode, cum->gprs + 2);
+    }
 
-         if (TARGET_64BIT)
-           gen_cmpdi (t, const0_rtx);
-         else
-           gen_cmpsi (t, const0_rtx);
+  /* 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.
 
-         emit_insn (gen_conditional_trap (gen_rtx_EQ (CCmode, 
-                                                      gen_rtx_REG (CCmode, 
-                                                                   CC_REGNUM),
-                                                      const0_rtx),
-                                          const0_rtx));
-       }
+     We don't need this feature ...  */
+  else if (type == void_type_node)
+    return const0_rtx;
 
-      if (s390_warn_framesize > 0 
-         && cfun_frame_layout.frame_size >= s390_warn_framesize)
-       warning ("frame size of %qs is " HOST_WIDE_INT_PRINT_DEC " bytes", 
-                current_function_name (), cfun_frame_layout.frame_size);
+  gcc_unreachable ();
+}
 
-      if (s390_warn_dynamicstack_p && cfun->calls_alloca)
-       warning ("%qs uses dynamic stack allocation", current_function_name ());
+/* 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.  */
 
-      /* Save incoming stack pointer into temp reg.  */
-      if (TARGET_BACKCHAIN || next_fpr)
-       insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx));
+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;
 
-      /* Subtract frame size from stack pointer.  */
+  /* 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;
 
-      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);
+  /* ??? 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;
+}
 
-          insn = emit_insn (gen_add2_insn (stack_pointer_rtx, frame_off));
-         annotate_constant_pool_refs (&PATTERN (insn));
-       }
+/* 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_FRAME_RELATED_P (insn) = 1;
-      REG_NOTES (insn) =
-       gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                          gen_rtx_SET (VOIDmode, stack_pointer_rtx,
-                            gen_rtx_PLUS (Pmode, stack_pointer_rtx,
-                              GEN_INT (-cfun_frame_layout.frame_size))),
-                          REG_NOTES (insn));
+rtx
+s390_function_value (tree type, enum machine_mode mode)
+{
+  if (type)
+    {
+      int unsignedp = TYPE_UNSIGNED (type);
+      mode = promote_mode (type, TYPE_MODE (type), &unsignedp, 1);
+    }
 
-      /* Set backchain.  */
+  gcc_assert (GET_MODE_CLASS (mode) == MODE_INT
+               || GET_MODE_CLASS (mode) == MODE_FLOAT);
+  gcc_assert (GET_MODE_SIZE (mode) <= 8);
 
-      if (TARGET_BACKCHAIN)
-       {
-         if (cfun_frame_layout.backchain_offset)
-           addr = gen_rtx_MEM (Pmode, 
-                               plus_constant (stack_pointer_rtx, 
-                                 cfun_frame_layout.backchain_offset));
-         else
-           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 (TARGET_HARD_FLOAT && GET_MODE_CLASS (mode) == MODE_FLOAT)
+    return gen_rtx_REG (mode, 16);
+  else
+    return gen_rtx_REG (mode, 2);
+}
 
-      /* 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));
-       }
-    }
+/* Create and return the va_list datatype.
 
-  /* Save fprs 8 - 15 (64 bit ABI).  */
+   On S/390, va_list is an array type equivalent to
 
-  if (cfun_save_high_fprs_p && next_fpr)
-    {
-      insn = emit_insn (gen_add2_insn (temp_reg, 
-                                      GEN_INT (cfun_frame_layout.f8_offset)));
+      typedef struct __va_list_tag
+        {
+            long __gpr;
+            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
+   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.  */
 
-      offset = 0;
+static tree
+s390_build_builtin_va_list (void)
+{
+  tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl;
 
-      for (i = 24; i <= next_fpr; i++)
-       if (cfun_fpr_bit_p (i - 16))
-         {
-           rtx addr = plus_constant (stack_pointer_rtx,
-                                     cfun_frame_layout.frame_size
-                                     + cfun_frame_layout.f8_offset
-                                     + offset);
-           
-           insn = save_fpr (temp_reg, offset, i);
-           offset += 8;
-           RTX_FRAME_RELATED_P (insn) = 1;
-           REG_NOTES (insn) =
-             gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                                gen_rtx_SET (VOIDmode,
-                                             gen_rtx_MEM (DFmode, addr),
-                                             gen_rtx_REG (DFmode, i)),
-                                REG_NOTES (insn));
-         }
-    }
+  record = lang_hooks.types.make_type (RECORD_TYPE);
 
-  /* Set frame pointer, if needed.  */
+  type_decl =
+    build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
 
-  if (frame_pointer_needed)
-    {
-      insn = emit_move_insn (hard_frame_pointer_rtx, stack_pointer_rtx);
-      RTX_FRAME_RELATED_P (insn) = 1;
-    }
+  f_gpr = build_decl (FIELD_DECL, get_identifier ("__gpr"),
+                     long_integer_type_node);
+  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);
+  f_sav = build_decl (FIELD_DECL, get_identifier ("__reg_save_area"),
+                     ptr_type_node);
 
-  /* Set up got pointer, if needed.  */
+  va_list_gpr_counter_field = f_gpr;
+  va_list_fpr_counter_field = f_fpr;
 
-  if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
-    {
-      rtx insns = s390_load_got ();
+  DECL_FIELD_CONTEXT (f_gpr) = record;
+  DECL_FIELD_CONTEXT (f_fpr) = record;
+  DECL_FIELD_CONTEXT (f_ovf) = record;
+  DECL_FIELD_CONTEXT (f_sav) = record;
 
-      for (insn = insns; insn; insn = NEXT_INSN (insn))
-       {
-         annotate_constant_pool_refs (&PATTERN (insn));
+  TREE_CHAIN (record) = type_decl;
+  TYPE_NAME (record) = type_decl;
+  TYPE_FIELDS (record) = f_gpr;
+  TREE_CHAIN (f_gpr) = f_fpr;
+  TREE_CHAIN (f_fpr) = f_ovf;
+  TREE_CHAIN (f_ovf) = f_sav;
 
-         REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
-                                               REG_NOTES (insn));
-       }
+  layout_type (record);
 
-      emit_insn (insns);
-    }
+  /* The correct type is an array type of one element.  */
+  return build_array_type (record, build_index_type (size_zero_node));
+}
 
-  if (TARGET_TPF_PROFILING)
-    {
-      /* Generate a BAS instruction to serve as a function
-        entry intercept to facilitate the use of tracing
-        algorithms located at the branch target.  */
-      emit_insn (gen_prologue_tpf ());
+/* Implement va_start by filling the va_list structure VALIST.
+   STDARG_P is always true, and ignored.
+   NEXTARG points to the first anonymous stack argument.
 
-      /* Emit a blockage here so that all code
-        lies between the profiling mechanisms.  */
-      emit_insn (gen_blockage ());
-    }
-}
+   The following global variables are used to initialize
+   the va_list structure:
 
-/* Expand the epilogue into a bunch of separate insns.  */
+     current_function_args_info:
+       holds number of gprs and fprs used for named arguments.
+     current_function_arg_offset_rtx:
+       holds the offset of the first anonymous stack argument
+       (relative to the virtual arg pointer).  */
 
 void
-s390_emit_epilogue (bool sibcall)
+s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
 {
-  rtx frame_pointer, return_reg;
-  int area_bottom, area_top, offset = 0;
-  int next_offset;
-  rtvec p;
-  int i;
+  HOST_WIDE_INT n_gpr, n_fpr;
+  int off;
+  tree f_gpr, f_fpr, f_ovf, f_sav;
+  tree gpr, fpr, ovf, sav, t;
 
-  if (TARGET_TPF_PROFILING)
-    {
+  f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
+  f_fpr = TREE_CHAIN (f_gpr);
+  f_ovf = TREE_CHAIN (f_fpr);
+  f_sav = TREE_CHAIN (f_ovf);
 
-      /* Generate a BAS instruction to serve as a function
-        entry intercept to facilitate the use of tracing
-        algorithms located at the branch target.  */
+  valist = build_va_arg_indirect_ref (valist);
+  gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
+  fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
+  ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
+  sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
 
-      /* Emit a blockage here so that all code
-         lies between the profiling mechanisms.  */
-      emit_insn (gen_blockage ());
+  /* Count number of gp and fp argument registers used.  */
 
-      emit_insn (gen_epilogue_tpf ());
+  n_gpr = current_function_args_info.gprs;
+  n_fpr = current_function_args_info.fprs;
+
+  if (cfun->va_list_gpr_size)
+    {
+      t = build2 (MODIFY_EXPR, TREE_TYPE (gpr), gpr,
+                 build_int_cst (NULL_TREE, n_gpr));
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
     }
 
-  /* Check whether to use frame or stack pointer for restore.  */
+  if (cfun->va_list_fpr_size)
+    {
+      t = build2 (MODIFY_EXPR, TREE_TYPE (fpr), fpr,
+                 build_int_cst (NULL_TREE, n_fpr));
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
 
-  frame_pointer = (frame_pointer_needed 
-                  ? hard_frame_pointer_rtx : stack_pointer_rtx);
+  /* Find the overflow area.  */
+  if (n_gpr + cfun->va_list_gpr_size > GP_ARG_NUM_REG
+      || n_fpr + cfun->va_list_fpr_size > FP_ARG_NUM_REG)
+    {
+      t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx);
 
-  s390_frame_area (&area_bottom, &area_top);
+      off = INTVAL (current_function_arg_offset_rtx);
+      off = off < 0 ? 0 : off;
+      if (TARGET_DEBUG_ARG)
+       fprintf (stderr, "va_start: n_gpr = %d, n_fpr = %d off %d\n",
+                (int)n_gpr, (int)n_fpr, off);
 
-  /* Check whether we can access the register save area.
-     If not, increment the frame pointer as required.  */
+      t = build2 (PLUS_EXPR, TREE_TYPE (ovf), t, build_int_cst (NULL_TREE, off));
 
-  if (area_top <= area_bottom)
-    {
-      /* Nothing to restore.  */
+      t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
     }
-  else if (DISP_IN_RANGE (cfun_frame_layout.frame_size + area_bottom)
-           && DISP_IN_RANGE (cfun_frame_layout.frame_size + area_top - 1))
+
+  /* Find the register save area.  */
+  if ((cfun->va_list_gpr_size && n_gpr < GP_ARG_NUM_REG)
+      || (cfun->va_list_fpr_size && n_fpr < FP_ARG_NUM_REG))
     {
-      /* Area is in range.  */
-      offset = cfun_frame_layout.frame_size;
+      t = make_tree (TREE_TYPE (sav), return_address_pointer_rtx);
+      t = build2 (PLUS_EXPR, TREE_TYPE (sav), t,
+                 build_int_cst (NULL_TREE, -RETURN_REGNUM * UNITS_PER_WORD));
+  
+      t = build2 (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
     }
-  else
-    {
-      rtx insn, frame_off;
+}
 
-      offset = area_bottom < 0 ? -area_bottom : 0;
-      frame_off = GEN_INT (cfun_frame_layout.frame_size - offset);
+/* Implement va_arg by updating the va_list structure
+   VALIST as required to retrieve an argument of type
+   TYPE, and returning that argument.
 
-      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);
+   Generates code equivalent to:
 
-         insn = emit_insn (gen_add2_insn (frame_pointer, frame_off));
-         annotate_constant_pool_refs (&PATTERN (insn));
-       }
-    }
+   if (integral value) {
+     if (size  <= 4 && args.gpr < 5 ||
+         size  > 4 && args.gpr < 4 )
+       ret = args.reg_save_area[args.gpr+8]
+     else
+       ret = *args.overflow_arg_area++;
+   } else if (float value) {
+     if (args.fgpr < 2)
+       ret = args.reg_save_area[args.fpr+64]
+     else
+       ret = *args.overflow_arg_area++;
+   } else if (aggregate value) {
+     if (args.gpr < 5)
+       ret = *args.reg_save_area[args.gpr]
+     else
+       ret = **args.overflow_arg_area++;
+   } */
 
-  /* Restore call saved fprs.  */
+static tree
+s390_gimplify_va_arg (tree valist, tree type, tree *pre_p, 
+                     tree *post_p ATTRIBUTE_UNUSED)
+{
+  tree f_gpr, f_fpr, f_ovf, f_sav;
+  tree gpr, fpr, ovf, sav, reg, t, u;
+  int indirect_p, size, n_reg, sav_ofs, sav_scale, max_reg;
+  tree lab_false, lab_over, addr;
 
-  if (TARGET_64BIT)
-    {
-      if (cfun_save_high_fprs_p)
-       {
-         next_offset = cfun_frame_layout.f8_offset;
-         for (i = 24; i < 32; i++)
-           {
-             if (cfun_fpr_bit_p (i - 16))
-               {
-                 restore_fpr (frame_pointer,
-                              offset + next_offset, i);
-                 next_offset += 8;
-               }
-           }
-       }
-             
-    }
-  else
+  f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
+  f_fpr = TREE_CHAIN (f_gpr);
+  f_ovf = TREE_CHAIN (f_fpr);
+  f_sav = TREE_CHAIN (f_ovf);
+
+  valist = build_va_arg_indirect_ref (valist);
+  gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
+  fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
+  ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
+  sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
+
+  size = int_size_in_bytes (type);
+
+  if (pass_by_reference (NULL, TYPE_MODE (type), type, false))
     {
-      next_offset = cfun_frame_layout.f4_offset;
-      for (i = 18; i < 20; i++)
+      if (TARGET_DEBUG_ARG)
        {
-         if (cfun_fpr_bit_p (i - 16))
-           {
-             restore_fpr (frame_pointer,
-                          offset + next_offset, i);
-             next_offset += 8;
-           }
-         else if (!TARGET_PACKED_STACK)
-           next_offset += 8;
+         fprintf (stderr, "va_arg: aggregate type");
+         debug_tree (type);
        }
-      
-    }
-
-  /* Return register.  */
 
-  return_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
-
-  /* Restore call saved gprs.  */
+      /* Aggregates are passed by reference.  */
+      indirect_p = 1;
+      reg = gpr;
+      n_reg = 1;
 
-  if (cfun_frame_layout.first_restore_gpr != -1)
+      /* kernel stack layout on 31 bit: It is assumed here that no padding
+        will be added by s390_frame_info because for va_args always an even
+        number of gprs has to be saved r15-r2 = 14 regs.  */
+      sav_ofs = 2 * UNITS_PER_WORD;
+      sav_scale = UNITS_PER_WORD;
+      size = UNITS_PER_WORD;
+      max_reg = GP_ARG_NUM_REG - n_reg;
+    }
+  else if (s390_function_arg_float (TYPE_MODE (type), type))
     {
-      rtx insn, addr;
-      int i;
-
-      /* Check for global register and save them
-        to stack location from where they get restored.  */
-
-      for (i = cfun_frame_layout.first_restore_gpr;
-          i <= cfun_frame_layout.last_restore_gpr;
-          i++)
+      if (TARGET_DEBUG_ARG)
        {
-         /* These registers are special and need to be
-            restored in any case.  */
-         if (i == STACK_POINTER_REGNUM
-              || i == RETURN_REGNUM
-              || i == BASE_REGNUM
-              || (flag_pic && i == (int)PIC_OFFSET_TABLE_REGNUM))
-           continue;
-
-         if (global_regs[i])
-           {
-             addr = plus_constant (frame_pointer,
-                                   offset + cfun_frame_layout.gprs_offset 
-                                   + (i - cfun_frame_layout.first_save_gpr)
-                                   * 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));
-           }
+         fprintf (stderr, "va_arg: float type");
+         debug_tree (type);
        }
 
-      if (! sibcall)
+      /* FP args go in FP registers, if present.  */
+      indirect_p = 0;
+      reg = fpr;
+      n_reg = 1;
+      sav_ofs = 16 * UNITS_PER_WORD;
+      sav_scale = 8;
+      max_reg = FP_ARG_NUM_REG - n_reg;
+    }
+  else
+    {
+      if (TARGET_DEBUG_ARG)
        {
-         /* Fetch return address from stack before load multiple,
-            this will do good for scheduling.  */
+         fprintf (stderr, "va_arg: other type");
+         debug_tree (type);
+       }
 
-         if (cfun_frame_layout.save_return_addr_p
-             || (cfun_frame_layout.first_restore_gpr < BASE_REGNUM
-                 && cfun_frame_layout.last_restore_gpr > RETURN_REGNUM))
-           {
-             int return_regnum = find_unused_clobbered_reg();
-             if (!return_regnum)
-               return_regnum = 4;
-             return_reg = gen_rtx_REG (Pmode, return_regnum);
+      /* Otherwise into GP registers.  */
+      indirect_p = 0;
+      reg = gpr;
+      n_reg = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
 
-             addr = plus_constant (frame_pointer,
-                                   offset + cfun_frame_layout.gprs_offset
-                                   + (RETURN_REGNUM 
-                                      - cfun_frame_layout.first_save_gpr)
-                                   * UNITS_PER_WORD);
-             addr = gen_rtx_MEM (Pmode, addr);
-             set_mem_alias_set (addr, s390_sr_alias_set);
-             emit_move_insn (return_reg, addr);
-           }
-       }
+      /* kernel stack layout on 31 bit: It is assumed here that no padding
+        will be added by s390_frame_info because for va_args always an even
+        number of gprs has to be saved r15-r2 = 14 regs.  */
+      sav_ofs = 2 * UNITS_PER_WORD;
 
-      insn = restore_gprs (frame_pointer,
-                          offset + cfun_frame_layout.gprs_offset
-                          + (cfun_frame_layout.first_restore_gpr 
-                             - cfun_frame_layout.first_save_gpr)
-                          * UNITS_PER_WORD,
-                          cfun_frame_layout.first_restore_gpr,
-                          cfun_frame_layout.last_restore_gpr);
-      emit_insn (insn);
+      if (size < UNITS_PER_WORD)
+       sav_ofs += UNITS_PER_WORD - size;
+
+      sav_scale = UNITS_PER_WORD;
+      max_reg = GP_ARG_NUM_REG - n_reg;
     }
 
-  if (! sibcall)
-    {
+  /* Pull the value out of the saved registers ...  */
 
-      /* Return to caller.  */
+  lab_false = create_artificial_label ();
+  lab_over = create_artificial_label ();
+  addr = create_tmp_var (ptr_type_node, "addr");
+  DECL_POINTER_ALIAS_SET (addr) = get_varargs_alias_set ();
 
-      p = rtvec_alloc (2);
+  t = fold_convert (TREE_TYPE (reg), size_int (max_reg));
+  t = build2 (GT_EXPR, boolean_type_node, reg, t);
+  u = build1 (GOTO_EXPR, void_type_node, lab_false);
+  t = build3 (COND_EXPR, void_type_node, t, u, NULL_TREE);
+  gimplify_and_add (t, pre_p);
 
-      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));
-    }
-}
+  t = build2 (PLUS_EXPR, ptr_type_node, sav, 
+             fold_convert (ptr_type_node, size_int (sav_ofs)));
+  u = build2 (MULT_EXPR, TREE_TYPE (reg), reg, 
+             fold_convert (TREE_TYPE (reg), size_int (sav_scale)));
+  t = build2 (PLUS_EXPR, ptr_type_node, t, fold_convert (ptr_type_node, u));
 
+  t = build2 (MODIFY_EXPR, void_type_node, addr, t);
+  gimplify_and_add (t, pre_p);
 
-/* 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.  */
+  t = build1 (GOTO_EXPR, void_type_node, lab_over);
+  gimplify_and_add (t, pre_p);
 
-static int
-s390_function_arg_size (enum machine_mode mode, tree type)
-{
-  if (type)
-    return int_size_in_bytes (type);
+  t = build1 (LABEL_EXPR, void_type_node, lab_false);
+  append_to_statement_list (t, pre_p);
 
-  /* No type info available for some library calls ...  */
-  if (mode != BLKmode)
-    return GET_MODE_SIZE (mode);
 
-  /* If we have neither type nor mode, abort */
-  abort ();
-}
+  /* ... Otherwise out of the overflow area.  */
 
-/* Return true if a function argument of type TYPE and mode MODE
-   is to be passed in a floating-point register, if available.  */
+  t = ovf;
+  if (size < UNITS_PER_WORD)
+    t = build2 (PLUS_EXPR, ptr_type_node, t, 
+               fold_convert (ptr_type_node, size_int (UNITS_PER_WORD - size)));
 
-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;
+  gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue);
 
-  /* Soft-float changes the ABI: no floating-point registers are used.  */
-  if (TARGET_SOFT_FLOAT)
-    return false;
+  u = build2 (MODIFY_EXPR, void_type_node, addr, t);
+  gimplify_and_add (u, pre_p);
 
-  /* No type info available for some library calls ...  */
-  if (!type)
-    return mode == SFmode || mode == DFmode;
+  t = build2 (PLUS_EXPR, ptr_type_node, t, 
+             fold_convert (ptr_type_node, size_int (size)));
+  t = build2 (MODIFY_EXPR, ptr_type_node, ovf, t);
+  gimplify_and_add (t, pre_p);
 
-  /* 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;
+  t = build1 (LABEL_EXPR, void_type_node, lab_over);
+  append_to_statement_list (t, pre_p);
 
-      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;
-       }
+  /* Increment register save count.  */
 
-      if (single == NULL_TREE)
-       return false;
-      else
-       type = single;
+  u = build2 (PREINCREMENT_EXPR, TREE_TYPE (reg), reg,
+             fold_convert (TREE_TYPE (reg), size_int (n_reg)));
+  gimplify_and_add (u, pre_p);
+
+  if (indirect_p)
+    {
+      t = build_pointer_type (build_pointer_type (type));
+      addr = fold_convert (t, addr);
+      addr = build_va_arg_indirect_ref (addr);
+    }
+  else
+    {
+      t = build_pointer_type (type);
+      addr = fold_convert (t, addr);
     }
 
-  return TREE_CODE (type) == REAL_TYPE;
+  return build_va_arg_indirect_ref (addr);
 }
 
-/* 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)
+/* Builtins.  */
+
+enum s390_builtin
 {
-  int size = s390_function_arg_size (mode, type);
-  if (size > 8)
-    return false;
+  S390_BUILTIN_THREAD_POINTER,
+  S390_BUILTIN_SET_THREAD_POINTER,
 
-  /* 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);
+  S390_BUILTIN_max
+};
 
-  /* 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;
+static unsigned int const code_for_builtin_64[S390_BUILTIN_max] = {
+  CODE_FOR_get_tp_64,
+  CODE_FOR_set_tp_64
+};
 
-  /* 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;
+static unsigned int const code_for_builtin_31[S390_BUILTIN_max] = {
+  CODE_FOR_get_tp_31,
+  CODE_FOR_set_tp_31
+};
 
-  return false;
+static void
+s390_init_builtins (void)
+{
+  tree ftype;
+
+  ftype = build_function_type (ptr_type_node, void_list_node);
+  lang_hooks.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);
+  lang_hooks.builtin_function ("__builtin_set_thread_pointer", ftype,
+                              S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD,
+                              NULL, NULL_TREE);
 }
 
-/* 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,
-   all other structures (and complex numbers) are passed by
-   reference.  */
+/* 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 bool
-s390_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
-                       enum machine_mode mode, tree type,
-                       bool named ATTRIBUTE_UNUSED)
+static rtx
+s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
+                    enum machine_mode mode ATTRIBUTE_UNUSED,
+                    int ignore ATTRIBUTE_UNUSED)
 {
-  int size = s390_function_arg_size (mode, type);
-  if (size > 8)
-    return true;
+#define MAX_ARGS 2
 
-  if (type)
-    {
-      if (AGGREGATE_TYPE_P (type) && exact_log2 (size) < 0)
-        return 1;
+  unsigned int const *code_for_builtin =
+    TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31;
 
-      if (TREE_CODE (type) == COMPLEX_TYPE
-         || TREE_CODE (type) == VECTOR_TYPE)
-        return 1;
-    }
+  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;
 
-  return 0;
-}
+  if (fcode >= S390_BUILTIN_max)
+    internal_error ("bad builtin fcode");
+  icode = code_for_builtin[fcode];
+  if (icode == 0)
+    internal_error ("bad builtin fcode");
 
-/* Update the data in CUM to advance over an argument of mode MODE and
-   data type TYPE.  (TYPE is null for libcalls where that information
-   may not be available.).  The boolean NAMED specifies whether the
-   argument is a named argument (as opposed to an unnamed argument
-   matching an ellipsis).  */
+  nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node;
 
-void
-s390_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
-                          tree type, int named ATTRIBUTE_UNUSED)
-{
-  if (s390_function_arg_float (mode, type))
-    {
-      cum->fprs += 1;
-    }
-  else if (s390_function_arg_integer (mode, type))
+  for (arglist = TREE_OPERAND (exp, 1), arity = 0;
+       arglist;
+       arglist = TREE_CHAIN (arglist), arity++)
     {
-      int size = s390_function_arg_size (mode, type);
-      cum->gprs += ((size + UNITS_PER_WORD-1) / UNITS_PER_WORD);
-    }
-  else
-    abort ();
-}
+      const struct insn_operand_data *insn_op;
 
-/* Define where to put the arguments to a function.
-   Value is zero to push the argument on the stack,
-   or a hard register in which to store the argument.
+      tree arg = TREE_VALUE (arglist);
+      if (arg == error_mark_node)
+       return NULL_RTX;
+      if (arity > MAX_ARGS)
+       return NULL_RTX;
 
-   MODE is the argument's machine mode.
-   TYPE is the data type of the argument (as a tree).
-    This is null for libcalls where that information may
-    not be available.
-   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).
+      insn_op = &insn_data[icode].operand[arity + nonvoid];
 
-   On S/390, we use general purpose registers 2 through 6 to
-   pass integer, pointer, and certain structure arguments, and
-   floating point registers 0 and 2 (0, 2, 4, and 6 on 64-bit)
-   to pass floating point arguments.  All remaining arguments
-   are pushed to the stack.  */
+      op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0);
 
-rtx
-s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
-                  int named ATTRIBUTE_UNUSED)
-{
-  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);
+      if (!(*insn_op->predicate) (op[arity], insn_op->mode))
+       op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]);
     }
-  else if (s390_function_arg_integer (mode, type))
+
+  if (nonvoid)
     {
-      int size = s390_function_arg_size (mode, type);
-      int n_gprs = (size + UNITS_PER_WORD-1) / UNITS_PER_WORD;
+      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);
+    }
 
-      if (cum->gprs + n_gprs > 5)
-       return 0;
+  switch (arity)
+    {
+    case 0:
+      pat = GEN_FCN (icode) (target);
+      break;
+    case 1:
+      if (nonvoid)
+        pat = GEN_FCN (icode) (target, op[0]);
       else
-       return gen_rtx_REG (mode, cum->gprs + 2);
+       pat = GEN_FCN (icode) (op[0]);
+      break;
+    case 2:
+      pat = GEN_FCN (icode) (target, op[0], op[1]);
+      break;
+    default:
+      gcc_unreachable ();
     }
+  if (!pat)
+    return NULL_RTX;
+  emit_insn (pat);
 
-  /* 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)
+  if (nonvoid)
+    return target;
+  else
     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;
-}
+/* Output assembly code for the trampoline template to
+   stdio stream FILE.
 
-/* 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.  */
+   On S/390, we use gpr 1 internally in the trampoline code;
+   gpr 0 is used to hold the static chain.  */
 
-rtx
-s390_function_value (tree type, enum machine_mode mode)
+void
+s390_trampoline_template (FILE *file)
 {
-  if (type)
+  rtx op[2];
+  op[0] = gen_rtx_REG (Pmode, 0);
+  op[1] = gen_rtx_REG (Pmode, 1);
+
+  if (TARGET_64BIT)
     {
-      int unsignedp = TYPE_UNSIGNED (type);
-      mode = promote_mode (type, TYPE_MODE (type), &unsignedp, 1);
+      output_asm_insn ("basr\t%1,0", op);
+      output_asm_insn ("lmg\t%0,%1,14(%1)", op);
+      output_asm_insn ("br\t%1", op);
+      ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 10));
     }
-
-  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);
+    {
+      output_asm_insn ("basr\t%1,0", op);
+      output_asm_insn ("lm\t%0,%1,6(%1)", op);
+      output_asm_insn ("br\t%1", op);
+      ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 8));
+    }
 }
 
+/* Emit RTL insns to initialize the variable parts of a trampoline.
+   FNADDR is an RTX for the address of the function's pure code.
+   CXT is an RTX for the static chain value for the function.  */
 
-/* Create and return the va_list datatype.
-
-   On S/390, va_list is an array type equivalent to
-
-      typedef struct __va_list_tag
-        {
-            long __gpr;
-            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
-   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.  */
-
-static tree
-s390_build_builtin_va_list (void)
+void
+s390_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt)
 {
-  tree f_gpr, f_fpr, f_ovf, f_sav, record, type_decl;
-
-  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"),
-                     long_integer_type_node);
-  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);
-  f_sav = build_decl (FIELD_DECL, get_identifier ("__reg_save_area"),
-                     ptr_type_node);
-
-  DECL_FIELD_CONTEXT (f_gpr) = record;
-  DECL_FIELD_CONTEXT (f_fpr) = record;
-  DECL_FIELD_CONTEXT (f_ovf) = record;
-  DECL_FIELD_CONTEXT (f_sav) = record;
+  emit_move_insn (gen_rtx_MEM (Pmode,
+                  memory_address (Pmode,
+                  plus_constant (addr, (TARGET_64BIT ? 16 : 8)))), cxt);
+  emit_move_insn (gen_rtx_MEM (Pmode,
+                  memory_address (Pmode,
+                  plus_constant (addr, (TARGET_64BIT ? 24 : 12)))), fnaddr);
+}
 
-  TREE_CHAIN (record) = type_decl;
-  TYPE_NAME (record) = type_decl;
-  TYPE_FIELDS (record) = f_gpr;
-  TREE_CHAIN (f_gpr) = f_fpr;
-  TREE_CHAIN (f_fpr) = f_ovf;
-  TREE_CHAIN (f_ovf) = f_sav;
+/* Return rtx for 64-bit constant formed from the 32-bit subwords
+   LOW and HIGH, independent of the host word size.  */
 
-  layout_type (record);
+rtx
+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;
 
-  /* The correct type is an array type of one element.  */
-  return build_array_type (record, build_index_type (size_zero_node));
+  return GEN_INT (val);
+#else
+#if HOST_BITS_PER_WIDE_INT >= 32
+  return immed_double_const ((HOST_WIDE_INT)low, (HOST_WIDE_INT)high, DImode);
+#else
+  gcc_unreachable ();
+#endif
+#endif
 }
 
-/* Implement va_start by filling the va_list structure VALIST.
-   STDARG_P is always true, and ignored.
-   NEXTARG points to the first anonymous stack argument.
-
-   The following global variables are used to initialize
-   the va_list structure:
-
-     current_function_args_info:
-       holds number of gprs and fprs used for named arguments.
-     current_function_arg_offset_rtx:
-       holds the offset of the first anonymous stack argument
-       (relative to the virtual arg pointer).  */
+/* Output assembler code to FILE to increment profiler label # LABELNO
+   for profiling a function entry.  */
 
 void
-s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
+s390_function_profiler (FILE *file, int labelno)
 {
-  HOST_WIDE_INT n_gpr, n_fpr;
-  int off;
-  tree f_gpr, f_fpr, f_ovf, f_sav;
-  tree gpr, fpr, ovf, sav, t;
+  rtx op[7];
 
-  f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
-  f_fpr = TREE_CHAIN (f_gpr);
-  f_ovf = TREE_CHAIN (f_fpr);
-  f_sav = TREE_CHAIN (f_ovf);
+  char label[128];
+  ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno);
 
-  valist = build_va_arg_indirect_ref (valist);
-  gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
-  fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
-  ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
-  sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
+  fprintf (file, "# function profiler \n");
 
-  /* Count number of gp and fp argument registers used.  */
+  op[0] = gen_rtx_REG (Pmode, RETURN_REGNUM);
+  op[1] = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
+  op[1] = gen_rtx_MEM (Pmode, plus_constant (op[1], UNITS_PER_WORD));
 
-  n_gpr = current_function_args_info.gprs;
-  n_fpr = current_function_args_info.fprs;
+  op[2] = gen_rtx_REG (Pmode, 1);
+  op[3] = gen_rtx_SYMBOL_REF (Pmode, label);
+  SYMBOL_REF_FLAGS (op[3]) = SYMBOL_FLAG_LOCAL;
 
-  t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr,
-            build_int_cst (NULL_TREE, n_gpr));
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  op[4] = gen_rtx_SYMBOL_REF (Pmode, "_mcount");
+  if (flag_pic)
+    {
+      op[4] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[4]), UNSPEC_PLT);
+      op[4] = gen_rtx_CONST (Pmode, op[4]);
+    }
 
-  t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr,
-            build_int_cst (NULL_TREE, n_fpr));
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  if (TARGET_64BIT)
+    {
+      output_asm_insn ("stg\t%0,%1", op);
+      output_asm_insn ("larl\t%2,%3", op);
+      output_asm_insn ("brasl\t%0,%4", op);
+      output_asm_insn ("lg\t%0,%1", op);
+    }
+  else if (!flag_pic)
+    {
+      op[6] = gen_label_rtx ();
 
-  /* Find the overflow area.  */
-  t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx);
+      output_asm_insn ("st\t%0,%1", op);
+      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]));
+      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);
+      output_asm_insn ("l\t%0,%1", op);
+    }
+  else
+    {
+      op[5] = gen_label_rtx ();
+      op[6] = gen_label_rtx ();
 
-  off = INTVAL (current_function_arg_offset_rtx);
-  off = off < 0 ? 0 : off;
-  if (TARGET_DEBUG_ARG)
-    fprintf (stderr, "va_start: n_gpr = %d, n_fpr = %d off %d\n",
-            (int)n_gpr, (int)n_fpr, off);
+      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]));
+      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]));
+      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);
+      output_asm_insn ("basr\t%0,%0", op);
+      output_asm_insn ("l\t%0,%1", op);
+    }
+}
 
-  t = build (PLUS_EXPR, TREE_TYPE (ovf), t, build_int_cst (NULL_TREE, off));
+/* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF
+   into its SYMBOL_REF_FLAGS.  */
 
-  t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+static void
+s390_encode_section_info (tree decl, rtx rtl, int first)
+{
+  default_encode_section_info (decl, rtl, first);
 
-  /* Find the register save area.  */
-  t = make_tree (TREE_TYPE (sav), return_address_pointer_rtx);
-  t = build (PLUS_EXPR, TREE_TYPE (sav), t,
-            build_int_cst (NULL_TREE, -RETURN_REGNUM * UNITS_PER_WORD));
-  
-  t = build (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  /* 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;
 }
 
-/* Implement va_arg by updating the va_list structure
-   VALIST as required to retrieve an argument of type
-   TYPE, and returning that argument.
+/* Output thunk to FILE that implements a C++ virtual function call (with
+   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.  */
 
-   Generates code equivalent to:
+static void
+s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
+                     HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
+                     tree function)
+{
+  rtx op[10];
+  int nonlocal = 0;
 
-   if (integral value) {
-     if (size  <= 4 && args.gpr < 5 ||
-         size  > 4 && args.gpr < 4 )
-       ret = args.reg_save_area[args.gpr+8]
-     else
-       ret = *args.overflow_arg_area++;
-   } else if (float value) {
-     if (args.fgpr < 2)
-       ret = args.reg_save_area[args.fpr+64]
-     else
-       ret = *args.overflow_arg_area++;
-   } else if (aggregate value) {
-     if (args.gpr < 5)
-       ret = *args.reg_save_area[args.gpr]
-     else
-       ret = **args.overflow_arg_area++;
-   } */
+  /* Operand 0 is the target function.  */
+  op[0] = XEXP (DECL_RTL (function), 0);
+  if (flag_pic && !SYMBOL_REF_LOCAL_P (op[0]))
+    {
+      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]);
+    }
 
-tree
-s390_gimplify_va_arg (tree valist, tree type, tree *pre_p, 
-                     tree *post_p ATTRIBUTE_UNUSED)
-{
-  tree f_gpr, f_fpr, f_ovf, f_sav;
-  tree gpr, fpr, ovf, sav, reg, t, u;
-  int indirect_p, size, n_reg, sav_ofs, sav_scale, max_reg;
-  tree lab_false, lab_over, addr;
+  /* Operand 1 is the 'this' pointer.  */
+  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);
 
-  f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
-  f_fpr = TREE_CHAIN (f_gpr);
-  f_ovf = TREE_CHAIN (f_fpr);
-  f_sav = TREE_CHAIN (f_ovf);
+  /* Operand 2 is the delta.  */
+  op[2] = GEN_INT (delta);
 
-  valist = build_va_arg_indirect_ref (valist);
-  gpr = build (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
-  fpr = build (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
-  ovf = build (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
-  sav = build (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
+  /* Operand 3 is the vcall_offset.  */
+  op[3] = GEN_INT (vcall_offset);
 
-  size = int_size_in_bytes (type);
+  /* Operand 4 is the temporary register.  */
+  op[4] = gen_rtx_REG (Pmode, 1);
 
-  if (pass_by_reference (NULL, TYPE_MODE (type), type, false))
+  /* Operands 5 to 8 can be used as labels.  */
+  op[5] = NULL_RTX;
+  op[6] = NULL_RTX;
+  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)
     {
-      if (TARGET_DEBUG_ARG)
+      /* Setup literal pool pointer if required.  */
+      if ((!DISP_IN_RANGE (delta)
+          && !CONST_OK_FOR_K (delta)
+          && !CONST_OK_FOR_Os (delta))
+         || (!DISP_IN_RANGE (vcall_offset)
+             && !CONST_OK_FOR_K (vcall_offset)
+             && !CONST_OK_FOR_Os (vcall_offset)))
        {
-         fprintf (stderr, "va_arg: aggregate type");
-         debug_tree (type);
+         op[5] = gen_label_rtx ();
+         output_asm_insn ("larl\t%4,%5", op);
        }
 
-      /* Aggregates are passed by reference.  */
-      indirect_p = 1;
-      reg = gpr;
-      n_reg = 1;
+      /* Add DELTA to this pointer.  */
+      if (delta)
+       {
+         if (CONST_OK_FOR_J (delta))
+           output_asm_insn ("la\t%1,%2(%1)", op);
+         else if (DISP_IN_RANGE (delta))
+           output_asm_insn ("lay\t%1,%2(%1)", op);
+         else if (CONST_OK_FOR_K (delta))
+           output_asm_insn ("aghi\t%1,%2", op);
+         else if (CONST_OK_FOR_Os (delta))
+           output_asm_insn ("agfi\t%1,%2", op);
+         else
+           {
+             op[6] = gen_label_rtx ();
+             output_asm_insn ("agf\t%1,%6-%5(%4)", op);
+           }
+       }
 
-      /* kernel stack layout on 31 bit: It is assumed here that no padding
-        will be added by s390_frame_info because for va_args always an even
-        number of gprs has to be saved r15-r2 = 14 regs.  */
-      sav_ofs = 2 * UNITS_PER_WORD;
-      sav_scale = UNITS_PER_WORD;
-      size = UNITS_PER_WORD;
-      max_reg = 4;
-    }
-  else if (s390_function_arg_float (TYPE_MODE (type), type))
-    {
-      if (TARGET_DEBUG_ARG)
+      /* Perform vcall adjustment.  */
+      if (vcall_offset)
+       {
+         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_K (vcall_offset))
+           {
+             output_asm_insn ("lghi\t%4,%3", op);
+             output_asm_insn ("ag\t%4,0(%1)", op);
+             output_asm_insn ("ag\t%1,0(%4)", op);
+           }
+         else if (CONST_OK_FOR_Os (vcall_offset))
+           {
+             output_asm_insn ("lgfi\t%4,%3", op);
+             output_asm_insn ("ag\t%4,0(%1)", op);
+             output_asm_insn ("ag\t%1,0(%4)", op);
+           }
+         else
+           {
+             op[7] = gen_label_rtx ();
+             output_asm_insn ("llgf\t%4,%7-%5(%4)", op);
+             output_asm_insn ("ag\t%4,0(%1)", op);
+             output_asm_insn ("ag\t%1,0(%4)", op);
+           }
+       }
+
+      /* Jump to target.  */
+      output_asm_insn ("jg\t%0", op);
+
+      /* Output literal pool if required.  */
+      if (op[5])
+       {
+         output_asm_insn (".align\t4", op);
+         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]));
+         output_asm_insn (".long\t%2", op);
+       }
+      if (op[7])
        {
-         fprintf (stderr, "va_arg: float type");
-         debug_tree (type);
+         targetm.asm_out.internal_label (file, "L",
+                                         CODE_LABEL_NUMBER (op[7]));
+         output_asm_insn (".long\t%3", op);
        }
-
-      /* FP args go in FP registers, if present.  */
-      indirect_p = 0;
-      reg = fpr;
-      n_reg = 1;
-      sav_ofs = 16 * UNITS_PER_WORD;
-      sav_scale = 8;
-      /* TARGET_64BIT has up to 4 parameter in fprs */
-      max_reg = TARGET_64BIT ? 3 : 1;
     }
   else
     {
-      if (TARGET_DEBUG_ARG)
+      /* Setup base pointer if required.  */
+      if (!vcall_offset
+         || (!DISP_IN_RANGE (delta)
+              && !CONST_OK_FOR_K (delta)
+             && !CONST_OK_FOR_Os (delta))
+         || (!DISP_IN_RANGE (delta)
+              && !CONST_OK_FOR_K (vcall_offset)
+             && !CONST_OK_FOR_Os (vcall_offset)))
        {
-         fprintf (stderr, "va_arg: other type");
-         debug_tree (type);
+         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]));
        }
 
-      /* Otherwise into GP registers.  */
-      indirect_p = 0;
-      reg = gpr;
-      n_reg = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
-
-      /* kernel stack layout on 31 bit: It is assumed here that no padding
-        will be added by s390_frame_info because for va_args always an even
-        number of gprs has to be saved r15-r2 = 14 regs.  */
-      sav_ofs = 2 * UNITS_PER_WORD;
-
-      if (size < UNITS_PER_WORD)
-       sav_ofs += UNITS_PER_WORD - size;
-
-      sav_scale = UNITS_PER_WORD;
-      if (n_reg > 1)
-       max_reg = 3;
-      else
-       max_reg = 4;
-    }
-
-  /* Pull the value out of the saved registers ...  */
-
-  lab_false = create_artificial_label ();
-  lab_over = create_artificial_label ();
-  addr = create_tmp_var (ptr_type_node, "addr");
-  DECL_POINTER_ALIAS_SET (addr) = s390_sr_alias_set;
-
-  t = fold_convert (TREE_TYPE (reg), size_int (max_reg));
-  t = build2 (GT_EXPR, boolean_type_node, reg, t);
-  u = build1 (GOTO_EXPR, void_type_node, lab_false);
-  t = build3 (COND_EXPR, void_type_node, t, u, NULL_TREE);
-  gimplify_and_add (t, pre_p);
-
-  t = build2 (PLUS_EXPR, ptr_type_node, sav, 
-             fold_convert (ptr_type_node, size_int (sav_ofs)));
-  u = build2 (MULT_EXPR, TREE_TYPE (reg), reg, 
-             fold_convert (TREE_TYPE (reg), size_int (sav_scale)));
-  t = build2 (PLUS_EXPR, ptr_type_node, t, fold_convert (ptr_type_node, u));
-
-  t = build2 (MODIFY_EXPR, void_type_node, addr, t);
-  gimplify_and_add (t, pre_p);
-
-  t = build1 (GOTO_EXPR, void_type_node, lab_over);
-  gimplify_and_add (t, pre_p);
-
-  t = build1 (LABEL_EXPR, void_type_node, lab_false);
-  append_to_statement_list (t, pre_p);
-
-
-  /* ... Otherwise out of the overflow area.  */
+      /* Add DELTA to this pointer.  */
+      if (delta)
+       {
+         if (CONST_OK_FOR_J (delta))
+           output_asm_insn ("la\t%1,%2(%1)", op);
+         else if (DISP_IN_RANGE (delta))
+           output_asm_insn ("lay\t%1,%2(%1)", op);
+         else if (CONST_OK_FOR_K (delta))
+           output_asm_insn ("ahi\t%1,%2", op);
+         else if (CONST_OK_FOR_Os (delta))
+           output_asm_insn ("afi\t%1,%2", op);
+         else
+           {
+             op[6] = gen_label_rtx ();
+             output_asm_insn ("a\t%1,%6-%5(%4)", op);
+           }
+       }
 
-  t = ovf;
-  if (size < UNITS_PER_WORD)
-    t = build2 (PLUS_EXPR, ptr_type_node, t, 
-               fold_convert (ptr_type_node, size_int (UNITS_PER_WORD - size)));
+      /* Perform vcall adjustment.  */
+      if (vcall_offset)
+        {
+         if (CONST_OK_FOR_J (vcall_offset))
+           {
+             output_asm_insn ("l\t%4,0(%1)", op);
+             output_asm_insn ("a\t%1,%3(%4)", op);
+           }
+         else if (DISP_IN_RANGE (vcall_offset))
+           {
+             output_asm_insn ("l\t%4,0(%1)", op);
+             output_asm_insn ("ay\t%1,%3(%4)", op);
+           }
+         else if (CONST_OK_FOR_K (vcall_offset))
+           {
+             output_asm_insn ("lhi\t%4,%3", op);
+             output_asm_insn ("a\t%4,0(%1)", op);
+             output_asm_insn ("a\t%1,0(%4)", op);
+           }
+         else if (CONST_OK_FOR_Os (vcall_offset))
+           {
+             output_asm_insn ("iilf\t%4,%3", op);
+             output_asm_insn ("a\t%4,0(%1)", op);
+             output_asm_insn ("a\t%1,0(%4)", op);
+           }
+         else
+           {
+             op[7] = gen_label_rtx ();
+             output_asm_insn ("l\t%4,%7-%5(%4)", op);
+             output_asm_insn ("a\t%4,0(%1)", op);
+             output_asm_insn ("a\t%1,0(%4)", op);
+           }
 
-  gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue);
+         /* We had to clobber the base pointer register.
+            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]));
+       }
 
-  u = build2 (MODIFY_EXPR, void_type_node, addr, t);
-  gimplify_and_add (u, pre_p);
+      /* Jump to target.  */
+      op[8] = gen_label_rtx ();
 
-  t = build2 (PLUS_EXPR, ptr_type_node, t, 
-             fold_convert (ptr_type_node, size_int (size)));
-  t = build2 (MODIFY_EXPR, ptr_type_node, ovf, t);
-  gimplify_and_add (t, pre_p);
+      if (!flag_pic)
+       output_asm_insn ("l\t%4,%8-%5(%4)", op);
+      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);
+       }
 
-  t = build1 (LABEL_EXPR, void_type_node, lab_over);
-  append_to_statement_list (t, pre_p);
+      output_asm_insn ("br\t%4", op);
 
+      /* Output literal pool.  */
+      output_asm_insn (".align\t4", op);
 
-  /* Increment register save count.  */
+      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;
+       }
 
-  u = build2 (PREINCREMENT_EXPR, TREE_TYPE (reg), reg,
-             fold_convert (TREE_TYPE (reg), size_int (n_reg)));
-  gimplify_and_add (u, pre_p);
+      targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[8]));
+      if (!flag_pic)
+       output_asm_insn (".long\t%0", op);
+      else
+       output_asm_insn (".long\t%0-%5", op);
 
-  if (indirect_p)
-    {
-      t = build_pointer_type (build_pointer_type (type));
-      addr = fold_convert (t, addr);
-      addr = build_va_arg_indirect_ref (addr);
-    }
-  else
-    {
-      t = build_pointer_type (type);
-      addr = fold_convert (t, addr);
+      if (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]));
+         output_asm_insn (".long\t%3", op);
+       }
     }
-
-  return build_va_arg_indirect_ref (addr);
 }
 
-
-/* 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)
+static bool
+s390_valid_pointer_mode (enum machine_mode mode)
 {
-  tree ftype;
-
-  ftype = build_function_type (ptr_type_node, void_list_node);
-  lang_hooks.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);
-  lang_hooks.builtin_function ("__builtin_set_thread_pointer", ftype,
-                              S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD,
-                              NULL, NULL_TREE);
+  return (mode == SImode || (TARGET_64BIT && mode == DImode));
 }
 
-/* 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.  */
+/* Checks whether the given ARGUMENT_LIST would use a caller
+   saved register.  This is used to decide whether sibling call
+   optimization could be performed on the respective function
+   call.  */
 
-static rtx
-s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
-                    enum machine_mode mode ATTRIBUTE_UNUSED,
-                    int ignore ATTRIBUTE_UNUSED)
+static bool
+s390_call_saved_register_used (tree argument_list)
 {
-#define MAX_ARGS 2
+  CUMULATIVE_ARGS cum;
+  tree parameter;
+  enum machine_mode mode;
+  tree type;
+  rtx parm_rtx;
+  int reg;
 
-  unsigned int const *code_for_builtin =
-    TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31;
+  INIT_CUMULATIVE_ARGS (cum, NULL, NULL, 0, 0);
+
+  while (argument_list)
+    {
+      parameter = TREE_VALUE (argument_list);
+      argument_list = TREE_CHAIN (argument_list);
 
-  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;
+      gcc_assert (parameter);
 
-  if (fcode >= S390_BUILTIN_max)
-    internal_error ("bad builtin fcode");
-  icode = code_for_builtin[fcode];
-  if (icode == 0)
-    internal_error ("bad builtin fcode");
+      /* For an undeclared variable passed as parameter we will get
+        an ERROR_MARK node here.  */
+      if (TREE_CODE (parameter) == ERROR_MARK)
+       return true;
 
-  nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node;
+      type = TREE_TYPE (parameter);
+      gcc_assert (type);
 
-  for (arglist = TREE_OPERAND (exp, 1), arity = 0;
-       arglist;
-       arglist = TREE_CHAIN (arglist), arity++)
-    {
-      const struct insn_operand_data *insn_op;
+      mode = TYPE_MODE (type);
+      gcc_assert (mode);
 
-      tree arg = TREE_VALUE (arglist);
-      if (arg == error_mark_node)
-       return NULL_RTX;
-      if (arity > MAX_ARGS)
-       return NULL_RTX;
+      if (pass_by_reference (&cum, mode, type, true))
+       {
+         mode = Pmode;
+         type = build_pointer_type (type);
+       }
 
-      insn_op = &insn_data[icode].operand[arity + nonvoid];
+       parm_rtx = s390_function_arg (&cum, mode, type, 0);
 
-      op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0);
+       s390_function_arg_advance (&cum, mode, type, 0);
 
-      if (!(*insn_op->predicate) (op[arity], insn_op->mode))
-       op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]);
+       if (parm_rtx && REG_P (parm_rtx))
+        {
+          for (reg = 0;
+               reg < HARD_REGNO_NREGS (REGNO (parm_rtx), GET_MODE (parm_rtx));
+               reg++)
+            if (! call_used_regs[reg + REGNO (parm_rtx)])
+              return true;
+        }
     }
+  return false;
+}
 
-  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);
-    }
+/* Return true if the given call expression can be
+   turned into a sibling call.
+   DECL holds the declaration of the function to be called whereas
+   EXP is the call expression itself.  */
 
-  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);
+static bool
+s390_function_ok_for_sibcall (tree decl, tree exp)
+{
+  /* The TPF epilogue uses register 1.  */
+  if (TARGET_TPF_PROFILING)
+    return false;
 
-  if (nonvoid)
-    return target;
-  else
-    return const0_rtx;
+  /* The 31 bit PLT code uses register 12 (GOT pointer - caller saved)
+     which would have to be restored before the sibcall.  */
+  if (!TARGET_64BIT && flag_pic && decl && TREE_PUBLIC (decl))
+    return false;
+
+  /* Register 6 on s390 is available as an argument register but unfortunately
+     "caller saved". This makes functions needing this register for arguments
+     not suitable for sibcalls.  */
+  if (TREE_OPERAND (exp, 1)
+      && s390_call_saved_register_used (TREE_OPERAND (exp, 1)))
+      return false;
+
+  return true;
 }
 
+/* Return the fixed registers used for condition codes.  */
 
-/* Output assembly code for the trampoline template to
-   stdio stream FILE.
+static bool
+s390_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2)
+{
+  *p1 = CC_REGNUM;
+  *p2 = INVALID_REGNUM;
+  return true;
+}
 
-   On S/390, we use gpr 1 internally in the trampoline code;
-   gpr 0 is used to hold the static chain.  */
+/* This function is used by the call expanders of the machine description.
+   It emits the call insn itself together with the necessary operations
+   to adjust the target address and returns the emitted insn.
+   ADDR_LOCATION is the target address rtx
+   TLS_CALL the location of the thread-local symbol
+   RESULT_REG the register where the result of the call should be stored
+   RETADDR_REG the register where the return address should be stored
+               If this parameter is NULL_RTX the call is considered
+               to be a sibling call.  */
 
-void
-s390_trampoline_template (FILE *file)
+rtx
+s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
+               rtx retaddr_reg)
 {
-  rtx op[2];
-  op[0] = gen_rtx_REG (Pmode, 0);
-  op[1] = gen_rtx_REG (Pmode, 1);
+  bool plt_call = false;
+  rtx insn;
+  rtx call;
+  rtx clobber;
+  rtvec vec;
 
-  if (TARGET_64BIT)
+  /* Direct function calls need special treatment.  */
+  if (GET_CODE (addr_location) == SYMBOL_REF)
     {
-      output_asm_insn ("basr\t%1,0", op);
-      output_asm_insn ("lmg\t%0,%1,14(%1)", op);
-      output_asm_insn ("br\t%1", op);
-      ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 10));
+      /* When calling a global routine in PIC mode, we must
+         replace the symbol itself with the PLT stub.  */
+      if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location))
+        {
+         addr_location = gen_rtx_UNSPEC (Pmode,
+                                         gen_rtvec (1, addr_location),
+                                         UNSPEC_PLT);
+         addr_location = gen_rtx_CONST (Pmode, addr_location);
+         plt_call = true;
+        }
+
+      /* Unless we can use the bras(l) insn, force the
+         routine address into a register.  */
+      if (!TARGET_SMALL_EXEC && !TARGET_CPU_ZARCH)
+        {
+         if (flag_pic)
+           addr_location = legitimize_pic_address (addr_location, 0);
+         else
+           addr_location = force_reg (Pmode, addr_location);
+       }
     }
-  else
+
+  /* If it is already an indirect call or the code above moved the
+     SYMBOL_REF to somewhere else make sure the address can be found in
+     register 1.  */
+  if (retaddr_reg == NULL_RTX
+      && GET_CODE (addr_location) != SYMBOL_REF
+      && !plt_call)
     {
-      output_asm_insn ("basr\t%1,0", op);
-      output_asm_insn ("lm\t%0,%1,6(%1)", op);
-      output_asm_insn ("br\t%1", op);
-      ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 8));
+      emit_move_insn (gen_rtx_REG (Pmode, SIBCALL_REGNUM), addr_location);
+      addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM);
     }
-}
-
-/* Emit RTL insns to initialize the variable parts of a trampoline.
-   FNADDR is an RTX for the address of the function's pure code.
-   CXT is an RTX for the static chain value for the function.  */
 
-void
-s390_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt)
-{
-  emit_move_insn (gen_rtx_MEM (Pmode,
-                  memory_address (Pmode,
-                  plus_constant (addr, (TARGET_64BIT ? 16 : 8)))), cxt);
-  emit_move_insn (gen_rtx_MEM (Pmode,
-                  memory_address (Pmode,
-                  plus_constant (addr, (TARGET_64BIT ? 24 : 12)))), fnaddr);
-}
+  addr_location = gen_rtx_MEM (QImode, addr_location);
+  call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);
 
-/* Return rtx for 64-bit constant formed from the 32-bit subwords
-   LOW and HIGH, independent of the host word size.  */
+  if (result_reg != NULL_RTX)
+    call = gen_rtx_SET (VOIDmode, result_reg, call);
 
-rtx
-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;
+  if (retaddr_reg != NULL_RTX)
+    {
+      clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg);
 
-  return GEN_INT (val);
-#else
-#if HOST_BITS_PER_WIDE_INT >= 32
-  return immed_double_const ((HOST_WIDE_INT)low, (HOST_WIDE_INT)high, DImode);
-#else
-  abort ();
-#endif
-#endif
-}
+      if (tls_call != NULL_RTX)
+       vec = gen_rtvec (3, call, clobber,
+                        gen_rtx_USE (VOIDmode, tls_call));
+      else
+       vec = gen_rtvec (2, call, clobber);
 
-/* Output assembler code to FILE to increment profiler label # LABELNO
-   for profiling a function entry.  */
+      call = gen_rtx_PARALLEL (VOIDmode, vec);
+    }
 
-void
-s390_function_profiler (FILE *file, int labelno)
-{
-  rtx op[7];
+  insn = emit_call_insn (call);
 
-  char label[128];
-  ASM_GENERATE_INTERNAL_LABEL (label, "LP", labelno);
+  /* 31-bit PLT stubs and tls calls use the GOT register implicitly.  */
+  if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX)
+    {
+      /* s390_function_ok_for_sibcall should
+        have denied sibcalls in this case.  */
+      gcc_assert (retaddr_reg != NULL_RTX);
 
-  fprintf (file, "# function profiler \n");
+      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
+    }
+  return insn;
+}
 
-  op[0] = gen_rtx_REG (Pmode, RETURN_REGNUM);
-  op[1] = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
-  op[1] = gen_rtx_MEM (Pmode, plus_constant (op[1], UNITS_PER_WORD));
+/* Implement CONDITIONAL_REGISTER_USAGE.  */
 
-  op[2] = gen_rtx_REG (Pmode, 1);
-  op[3] = gen_rtx_SYMBOL_REF (Pmode, label);
-  SYMBOL_REF_FLAGS (op[3]) = SYMBOL_FLAG_LOCAL;
+void
+s390_conditional_register_usage (void)
+{
+  int i;
 
-  op[4] = gen_rtx_SYMBOL_REF (Pmode, "_mcount");
   if (flag_pic)
     {
-      op[4] = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op[4]), UNSPEC_PLT);
-      op[4] = gen_rtx_CONST (Pmode, op[4]);
+      fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
+      call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
     }
-
-  if (TARGET_64BIT)
+  if (TARGET_CPU_ZARCH)
     {
-      output_asm_insn ("stg\t%0,%1", op);
-      output_asm_insn ("larl\t%2,%3", op);
-      output_asm_insn ("brasl\t%0,%4", op);
-      output_asm_insn ("lg\t%0,%1", op);
+      fixed_regs[BASE_REGNUM] = 0;
+      call_used_regs[BASE_REGNUM] = 0;
+      fixed_regs[RETURN_REGNUM] = 0;
+      call_used_regs[RETURN_REGNUM] = 0;
     }
-  else if (!flag_pic)
+  if (TARGET_64BIT)
     {
-      op[6] = gen_label_rtx ();
-
-      output_asm_insn ("st\t%0,%1", op);
-      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]));
-      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);
-      output_asm_insn ("l\t%0,%1", op);
+      for (i = 24; i < 32; i++)
+       call_used_regs[i] = call_really_used_regs[i] = 0;
     }
   else
     {
-      op[5] = gen_label_rtx ();
-      op[6] = gen_label_rtx ();
+      for (i = 18; i < 20; i++)
+       call_used_regs[i] = call_really_used_regs[i] = 0;
+    }
 
-      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]));
-      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]));
-      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);
-      output_asm_insn ("basr\t%0,%0", op);
-      output_asm_insn ("l\t%0,%1", op);
+  if (TARGET_SOFT_FLOAT)
+    {
+      for (i = 16; i < 32; i++)
+       call_used_regs[i] = fixed_regs[i] = 1;
     }
 }
 
-/* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF
-   into its SYMBOL_REF_FLAGS.  */
+/* Corresponding function to eh_return expander.  */
 
-static void
-s390_encode_section_info (tree decl, rtx rtl, int first)
+static GTY(()) rtx s390_tpf_eh_return_symbol;
+void
+s390_emit_tpf_eh_return (rtx target)
 {
-  default_encode_section_info (decl, rtl, first);
+  rtx insn, reg;
 
-  /* 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;
+  if (!s390_tpf_eh_return_symbol)
+    s390_tpf_eh_return_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tpf_eh_return");
+
+  reg = gen_rtx_REG (Pmode, 2);
+
+  emit_move_insn (reg, target);
+  insn = s390_emit_call (s390_tpf_eh_return_symbol, NULL_RTX, reg,
+                                     gen_rtx_REG (Pmode, RETURN_REGNUM));
+  use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg);
+
+  emit_move_insn (EH_RETURN_HANDLER_RTX, reg);
 }
 
-/* Output thunk to FILE that implements a C++ virtual function call (with
-   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.  */
+/* Rework the prologue/epilogue to avoid saving/restoring
+   registers unnecessarily.  */
 
 static void
-s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
-                     HOST_WIDE_INT delta, HOST_WIDE_INT vcall_offset,
-                     tree function)
+s390_optimize_prologue (void)
 {
-  rtx op[10];
-  int nonlocal = 0;
-
-  /* Operand 0 is the target function.  */
-  op[0] = XEXP (DECL_RTL (function), 0);
-  if (flag_pic && !SYMBOL_REF_LOCAL_P (op[0]))
-    {
-      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)), function))
-    op[1] = gen_rtx_REG (Pmode, 3);
-  else
-    op[1] = gen_rtx_REG (Pmode, 2);
+  rtx insn, new_insn, next_insn;
 
-  /* Operand 2 is the delta.  */
-  op[2] = GEN_INT (delta);
+  /* Do a final recompute of the frame-related data.  */
 
-  /* Operand 3 is the vcall_offset.  */
-  op[3] = GEN_INT (vcall_offset);
+  s390_update_frame_layout ();
 
-  /* Operand 4 is the temporary register.  */
-  op[4] = gen_rtx_REG (Pmode, 1);
+  /* If all special registers are in fact used, there's nothing we
+     can do, so no point in walking the insn list.  */
 
-  /* Operands 5 to 8 can be used as labels.  */
-  op[5] = NULL_RTX;
-  op[6] = NULL_RTX;
-  op[7] = NULL_RTX;
-  op[8] = NULL_RTX;
+  if (cfun_frame_layout.first_save_gpr <= BASE_REGNUM 
+      && cfun_frame_layout.last_save_gpr >= BASE_REGNUM
+      && (TARGET_CPU_ZARCH 
+          || (cfun_frame_layout.first_save_gpr <= RETURN_REGNUM 
+              && cfun_frame_layout.last_save_gpr >= RETURN_REGNUM)))
+    return;
 
-  /* Operand 9 can be used for temporary register.  */
-  op[9] = NULL_RTX;
+  /* Search for prologue/epilogue insns and replace them.  */
 
-  /* Generate code.  */
-  if (TARGET_64BIT)
+  for (insn = get_insns (); insn; insn = next_insn)
     {
-      /* Setup literal pool pointer if required.  */
-      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);
-       }
-
-      /* Add DELTA to this pointer.  */
-      if (delta)
-       {
-         if (CONST_OK_FOR_CONSTRAINT_P (delta, 'J', "J"))
-           output_asm_insn ("la\t%1,%2(%1)", op);
-         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
-           {
-             op[6] = gen_label_rtx ();
-             output_asm_insn ("agf\t%1,%6-%5(%4)", op);
-           }
-       }
-
-      /* Perform vcall adjustment.  */
-      if (vcall_offset)
-       {
-         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_CONSTRAINT_P (vcall_offset, 'K', "K"))
-           {
-             output_asm_insn ("lghi\t%4,%3", op);
-             output_asm_insn ("ag\t%4,0(%1)", op);
-             output_asm_insn ("ag\t%1,0(%4)", op);
-           }
-         else
-           {
-             op[7] = gen_label_rtx ();
-             output_asm_insn ("llgf\t%4,%7-%5(%4)", op);
-             output_asm_insn ("ag\t%4,0(%1)", op);
-             output_asm_insn ("ag\t%1,0(%4)", op);
-           }
-       }
+      int first, last, off;
+      rtx set, base, offset;
 
-      /* Jump to target.  */
-      output_asm_insn ("jg\t%0", op);
+      next_insn = NEXT_INSN (insn);
 
-      /* Output literal pool if required.  */
-      if (op[5])
-       {
-         output_asm_insn (".align\t4", op);
-         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]));
-         output_asm_insn (".long\t%2", op);
-       }
-      if (op[7])
-       {
-         targetm.asm_out.internal_label (file, "L",
-                                         CODE_LABEL_NUMBER (op[7]));
-         output_asm_insn (".long\t%3", op);
-       }
-    }
-  else
-    {
-      /* Setup base pointer if required.  */
-      if (!vcall_offset
-         || (!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]));
-       }
+      if (GET_CODE (insn) != INSN)
+       continue;
 
-      /* Add DELTA to this pointer.  */
-      if (delta)
+      if (GET_CODE (PATTERN (insn)) == PARALLEL
+         && store_multiple_operation (PATTERN (insn), VOIDmode))
        {
-         if (CONST_OK_FOR_CONSTRAINT_P (delta, 'J', "J"))
-           output_asm_insn ("la\t%1,%2(%1)", op);
-         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
-           {
-             op[6] = gen_label_rtx ();
-             output_asm_insn ("a\t%1,%6-%5(%4)", op);
-           }
-       }
+         set = XVECEXP (PATTERN (insn), 0, 0);
+         first = REGNO (SET_SRC (set));
+         last = first + XVECLEN (PATTERN (insn), 0) - 1;
+         offset = const0_rtx;
+         base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
+         off = INTVAL (offset);
 
-      /* Perform vcall adjustment.  */
-      if (vcall_offset)
-        {
-         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 (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);
-             output_asm_insn ("a\t%1,0(%4)", op);
-           }
-         else
+         if (GET_CODE (base) != REG || off < 0)
+           continue;
+         if (cfun_frame_layout.first_save_gpr != -1
+             && (cfun_frame_layout.first_save_gpr < first
+                 || cfun_frame_layout.last_save_gpr > last))
+           continue;
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
+         if (first > BASE_REGNUM || last < BASE_REGNUM)
+           continue;
+
+         if (cfun_frame_layout.first_save_gpr != -1)
            {
-             op[7] = gen_label_rtx ();
-             output_asm_insn ("l\t%4,%7-%5(%4)", op);
-             output_asm_insn ("a\t%4,0(%1)", op);
-             output_asm_insn ("a\t%1,0(%4)", op);
+             new_insn  = save_gprs (base, 
+                                    off + (cfun_frame_layout.first_save_gpr
+                                           - first) * UNITS_PER_WORD, 
+                                    cfun_frame_layout.first_save_gpr,
+                                    cfun_frame_layout.last_save_gpr);
+             new_insn = emit_insn_before (new_insn, insn);
+             INSN_ADDRESSES_NEW (new_insn, -1);
            }
 
-         /* We had to clobber the base pointer register.
-            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]));
+         remove_insn (insn);
+         continue;
        }
 
-      /* Jump to target.  */
-      op[8] = gen_label_rtx ();
-
-      if (!flag_pic)
-       output_asm_insn ("l\t%4,%8-%5(%4)", op);
-      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)
+      if (cfun_frame_layout.first_save_gpr == -1
+         && GET_CODE (PATTERN (insn)) == SET
+         && GET_CODE (SET_SRC (PATTERN (insn))) == REG
+         && (REGNO (SET_SRC (PATTERN (insn))) == BASE_REGNUM
+             || (!TARGET_CPU_ZARCH
+                 && REGNO (SET_SRC (PATTERN (insn))) == RETURN_REGNUM))
+         && GET_CODE (SET_DEST (PATTERN (insn))) == MEM)
        {
-         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);
-       }
+         set = PATTERN (insn);
+         first = REGNO (SET_SRC (set));
+         offset = const0_rtx;
+         base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
+         off = INTVAL (offset);
 
-      output_asm_insn ("br\t%4", op);
+         if (GET_CODE (base) != REG || off < 0)
+           continue;
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
 
-      /* Output literal pool.  */
-      output_asm_insn (".align\t4", op);
+         remove_insn (insn);
+         continue;
+       }
 
-      if (nonlocal && flag_pic == 2)
-       output_asm_insn (".long\t%0", op);
-      if (nonlocal)
+      if (GET_CODE (PATTERN (insn)) == PARALLEL
+         && load_multiple_operation (PATTERN (insn), VOIDmode))
        {
-         op[0] = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
-         SYMBOL_REF_FLAGS (op[0]) = SYMBOL_FLAG_LOCAL;
-       }
+         set = XVECEXP (PATTERN (insn), 0, 0);
+         first = REGNO (SET_DEST (set));
+         last = first + XVECLEN (PATTERN (insn), 0) - 1;
+         offset = const0_rtx;
+         base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
+         off = INTVAL (offset);
 
-      targetm.asm_out.internal_label (file, "L", CODE_LABEL_NUMBER (op[8]));
-      if (!flag_pic)
-       output_asm_insn (".long\t%0", op);
-      else
-       output_asm_insn (".long\t%0-%5", op);
+         if (GET_CODE (base) != REG || off < 0)
+           continue;
+         if (cfun_frame_layout.first_restore_gpr != -1
+             && (cfun_frame_layout.first_restore_gpr < first
+                 || cfun_frame_layout.last_restore_gpr > last))
+           continue;
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
+         if (first > BASE_REGNUM || last < BASE_REGNUM)
+           continue;
 
-      if (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]));
-         output_asm_insn (".long\t%3", op);
+         if (cfun_frame_layout.first_restore_gpr != -1)
+           {
+             new_insn = restore_gprs (base, 
+                                      off + (cfun_frame_layout.first_restore_gpr
+                                             - first) * UNITS_PER_WORD, 
+                                      cfun_frame_layout.first_restore_gpr,
+                                      cfun_frame_layout.last_restore_gpr);
+             new_insn = emit_insn_before (new_insn, insn);
+             INSN_ADDRESSES_NEW (new_insn, -1);
+           }
+
+         remove_insn (insn);
+         continue;
        }
-    }
-}
 
-bool
-s390_valid_pointer_mode (enum machine_mode mode)
-{
-  return (mode == SImode || (TARGET_64BIT && mode == DImode));
-}
+      if (cfun_frame_layout.first_restore_gpr == -1
+         && GET_CODE (PATTERN (insn)) == SET
+         && GET_CODE (SET_DEST (PATTERN (insn))) == REG
+         && (REGNO (SET_DEST (PATTERN (insn))) == BASE_REGNUM
+             || (!TARGET_CPU_ZARCH
+                 && REGNO (SET_DEST (PATTERN (insn))) == RETURN_REGNUM))
+         && GET_CODE (SET_SRC (PATTERN (insn))) == MEM)
+       {
+         set = PATTERN (insn);
+         first = REGNO (SET_DEST (set));
+         offset = const0_rtx;
+         base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
+         off = INTVAL (offset);
 
-/* How to allocate a 'struct machine_function'.  */
+         if (GET_CODE (base) != REG || off < 0)
+           continue;
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
 
-static struct machine_function *
-s390_init_machine_status (void)
-{
-  return ggc_alloc_cleared (sizeof (struct machine_function));
+         remove_insn (insn);
+         continue;
+       }
+    }
 }
 
-/* Checks whether the given ARGUMENT_LIST would use a caller
-   saved register.  This is used to decide whether sibling call
-   optimization could be performed on the respective function
-   call.  */
+/* Perform machine-dependent processing.  */
 
-static bool
-s390_call_saved_register_used (tree argument_list)
+static void
+s390_reorg (void)
 {
-  CUMULATIVE_ARGS cum;
-  tree parameter;
-  enum machine_mode mode;
-  tree type;
-  rtx parm_rtx;
-  int reg;
-
-  INIT_CUMULATIVE_ARGS (cum, NULL, NULL, 0, 0);
-
-  while (argument_list)
-    {
-      parameter = TREE_VALUE (argument_list);
-      argument_list = TREE_CHAIN (argument_list);
-
-      if (!parameter)
-       abort();
+  bool pool_overflow = false;
 
-      /* For an undeclared variable passed as parameter we will get
-        an ERROR_MARK node here.  */
-      if (TREE_CODE (parameter) == ERROR_MARK)
-       return true;
+  /* Make sure all splits have been performed; splits after
+     machine_dependent_reorg might confuse insn length counts.  */
+  split_all_insns_noflow ();
 
-      if (! (type = TREE_TYPE (parameter)))
-       abort();
 
-      if (! (mode = TYPE_MODE (TREE_TYPE (parameter))))
-       abort();
+  /* Install the main literal pool and the associated base
+     register load insns.
 
-      if (pass_by_reference (&cum, mode, type, true))
-       {
-         mode = Pmode;
-         type = build_pointer_type (type);
-       }
+     In addition, there are two problematic situations we need
+     to correct:
 
-       parm_rtx = s390_function_arg (&cum, mode, type, 0);
+     - the literal pool might be > 4096 bytes in size, so that
+       some of its elements cannot be directly accessed
 
-       s390_function_arg_advance (&cum, mode, type, 0);
+     - a branch target might be > 64K away from the branch, so that
+       it is not possible to use a PC-relative instruction.
 
-       if (parm_rtx && REG_P (parm_rtx))
-        {
-          for (reg = 0;
-               reg < HARD_REGNO_NREGS (REGNO (parm_rtx), GET_MODE (parm_rtx));
-               reg++)
-            if (! call_used_regs[reg + REGNO (parm_rtx)])
-              return true;
-        }
-    }
-  return false;
-}
+     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.
 
-/* Return true if the given call expression can be
-   turned into a sibling call.
-   DECL holds the declaration of the function to be called whereas
-   EXP is the call expression itself.  */
+     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.
 
-static bool
-s390_function_ok_for_sibcall (tree decl, tree exp)
-{
-  /* The TPF epilogue uses register 1.  */
-  if (TARGET_TPF_PROFILING)
-    return false;
+     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
+     strictly decreases the total number of PC-relative branches
+     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.)  */
 
-  /* The 31 bit PLT code uses register 12 (GOT pointer - caller saved)
-     which would have to be restored before the sibcall.  */
-  if (!TARGET_64BIT && flag_pic && decl && TREE_PUBLIC (decl))
-    return false;
+  for (;;)
+    {
+      struct constant_pool *pool = NULL;
 
-  /* Register 6 on s390 is available as an argument register but unfortunately
-     "caller saved". This makes functions needing this register for arguments
-     not suitable for sibcalls.  */
-  if (TREE_OPERAND (exp, 1)
-      && s390_call_saved_register_used (TREE_OPERAND (exp, 1)))
-      return false;
+      /* Collect the literal pool.  */
+      if (!pool_overflow)
+       {
+         pool = s390_mainpool_start ();
+         if (!pool)
+           pool_overflow = true;
+       }
 
-  return true;
-}
+      /* If literal pool overflowed, start to chunkify it.  */
+      if (pool_overflow)
+        pool = s390_chunkify_start ();
 
-/* Return the fixed registers used for condition codes.  */
+      /* Split out-of-range branches.  If this has created new
+        literal pool entries, cancel current chunk list and
+        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_overflow)
+            s390_chunkify_cancel (pool);
+         else
+            s390_mainpool_cancel (pool);
 
-static bool
-s390_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2)
-{
-  *p1 = CC_REGNUM;
-  *p2 = INVALID_REGNUM;
-  return true;
-}
+          continue;
+        }
 
-/* If two condition code modes are compatible, return a condition code
-   mode which is compatible with both.  Otherwise, return
-   VOIDmode.  */
+      /* If we made it up to here, both conditions are satisfied.
+        Finish up literal pool related changes.  */
+      if (pool_overflow)
+       s390_chunkify_finish (pool);
+      else
+       s390_mainpool_finish (pool);
 
-static enum machine_mode
-s390_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2)
-{
-  if (m1 == m2)
-    return m1;
+      /* We're done splitting branches.  */
+      cfun->machine->split_branches_pending_p = false;
+      break;
+    }
 
-  switch (m1)
+  /* Generate out-of-pool execute target insns.  */
+  if (TARGET_CPU_ZARCH)
     {
-    case CCZmode:
-      if (m2 == CCUmode || m2 == CCTmode
-         || m2 == CCSmode || m2 == CCSRmode || m2 == CCURmode)
-        return m2;
-      return VOIDmode;
+      rtx insn, label, target;
 
-    case CCSmode:
-    case CCUmode:
-    case CCTmode:
-    case CCSRmode:
-    case CCURmode:
-      if (m2 == CCZmode)
-       return m1;
-      
-      return VOIDmode;
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+       {
+         label = s390_execute_label (insn);
+         if (!label)
+           continue;
 
-    default:
-      return VOIDmode;
+         gcc_assert (label != const0_rtx);
+
+         target = emit_label (XEXP (label, 0));
+         INSN_ADDRESSES_NEW (target, -1);
+
+         target = emit_insn (s390_execute_target (insn));
+         INSN_ADDRESSES_NEW (target, -1);
+       }
     }
-  return VOIDmode;
+
+  /* Try to optimize prologue and epilogue further.  */
+  s390_optimize_prologue ();
 }
 
-/* This function is used by the call expanders of the machine description.
-   It emits the call insn itself together with the necessary operations
-   to adjust the target address and returns the emitted insn.
-   ADDR_LOCATION is the target address rtx
-   TLS_CALL the location of the thread-local symbol
-   RESULT_REG the register where the result of the call should be stored
-   RETADDR_REG the register where the return address should be stored
-               If this parameter is NULL_RTX the call is considered
-               to be a sibling call.  */
 
-rtx
-s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
-               rtx retaddr_reg)
-{
-  bool plt_call = false;
-  rtx insn;
-  rtx call;
-  rtx clobber;
-  rtvec vec;
+/* Initialize GCC target structure.  */
 
-  /* Direct function calls need special treatment.  */
-  if (GET_CODE (addr_location) == SYMBOL_REF)
-    {
-      /* When calling a global routine in PIC mode, we must
-         replace the symbol itself with the PLT stub.  */
-      if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location))
-        {
-         addr_location = gen_rtx_UNSPEC (Pmode,
-                                         gen_rtvec (1, addr_location),
-                                         UNSPEC_PLT);
-         addr_location = gen_rtx_CONST (Pmode, addr_location);
-         plt_call = true;
-        }
+#undef  TARGET_ASM_ALIGNED_HI_OP
+#define TARGET_ASM_ALIGNED_HI_OP "\t.word\t"
+#undef  TARGET_ASM_ALIGNED_DI_OP
+#define TARGET_ASM_ALIGNED_DI_OP "\t.quad\t"
+#undef  TARGET_ASM_INTEGER
+#define TARGET_ASM_INTEGER s390_assemble_integer
 
-      /* Unless we can use the bras(l) insn, force the
-         routine address into a register.  */
-      if (!TARGET_SMALL_EXEC && !TARGET_CPU_ZARCH)
-        {
-         if (flag_pic)
-           addr_location = legitimize_pic_address (addr_location, 0);
-         else
-           addr_location = force_reg (Pmode, addr_location);
-       }
-    }
+#undef  TARGET_ASM_OPEN_PAREN
+#define TARGET_ASM_OPEN_PAREN ""
 
-  /* If it is already an indirect call or the code above moved the
-     SYMBOL_REF to somewhere else make sure the address can be found in
-     register 1.  */
-  if (retaddr_reg == NULL_RTX
-      && GET_CODE (addr_location) != SYMBOL_REF
-      && !plt_call)
-    {
-      emit_move_insn (gen_rtx_REG (Pmode, SIBCALL_REGNUM), addr_location);
-      addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM);
-    }
+#undef  TARGET_ASM_CLOSE_PAREN
+#define TARGET_ASM_CLOSE_PAREN ""
 
-  addr_location = gen_rtx_MEM (QImode, addr_location);
-  call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);
+#undef TARGET_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_FUSED_MADD)
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION s390_handle_option
 
-  if (result_reg != NULL_RTX)
-    call = gen_rtx_SET (VOIDmode, result_reg, call);
+#undef TARGET_ENCODE_SECTION_INFO
+#define TARGET_ENCODE_SECTION_INFO s390_encode_section_info
 
-  if (retaddr_reg != NULL_RTX)
-    {
-      clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg);
+#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
 
-      if (tls_call != NULL_RTX)
-       vec = gen_rtvec (3, call, clobber,
-                        gen_rtx_USE (VOIDmode, tls_call));
-      else
-       vec = gen_rtvec (2, call, clobber);
+#undef TARGET_DELEGITIMIZE_ADDRESS
+#define TARGET_DELEGITIMIZE_ADDRESS s390_delegitimize_address
 
-      call = gen_rtx_PARALLEL (VOIDmode, vec);
-    }
+#undef TARGET_RETURN_IN_MEMORY
+#define TARGET_RETURN_IN_MEMORY s390_return_in_memory
 
-  insn = emit_call_insn (call);
+#undef  TARGET_INIT_BUILTINS
+#define TARGET_INIT_BUILTINS s390_init_builtins
+#undef  TARGET_EXPAND_BUILTIN
+#define TARGET_EXPAND_BUILTIN s390_expand_builtin
 
-  /* 31-bit PLT stubs and tls calls use the GOT register implicitly.  */
-  if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX)
-    {
-      /* s390_function_ok_for_sibcall should
-        have denied sibcalls in this case.  */
-      if (retaddr_reg == NULL_RTX)
-       abort ();
+#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
 
-      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
-    }
-  return insn;
-}
+#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_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
+#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD s390_first_cycle_multipass_dfa_lookahead
 
-/* Implement CONDITIONAL_REGISTER_USAGE.  */
+#undef TARGET_CANNOT_COPY_INSN_P
+#define TARGET_CANNOT_COPY_INSN_P s390_cannot_copy_insn_p
+#undef TARGET_RTX_COSTS
+#define TARGET_RTX_COSTS s390_rtx_costs
+#undef TARGET_ADDRESS_COST
+#define TARGET_ADDRESS_COST s390_address_cost
 
-void
-s390_conditional_register_usage (void)
-{
-  int i;
+#undef TARGET_MACHINE_DEPENDENT_REORG
+#define TARGET_MACHINE_DEPENDENT_REORG s390_reorg
 
-  if (flag_pic)
-    {
-      fixed_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
-      call_used_regs[PIC_OFFSET_TABLE_REGNUM] = 1;
-    }
-  if (TARGET_CPU_ZARCH)
-    {
-      fixed_regs[RETURN_REGNUM] = 0;
-      call_used_regs[RETURN_REGNUM] = 0;
-    }
-  if (TARGET_64BIT)
-    {
-      for (i = 24; i < 32; i++)
-       call_used_regs[i] = call_really_used_regs[i] = 0;
-    }
-  else
-    {
-      for (i = 18; i < 20; i++)
-       call_used_regs[i] = call_really_used_regs[i] = 0;
-    }
+#undef TARGET_VALID_POINTER_MODE
+#define TARGET_VALID_POINTER_MODE s390_valid_pointer_mode
 
-  if (TARGET_SOFT_FLOAT)
-    {
-      for (i = 16; i < 32; i++)
-       call_used_regs[i] = fixed_regs[i] = 1;
-    }
-}
+#undef TARGET_BUILD_BUILTIN_VA_LIST
+#define TARGET_BUILD_BUILTIN_VA_LIST s390_build_builtin_va_list
+#undef TARGET_GIMPLIFY_VA_ARG_EXPR
+#define TARGET_GIMPLIFY_VA_ARG_EXPR s390_gimplify_va_arg
 
-/* Corresponding function to eh_return expander.  */
+#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
+#undef TARGET_PASS_BY_REFERENCE
+#define TARGET_PASS_BY_REFERENCE s390_pass_by_reference
 
-static GTY(()) rtx s390_tpf_eh_return_symbol;
-void
-s390_emit_tpf_eh_return (rtx target)
-{
-  rtx insn, reg;
+#undef TARGET_FUNCTION_OK_FOR_SIBCALL
+#define TARGET_FUNCTION_OK_FOR_SIBCALL s390_function_ok_for_sibcall
 
-  if (!s390_tpf_eh_return_symbol)
-    s390_tpf_eh_return_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tpf_eh_return");
+#undef TARGET_FIXED_CONDITION_CODE_REGS
+#define TARGET_FIXED_CONDITION_CODE_REGS s390_fixed_condition_code_regs
 
-  reg = gen_rtx_REG (Pmode, 2);
+#undef TARGET_CC_MODES_COMPATIBLE
+#define TARGET_CC_MODES_COMPATIBLE s390_cc_modes_compatible
 
-  emit_move_insn (reg, target);
-  insn = s390_emit_call (s390_tpf_eh_return_symbol, NULL_RTX, reg,
-                                     gen_rtx_REG (Pmode, RETURN_REGNUM));
-  use_reg (&CALL_INSN_FUNCTION_USAGE (insn), reg);
+#undef TARGET_INVALID_WITHIN_DOLOOP
+#define TARGET_INVALID_WITHIN_DOLOOP hook_constcharptr_rtx_null
 
-  emit_move_insn (EH_RETURN_HANDLER_RTX, reg);
-}
+#ifdef HAVE_AS_TLS
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL s390_output_dwarf_dtprel
+#endif
+
+struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-s390.h"