OSDN Git Service

gcc/
[pf3gnuchains/gcc-fork.git] / gcc / config / s390 / s390.c
index cde728a..979c167 100644 (file)
@@ -1,6 +1,6 @@
 /* Subroutines used for code generation on IBM S/390 and zSeries
-   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004
-   Free Software Foundation, Inc.
+   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
+   2007 Free Software Foundation, Inc.
    Contributed by Hartmut Penner (hpenner@de.ibm.com) and
                   Ulrich Weigand (uweigand@de.ibm.com).
 
@@ -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"
@@ -51,129 +51,154 @@ Software Foundation, 59 Temple Place - Suite 330, Boston, MA
 #include "langhooks.h"
 #include "optabs.h"
 #include "tree-gimple.h"
+#include "df.h"
+
+
+/* Define the specific costs for a given cpu.  */
+
+struct processor_costs 
+{
+  /* multiplication */
+  const int m;        /* cost of an M instruction.  */
+  const int mghi;     /* cost of an MGHI instruction.  */
+  const int mh;       /* cost of an MH instruction.  */
+  const int mhi;      /* cost of an MHI instruction.  */
+  const int ml;       /* cost of an ML instruction.  */
+  const int mr;       /* cost of an MR instruction.  */
+  const int ms;       /* cost of an MS instruction.  */
+  const int msg;      /* cost of an MSG instruction.  */
+  const int msgf;     /* cost of an MSGF instruction.  */
+  const int msgfr;    /* cost of an MSGFR instruction.  */
+  const int msgr;     /* cost of an MSGR instruction.  */
+  const int msr;      /* cost of an MSR instruction.  */
+  const int mult_df;  /* cost of multiplication in DFmode.  */
+  const int mxbr;
+  /* square root */
+  const int sqxbr;    /* cost of square root in TFmode.  */
+  const int sqdbr;    /* cost of square root in DFmode.  */
+  const int sqebr;    /* cost of square root in SFmode.  */
+  /* multiply and add */
+  const int madbr;    /* cost of multiply and add in DFmode.  */
+  const int maebr;    /* cost of multiply and add in SFmode.  */
+  /* division */
+  const int dxbr;
+  const int ddbr;
+  const int debr;
+  const int dlgr;
+  const int dlr;
+  const int dr;
+  const int dsgfr;
+  const int dsgr;
+};
 
-/* Machine-specific symbol_ref flags.  */
-#define SYMBOL_FLAG_ALIGN1     (SYMBOL_FLAG_MACH_DEP << 0)
-
-
-static bool s390_assemble_integer (rtx, unsigned int, int);
-static void s390_select_rtx_section (enum machine_mode, rtx,
-                                    unsigned HOST_WIDE_INT);
-static void s390_encode_section_info (tree, rtx, int);
-static bool s390_cannot_force_const_mem (rtx);
-static rtx s390_delegitimize_address (rtx);
-static bool s390_return_in_memory (tree, tree);
-static void s390_init_builtins (void);
-static rtx s390_expand_builtin (tree, rtx, rtx, enum machine_mode, int);
-static void s390_output_mi_thunk (FILE *, tree, HOST_WIDE_INT,
-                                 HOST_WIDE_INT, tree);
-static enum attr_type s390_safe_attr_type (rtx);
-
-static int s390_adjust_cost (rtx, rtx, rtx, int);
-static int s390_adjust_priority (rtx, int);
-static int s390_issue_rate (void);
-static int s390_first_cycle_multipass_dfa_lookahead (void);
-static bool s390_rtx_costs (rtx, int, int, int *);
-static int s390_address_cost (rtx);
-static void s390_reorg (void);
-static bool s390_valid_pointer_mode (enum machine_mode);
-static tree s390_build_builtin_va_list (void);
-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);
-
-#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_ASM_SELECT_RTX_SECTION
-#define        TARGET_ASM_SELECT_RTX_SECTION  s390_select_rtx_section
-
-#undef TARGET_ENCODE_SECTION_INFO
-#define TARGET_ENCODE_SECTION_INFO s390_encode_section_info
-
-#ifdef HAVE_AS_TLS
-#undef TARGET_HAVE_TLS
-#define TARGET_HAVE_TLS true
-#endif
-#undef TARGET_CANNOT_FORCE_CONST_MEM
-#define TARGET_CANNOT_FORCE_CONST_MEM s390_cannot_force_const_mem
-
-#undef TARGET_DELEGITIMIZE_ADDRESS
-#define TARGET_DELEGITIMIZE_ADDRESS s390_delegitimize_address
-
-#undef TARGET_RETURN_IN_MEMORY
-#define TARGET_RETURN_IN_MEMORY s390_return_in_memory
-
-#undef  TARGET_INIT_BUILTINS
-#define TARGET_INIT_BUILTINS s390_init_builtins
-#undef  TARGET_EXPAND_BUILTIN
-#define TARGET_EXPAND_BUILTIN s390_expand_builtin
-
-#undef TARGET_ASM_OUTPUT_MI_THUNK
-#define TARGET_ASM_OUTPUT_MI_THUNK s390_output_mi_thunk
-#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
-#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_tree_hwi_hwi_tree_true
-
-#undef  TARGET_SCHED_ADJUST_COST
-#define TARGET_SCHED_ADJUST_COST s390_adjust_cost
-#undef  TARGET_SCHED_ADJUST_PRIORITY
-#define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority
-#undef TARGET_SCHED_ISSUE_RATE
-#define TARGET_SCHED_ISSUE_RATE s390_issue_rate
-#undef TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE
-#define TARGET_SCHED_USE_DFA_PIPELINE_INTERFACE hook_int_void_1
-#undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
-#define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD s390_first_cycle_multipass_dfa_lookahead
-
-#undef TARGET_RTX_COSTS
-#define TARGET_RTX_COSTS s390_rtx_costs
-#undef TARGET_ADDRESS_COST
-#define TARGET_ADDRESS_COST s390_address_cost
-
-#undef TARGET_MACHINE_DEPENDENT_REORG
-#define TARGET_MACHINE_DEPENDENT_REORG s390_reorg
-
-#undef TARGET_VALID_POINTER_MODE
-#define TARGET_VALID_POINTER_MODE s390_valid_pointer_mode
-
-#undef TARGET_BUILD_BUILTIN_VA_LIST
-#define TARGET_BUILD_BUILTIN_VA_LIST s390_build_builtin_va_list
-#undef TARGET_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
+const struct processor_costs *s390_cost;
+
+static const
+struct processor_costs z900_cost = 
+{
+  COSTS_N_INSNS (5),     /* M     */
+  COSTS_N_INSNS (10),    /* MGHI  */
+  COSTS_N_INSNS (5),     /* MH    */
+  COSTS_N_INSNS (4),     /* MHI   */
+  COSTS_N_INSNS (5),     /* ML    */
+  COSTS_N_INSNS (5),     /* MR    */
+  COSTS_N_INSNS (4),     /* MS    */
+  COSTS_N_INSNS (15),    /* MSG   */
+  COSTS_N_INSNS (7),     /* MSGF  */
+  COSTS_N_INSNS (7),     /* MSGFR */
+  COSTS_N_INSNS (10),    /* MSGR  */
+  COSTS_N_INSNS (4),     /* MSR   */
+  COSTS_N_INSNS (7),     /* multiplication in DFmode */
+  COSTS_N_INSNS (13),    /* MXBR */
+  COSTS_N_INSNS (136),   /* SQXBR */
+  COSTS_N_INSNS (44),    /* SQDBR */
+  COSTS_N_INSNS (35),    /* SQEBR */
+  COSTS_N_INSNS (18),    /* MADBR */
+  COSTS_N_INSNS (13),    /* MAEBR */
+  COSTS_N_INSNS (134),   /* DXBR */
+  COSTS_N_INSNS (30),    /* DDBR */
+  COSTS_N_INSNS (27),    /* DEBR */
+  COSTS_N_INSNS (220),   /* DLGR */
+  COSTS_N_INSNS (34),    /* DLR */
+  COSTS_N_INSNS (34),    /* DR */
+  COSTS_N_INSNS (32),    /* DSGFR */
+  COSTS_N_INSNS (32),    /* DSGR */
+};
 
-#undef TARGET_FUNCTION_OK_FOR_SIBCALL
-#define TARGET_FUNCTION_OK_FOR_SIBCALL s390_function_ok_for_sibcall
+static const
+struct processor_costs z990_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 (28),    /* MXBR */
+  COSTS_N_INSNS (130),   /* SQXBR */
+  COSTS_N_INSNS (66),    /* SQDBR */
+  COSTS_N_INSNS (38),    /* SQEBR */
+  COSTS_N_INSNS (1),     /* MADBR */
+  COSTS_N_INSNS (1),     /* MAEBR */
+  COSTS_N_INSNS (60),    /* DXBR */
+  COSTS_N_INSNS (40),    /* DDBR */
+  COSTS_N_INSNS (26),    /* DEBR */
+  COSTS_N_INSNS (176),   /* DLGR */
+  COSTS_N_INSNS (31),    /* DLR */
+  COSTS_N_INSNS (31),    /* DR */
+  COSTS_N_INSNS (31),    /* DSGFR */
+  COSTS_N_INSNS (31),    /* DSGR */
+};
 
-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 (28),    /* MXBR */
+  COSTS_N_INSNS (130),   /* SQXBR */
+  COSTS_N_INSNS (66),    /* SQDBR */
+  COSTS_N_INSNS (38),    /* SQEBR */
+  COSTS_N_INSNS (1),     /* MADBR */
+  COSTS_N_INSNS (1),     /* MAEBR */
+  COSTS_N_INSNS (60),    /* DXBR */
+  COSTS_N_INSNS (40),    /* DDBR */
+  COSTS_N_INSNS (26),    /* DEBR */
+  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
@@ -188,29 +213,37 @@ struct s390_address
   rtx base;
   rtx indx;
   rtx disp;
-  int pointer;
+  bool pointer;
+  bool literal_pool;
 };
 
 /* 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> */
+HOST_WIDE_INT s390_warn_framesize = 0;
+HOST_WIDE_INT s390_stack_size = 0;
+HOST_WIDE_INT s390_stack_guard = 0;
 
-/* Define the structure for the machine field in struct function.  */
+/* The following structure is embedded in the machine 
+   specific part of struct function.  */
 
-struct machine_function GTY(())
+struct s390_frame_layout GTY (())
 {
-  /* Set, if some of the fprs 8-15 need to be saved (64 bit abi).  */
-  int save_fprs_p;
+  /* Offset within stack frame.  */
+  HOST_WIDE_INT gprs_offset;
+  HOST_WIDE_INT f0_offset;
+  HOST_WIDE_INT f4_offset;
+  HOST_WIDE_INT f8_offset;
+  HOST_WIDE_INT backchain_offset;
 
-  /* Set if return address needs to be saved.  */
-  bool save_return_addr_p;
+  /* 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;
@@ -218,63 +251,153 @@ struct machine_function GTY(())
   int last_save_gpr;
   int last_restore_gpr;
 
+  /* Bits standing for floating point registers. Set, if the 
+     respective register has to be saved. Starting with reg 16 (f0) 
+     at the rightmost bit.
+     Bit 15 -  8  7  6  5  4  3  2  1  0
+     fpr 15 -  8  7  5  3  1  6  4  2  0
+     reg 31 - 24 23 22 21 20 19 18 17 16  */
+  unsigned int fpr_bitmap;
+
+  /* Number of floating point registers f8-f15 which must be saved.  */
+  int high_fprs;
+
+  /* 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.  */
   HOST_WIDE_INT frame_size;
+};
+
+/* Define the structure for the machine field in struct function.  */
+
+struct machine_function GTY(())
+{
+  struct s390_frame_layout frame_layout;
 
   /* Literal pool base register.  */
   rtx base_reg;
 
+  /* True if we may need to perform branch splitting.  */
+  bool split_branches_pending_p;
+
+  /* True during final stage of literal pool processing.  */
+  bool decomposed_literal_pool_addresses_ok_p;
+
   /* Some local-dynamic TLS symbol name.  */
   const char *some_ld_name;
+
+  bool has_landing_pad_p;
 };
 
-static int s390_match_ccmode_set (rtx, enum machine_mode);
-static int s390_branch_condition_mask (rtx);
-static const char *s390_branch_condition_mnemonic (rtx, int);
-static int check_mode (rtx, enum machine_mode *);
-static int general_s_operand (rtx, enum machine_mode, int);
-static int s390_short_displacement (rtx);
-static int s390_decompose_address (rtx, struct s390_address *);
-static rtx get_thread_pointer (void);
-static rtx legitimize_tls_address (rtx, rtx);
-static void print_shift_count_operand (FILE *, rtx);
-static const char *get_some_local_dynamic_name (void);
-static int get_some_local_dynamic_name_1 (rtx *, void *);
-static int reg_used_in_mem_p (int, rtx);
-static int addr_generation_dependency_p (rtx, rtx);
-static int s390_split_branches (void);
-static void 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_prolog (bool);
-static int find_unused_clobbered_reg (void);
-static void s390_frame_info (int, int);
-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))
+/* 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_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))))
+
+/* 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")
+
+#define REGNO_PAIR_OK(REGNO, MODE)                               \
+  (HARD_REGNO_NREGS ((REGNO), (MODE)) == 1 || !((REGNO) & 1))
+
+static enum machine_mode
+s390_libgcc_cmp_return_mode (void)
+{
+  return TARGET_64BIT ? DImode : SImode;
+}
+
+static enum machine_mode
+s390_libgcc_shift_count_mode (void)
+{
+  return TARGET_64BIT ? DImode : SImode;
+}
+
+/* Return true if the back end supports mode MODE.  */
+static bool
+s390_scalar_mode_supported_p (enum machine_mode mode)
+{
+  if (DECIMAL_FLOAT_MODE_P (mode))
+    return true;
+  else
+    return default_scalar_mode_supported_p (mode);
+}
+
+/* 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;
@@ -310,7 +433,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);
@@ -321,14 +444,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);
@@ -339,10 +462,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
@@ -353,7 +476,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;
 
@@ -361,15 +484,20 @@ s390_tm_ccmode (rtx op1, rtx op2, int mixed)
   if (GET_CODE (op1) != CONST_INT || GET_CODE (op2) != CONST_INT)
     return VOIDmode;
 
-  /* Selected bits all zero: CC0.  */
+  /* Selected bits all zero: CC0.
+     e.g.: int a; if ((a & (16 + 128)) == 0) */
   if (INTVAL (op2) == 0)
     return CCTmode;
 
-  /* Selected bits all one: CC3.  */
+  /* Selected bits all one: CC3. 
+     e.g.: int a; if ((a & (16 + 128)) == 16 + 128) */
   if (INTVAL (op2) == INTVAL (op1))
     return CCT3mode;
 
-  /* Exactly two bits selected, mixed zeroes and ones: CC1 or CC2.  */
+  /* Exactly two bits selected, mixed zeroes and ones: CC1 or CC2. e.g.:
+     int a;
+     if ((a & (16 + 128)) == 16)         -> CCT1
+     if ((a & (16 + 128)) == 128)        -> CCT2  */
   if (mixed)
     {
       bit1 = exact_log2 (INTVAL (op2));
@@ -392,8 +520,11 @@ s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
     {
       case EQ:
       case NE:
+       if ((GET_CODE (op0) == NEG || GET_CODE (op0) == ABS)
+           && 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)
@@ -428,14 +559,28 @@ s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
       case LT:
       case GE:
       case GT:
-         if (GET_CODE (op0) == PLUS && GET_CODE (XEXP (op0, 1)) == CONST_INT
-             && CONST_OK_FOR_CONSTRAINT_P (INTVAL (XEXP (op0, 1)), 'K', "K"))
-            {
-             if (INTVAL (XEXP((op0), 1)) < 0)
-               return CCANmode;
-              else
-               return CCAPmode;
-           }
+       /* The only overflow condition of NEG and ABS happens when
+          -INT_MAX is used as parameter, which stays negative. So
+          we have an overflow from a positive value to a negative. 
+          Using CCAP mode the resulting cc can be used for comparisons.  */
+       if ((GET_CODE (op0) == NEG || GET_CODE (op0) == ABS)
+           && GET_MODE_CLASS (GET_MODE (op0)) == MODE_INT)
+         return CCAPmode;
+
+       /* 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 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_K (INTVAL (XEXP (op0, 1))))
+         {
+           if (INTVAL (XEXP((op0), 1)) < 0)
+             return CCANmode;
+           else
+             return CCAPmode;
+         }
+       /* Fall through.  */
       case UNORDERED:
       case ORDERED:
       case UNEQ:
@@ -472,7 +617,163 @@ s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
        return CCUmode;
 
       default:
-       abort ();
+       gcc_unreachable ();
+    }
+}
+
+/* Replace the comparison OP0 CODE OP1 by a semantically equivalent one
+   that we can implement more efficiently.  */
+
+void
+s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1)
+{
+  /* Convert ZERO_EXTRACT back to AND to enable TM patterns.  */
+  if ((*code == EQ || *code == NE)
+      && *op1 == const0_rtx
+      && GET_CODE (*op0) == ZERO_EXTRACT
+      && GET_CODE (XEXP (*op0, 1)) == CONST_INT
+      && GET_CODE (XEXP (*op0, 2)) == CONST_INT
+      && SCALAR_INT_MODE_P (GET_MODE (XEXP (*op0, 0))))
+    {
+      rtx inner = XEXP (*op0, 0);
+      HOST_WIDE_INT modesize = GET_MODE_BITSIZE (GET_MODE (inner));
+      HOST_WIDE_INT len = INTVAL (XEXP (*op0, 1));
+      HOST_WIDE_INT pos = INTVAL (XEXP (*op0, 2));
+
+      if (len > 0 && len < modesize
+         && pos >= 0 && pos + len <= modesize
+         && modesize <= HOST_BITS_PER_WIDE_INT)
+       {
+         unsigned HOST_WIDE_INT block;
+         block = ((unsigned HOST_WIDE_INT) 1 << len) - 1;
+         block <<= modesize - pos - len;
+
+         *op0 = gen_rtx_AND (GET_MODE (inner), inner,
+                             gen_int_mode (block, GET_MODE (inner)));
+       }
+    }
+
+  /* Narrow AND of memory against immediate to enable TM.  */
+  if ((*code == EQ || *code == NE)
+      && *op1 == const0_rtx
+      && GET_CODE (*op0) == AND
+      && GET_CODE (XEXP (*op0, 1)) == CONST_INT
+      && SCALAR_INT_MODE_P (GET_MODE (XEXP (*op0, 0))))
+    {
+      rtx inner = XEXP (*op0, 0);
+      rtx mask = XEXP (*op0, 1);
+
+      /* Ignore paradoxical SUBREGs if all extra bits are masked out.  */
+      if (GET_CODE (inner) == SUBREG
+         && SCALAR_INT_MODE_P (GET_MODE (SUBREG_REG (inner)))
+         && (GET_MODE_SIZE (GET_MODE (inner))
+             >= GET_MODE_SIZE (GET_MODE (SUBREG_REG (inner))))
+         && ((INTVAL (mask)
+               & GET_MODE_MASK (GET_MODE (inner))
+               & ~GET_MODE_MASK (GET_MODE (SUBREG_REG (inner))))
+             == 0))
+       inner = SUBREG_REG (inner);
+
+      /* Do not change volatile MEMs.  */
+      if (MEM_P (inner) && !MEM_VOLATILE_P (inner))
+       {
+         int part = s390_single_part (XEXP (*op0, 1),
+                                      GET_MODE (inner), QImode, 0);
+         if (part >= 0)
+           {
+             mask = gen_int_mode (s390_extract_part (mask, QImode, 0), QImode);
+             inner = adjust_address_nv (inner, QImode, part);
+             *op0 = gen_rtx_AND (QImode, inner, mask);
+           }
+       }
+    }
+
+  /* Narrow comparisons against 0xffff to HImode if possible.  */
+  if ((*code == EQ || *code == NE)
+      && GET_CODE (*op1) == CONST_INT
+      && INTVAL (*op1) == 0xffff
+      && SCALAR_INT_MODE_P (GET_MODE (*op0))
+      && (nonzero_bits (*op0, GET_MODE (*op0)) 
+         & ~(unsigned HOST_WIDE_INT) 0xffff) == 0)
+    {
+      *op0 = gen_lowpart (HImode, *op0);
+      *op1 = constm1_rtx;
+    }
+
+  /* Remove redundant UNSPEC_CCU_TO_INT conversions if possible.  */
+  if (GET_CODE (*op0) == UNSPEC
+      && XINT (*op0, 1) == UNSPEC_CCU_TO_INT
+      && XVECLEN (*op0, 0) == 1
+      && GET_MODE (XVECEXP (*op0, 0, 0)) == CCUmode
+      && GET_CODE (XVECEXP (*op0, 0, 0)) == REG
+      && REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM
+      && *op1 == const0_rtx)
+    {
+      enum rtx_code new_code = UNKNOWN;
+      switch (*code)
+       {
+         case EQ: new_code = EQ;  break;
+         case NE: new_code = NE;  break;
+         case LT: new_code = GTU; break;
+         case GT: new_code = LTU; break;
+         case LE: new_code = GEU; break;
+         case GE: new_code = LEU; break;
+         default: break;
+       }
+
+      if (new_code != UNKNOWN)
+       {
+         *op0 = XVECEXP (*op0, 0, 0);
+         *code = new_code;
+       }
+    }
+
+  /* Remove redundant UNSPEC_CCZ_TO_INT conversions if possible.  */
+  if (GET_CODE (*op0) == UNSPEC
+      && XINT (*op0, 1) == UNSPEC_CCZ_TO_INT
+      && XVECLEN (*op0, 0) == 1
+      && GET_MODE (XVECEXP (*op0, 0, 0)) == CCZmode
+      && GET_CODE (XVECEXP (*op0, 0, 0)) == REG
+      && REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM
+      && *op1 == const0_rtx)
+    {
+      enum rtx_code new_code = UNKNOWN;
+      switch (*code)
+       {
+         case EQ: new_code = EQ;  break;
+         case NE: new_code = NE;  break;
+         default: break;
+       }
+
+      if (new_code != UNKNOWN)
+       {
+         *op0 = XVECEXP (*op0, 0, 0);
+         *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);
     }
 }
 
@@ -484,10 +785,41 @@ 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);
+  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 SImode compare and swap instruction setting MEM to NEW if OLD
+   matches CMP.
+   Return the correct condition RTL to be placed in the IF_THEN_ELSE of the
+   conditional branch testing the result.  */
+
+static rtx
+s390_emit_compare_and_swap (enum rtx_code code, rtx old, rtx mem, rtx cmp, rtx new)
+{
+  rtx ret;
+
+  emit_insn (gen_sync_compare_and_swap_ccsi (old, mem, cmp, new));
+  ret = gen_rtx_fmt_ee (code, VOIDmode, s390_compare_emitted, const0_rtx);
 
-  emit_insn (gen_rtx_SET (VOIDmode, cc, gen_rtx_COMPARE (mode, op0, op1)));
-  return 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
@@ -506,177 +838,75 @@ s390_emit_jump (rtx target, rtx cond)
   emit_jump_insn (insn);
 }
 
-/* Return nonzero if OP is a valid comparison operator
-   for an ALC condition in mode MODE.  */
+/* Return branch condition mask to implement a branch
+   specified by CODE.  Return -1 for invalid comparisons.  */
 
 int
-s390_alc_comparison (rtx op, enum machine_mode mode)
+s390_branch_condition_mask (rtx code)
 {
-  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;
+  const int CC0 = 1 << 3;
+  const int CC1 = 1 << 2;
+  const int CC2 = 1 << 1;
+  const int CC3 = 1 << 0;
 
-  if (GET_CODE (XEXP (op, 0)) != REG
-      || REGNO (XEXP (op, 0)) != CC_REGNUM
-      || XEXP (op, 1) != const0_rtx)
-    return 0;
+  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 (op, 0)))
+  switch (GET_MODE (XEXP (code, 0)))
     {
-    case CCL1mode:
-      return GET_CODE (op) == LTU;
-
-    case CCL2mode:
-      return GET_CODE (op) == LEU;
+    case CCZmode:
+    case CCZ1mode:
+      switch (GET_CODE (code))
+        {
+        case EQ:       return CC0;
+       case NE:        return CC1 | CC2 | CC3;
+       default:        return -1;
+        }
+      break;
 
-    case CCL3mode:
-      return GET_CODE (op) == GEU;
+    case CCT1mode:
+      switch (GET_CODE (code))
+        {
+        case EQ:       return CC1;
+       case NE:        return CC0 | CC2 | CC3;
+       default:        return -1;
+        }
+      break;
 
-    case CCUmode:
-      return GET_CODE (op) == GTU;
+    case CCT2mode:
+      switch (GET_CODE (code))
+        {
+        case EQ:       return CC2;
+       case NE:        return CC0 | CC1 | CC3;
+       default:        return -1;
+        }
+      break;
 
-    case CCURmode:
-      return GET_CODE (op) == LTU;
+    case CCT3mode:
+      switch (GET_CODE (code))
+        {
+        case EQ:       return CC3;
+       case NE:        return CC0 | CC1 | CC2;
+       default:        return -1;
+        }
+      break;
 
-    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.  */
-
-static int
-s390_branch_condition_mask (rtx code)
-{
-  const int CC0 = 1 << 3;
-  const int CC1 = 1 << 2;
-  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 ();
-
-  switch (GET_MODE (XEXP (code, 0)))
-    {
-    case CCZmode:
-      switch (GET_CODE (code))
-        {
-        case EQ:       return CC0;
-       case NE:        return CC1 | CC2 | CC3;
-       default:
-         abort ();
-        }
-      break;
-
-    case CCT1mode:
-      switch (GET_CODE (code))
-        {
-        case EQ:       return CC1;
-       case NE:        return CC0 | CC2 | CC3;
-       default:
-         abort ();
-        }
-      break;
-
-    case CCT2mode:
-      switch (GET_CODE (code))
-        {
-        case EQ:       return CC2;
-       case NE:        return CC0 | CC1 | CC3;
-       default:
-         abort ();
-        }
-      break;
-
-    case CCT3mode:
-      switch (GET_CODE (code))
-        {
-        case EQ:       return CC3;
-       case NE:        return CC0 | CC1 | CC2;
-       default:
-         abort ();
-        }
-      break;
-
-    case CCLmode:
-      switch (GET_CODE (code))
-        {
-        case EQ:       return CC0 | CC2;
-       case NE:        return CC1 | CC3;
-       default:
-         abort ();
-        }
-      break;
+    case CCLmode:
+      switch (GET_CODE (code))
+        {
+        case EQ:       return CC0 | CC2;
+       case NE:        return CC1 | CC3;
+       default:        return -1;
+        }
+      break;
 
     case CCL1mode:
       switch (GET_CODE (code))
         {
        case LTU:       return CC2 | CC3;  /* carry */
        case GEU:       return CC0 | CC1;  /* no carry */
-       default:
-         abort ();
+       default:        return -1;
         }
       break;
 
@@ -685,8 +915,7 @@ s390_branch_condition_mask (rtx code)
         {
        case GTU:       return CC0 | CC1;  /* borrow */
        case LEU:       return CC2 | CC3;  /* no borrow */
-       default:
-         abort ();
+       default:        return -1;
         }
       break;
 
@@ -699,8 +928,7 @@ s390_branch_condition_mask (rtx code)
        case GTU:       return CC3;
        case LEU:       return CC1 | CC2;
        case GEU:       return CC2 | CC3;
-       default:
-         abort ();
+       default:        return -1;
        }
 
     case CCUmode:
@@ -712,8 +940,7 @@ s390_branch_condition_mask (rtx code)
         case GTU:      return CC2;
         case LEU:      return CC0 | CC1;
         case GEU:      return CC0 | CC2;
-       default:
-         abort ();
+       default:        return -1;
         }
       break;
 
@@ -726,8 +953,7 @@ s390_branch_condition_mask (rtx code)
         case GTU:      return CC1;
         case LEU:      return CC0 | CC2;
         case GEU:      return CC0 | CC1;
-       default:
-         abort ();
+       default:        return -1;
         }
       break;
 
@@ -740,8 +966,7 @@ s390_branch_condition_mask (rtx code)
         case GT:       return CC2;
         case LE:       return CC0 | CC1 | CC3;
         case GE:       return CC0 | CC2;
-       default:
-         abort ();
+       default:        return -1;
         }
       break;
 
@@ -754,8 +979,7 @@ s390_branch_condition_mask (rtx code)
         case GT:       return CC2 | CC3;
         case LE:       return CC0 | CC1;
         case GE:       return CC0 | CC2 | CC3;
-       default:
-         abort ();
+       default:        return -1;
         }
       break;
 
@@ -776,8 +1000,7 @@ s390_branch_condition_mask (rtx code)
         case UNLE:     return CC0 | CC1 | CC3;
         case UNGE:     return CC0 | CC2 | CC3;
        case LTGT:      return CC1 | CC2;
-       default:
-         abort ();
+       default:        return -1;
         }
       break;
 
@@ -798,13 +1021,12 @@ s390_branch_condition_mask (rtx code)
         case UNLE:     return CC0 | CC2 | CC3;
         case UNGE:     return CC0 | CC1 | CC3;
        case LTGT:      return CC2 | CC1;
-       default:
-         abort ();
+       default:        return -1;
         }
       break;
 
     default:
-      abort ();
+      return -1;
     }
 }
 
@@ -824,12 +1046,12 @@ s390_branch_condition_mnemonic (rtx code, int inv)
     };
 
   int mask = s390_branch_condition_mask (code);
+  gcc_assert (mask >= 0);
 
   if (inv)
     mask ^= 15;
 
-  if (mask < 1 || mask > 14)
-    abort ();
+  gcc_assert (mask >= 1 && mask <= 14);
 
   return mnemonic[mask];
 }
@@ -845,7 +1067,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++)
@@ -859,7 +1082,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
@@ -874,7 +1097,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)
@@ -930,6 +1154,169 @@ s390_split_ok_p (rtx dst, rtx src, enum machine_mode mode, int first_subword)
   return true;
 }
 
+/* Return true if it can be proven that [MEM1, MEM1 + SIZE]
+   and [MEM2, MEM2 + SIZE] do overlap and false
+   otherwise.  */
+
+bool
+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);
+
+  /* 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;
+}
+
+/* Expand logical operator CODE in mode MODE with operands OPERANDS.  */
+
+void
+s390_expand_logical_operator (enum rtx_code code, enum machine_mode mode,
+                             rtx *operands)
+{
+  enum machine_mode wmode = mode;
+  rtx dst = operands[0];
+  rtx src1 = operands[1];
+  rtx src2 = operands[2];
+  rtx op, clob, tem;
+
+  /* If we cannot handle the operation directly, use a temp register.  */
+  if (!s390_logical_operator_ok_p (operands))
+    dst = gen_reg_rtx (mode);
+
+  /* QImode and HImode patterns make sense only if we have a destination
+     in memory.  Otherwise perform the operation in SImode.  */
+  if ((mode == QImode || mode == HImode) && GET_CODE (dst) != MEM)
+    wmode = SImode;
+
+  /* Widen operands if required.  */
+  if (mode != wmode)
+    {
+      if (GET_CODE (dst) == SUBREG
+         && (tem = simplify_subreg (wmode, dst, mode, 0)) != 0)
+       dst = tem;
+      else if (REG_P (dst))
+       dst = gen_rtx_SUBREG (wmode, dst, 0);
+      else
+        dst = gen_reg_rtx (wmode);
+
+      if (GET_CODE (src1) == SUBREG
+         && (tem = simplify_subreg (wmode, src1, mode, 0)) != 0)
+       src1 = tem;
+      else if (GET_MODE (src1) != VOIDmode)
+       src1 = gen_rtx_SUBREG (wmode, force_reg (mode, src1), 0);
+
+      if (GET_CODE (src2) == SUBREG
+         && (tem = simplify_subreg (wmode, src2, mode, 0)) != 0)
+       src2 = tem;
+      else if (GET_MODE (src2) != VOIDmode)
+       src2 = gen_rtx_SUBREG (wmode, force_reg (mode, src2), 0);
+    }
+
+  /* Emit the instruction.  */
+  op = gen_rtx_SET (VOIDmode, dst, gen_rtx_fmt_ee (code, wmode, src1, src2));
+  clob = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
+  emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clob)));
+
+  /* Fix up the destination if needed.  */
+  if (dst != operands[0])
+    emit_move_insn (operands[0], gen_lowpart (mode, dst));
+}
+
+/* Check whether OPERANDS are OK for a logical operation (AND, IOR, XOR).  */
+
+bool
+s390_logical_operator_ok_p (rtx *operands)
+{
+  /* If the destination operand is in memory, it needs to coincide
+     with one of the source operands.  After reload, it has to be
+     the first source operand.  */
+  if (GET_CODE (operands[0]) == MEM)
+    return rtx_equal_p (operands[0], operands[1])
+          || (!reload_completed && rtx_equal_p (operands[0], operands[2]));
+
+  return true;
+}
+
+/* Narrow logical operation CODE of memory operand MEMOP with immediate
+   operand IMMOP to switch from SS to SI type instructions.  */
+
+void
+s390_narrow_logical_operator (enum rtx_code code, rtx *memop, rtx *immop)
+{
+  int def = code == AND ? -1 : 0;
+  HOST_WIDE_INT mask;
+  int part;
+
+  gcc_assert (GET_CODE (*memop) == MEM);
+  gcc_assert (!MEM_VOLATILE_P (*memop));
+
+  mask = s390_extract_part (*immop, QImode, def);
+  part = s390_single_part (*immop, GET_MODE (*memop), QImode, def);
+  gcc_assert (part >= 0);
+
+  *memop = adjust_address (*memop, QImode, part);
+  *immop = gen_int_mode (mask, QImode);
+}
+
+
+/* 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.
@@ -948,12 +1335,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.  */
@@ -967,13 +1362,61 @@ 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},
+      {"z9-ec", PROCESSOR_2094_Z9_109, PF_IEEE_FLOAT | PF_ZARCH
+                             | PF_LONG_DISPLACEMENT | PF_EXTIMM | PF_DFP },
     };
+  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;
+
+    case OPT_mtune_:
+      return s390_handle_arch_option (arg, &s390_tune, &s390_tune_flags);
 
-  int const pta_size = ARRAY_SIZE (processor_alias_table);
+    case OPT_mwarn_framesize_:
+      return sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_warn_framesize) == 1;
 
-  /* Acquire a unique set number for our register saves and restores.  */
-  s390_sr_alias_set = new_alias_set ();
+    default:
+      return true;
+    }
+}
 
+void
+override_options (void)
+{
   /* Set up function hooks.  */
   init_machine_status = s390_init_machine_status;
 
@@ -988,43 +1431,73 @@ 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;
     }
+
+  /* Sanity checks.  */
+  if (TARGET_ZARCH && !TARGET_CPU_ZARCH)
+    error ("z/Architecture mode not supported on %s", s390_arch_string);
+  if (TARGET_64BIT && !TARGET_ZARCH)
+    error ("64-bit ABI not supported in ESA/390 mode");
+
+  if (TARGET_HARD_DFP && (!TARGET_CPU_DFP || !TARGET_ZARCH))
+    {
+      if (target_flags_explicit & MASK_SOFT_DFP)
+       {
+         if (!TARGET_CPU_DFP)
+           error ("Hardware decimal floating point instructions"
+                  " not available on %s", s390_arch_string);
+         if (!TARGET_ZARCH)
+           error ("Hardware decimal floating point instructions"
+                  " not available in ESA/390 mode");
+       }
+      else
+       target_flags |= MASK_SOFT_DFP;
+    }
+
+  if ((target_flags_explicit & MASK_SOFT_FLOAT) && TARGET_SOFT_FLOAT)
+    {
+      if ((target_flags_explicit & MASK_SOFT_DFP) && TARGET_HARD_DFP)
+       error ("-mhard-dfp can't be used in conjunction with -msoft-float");
+
+      target_flags |= MASK_SOFT_DFP;
+    }
+
+  /* Set processor cost function.  */
+  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");
+
+  if (s390_stack_size)
     {
-      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);
+      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");
     }
+  else if (s390_stack_guard)
+    error ("-mstack-guard implies use of -mstack-size"); 
 
-  /* Sanity checks.  */
-  if (TARGET_ZARCH && !(s390_arch_flags & PF_ZARCH))
-    error ("z/Architecture mode not supported on %s.", s390_arch_string);
-  if (TARGET_64BIT && !TARGET_ZARCH)
-    error ("64-bit ABI not supported in ESA/390 mode.");
+#ifdef TARGET_DEFAULT_LONG_DOUBLE_128
+  if (!(target_flags_explicit & MASK_LONG_DOUBLE_128))
+    target_flags |= MASK_LONG_DOUBLE_128;
+#endif
 }
 
 /* Map for smallest class containing reg regno.  */
@@ -1038,7 +1511,8 @@ const enum reg_class regclass_map[FIRST_PSEUDO_REGISTER] =
   FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
   FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
   FP_REGS,      FP_REGS,   FP_REGS,   FP_REGS,
-  ADDR_REGS,    NO_REGS,   ADDR_REGS
+  ADDR_REGS,    CC_REGS,   ADDR_REGS, ADDR_REGS,
+  ACCESS_REGS, ACCESS_REGS
 };
 
 /* Return attribute type of insn.  */
@@ -1052,255 +1526,424 @@ 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.  */
+/* Return true if DISP is a valid short displacement.  */
 
-int
-const0_operand (register rtx op, enum machine_mode mode)
+static bool
+s390_short_displacement (rtx disp)
 {
-  return op == CONST0_RTX (mode);
-}
+  /* No displacement is OK.  */
+  if (!disp)
+    return true;
 
-/* Return true if OP is constant.
-   OP is the current operation.
-   MODE is the current operation mode.  */
+  /* Integer displacement in range.  */
+  if (GET_CODE (disp) == CONST_INT)
+    return INTVAL (disp) >= 0 && INTVAL (disp) < 4096;
 
-int
-consttable_operand (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
-{
-  return CONSTANT_P (op);
-}
+  /* GOT offset is not OK, the GOT can be large.  */
+  if (GET_CODE (disp) == CONST
+      && GET_CODE (XEXP (disp, 0)) == UNSPEC
+      && (XINT (XEXP (disp, 0), 1) == UNSPEC_GOT
+          || XINT (XEXP (disp, 0), 1) == UNSPEC_GOTNTPOFF))
+    return false;
 
-/* Return true if the mode of operand OP matches MODE.
-   If MODE is set to VOIDmode, set it to the mode of OP.  */
+  /* All other symbolic constants are literal pool references,
+     which are OK as the literal pool must be small.  */
+  if (GET_CODE (disp) == CONST)
+    return true;
 
-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 false;
 }
 
-/* Return true if OP a valid operand for the LARL instruction.
-   OP is the current operation.
-   MODE is the current operation mode.  */
+/* Decompose a RTL expression ADDR for a memory address into
+   its components, returned in OUT.
 
-int
-larl_operand (register rtx op, enum machine_mode mode)
+   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.
+
+   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 (rtx addr, struct s390_address *out)
 {
-  if (! check_mode (op, &mode))
-    return 0;
+  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;
+  bool literal_pool = false;
+
+  /* We may need to substitute the literal pool base register into the address
+     below.  However, at this point we do not know which register is going to
+     be used as base, so we substitute the arg pointer register.  This is going
+     to be treated as holding a pointer below -- it shouldn't be used for any
+     other purpose.  */
+  rtx fake_pool_base = gen_rtx_REG (Pmode, ARG_POINTER_REGNUM);
 
-  /* 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)));
+  /* Decompose address into base + index + displacement.  */
 
-  /* Everything else must have a CONST, so strip it.  */
-  if (GET_CODE (op) != CONST)
-    return 0;
-  op = XEXP (op, 0);
+  if (GET_CODE (addr) == REG || GET_CODE (addr) == UNSPEC)
+    base = addr;
 
-  /* Allow adding *even* in-range constants.  */
-  if (GET_CODE (op) == PLUS)
+  else if (GET_CODE (addr) == 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);
+      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;
+       }
     }
 
-  /* 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;
+  else
+    disp = addr;               /* displacement */
 
-  return 0;
-}
+  /* 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);
+       }
+    }
 
-/* Helper routine to implement s_operand and s_imm_operand.
-   OP is the current operation.
-   MODE is the current operation mode.
-   ALLOW_IMMEDIATE specifies whether immediate operands should
-   be accepted or not.  */
+  /* Strip off CONST here to avoid special case tests later.  */
+  if (disp && GET_CODE (disp) == CONST)
+    disp = XEXP (disp, 0);
 
-static int
-general_s_operand (register rtx op, enum machine_mode mode,
-                  int allow_immediate)
-{
-  struct s390_address addr;
+  /* 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 = fake_pool_base, literal_pool = true;
+      else if (!indx)
+        indx = fake_pool_base, literal_pool = true;
+      else
+        return false;
 
-  /* Call general_operand first, so that we don't have to
-     check for many special cases.  */
-  if (!general_operand (op, mode))
-    return 0;
+      /* Mark up the displacement.  */
+      disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp),
+                            UNSPEC_LTREL_OFFSET);
+    }
 
-  /* 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);
+  /* 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;
 
-  switch (GET_CODE (op))
+           base = XVECEXP (base, 0, 1);
+           break;
+
+         case UNSPEC_LTREL_BASE:
+           if (XVECLEN (base, 0) == 1)
+             base = fake_pool_base, literal_pool = true;
+           else
+             base = XVECEXP (base, 0, 1);
+           break;
+
+         default:
+           return false;
+         }
+
+      if (!REG_P (base) 
+         || (GET_MODE (base) != SImode 
+             && GET_MODE (base) != Pmode))
+       return false;
+
+      if (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;
+
+      if ((reload_completed || reload_in_progress)
+         && base == cfun->machine->base_reg)
+        pointer = base_ptr = literal_pool = true;
+    }
+
+  /* Validate index register.  */
+  if (indx)
     {
-      /* Constants are OK as s-operand if ALLOW_IMMEDIATE
-        is true and we are still before reload.  */
-      case CONST_INT:
-      case CONST_DOUBLE:
-       if (!allow_immediate || reload_completed)
-         return 0;
-       return 1;
+      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;
 
-      /* Memory operands are OK unless they already use an
-        index register.  */
-      case MEM:
-       if (!s390_decompose_address (XEXP (op, 0), &addr))
-         return 0;
-       if (addr.indx)
-         return 0;
-       /* Do not allow literal pool references unless ALLOW_IMMEDIATE
-          is true.  This prevents compares between two literal pool
-          entries from being accepted.  */
-       if (!allow_immediate
-           && addr.base && REGNO (addr.base) == BASE_REGNUM)
-         return 0;
-       return 1;
+           indx = XVECEXP (indx, 0, 1);
+           break;
 
-      default:
-       break;
+         case UNSPEC_LTREL_BASE:
+           if (XVECLEN (indx, 0) == 1)
+             indx = fake_pool_base, literal_pool = true;
+           else
+             indx = XVECEXP (indx, 0, 1);
+           break;
+
+         default:
+           return false;
+         }
+
+      if (!REG_P (indx) 
+         || (GET_MODE (indx) != SImode
+             && GET_MODE (indx) != Pmode))
+       return false;
+
+      if (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;
+
+      if ((reload_completed || reload_in_progress)
+         && indx == cfun->machine->base_reg)
+        pointer = indx_ptr = literal_pool = true;
     }
 
-  return 0;
-}
+  /* Prefer to use pointer as base, not index.  */
+  if (base && indx && !base_ptr
+      && (indx_ptr || (!REG_POINTER (base) && REG_POINTER (indx))))
+    {
+      rtx tmp = base;
+      base = indx;
+      indx = tmp;
+    }
+
+  /* Validate displacement.  */
+  if (!disp)
+    {
+      /* 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;
 
-/* Return true if OP is a valid S-type operand.
-   OP is the current operation.
-   MODE is the current operation mode.  */
+      /* 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)
+         && flag_pic == 1)
+        {
+         ;
+        }
 
-int
-s_operand (register rtx op, enum machine_mode mode)
-{
-  return general_s_operand (op, mode, 0);
-}
+      /* Accept chunkified literal pool symbol references.  */
+      else if (cfun && cfun->machine
+              && cfun->machine->decomposed_literal_pool_addresses_ok_p
+              && 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;
 
-/* Return true if OP is a valid S-type operand or an immediate
-   operand that can be addressed as S-type operand by forcing
-   it into the literal pool.
-   OP is the current operation.
-   MODE is the current operation mode.  */
+              orig_disp = plus_constant (orig_disp, offset);
+           }
+        }
 
-int
-s_imm_operand (register rtx op, enum machine_mode mode)
-{
-  return general_s_operand (op, mode, 1);
+      else
+       return false;
+    }
+
+  if (!base && !indx)
+    pointer = true;
+
+  if (out)
+    {
+      out->base = base;
+      out->indx = indx;
+      out->disp = orig_disp;
+      out->pointer = pointer;
+      out->literal_pool = literal_pool;
+    }
+
+  return true;
 }
 
-/* Return true if OP a valid shift count operand.
-   OP is the current operation.
-   MODE is the current operation mode.  */
+/* Decompose a RTL expression OP for a shift count into its components,
+   and return the base register in BASE and the offset in OFFSET.
 
-int
-shift_count_operand (rtx op, enum machine_mode mode)
-{
-  HOST_WIDE_INT offset = 0;
+   Return true if OP is a valid shift count, false if not.  */
 
-  if (! check_mode (op, &mode))
-    return 0;
+bool
+s390_decompose_shift_count (rtx op, rtx *base, HOST_WIDE_INT *offset)
+{
+  HOST_WIDE_INT off = 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.  */
+     or a sum of the two.  */
   if (GET_CODE (op) == CONST_INT)
     {
-      offset = INTVAL (op);
+      off = INTVAL (op);
       op = NULL_RTX;
     }
   if (op && GET_CODE (op) == PLUS && GET_CODE (XEXP (op, 1)) == CONST_INT)
     {
-      offset = INTVAL (XEXP (op, 1));
+      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 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;
+  if (op && GET_CODE (op) != REG)
+    return false;
 
-  return 1;
-}
+  if (offset)
+    *offset = off;
+  if (base)
+    *base = op;
 
-/* Return true if DISP is a valid short displacement.  */
+   return true;
+}
 
-static int
-s390_short_displacement (rtx disp)
-{
-  /* No displacement is OK.  */
-  if (!disp)
-    return 1;
 
-  /* Integer displacement in range.  */
-  if (GET_CODE (disp) == CONST_INT)
-    return INTVAL (disp) >= 0 && INTVAL (disp) < 4096;
+/* Return true if CODE is a valid address without index.  */
 
-  /* GOT offset is not OK, the GOT can be large.  */
-  if (GET_CODE (disp) == CONST
-      && GET_CODE (XEXP (disp, 0)) == UNSPEC
-      && XINT (XEXP (disp, 0), 1) == UNSPEC_GOT)
-    return 0;
+bool
+s390_legitimate_address_without_index_p (rtx op)
+{
+  struct s390_address addr;
 
-  /* 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;
+  if (!s390_decompose_address (XEXP (op, 0), &addr))
+    return false;
+  if (addr.indx)
+    return false;
 
-  return 0;
+  return true;
 }
 
-/* Return true if OP is a valid operand for a C constraint.  */
+
+/* Evaluates constraint strings described by the regular expression
+   ([A|B](Q|R|S|T))|U|W and returns 1 if OP is a valid operand for the
+   constraint given in STR, or 0 else.  */
 
 int
-s390_extra_constraint_str (rtx op, int c, const char * str)
+s390_mem_constraint (const char *str, rtx op)
 {
   struct s390_address addr;
+  char 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];
+    }
 
-  if (c != str[0])
-    abort ();
+  /* 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.literal_pool)
+       return 0;
+
+      c = str[1];
+    }
 
   switch (c)
     {
@@ -1378,7 +2021,11 @@ s390_extra_constraint_str (rtx op, int c, const char * str)
       break;
 
     case 'Y':
-      return shift_count_operand (op, VOIDmode);
+      /* 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))
+       return 0;
+      break;
 
     default:
       return 0;
@@ -1387,81 +2034,125 @@ s390_extra_constraint_str (rtx op, int c, const char * str)
   return 1;
 }
 
-/* Return true if VALUE matches the constraint STR.  */
+
+
+/* Evaluates constraint strings starting with letter O.  Input
+   parameter C is the second letter following the "O" in the constraint
+   string. Returns 1 if VALUE meets the respective constraint and 0
+   otherwise.  */
 
 int
-s390_const_ok_for_constraint_p (HOST_WIDE_INT value,
-                               int c,
-                               const char * str)
+s390_O_constraint_str (const char c, HOST_WIDE_INT value)
 {
-  enum machine_mode mode, part_mode;
-  int def;
-  unsigned char part;
-
-  if (c != str[0])
-    abort ();
+  if (!TARGET_EXTIMM)
+    return 0;
 
-  switch (str[0])
+  switch (c)
     {
-    case 'I':
-      return (unsigned int)value < 256;
+    case 's':
+      return trunc_int_for_mode (value, SImode) == value;
 
-    case 'J':
-      return (unsigned int)value < 4096;
+    case 'p':
+      return value == 0
+       || s390_single_part (GEN_INT (value), DImode, SImode, 0) == 1;
 
-    case 'K':
-      return value >= -32768 && value < 32768;
+    case 'n':
+      return value == -1
+       || s390_single_part (GEN_INT (value), DImode, SImode, -1) == 1;
 
-    case 'L':
-      return (TARGET_LONG_DISPLACEMENT ?
-             (value >= -524288 && value <= 524287)
-             : (value >= 0 && value <= 4095));
-    case 'M':
-      return value == 2147483647;
+    default:
+      gcc_unreachable ();
+    }
+}
 
-    case 'N':
-      part = str[1] - '0';
 
-      switch (str[2])
-       {
-       case 'H': part_mode = HImode; break;
-       case 'Q': part_mode = QImode; break;
-       default:  return 0;
-       }
+/* Evaluates constraint strings starting with letter N.  Parameter STR
+   contains the letters following letter "N" in the constraint string.
+   Returns true if VALUE matches the constraint.  */
 
-      switch (str[3])
-       {
-       case 'H': mode = HImode; break;
-       case 'S': mode = SImode; break;
-       case 'D': mode = DImode; break;
-       default: return 0;
-       }
+int
+s390_N_constraint_str (const char *str, HOST_WIDE_INT value)
+{
+  enum machine_mode mode, part_mode;
+  int def;
+  int part, part_goal;
 
-      switch (str[4])
-       {
-       case '0': def = 0;  break;
-       case 'F': def = -1; break;
-       default: return 0;
-       }
 
-      if (GET_MODE_SIZE (mode) <= GET_MODE_SIZE (part_mode))
-       return 0;
+  if (str[0] == 'x')
+    part_goal = -1;
+  else
+    part_goal = str[0] - '0';
 
-      if (s390_single_part (GEN_INT (value), mode, part_mode, def) != part)
-       return 0;
+  switch (str[1])
+    {
+    case 'Q':
+      part_mode = QImode;
+      break;
+    case 'H':
+      part_mode = HImode;
+      break;
+    case 'S':
+      part_mode = SImode;
+      break;
+    default:
+      return 0;
+    }
 
+  switch (str[2])
+    {
+    case 'H':
+      mode = HImode;
+      break;
+    case 'S':
+      mode = SImode;
       break;
+    case 'D':
+      mode = DImode;
+      break;
+    default:
+      return 0;
+    }
 
+  switch (str[3])
+    {
+    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;
+
   return 1;
 }
 
+
+/* Returns true if the input parameter VALUE is a float zero.  */
+
+int
+s390_float_const_zero_p (rtx value)
+{
+  return (GET_MODE_CLASS (GET_MODE (value)) == MODE_FLOAT
+         && value == CONST0_RTX (GET_MODE (value)));
+}
+
+
 /* 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.  */
+   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)
@@ -1469,60 +2160,186 @@ s390_rtx_costs (rtx x, int code, int outer_code, int *total)
   switch (code)
     {
     case CONST:
-      if (GET_CODE (XEXP (x, 0)) == MINUS
-         && GET_CODE (XEXP (XEXP (x, 0), 1)) != CONST_INT)
-       *total = 1000;
-      else
-       *total = 0;
-      return true;
-
     case CONST_INT:
-      /* Force_const_mem does not work out of reload, because the
-        saveable_obstack is set to reload_obstack, which does not
-        live long enough.  Because of this we cannot use force_const_mem
-        in addsi3.  This leads to problems with gen_add2_insn with a
-        constant greater than a short. Because of that we give an
-        addition of greater constants a cost of 3 (reload1.c 10096).  */
-      /* ??? saveable_obstack no longer exists.  */
-      if (outer_code == PLUS
-         && (INTVAL (x) > 32767 || INTVAL (x) < -32768))
-       *total = COSTS_N_INSNS (3);
-      else
-       *total = 0;
-      return true;
-
     case LABEL_REF:
     case SYMBOL_REF:
     case CONST_DOUBLE:
+    case MEM:
       *total = 0;
       return true;
 
     case ASHIFT:
     case ASHIFTRT:
     case LSHIFTRT:
-    case PLUS:
+    case ROTATE:
+    case ROTATERT:
     case AND:
     case IOR:
     case XOR:
-    case MINUS:
     case NEG:
     case NOT:
       *total = COSTS_N_INSNS (1);
-      return true;
+      return false;
 
-    case MULT:
-      if (GET_MODE (XEXP (x, 0)) == DImode)
-        *total = COSTS_N_INSNS (40);
-      else
-        *total = COSTS_N_INSNS (7);
-      return true;
+    case PLUS:
+    case MINUS:
+      /* Check for multiply and add.  */
+      if ((GET_MODE (x) == DFmode || GET_MODE (x) == SFmode)
+         && GET_CODE (XEXP (x, 0)) == MULT
+         && TARGET_HARD_FLOAT && TARGET_FUSED_MADD)
+       {
+         /* This is the multiply and add case.  */
+         if (GET_MODE (x) == DFmode)
+           *total = s390_cost->madbr;
+         else
+           *total = s390_cost->maebr;
+         *total += rtx_cost (XEXP (XEXP (x, 0), 0), MULT) 
+           + rtx_cost (XEXP (XEXP (x, 0), 1), MULT) 
+           + rtx_cost (XEXP (x, 1), code);
+         return true;  /* Do not do an additional recursive descent.  */
+       }
+      *total = COSTS_N_INSNS (1);
+      return false;
+
+    case MULT:      
+      switch (GET_MODE (x))
+       {
+       case SImode:
+         {
+           rtx left = XEXP (x, 0);
+           rtx right = XEXP (x, 1);
+           if (GET_CODE (right) == CONST_INT
+               && CONST_OK_FOR_K (INTVAL (right)))
+             *total = s390_cost->mhi;
+           else if (GET_CODE (left) == SIGN_EXTEND)
+             *total = s390_cost->mh;
+           else
+             *total = s390_cost->ms;  /* msr, ms, msy */
+           break;
+         }
+       case DImode:
+         {
+           rtx left = XEXP (x, 0);
+           rtx right = XEXP (x, 1);
+           if (TARGET_64BIT)
+             {
+               if (GET_CODE (right) == CONST_INT
+                   && CONST_OK_FOR_K (INTVAL (right)))
+                 *total = s390_cost->mghi;
+               else if (GET_CODE (left) == SIGN_EXTEND)
+                 *total = s390_cost->msgf;
+               else
+                 *total = s390_cost->msg;  /* msgr, msg */
+             }
+           else /* TARGET_31BIT */
+             {
+               if (GET_CODE (left) == SIGN_EXTEND
+                   && GET_CODE (right) == SIGN_EXTEND)
+                 /* mulsidi case: mr, m */
+                 *total = s390_cost->m;
+               else if (GET_CODE (left) == ZERO_EXTEND
+                        && GET_CODE (right) == ZERO_EXTEND
+                        && TARGET_CPU_ZARCH)
+                 /* umulsidi case: ml, mlr */
+                 *total = s390_cost->ml;
+               else
+                 /* Complex calculation is required.  */
+                 *total = COSTS_N_INSNS (40);
+             }
+           break;
+         }
+       case SFmode:
+       case DFmode:
+         *total = s390_cost->mult_df;
+         break;
+       case TFmode:
+         *total = s390_cost->mxbr;
+         break;
+       default:
+         return false;
+       }
+      return false;
 
-    case DIV:
     case UDIV:
-    case MOD:
     case UMOD:
-      *total = COSTS_N_INSNS (33);
-      return true;
+      if (GET_MODE (x) == TImode)             /* 128 bit division */
+       *total = s390_cost->dlgr;
+      else if (GET_MODE (x) == DImode)
+       {
+         rtx right = XEXP (x, 1);
+         if (GET_CODE (right) == ZERO_EXTEND) /* 64 by 32 bit division */
+           *total = s390_cost->dlr;
+         else                                 /* 64 by 64 bit division */
+           *total = s390_cost->dlgr;
+       }
+      else if (GET_MODE (x) == SImode)         /* 32 bit division */
+       *total = s390_cost->dlr;
+      return false;
+
+    case DIV:
+    case MOD:
+      if (GET_MODE (x) == DImode)
+       {
+         rtx right = XEXP (x, 1);
+         if (GET_CODE (right) == ZERO_EXTEND) /* 64 by 32 bit division */
+           if (TARGET_64BIT)
+             *total = s390_cost->dsgfr;
+           else
+             *total = s390_cost->dr;
+         else                                 /* 64 by 64 bit division */
+           *total = s390_cost->dsgr;
+       }
+      else if (GET_MODE (x) == SImode)         /* 32 bit division */
+       *total = s390_cost->dlr;
+      else if (GET_MODE (x) == SFmode)
+       {
+         *total = s390_cost->debr;
+       }
+      else if (GET_MODE (x) == DFmode)
+       {
+         *total = s390_cost->ddbr;
+       }
+      else if (GET_MODE (x) == TFmode)
+       {
+         *total = s390_cost->dxbr;
+       }
+      return false;
+
+    case SQRT:
+      if (GET_MODE (x) == SFmode)
+       *total = s390_cost->sqebr;
+      else if (GET_MODE (x) == DFmode)
+       *total = s390_cost->sqdbr;
+      else /* TFmode */
+       *total = s390_cost->sqxbr;
+      return false;
+
+    case SIGN_EXTEND:
+    case ZERO_EXTEND:
+      if (outer_code == MULT || outer_code == DIV || outer_code == MOD
+         || outer_code == PLUS || outer_code == MINUS
+         || outer_code == COMPARE)
+       *total = 0;
+      return false;
+
+    case COMPARE:
+      *total = COSTS_N_INSNS (1);
+      if (GET_CODE (XEXP (x, 0)) == AND
+         && GET_CODE (XEXP (x, 1)) == CONST_INT
+         && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT)
+       {
+         rtx op0 = XEXP (XEXP (x, 0), 0);
+         rtx op1 = XEXP (XEXP (x, 0), 1);
+         rtx op2 = XEXP (x, 1);
+
+         if (memory_operand (op0, GET_MODE (op0))
+             && s390_tm_ccmode (op1, op2, 0) != VOIDmode)
+           return true;
+         if (register_operand (op0, GET_MODE (op0))
+             && s390_tm_ccmode (op1, op2, 1) != VOIDmode)
+           return true;
+       }
+      return false;
 
     default:
       return false;
@@ -1541,172 +2358,41 @@ 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.  */
+/* 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,
+   while our access registers have only half that size.  */
 
-int
-load_multiple_operation (rtx op, enum machine_mode mode ATTRIBUTE_UNUSED)
+void
+s390_split_access_reg (rtx reg, rtx *lo, rtx *hi)
 {
-  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;
-
-  if (src_addr == frame_pointer_rtx || src_addr == arg_pointer_rtx)
-    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;
-}
+  gcc_assert (TARGET_64BIT);
+  gcc_assert (ACCESS_REG_P (reg));
+  gcc_assert (GET_MODE (reg) == DImode);
+  gcc_assert (!(REGNO (reg) & 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;
-
-  if (dest_addr == frame_pointer_rtx || dest_addr == arg_pointer_rtx)
-    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;
+  *lo = gen_rtx_REG (SImode, REGNO (reg) + 1);
+  *hi = gen_rtx_REG (SImode, REGNO (reg));
 }
 
-
 /* 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;
@@ -1716,7 +2402,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)))
@@ -1732,11 +2418,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);
@@ -1746,18 +2432,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;
 }
 
 
@@ -1766,7 +2452,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))
@@ -1781,7 +2467,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))
@@ -1857,13 +2543,18 @@ s390_cannot_force_const_mem (rtx x)
        case UNSPEC_INDNTPOFF:
          return false;
 
+       /* If the literal pool shares the code section, be put
+          execute template placeholders into the pool as well.  */
+       case UNSPEC_INSN:
+         return TARGET_CPU_ZARCH;
+
        default:
          return true;
        }
       break;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 
@@ -1873,45 +2564,63 @@ 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;
 
-  /* Everything else cannot be handled without reload.  */
-  return 0;
-}
+  /* Accept lzXX operands.  */
+  if (GET_CODE (op) == CONST_DOUBLE
+      && CONST_DOUBLE_OK_FOR_CONSTRAINT_P (op, 'G', "G"))
+    return true;
 
-/* Given an rtx OP being reloaded into a reg required to be in class CLASS,
+  /* 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 false;
+}
+
+/* Given an rtx OP being reloaded into a reg required to be in class CLASS,
    return the class of reg to actually use.  */
 
 enum reg_class
 s390_preferred_reload_class (rtx op, enum reg_class class)
 {
-  /* This can happen if a floating point constant is being
-     reloaded into an integer register.  Leave well alone.  */
-  if (GET_MODE_CLASS (GET_MODE (op)) == MODE_FLOAT
-      && class != FP_REGS)
-    return class;
-
   switch (GET_CODE (op))
     {
       /* Constants we cannot reload must be forced into the
@@ -1944,77 +2653,74 @@ s390_preferred_reload_class (rtx op, enum reg_class class)
   return class;
 }
 
-/* Return the register class of a scratch register needed to
-   load IN into a register of class CLASS in MODE.
-
-   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 ATTRIBUTE_UNUSED,
-                                  enum machine_mode mode, rtx in)
-{
-  if (s390_plus_operand (in, mode))
-    return ADDR_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;
+/* Inform reload about cases where moving X with a mode MODE to a register in
+   CLASS requires an extra scratch or immediate register.  Return the class
+   needed for the immediate register.  */
+
+static enum reg_class
+s390_secondary_reload (bool in_p, rtx x, enum reg_class class,
+                      enum machine_mode mode, secondary_reload_info *sri)
+{
+  /* Intermediate register needed.  */
+  if (reg_classes_intersect_p (CC_REGS, class))
+    return GENERAL_REGS;
+
+  /* We need a scratch register when loading a PLUS expression which
+     is not a legitimate operand of the LOAD ADDRESS instruction.  */
+  if (in_p && s390_plus_operand (x, mode))
+    sri->icode = (TARGET_64BIT ?
+                 CODE_FOR_reloaddi_plus : CODE_FOR_reloadsi_plus);
+
+  /* Performing a multiword move from or to memory we have to make sure the
+     second chunk in memory is addressable without causing a displacement
+     overflow.  If that would be the case we calculate the address in
+     a scratch register.  */
+  if (MEM_P (x)
+      && GET_CODE (XEXP (x, 0)) == PLUS
+      && GET_CODE (XEXP (XEXP (x, 0), 1)) == CONST_INT
+      && !DISP_IN_RANGE (INTVAL (XEXP (XEXP (x, 0), 1))
+                        + GET_MODE_SIZE (mode) - 1))
+    {
+      /* For GENERAL_REGS a displacement overflow is no problem if occurring
+        in a s_operand address since we may fallback to lm/stm.  So we only
+        have to care about overflows in the b+i+d case.  */
+      if ((reg_classes_intersect_p (GENERAL_REGS, class)
+          && s390_class_max_nregs (GENERAL_REGS, mode) > 1
+          && GET_CODE (XEXP (XEXP (x, 0), 0)) == PLUS)
+         /* For FP_REGS no lm/stm is available so this check is triggered
+            for displacement overflows in b+i+d and b+d like addresses.  */
+         || (reg_classes_intersect_p (FP_REGS, class)
+             && s390_class_max_nregs (FP_REGS, mode) > 1))
+       {
+         if (in_p)
+           sri->icode = (TARGET_64BIT ?
+                         CODE_FOR_reloaddi_nonoffmem_in :
+                         CODE_FOR_reloadsi_nonoffmem_in);
+         else
+           sri->icode = (TARGET_64BIT ?
+                         CODE_FOR_reloaddi_nonoffmem_out :
+                         CODE_FOR_reloadsi_nonoffmem_out);
+       }
+    }
 
+  /* Either scratch or no register needed.  */
   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)
+s390_expand_plus_operand (rtx target, rtx src,
+                         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 ();
+  gcc_assert (GET_CODE (src) == PLUS);
+  gcc_assert (GET_MODE (src) == Pmode);
 
   /* Check if any of the two operands is already scheduled
      for replacement by reload.  This can happen e.g. when
@@ -2025,8 +2731,8 @@ s390_expand_plus_operand (register rtx target, register rtx src,
 
   /* 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)))
+      || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
+      || (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx))))
     {
       /* Otherwise, one of the operands cannot be an address register;
          we reload its value into the scratch register.  */
@@ -2048,7 +2754,7 @@ s390_expand_plus_operand (register rtx target, register rtx src,
       if (sum1 == scratch && sum2 == scratch)
        {
          debug_rtx (src);
-         abort ();
+         gcc_unreachable ();
        }
 
       src = gen_rtx_PLUS (Pmode, sum1, sum2);
@@ -2061,417 +2767,161 @@ s390_expand_plus_operand (register rtx target, register rtx src,
 }
 
 
-/* Decompose a RTL expression ADDR for a memory address into
-   its components, returned in OUT.
+/* Return true if ADDR is a valid memory address.
+   STRICT specifies whether strict register checking applies.  */
 
-   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.
+bool
+legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
+                     rtx addr, int strict)
+{
+  struct s390_address ad;
+  if (!s390_decompose_address (addr, &ad))
+    return false;
 
-   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.  */
+  if (strict)
+    {
+      if (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
+       return false;
 
-static int
-s390_decompose_address (register rtx addr, struct s390_address *out)
+      if (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx)))
+       return false;
+    }
+  else
+    {
+      if (ad.base 
+         && !(REGNO (ad.base) >= FIRST_PSEUDO_REGISTER
+              || REGNO_REG_CLASS (REGNO (ad.base)) == ADDR_REGS))
+       return false;
+      
+      if (ad.indx
+         && !(REGNO (ad.indx) >= FIRST_PSEUDO_REGISTER
+              || REGNO_REG_CLASS (REGNO (ad.indx)) == ADDR_REGS))
+         return false;
+    }
+  return true;
+}
+
+/* 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.  */
+
+bool
+legitimate_la_operand_p (rtx op)
 {
-  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;
+  struct s390_address addr;
+  if (!s390_decompose_address (op, &addr))
+    return false;
 
-  /* Decompose address into base + index + displacement.  */
+  return (TARGET_64BIT || addr.pointer);
+}
 
-  if (GET_CODE (addr) == REG || GET_CODE (addr) == UNSPEC)
-    base = addr;
+/* Return true if it is valid *and* preferable to use LA to
+   compute the sum of OP1 and OP2.  */
 
-  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);
+bool
+preferred_la_operand_p (rtx op1, rtx op2)
+{
+  struct s390_address addr;
 
-      if (code0 == REG || code0 == UNSPEC)
-       {
-         if (code1 == REG || code1 == UNSPEC)
-           {
-             indx = op0;       /* index + base */
-             base = op1;
-           }
+  if (op2 != const0_rtx)
+    op1 = gen_rtx_PLUS (Pmode, op1, op2);
 
-         else
-           {
-             base = op0;       /* base + displacement */
-             disp = op1;
-           }
-       }
+  if (!s390_decompose_address (op1, &addr))
+    return false;
+  if (addr.base && !REGNO_OK_FOR_BASE_P (REGNO (addr.base)))
+    return false;
+  if (addr.indx && !REGNO_OK_FOR_INDEX_P (REGNO (addr.indx)))
+    return false;
 
-      else if (code0 == PLUS)
-       {
-         indx = XEXP (op0, 0); /* index + base + disp */
-         base = XEXP (op0, 1);
-         disp = op1;
-       }
+  if (!TARGET_64BIT && !addr.pointer)
+    return false;
 
-      else
-       {
-         return FALSE;
-       }
-    }
+  if (addr.pointer)
+    return true;
 
-  else
-    disp = addr;               /* displacement */
+  if ((addr.base && REG_P (addr.base) && REG_POINTER (addr.base))
+      || (addr.indx && REG_P (addr.indx) && REG_POINTER (addr.indx)))
+    return true;
 
-  /* 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);
-       }
-    }
+  return false;
+}
 
-  /* Strip off CONST here to avoid special case tests later.  */
-  if (disp && GET_CODE (disp) == CONST)
-    disp = XEXP (disp, 0);
+/* Emit a forced load-address operation to load SRC into DST.
+   This will use the LOAD ADDRESS instruction even in situations
+   where legitimate_la_operand_p (SRC) returns false.  */
 
-  /* 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;
+void
+s390_load_address (rtx dst, rtx src)
+{
+  if (TARGET_64BIT)
+    emit_move_insn (dst, src);
+  else
+    emit_insn (gen_force_la_31 (dst, src));
+}
 
-      /* Mark up the displacement.  */
-      disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp),
-                            UNSPEC_LTREL_OFFSET);
-    }
+/* Return a legitimate reference for ORIG (an address) using the
+   register REG.  If REG is 0, a new pseudo is generated.
 
-  /* 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;
+   There are two types of references that must be handled:
 
-           base = gen_rtx_REG (Pmode, BASE_REGNUM);
-           break;
+   1. Global data references must load the address from the GOT, via
+      the PIC reg.  An insn is emitted to do this load, and the reg is
+      returned.
 
-         case UNSPEC_LTREL_BASE:
-           base = gen_rtx_REG (Pmode, BASE_REGNUM);
-           break;
+   2. Static data references, constant pool addresses, and code labels
+      compute the address as an offset from the GOT, whose base is in
+      the PIC reg.  Static data objects have SYMBOL_FLAG_LOCAL set to
+      differentiate them from global data objects.  The returned
+      address is the PIC reg + an unspec constant.
 
-         default:
-           return FALSE;
-         }
+   GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC
+   reg also appears in the address.  */
 
-      if (GET_CODE (base) != REG || GET_MODE (base) != Pmode)
-       return FALSE;
+rtx
+legitimize_pic_address (rtx orig, rtx reg)
+{
+  rtx addr = orig;
+  rtx new = orig;
+  rtx base;
 
-      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;
-    }
+  gcc_assert (!TLS_SYMBOLIC_CONST (addr));
 
-  /* Validate index register.  */
-  if (indx)
+  if (GET_CODE (addr) == LABEL_REF
+      || (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (addr)))
     {
-      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;
+      /* This is a local symbol.  */
+      if (TARGET_CPU_ZARCH && larl_operand (addr, VOIDmode))
+        {
+          /* Access local symbols PC-relative via LARL.
+             This is the same as in the non-PIC case, so it is
+             handled automatically ...  */
+        }
+      else
+        {
+          /* Access local symbols relative to the GOT.  */
 
-           indx = gen_rtx_REG (Pmode, BASE_REGNUM);
-           break;
+          rtx temp = reg? reg : gen_reg_rtx (Pmode);
 
-         case UNSPEC_LTREL_BASE:
-           indx = gen_rtx_REG (Pmode, BASE_REGNUM);
-           break;
+         if (reload_in_progress || reload_completed)
+           df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
 
-         default:
-           return FALSE;
-         }
+          addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTOFF);
+          addr = gen_rtx_CONST (Pmode, addr);
+          addr = force_const_mem (Pmode, addr);
+         emit_move_insn (temp, addr);
 
-      if (GET_CODE (indx) != REG || GET_MODE (indx) != Pmode)
-       return FALSE;
-
-      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;
-    }
-
-  /* Prefer to use pointer as base, not index.  */
-  if (base && indx && !base_ptr
-      && (indx_ptr || (!REG_POINTER (base) && REG_POINTER (indx))))
-    {
-      rtx tmp = base;
-      base = indx;
-      indx = tmp;
-    }
-
-  /* Validate displacement.  */
-  if (!disp)
-    {
-      /* If the argument pointer is involved, the displacement will change
-        later anyway as the argument pointer gets 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 the arg pointer.
-        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)
-       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;
-}
-
-/* Return nonzero if ADDR is a valid memory address.
-   STRICT specifies whether strict register checking applies.  */
-
-int
-legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
-                     register rtx addr, int strict)
-{
-  struct s390_address ad;
-  if (!s390_decompose_address (addr, &ad))
-    return FALSE;
-
-  if (strict)
-    {
-      if (ad.base && !REG_OK_FOR_BASE_STRICT_P (ad.base))
-       return FALSE;
-      if (ad.indx && !REG_OK_FOR_INDEX_STRICT_P (ad.indx))
-       return FALSE;
-    }
-  else
-    {
-      if (ad.base && !REG_OK_FOR_BASE_NONSTRICT_P (ad.base))
-       return FALSE;
-      if (ad.indx && !REG_OK_FOR_INDEX_NONSTRICT_P (ad.indx))
-       return FALSE;
-    }
-
-  return TRUE;
-}
-
-/* Return 1 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)
-{
-  struct s390_address addr;
-  if (!s390_decompose_address (op, &addr))
-    return FALSE;
-
-  if (TARGET_64BIT || addr.pointer)
-    return TRUE;
-
-  return FALSE;
-}
-
-/* Return 1 if OP is a valid operand for the LA instruction,
-   and we prefer to use LA over addition to compute it.  */
-
-int
-preferred_la_operand_p (register rtx op)
-{
-  struct s390_address addr;
-  if (!s390_decompose_address (op, &addr))
-    return FALSE;
-
-  if (!TARGET_64BIT && !addr.pointer)
-    return FALSE;
-
-  if (addr.pointer)
-    return TRUE;
-
-  if ((addr.base && REG_P (addr.base) && REG_POINTER (addr.base))
-      || (addr.indx && REG_P (addr.indx) && REG_POINTER (addr.indx)))
-    return TRUE;
-
-  return FALSE;
-}
-
-/* Emit a forced load-address operation to load SRC into DST.
-   This will use the LOAD ADDRESS instruction even in situations
-   where legitimate_la_operand_p (SRC) returns false.  */
-
-void
-s390_load_address (rtx dst, rtx src)
-{
-  if (TARGET_64BIT)
-    emit_move_insn (dst, src);
-  else
-    emit_insn (gen_force_la_31 (dst, src));
-}
-
-/* Return a legitimate reference for ORIG (an address) using the
-   register REG.  If REG is 0, a new pseudo is generated.
-
-   There are two types of references that must be handled:
-
-   1. Global data references must load the address from the GOT, via
-      the PIC reg.  An insn is emitted to do this load, and the reg is
-      returned.
-
-   2. Static data references, constant pool addresses, and code labels
-      compute the address as an offset from the GOT, whose base is in
-      the PIC reg.  Static data objects have SYMBOL_FLAG_LOCAL set to
-      differentiate them from global data objects.  The returned
-      address is the PIC reg + an unspec constant.
-
-   GO_IF_LEGITIMATE_ADDRESS rejects symbolic references unless the PIC
-   reg also appears in the address.  */
-
-rtx
-legitimize_pic_address (rtx orig, rtx reg)
-{
-  rtx addr = orig;
-  rtx new = orig;
-  rtx base;
-
-  if (GET_CODE (addr) == LABEL_REF
-      || (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (addr)))
-    {
-      /* This is a local symbol.  */
-      if (TARGET_CPU_ZARCH && larl_operand (addr, VOIDmode))
-        {
-          /* Access local symbols PC-relative via LARL.
-             This is the same as in the non-PIC case, so it is
-             handled automatically ...  */
-        }
-      else
-        {
-          /* Access local symbols relative to the GOT.  */
-
-          rtx temp = reg? reg : gen_reg_rtx (Pmode);
-
-         if (reload_in_progress || reload_completed)
-           regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
-
-          addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTOFF);
-          addr = gen_rtx_CONST (Pmode, addr);
-          addr = force_const_mem (Pmode, addr);
-         emit_move_insn (temp, addr);
-
-          new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
-          if (reg != 0)
-            {
-              emit_move_insn (reg, new);
-              new = reg;
-            }
-        }
-    }
-  else if (GET_CODE (addr) == SYMBOL_REF)
-    {
-      if (reg == 0)
-        reg = gen_reg_rtx (Pmode);
+          new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
+          if (reg != 0)
+            {
+              s390_load_address (reg, new);
+              new = reg;
+            }
+        }
+    }
+  else if (GET_CODE (addr) == SYMBOL_REF)
+    {
+      if (reg == 0)
+        reg = gen_reg_rtx (Pmode);
 
       if (flag_pic == 1)
         {
@@ -2479,13 +2929,12 @@ legitimize_pic_address (rtx orig, rtx reg)
              in both 31- and 64-bit code (@GOT).  */
 
          if (reload_in_progress || reload_completed)
-           regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
+           df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
 
           new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT);
           new = gen_rtx_CONST (Pmode, new);
           new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new);
-          new = gen_rtx_MEM (Pmode, new);
-          RTX_UNCHANGING_P (new) = 1;
+          new = gen_const_mem (Pmode, new);
           emit_move_insn (reg, new);
           new = reg;
         }
@@ -2500,8 +2949,7 @@ legitimize_pic_address (rtx orig, rtx reg)
           new = gen_rtx_CONST (Pmode, new);
           emit_move_insn (temp, new);
 
-          new = gen_rtx_MEM (Pmode, temp);
-          RTX_UNCHANGING_P (new) = 1;
+          new = gen_const_mem (Pmode, temp);
           emit_move_insn (reg, new);
           new = reg;
         }
@@ -2513,7 +2961,7 @@ legitimize_pic_address (rtx orig, rtx reg)
           rtx temp = gen_reg_rtx (Pmode);
 
          if (reload_in_progress || reload_completed)
-           regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
+           df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
 
           addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT);
           addr = gen_rtx_CONST (Pmode, addr);
@@ -2521,8 +2969,7 @@ legitimize_pic_address (rtx orig, rtx reg)
           emit_move_insn (temp, addr);
 
           new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
-          new = gen_rtx_MEM (Pmode, new);
-          RTX_UNCHANGING_P (new) = 1;
+          new = gen_const_mem (Pmode, new);
           emit_move_insn (reg, new);
           new = reg;
         }
@@ -2534,8 +2981,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
@@ -2563,7 +3009,7 @@ legitimize_pic_address (rtx orig, rtx reg)
                         rtx temp = reg? reg : gen_reg_rtx (Pmode);
 
                        if (reload_in_progress || reload_completed)
-                         regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
+                         df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
 
                         addr = XVECEXP (addr, 0, 0);
                         addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr),
@@ -2575,7 +3021,7 @@ legitimize_pic_address (rtx orig, rtx reg)
                         new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
                         if (reg != 0)
                           {
-                            emit_move_insn (reg, new);
+                            s390_load_address (reg, new);
                             new = reg;
                           }
                       }
@@ -2583,22 +3029,29 @@ 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)
        {
          rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1);
+
+         gcc_assert (!TLS_SYMBOLIC_CONST (op0));
+         gcc_assert (!TLS_SYMBOLIC_CONST (op1));
+
          /* Check first to see if this is a constant offset
              from a local symbol reference.  */
          if ((GET_CODE (op0) == LABEL_REF
                || (GET_CODE (op0) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (op0)))
              && GET_CODE (op1) == CONST_INT)
            {
-              if (TARGET_CPU_ZARCH && larl_operand (op0, VOIDmode))
+              if (TARGET_CPU_ZARCH
+                 && larl_operand (op0, VOIDmode)
+                 && INTVAL (op1) < (HOST_WIDE_INT)1 << 31
+                 && INTVAL (op1) >= -((HOST_WIDE_INT)1 << 31))
                 {
                   if (INTVAL (op1) & 1)
                     {
@@ -2608,7 +3061,7 @@ legitimize_pic_address (rtx orig, rtx reg)
 
                       if (!DISP_IN_RANGE (INTVAL (op1)))
                         {
-                          int even = INTVAL (op1) - 1;
+                          HOST_WIDE_INT even = INTVAL (op1) - 1;
                           op0 = gen_rtx_PLUS (Pmode, op0, GEN_INT (even));
                          op0 = gen_rtx_CONST (Pmode, op0);
                           op1 = const1_rtx;
@@ -2619,7 +3072,7 @@ legitimize_pic_address (rtx orig, rtx reg)
 
                       if (reg != 0)
                         {
-                          emit_move_insn (reg, new);
+                          s390_load_address (reg, new);
                           new = reg;
                         }
                     }
@@ -2636,7 +3089,7 @@ legitimize_pic_address (rtx orig, rtx reg)
                   rtx temp = reg? reg : gen_reg_rtx (Pmode);
 
                  if (reload_in_progress || reload_completed)
-                   regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
+                   df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
 
                   addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, op0),
                                         UNSPEC_GOTOFF);
@@ -2648,7 +3101,7 @@ legitimize_pic_address (rtx orig, rtx reg)
                   new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
                   if (reg != 0)
                     {
-                      emit_move_insn (reg, new);
+                      s390_load_address (reg, new);
                       new = reg;
                     }
                 }
@@ -2661,8 +3114,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);
             }
@@ -2696,13 +3148,12 @@ 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;
+  rtx tp = gen_reg_rtx (Pmode);
 
-  tp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TP);
-  tp = force_reg (Pmode, tp);
+  emit_move_insn (tp, gen_rtx_REG (Pmode, TP_REGNUM));
   mark_reg_pointer (tp, BITS_PER_WORD);
 
   return tp;
@@ -2720,8 +3171,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");
@@ -2759,7 +3209,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);
@@ -2782,7 +3232,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);
 
@@ -2807,13 +3257,12 @@ legitimize_tls_address (rtx addr, rtx reg)
               in both 31- and 64-bit code.  */
 
            if (reload_in_progress || reload_completed)
-             regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
+             df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
 
            new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF);
            new = gen_rtx_CONST (Pmode, new);
            new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new);
-           new = gen_rtx_MEM (Pmode, new);
-           RTX_UNCHANGING_P (new) = 1;
+           new = gen_const_mem (Pmode, new);
            temp = gen_reg_rtx (Pmode);
            emit_move_insn (temp, new);
          }
@@ -2827,8 +3276,7 @@ legitimize_tls_address (rtx addr, rtx reg)
            temp = gen_reg_rtx (Pmode);
            emit_move_insn (temp, new);
 
-           new = gen_rtx_MEM (Pmode, temp);
-           RTX_UNCHANGING_P (new) = 1;
+           new = gen_const_mem (Pmode, temp);
            temp = gen_reg_rtx (Pmode);
            emit_move_insn (temp, new);
          }
@@ -2838,7 +3286,7 @@ legitimize_tls_address (rtx addr, rtx reg)
               from the literal pool.  */
 
            if (reload_in_progress || reload_completed)
-             regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
+             df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
 
            new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF);
            new = gen_rtx_CONST (Pmode, new);
@@ -2847,8 +3295,7 @@ legitimize_tls_address (rtx addr, rtx reg)
            emit_move_insn (temp, new);
 
             new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
-           new = gen_rtx_MEM (Pmode, new);
-           RTX_UNCHANGING_P (new) = 1;
+           new = gen_const_mem (Pmode, new);
 
            new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD);
            temp = gen_reg_rtx (Pmode);
@@ -2866,15 +3313,13 @@ legitimize_tls_address (rtx addr, rtx reg)
            emit_move_insn (temp, new);
 
            new = temp;
-           new = gen_rtx_MEM (Pmode, new);
-           RTX_UNCHANGING_P (new) = 1;
-
+           new = gen_const_mem (Pmode, new);
            new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD);
            temp = gen_reg_rtx (Pmode);
            emit_insn (gen_rtx_SET (Pmode, temp, new));
          }
 
-       new = gen_rtx_PLUS (Pmode, get_thread_pointer (), temp);
+       new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
        if (reg != 0)
          {
            s390_load_address (reg, new);
@@ -2889,7 +3334,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);
@@ -2898,7 +3343,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)
@@ -2906,14 +3351,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 ();
        }
     }
 
@@ -2930,7 +3373,7 @@ legitimize_tls_address (rtx addr, rtx reg)
     }
 
   else
-    abort ();  /* for now ... */
+    gcc_unreachable ();  /* for now ... */
 
   return new;
 }
@@ -2940,7 +3383,7 @@ legitimize_tls_address (rtx addr, rtx reg)
 void
 emit_symbolic_move (rtx *operands)
 {
-  rtx temp = no_new_pseudos ? operands[0] : gen_reg_rtx (Pmode);
+  rtx temp = !can_create_pseudo_p () ? operands[0] : gen_reg_rtx (Pmode);
 
   if (GET_CODE (operands[0]) == MEM)
     operands[1] = force_reg (Pmode, operands[1]);
@@ -2962,7 +3405,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;
@@ -2974,6 +3417,12 @@ legitimize_address (register rtx x, register rtx oldx ATTRIBUTE_UNUSED,
       if (legitimate_address_p (mode, x, FALSE))
        return x;
     }
+  else if (GET_CODE (x) == PLUS
+          && (TLS_SYMBOLIC_CONST (XEXP (x, 0)) 
+              || TLS_SYMBOLIC_CONST (XEXP (x, 1))))
+    {
+      return x;
+    }
   else if (flag_pic)
     {
       if (SYMBOLIC_CONST (x)
@@ -3017,8 +3466,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);
 
@@ -3027,8 +3476,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);
 
@@ -3042,6 +3491,52 @@ legitimize_address (register rtx x, register rtx oldx ATTRIBUTE_UNUSED,
   return x;
 }
 
+/* Try a machine-dependent way of reloading an illegitimate address AD
+   operand.  If we find one, push the reload and and return the new address.
+
+   MODE is the mode of the enclosing MEM.  OPNUM is the operand number
+   and TYPE is the reload type of the current reload.  */
+
+rtx 
+legitimize_reload_address (rtx ad, enum machine_mode mode ATTRIBUTE_UNUSED,
+                          int opnum, int type)
+{
+  if (!optimize || TARGET_LONG_DISPLACEMENT)
+    return NULL_RTX;
+
+  if (GET_CODE (ad) == PLUS)
+    {
+      rtx tem = simplify_binary_operation (PLUS, Pmode,
+                                          XEXP (ad, 0), XEXP (ad, 1));
+      if (tem)
+       ad = tem;
+    }
+
+  if (GET_CODE (ad) == PLUS
+      && GET_CODE (XEXP (ad, 0)) == REG
+      && GET_CODE (XEXP (ad, 1)) == CONST_INT
+      && !DISP_IN_RANGE (INTVAL (XEXP (ad, 1))))
+    {
+      HOST_WIDE_INT lower = INTVAL (XEXP (ad, 1)) & 0xfff;
+      HOST_WIDE_INT upper = INTVAL (XEXP (ad, 1)) ^ lower;
+      rtx cst, tem, new;
+
+      cst = GEN_INT (upper);
+      if (!legitimate_reload_constant_p (cst))
+       cst = force_const_mem (Pmode, cst);
+
+      tem = gen_rtx_PLUS (Pmode, XEXP (ad, 0), cst);
+      new = gen_rtx_PLUS (Pmode, tem, GEN_INT (lower));
+
+      push_reload (XEXP (tem, 1), 0, &XEXP (tem, 1), 0,
+                  BASE_REG_CLASS, Pmode, VOIDmode, 0, 0, 
+                  opnum, (enum reload_type) type);
+      return new;
+    }
+
+  return NULL_RTX;
+}
+
 /* Emit code to move LEN bytes from DST to SRC.  */
 
 void
@@ -3088,7 +3583,7 @@ s390_expand_movmem (rtx dst, rtx src, rtx len)
       if (temp != count)
         emit_move_insn (count, temp);
 
-      temp = expand_binop (mode, ashr_optab, count, GEN_INT (8), blocks, 1, 0);
+      temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0);
       if (temp != blocks)
         emit_move_insn (blocks, temp);
 
@@ -3119,25 +3614,51 @@ 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)
+  if (GET_CODE (len) == CONST_INT && INTVAL (len) == 0)
+    return;
+
+  gcc_assert (GET_CODE (val) == CONST_INT || GET_MODE (val) == QImode);
+  
+  if (GET_CODE (len) == CONST_INT && INTVAL (len) > 0 && 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 ();
@@ -3159,11 +3680,26 @@ 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);
 
-      temp = expand_binop (mode, ashr_optab, count, GEN_INT (8), blocks, 1, 0);
+      temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0);
       if (temp != blocks)
         emit_move_insn (blocks, temp);
 
@@ -3172,7 +3708,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)));
 
@@ -3186,7 +3725,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);
     }
 }
@@ -3197,29 +3739,28 @@ s390_expand_clrmem (rtx dst, rtx len)
 void
 s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
 {
-  rtx (*gen_result) (rtx) =
-    GET_MODE (target) == DImode ? gen_cmpint_di : gen_cmpint_si;
+  rtx ccreg = gen_rtx_REG (CCUmode, CC_REGNUM);
+  rtx tmp;
+
+  /* As the result of CMPINT is inverted compared to what we need,
+     we have to swap the operands.  */
+  tmp = op0; op0 = op1; op1 = tmp;
 
   if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256)
     {
       if (INTVAL (len) > 0)
         {
           emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (INTVAL (len) - 1)));
-          emit_insn (gen_result (target));
+          emit_insn (gen_cmpint (target, ccreg));
         }
       else
         emit_move_insn (target, const0_rtx);
     }
-
-  else /* if (TARGET_MVCLE) */
+  else if (TARGET_MVCLE)
     {
       emit_insn (gen_cmpmem_long (op0, op1, convert_to_mode (Pmode, len, 1)));
-      emit_insn (gen_result (target));
+      emit_insn (gen_cmpint (target, ccreg));
     }
-
-#if 0
-  /* Deactivate for now as profile code cannot cope with
-     CC being live across basic block boundaries.  */
   else
     {
       rtx addr0, addr1, count, blocks, temp;
@@ -3250,7 +3791,7 @@ s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
       if (temp != count)
         emit_move_insn (count, temp);
 
-      temp = expand_binop (mode, ashr_optab, count, GEN_INT (8), blocks, 1, 0);
+      temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1, 0);
       if (temp != blocks)
         emit_move_insn (blocks, temp);
 
@@ -3260,7 +3801,7 @@ s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
       emit_label (loop_start_label);
 
       emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (255)));
-      temp = gen_rtx_NE (VOIDmode, gen_rtx_REG (CCSmode, 33), const0_rtx);
+      temp = gen_rtx_NE (VOIDmode, ccreg, const0_rtx);
       temp = gen_rtx_IF_THEN_ELSE (VOIDmode, temp,
                        gen_rtx_LABEL_REF (VOIDmode, end_label), pc_rtx);
       temp = gen_rtx_SET (VOIDmode, pc_rtx, temp);
@@ -3285,16 +3826,29 @@ s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
                                   convert_to_mode (Pmode, count, 1)));
       emit_label (end_label);
 
-      emit_insn (gen_result (target));
+      emit_insn (gen_cmpint (target, ccreg));
     }
-#endif
 }
 
 
 /* Expand conditional increment or decrement using alc/slb instructions.
    Should generate code setting DST to either SRC or SRC + INCREMENT,
    depending on the result of the comparison CMP_OP0 CMP_CODE CMP_OP1.
-   Returns true if successful, false otherwise.  */
+   Returns true if successful, false otherwise.
+
+   That makes it possible to implement some if-constructs without jumps e.g.:
+   (borrow = CC0 | CC1 and carry = CC2 | CC3)
+   unsigned int a, b, c;
+   if (a < b)  c++; -> CCU  b > a  -> CC2;    c += carry;
+   if (a < b)  c--; -> CCL3 a - b  -> borrow; c -= borrow;
+   if (a <= b) c++; -> CCL3 b - a  -> borrow; c += carry;
+   if (a <= b) c--; -> CCU  a <= b -> borrow; c -= borrow;
+
+   Checks for EQ and NE with a nonzero value need an additional xor e.g.:
+   if (a == b) c++; -> CCL3 a ^= b; 0 - a  -> borrow;    c += carry;
+   if (a == b) c--; -> CCU  a ^= b; a <= 0 -> CC0 | CC1; c -= borrow;
+   if (a != b) c++; -> CCU  a ^= b; a > 0  -> CC2;       c += carry;
+   if (a != b) c--; -> CCL3 a ^= b; 0 - a  -> borrow;    c -= borrow; */
 
 bool
 s390_expand_addcc (enum rtx_code cmp_code, rtx cmp_op0, rtx cmp_op1,
@@ -3305,6 +3859,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))
@@ -3360,8 +3915,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),
@@ -3432,8 +3987,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)))
@@ -3457,30 +4012,378 @@ 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.  */
 
-/* This is called from dwarf2out.c via ASM_OUTPUT_DWARF_DTPREL.
-   We need to emit DTP-relative relocations.  */
-
-void
-s390_output_dwarf_dtprel (FILE *file, int size, rtx x)
+bool 
+s390_expand_insv (rtx dest, rtx op1, rtx op2, rtx src)
 {
-  switch (size)
+  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)))
     {
-    case 4:
-      fputs ("\t.long\t", file);
-      break;
-    case 8:
-      fputs ("\t.quad\t", file);
-      break;
+      /* 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;
+}
+
+/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic which returns a
+   register that holds VAL of mode MODE shifted by COUNT bits.  */
+
+static inline rtx
+s390_expand_mask_and_shift (rtx val, enum machine_mode mode, rtx count)
+{
+  val = expand_simple_binop (SImode, AND, val, GEN_INT (GET_MODE_MASK (mode)),
+                            NULL_RTX, 1, OPTAB_DIRECT);
+  return expand_simple_binop (SImode, ASHIFT, val, count, 
+                             NULL_RTX, 1, OPTAB_DIRECT);
+}
+
+/* Structure to hold the initial parameters for a compare_and_swap operation
+   in HImode and QImode.  */ 
+
+struct alignment_context
+{
+  rtx memsi;     /* SI aligned memory location.  */ 
+  rtx shift;     /* Bit offset with regard to lsb.  */
+  rtx modemask;          /* Mask of the HQImode shifted by SHIFT bits.  */
+  rtx modemaski;  /* ~modemask */
+  bool aligned;          /* True if memory is aligned, false else.  */
+};
+
+/* A subroutine of s390_expand_cs_hqi and s390_expand_atomic to initialize
+   structure AC for transparent simplifying, if the memory alignment is known
+   to be at least 32bit.  MEM is the memory location for the actual operation
+   and MODE its mode.  */
+
+static void
+init_alignment_context (struct alignment_context *ac, rtx mem,
+                       enum machine_mode mode)
+{
+  ac->shift = GEN_INT (GET_MODE_SIZE (SImode) - GET_MODE_SIZE (mode));
+  ac->aligned = (MEM_ALIGN (mem) >= GET_MODE_BITSIZE (SImode));
+
+  if (ac->aligned)
+    ac->memsi = adjust_address (mem, SImode, 0); /* Memory is aligned.  */
+  else
+    {
+      /* Alignment is unknown.  */
+      rtx byteoffset, addr, align;
+
+      /* Force the address into a register.  */
+      addr = force_reg (Pmode, XEXP (mem, 0));
+
+      /* Align it to SImode.  */
+      align = expand_simple_binop (Pmode, AND, addr,
+                                  GEN_INT (-GET_MODE_SIZE (SImode)),
+                                  NULL_RTX, 1, OPTAB_DIRECT);
+      /* Generate MEM.  */
+      ac->memsi = gen_rtx_MEM (SImode, align);
+      MEM_VOLATILE_P (ac->memsi) = MEM_VOLATILE_P (mem);
+      set_mem_alias_set (ac->memsi, ALIAS_SET_MEMORY_BARRIER);
+      set_mem_align (ac->memsi, GET_MODE_BITSIZE (SImode));
+
+      /* Calculate shiftcount.  */
+      byteoffset = expand_simple_binop (Pmode, AND, addr,
+                                       GEN_INT (GET_MODE_SIZE (SImode) - 1),
+                                       NULL_RTX, 1, OPTAB_DIRECT);
+      /* As we already have some offset, evaluate the remaining distance.  */
+      ac->shift = expand_simple_binop (SImode, MINUS, ac->shift, byteoffset,
+                                     NULL_RTX, 1, OPTAB_DIRECT);
+
+    }
+  /* Shift is the byte count, but we need the bitcount.  */
+  ac->shift = expand_simple_binop (SImode, MULT, ac->shift, GEN_INT (BITS_PER_UNIT),
+                                 NULL_RTX, 1, OPTAB_DIRECT);
+  /* Calculate masks.  */
+  ac->modemask = expand_simple_binop (SImode, ASHIFT, 
+                                    GEN_INT (GET_MODE_MASK (mode)), ac->shift,
+                                    NULL_RTX, 1, OPTAB_DIRECT);
+  ac->modemaski = expand_simple_unop (SImode, NOT, ac->modemask, NULL_RTX, 1);
+}
+
+/* Expand an atomic compare and swap operation for HImode and QImode.  MEM is
+   the memory location, CMP the old value to compare MEM with and NEW the value
+   to set if CMP == MEM.
+   CMP is never in memory for compare_and_swap_cc because
+   expand_bool_compare_and_swap puts it into a register for later compare.  */
+
+void
+s390_expand_cs_hqi (enum machine_mode mode, rtx target, rtx mem, rtx cmp, rtx new)
+{
+  struct alignment_context ac;
+  rtx cmpv, newv, val, resv, cc;
+  rtx res = gen_reg_rtx (SImode);
+  rtx csloop = gen_label_rtx ();
+  rtx csend = gen_label_rtx ();
+
+  gcc_assert (register_operand (target, VOIDmode));
+  gcc_assert (MEM_P (mem));
+
+  init_alignment_context (&ac, mem, mode);
+
+  /* Shift the values to the correct bit positions.  */
+  if (!(ac.aligned && MEM_P (cmp)))
+    cmp = s390_expand_mask_and_shift (cmp, mode, ac.shift);
+  if (!(ac.aligned && MEM_P (new)))
+    new = s390_expand_mask_and_shift (new, mode, ac.shift);
+
+  /* Load full word.  Subsequent loads are performed by CS.  */
+  val = expand_simple_binop (SImode, AND, ac.memsi, ac.modemaski,
+                            NULL_RTX, 1, OPTAB_DIRECT);
+
+  /* Start CS loop.  */
+  emit_label (csloop);
+  /* val = "<mem>00..0<mem>" 
+   * cmp = "00..0<cmp>00..0"
+   * new = "00..0<new>00..0" 
+   */
+
+  /* Patch cmp and new with val at correct position.  */
+  if (ac.aligned && MEM_P (cmp))
+    {
+      cmpv = force_reg (SImode, val);
+      store_bit_field (cmpv, GET_MODE_BITSIZE (mode), 0, SImode, cmp);
+    }
+  else
+    cmpv = force_reg (SImode, expand_simple_binop (SImode, IOR, cmp, val,
+                                                  NULL_RTX, 1, OPTAB_DIRECT));
+  if (ac.aligned && MEM_P (new))
+    {
+      newv = force_reg (SImode, val);
+      store_bit_field (newv, GET_MODE_BITSIZE (mode), 0, SImode, new);
+    }
+  else
+    newv = force_reg (SImode, expand_simple_binop (SImode, IOR, new, val,
+                                                  NULL_RTX, 1, OPTAB_DIRECT));
+
+  /* Jump to end if we're done (likely?).  */
+  s390_emit_jump (csend, s390_emit_compare_and_swap (EQ, res, ac.memsi,
+                                                    cmpv, newv));
+
+  /* Check for changes outside mode.  */
+  resv = expand_simple_binop (SImode, AND, res, ac.modemaski, 
+                             NULL_RTX, 1, OPTAB_DIRECT);
+  cc = s390_emit_compare (NE, resv, val); 
+  emit_move_insn (val, resv);
+  /* Loop internal if so.  */
+  s390_emit_jump (csloop, cc);
+
+  emit_label (csend);
+  
+  /* Return the correct part of the bitfield.  */
+  convert_move (target, expand_simple_binop (SImode, LSHIFTRT, res, ac.shift, 
+                                            NULL_RTX, 1, OPTAB_DIRECT), 1);
+}
+
+/* Expand an atomic operation CODE of mode MODE.  MEM is the memory location
+   and VAL the value to play with.  If AFTER is true then store the value
+   MEM holds after the operation, if AFTER is false then store the value MEM
+   holds before the operation.  If TARGET is zero then discard that value, else
+   store it to TARGET.  */
+
+void
+s390_expand_atomic (enum machine_mode mode, enum rtx_code code,
+                   rtx target, rtx mem, rtx val, bool after)
+{
+  struct alignment_context ac;
+  rtx cmp;
+  rtx new = gen_reg_rtx (SImode);
+  rtx orig = gen_reg_rtx (SImode);
+  rtx csloop = gen_label_rtx ();
+
+  gcc_assert (!target || register_operand (target, VOIDmode));
+  gcc_assert (MEM_P (mem));
+
+  init_alignment_context (&ac, mem, mode);
+
+  /* Shift val to the correct bit positions.
+     Preserve "icm", but prevent "ex icm".  */
+  if (!(ac.aligned && code == SET && MEM_P (val)))
+    val = s390_expand_mask_and_shift (val, mode, ac.shift);
+
+  /* Further preparation insns.  */
+  if (code == PLUS || code == MINUS)
+    emit_move_insn (orig, val);
+  else if (code == MULT || code == AND) /* val = "11..1<val>11..1" */
+    val = expand_simple_binop (SImode, XOR, val, ac.modemaski,
+                              NULL_RTX, 1, OPTAB_DIRECT);
+
+  /* Load full word.  Subsequent loads are performed by CS.  */
+  cmp = force_reg (SImode, ac.memsi);
+
+  /* Start CS loop.  */
+  emit_label (csloop);
+  emit_move_insn (new, cmp);
+
+  /* Patch new with val at correct position.  */
+  switch (code)
+    {
+    case PLUS:
+    case MINUS:
+      val = expand_simple_binop (SImode, code, new, orig,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      val = expand_simple_binop (SImode, AND, val, ac.modemask,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      /* FALLTHRU */
+    case SET: 
+      if (ac.aligned && MEM_P (val))
+       store_bit_field (new, GET_MODE_BITSIZE (mode), 0, SImode, val);
+      else
+       {
+         new = expand_simple_binop (SImode, AND, new, ac.modemaski,
+                                    NULL_RTX, 1, OPTAB_DIRECT);
+         new = expand_simple_binop (SImode, IOR, new, val,
+                                    NULL_RTX, 1, OPTAB_DIRECT);
+       }
+      break;
+    case AND:
+    case IOR:
+    case XOR:
+      new = expand_simple_binop (SImode, code, new, val,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      break;
+    case MULT: /* NAND */
+      new = expand_simple_binop (SImode, XOR, new, ac.modemask,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      new = expand_simple_binop (SImode, AND, new, val,
+                                NULL_RTX, 1, OPTAB_DIRECT);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+
+  s390_emit_jump (csloop, s390_emit_compare_and_swap (NE, cmp,
+                                                     ac.memsi, cmp, new));
+
+  /* Return the correct part of the bitfield.  */
+  if (target)
+    convert_move (target, expand_simple_binop (SImode, LSHIFTRT,
+                                              after ? new : cmp, ac.shift,
+                                              NULL_RTX, 1, OPTAB_DIRECT), 1);
+}
+
+/* This is called from dwarf2out.c via TARGET_ASM_OUTPUT_DWARF_DTPREL.
+   We need to emit DTP-relative relocations.  */
+
+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)
+    {
+    case 4:
+      fputs ("\t.long\t", file);
+      break;
+    case 8:
+      fputs ("\t.quad\t", file);
+      break;
     default:
-      abort ();
+      gcc_unreachable ();
     }
   output_addr_const (file, x);
   fputs ("@DTPOFF", file);
 }
 
+#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
+/* Implement TARGET_MANGLE_TYPE.  */
+
+static const char *
+s390_mangle_type (tree type)
+{
+  if (TYPE_MAIN_VARIANT (type) == long_double_type_node
+      && TARGET_LONG_DOUBLE_128)
+    return "g";
+
+  /* For all other types, use normal C++ mangling.  */
+  return NULL;
+}
+#endif
+
 /* 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
@@ -3516,58 +4419,35 @@ 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;
+  HOST_WIDE_INT offset;
+  rtx base;
 
-  /* 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);
+  /* Extract base register and offset.  */
+  if (!s390_decompose_shift_count (op, &base, &offset))
+    gcc_unreachable ();
 
   /* Sanity check.  */
-  if (op && (GET_CODE (op) != REG
-            || REGNO (op) >= FIRST_PSEUDO_REGISTER
-            || REGNO_REG_CLASS (REGNO (op)) != ADDR_REGS))
-    abort ();
+  if (base)
+    {
+      gcc_assert (GET_CODE (base) == REG);
+      gcc_assert (REGNO (base) < FIRST_PSEUDO_REGISTER);
+      gcc_assert (REGNO_REG_CLASS (REGNO (base)) == ADDR_REGS);
+    }
 
-  /* 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)]);
+  /* 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)]);
 }
 
-/* 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;
-
-  abort ();
-}
+/* See 'get_some_local_dynamic_name'.  */
 
 static int
 get_some_local_dynamic_name_1 (rtx *px, void *data ATTRIBUTE_UNUSED)
@@ -3590,6 +4470,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.  */
@@ -3658,9 +4557,9 @@ print_operand_address (FILE *file, rtx addr)
   struct s390_address ad;
 
   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.");
+      || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
+      || (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx))))
+    output_operand_lossage ("cannot decompose address");
 
   if (ad.disp)
     output_addr_const (file, ad.disp);
@@ -3681,17 +4580,22 @@ 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).
     'N': print the second word of a DImode operand.
     'M': print the second word of a TImode operand.
     'Y': print shift count operand.
 
     'b': print integer X as if it's an unsigned byte.
-    'x': print integer X as if it's an unsigned word.
-    'h': print integer X as if it's a signed word.
+    '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)
@@ -3723,18 +4627,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 || REGNO_OK_FOR_BASE_P (REGNO (ad.base)));
+       gcc_assert (!ad.indx);
 
         if (ad.disp)
           output_addr_const (file, ad.disp);
@@ -3746,12 +4655,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 || REGNO_OK_FOR_BASE_P (REGNO (ad.base)));
+       gcc_assert (!ad.indx);
 
         if (ad.base)
           fprintf (file, "%s", reg_names[REGNO (ad.base)]);
@@ -3760,13 +4670,34 @@ print_operand (FILE *file, rtx x, int code)
       }
       return;
 
+    case 'S':
+      {
+       struct s390_address ad;
+       int ret;
+
+        gcc_assert (GET_CODE (x) == MEM);
+       ret = s390_decompose_address (XEXP (x, 0), &ad);
+       gcc_assert (ret);
+       gcc_assert (!ad.base || REGNO_OK_FOR_BASE_P (REGNO (ad.base)));
+       gcc_assert (!ad.indx);
+
+       if (ad.disp)
+         output_addr_const (file, ad.disp);
+       else
+         fprintf (file, "0");
+
+       if (ad.base)
+         fprintf (file, "(%s)", reg_names[REGNO (ad.base)]);
+      }
+      return;
+
     case 'N':
       if (GET_CODE (x) == REG)
        x = gen_rtx_REG (GET_MODE (x), REGNO (x) + 1);
       else if (GET_CODE (x) == MEM)
        x = change_address (x, VOIDmode, plus_constant (XEXP (x, 0), 4));
       else
-        abort ();
+        gcc_unreachable ();
       break;
 
     case 'M':
@@ -3775,7 +4706,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':
@@ -3813,13 +4744,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')
@@ -3827,7 +4765,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:
@@ -3856,7 +4794,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);
@@ -3867,14 +4805,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);
@@ -3882,20 +4820,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;
@@ -3920,20 +4858,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.  */
@@ -3958,41 +4893,6 @@ s390_agen_dep_p (rtx dep_insn, rtx insn)
   return 0;
 }
 
-/* Return the modified cost of the dependency of instruction INSN
-   on instruction DEP_INSN through the link LINK.  COST is the
-   default cost of that dependency.
-
-   Data dependencies are all handled without delay.  However, if a
-   register is modified and subsequently used as base or index
-   register of a memory reference, at least 4 cycles need to pass
-   between setting and using the register to avoid pipeline stalls.
-   An exception is the LA instruction. An address generated by LA can
-   be used by introducing only a one cycle stall on the pipeline.  */
-
-static int
-s390_adjust_cost (rtx insn, rtx link, rtx dep_insn, int cost)
-{
-  /* If the dependence is an anti-dependence, there is no cost.  For an
-     output dependence, there is sometimes a cost, but it doesn't seem
-     worth handling those few cases.  */
-
-  if (REG_NOTE_KIND (link) != 0)
-    return 0;
-
-  /* If we can't recognize the insns, we can't really do anything.  */
-  if (recog_memoized (insn) < 0 || recog_memoized (dep_insn) < 0)
-    return cost;
-
-  /* Operand forward in case of lr, load and la.  */
-  if (s390_tune == PROCESSOR_2084_Z990
-      && cost == 1
-      && (s390_safe_attr_type (dep_insn) == TYPE_LA
-         || s390_safe_attr_type (dep_insn) == TYPE_LR
-         || s390_safe_attr_type (dep_insn) == TYPE_LOAD))
-    return 0;
-  return cost;
-}
-
 /* A C statement (sans semicolon) to update the integer scheduling priority
    INSN_PRIORITY (INSN).  Increase the priority to execute the INSN earlier,
    reduce the priority to execute INSN later.  Do not define this macro if
@@ -4007,16 +4907,18 @@ 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:
+      case TYPE_STM:
        priority = priority << 1;
        break;
       default:
@@ -4030,7 +4932,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;
 }
@@ -4042,119 +4945,33 @@ 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.  */
+/* Annotate every literal pool reference in X by an UNSPEC_LTREF expression.
+   Fix up MEMs as required.  */
 
-static int
-s390_split_branches (void)
+static void
+annotate_constant_pool_refs (rtx *x)
 {
-  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 ());
+  int i, j;
+  const char *fmt;
 
-  /* Find all branches that exceed 64KB, and split them.  */
+  gcc_assert (GET_CODE (*x) != SYMBOL_REF
+             || !CONSTANT_POOL_ADDRESS_P (*x));
 
-  for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+  /* Literal pool references can only occur inside a MEM ...  */
+  if (GET_CODE (*x) == MEM)
     {
-      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;
+      rtx memref = XEXP (*x, 0);
 
-      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 (memref) == SYMBOL_REF
+         && CONSTANT_POOL_ADDRESS_P (memref))
        {
-         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;
+         rtx base = cfun->machine->base_reg;
+         rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, memref, base),
+                                    UNSPEC_LTREF);
 
-      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->machine->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.  */
-
-static void
-annotate_constant_pool_refs (rtx *x)
-{
-  int i, j;
-  const char *fmt;
-
-  if (GET_CODE (*x) == SYMBOL_REF
-      && CONSTANT_POOL_ADDRESS_P (*x))
-    abort ();
-
-  /* Literal pool references can only occur inside a MEM ...  */
-  if (GET_CODE (*x) == MEM)
-    {
-      rtx memref = XEXP (*x, 0);
-
-      if (GET_CODE (memref) == SYMBOL_REF
-         && CONSTANT_POOL_ADDRESS_P (memref))
-       {
-         rtx base = cfun->machine->base_reg;
-         rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, memref, base),
-                                    UNSPEC_LTREF);
-
-         *x = replace_equiv_address (*x, addr);
-         return;
-       }
+         *x = replace_equiv_address (*x, addr);
+         return;
+       }
 
       if (GET_CODE (memref) == CONST
          && GET_CODE (XEXP (memref, 0)) == PLUS
@@ -4231,6 +5048,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 
@@ -4255,21 +5157,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;
     }
@@ -4298,8 +5198,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
@@ -4405,12 +5304,12 @@ replace_ltrel_base (rtx *x)
 /* We keep a list of constants which we have to add to internal
    constant tables in the middle of large functions.  */
 
-#define NR_C_MODES 7
+#define NR_C_MODES 11
 enum machine_mode constant_modes[NR_C_MODES] =
 {
-  TImode,
-  DFmode, DImode,
-  SFmode, SImode,
+  TFmode, TImode, TDmode,
+  DFmode, DImode, DDmode,
+  SFmode, SImode, SDmode,
   HImode,
   QImode
 };
@@ -4430,27 +5329,33 @@ struct constant_pool
   bitmap insns;
 
   struct constant *constants[NR_C_MODES];
+  struct constant *execute;
   rtx label;
   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 *);
+/* 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;
 
-static struct constant_pool * s390_chunkify_start (void);
-static void s390_chunkify_finish (struct constant_pool *);
-static void s390_chunkify_cancel (struct constant_pool *);
+  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;
 
-static struct constant_pool *s390_start_pool (struct constant_pool **, rtx);
-static void s390_end_pool (struct constant_pool *, rtx);
-static void s390_add_pool_insn (struct constant_pool *, rtx);
-static struct constant_pool *s390_find_pool (struct constant_pool *, rtx);
-static void s390_add_constant (struct constant_pool *, rtx, enum machine_mode);
-static rtx s390_find_constant (struct constant_pool *, rtx, enum machine_mode);
-static rtx s390_dump_pool (struct constant_pool *, bool);
-static struct constant_pool *s390_alloc_pool (void);
-static void s390_free_pool (struct constant_pool *);
+  return pool;
+}
 
 /* Create new constant pool covering instructions starting at INSN
    and chain it to the end of POOL_LIST.  */
@@ -4518,8 +5423,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))
@@ -4551,15 +5455,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));
@@ -4567,24 +5469,126 @@ 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
+s390_add_execute (struct constant_pool *pool, rtx insn)
+{
+  struct constant *c;
+
+  for (c = pool->execute; c != NULL; c = c->next)
+    if (INSN_UID (insn) == INSN_UID (c->value))
+      break;
+
+  if (c == NULL)
+    {
+      c = (struct constant *) xmalloc (sizeof *c);
+      c->value = insn;
+      c->label = gen_label_rtx ();
+      c->next = pool->execute;
+      pool->execute = c;
+      pool->size += 6;
+    }
+}
+
+/* Find execute target for INSN in the constant pool POOL.
+   Return an RTX describing the distance from the start of
+   the pool to the location of the execute target.  */
+
+static rtx
+s390_find_execute (struct constant_pool *pool, rtx insn)
+{
+  struct constant *c;
+  rtx offset;
+
+  for (c = pool->execute; c != NULL; c = c->next)
+    if (INSN_UID (insn) == INSN_UID (c->value))
+      break;
+
+  gcc_assert (c);
+
+  offset = gen_rtx_MINUS (Pmode, gen_rtx_LABEL_REF (Pmode, c->label),
+                                gen_rtx_LABEL_REF (Pmode, pool->label));
+  offset = gen_rtx_CONST (Pmode, offset);
+  return offset;
+}
+
+/* For an execute INSN, extract the execute target template.  */
+
+static rtx
+s390_execute_target (rtx insn)
+{
+  rtx pattern = PATTERN (insn);
+  gcc_assert (s390_execute_label (insn));
+
+  if (XVECLEN (pattern, 0) == 2)
+    {
+      pattern = copy_rtx (XVECEXP (pattern, 0, 1));
+    }
+  else
+    {
+      rtvec vec = rtvec_alloc (XVECLEN (pattern, 0) - 1);
+      int i;
+
+      for (i = 0; i < XVECLEN (pattern, 0) - 1; i++)
+       RTVEC_ELT (vec, i) = copy_rtx (XVECEXP (pattern, 0, i + 1));
+
+      pattern = gen_rtx_PARALLEL (VOIDmode, vec);
+    }
+
+  return pattern;
+}
+
+/* Indicate that INSN cannot be duplicated.  This is the case for
+   execute insns that carry a unique label.  */
+
+static bool
+s390_cannot_copy_insn_p (rtx insn)
+{
+  rtx label = s390_execute_label (insn);
+  return label && label != const0_rtx;
+}
+
 /* Dump out the constants in POOL.  If REMOTE_LABEL is true,
    do not emit the pool base label.  */
 
-static rtx
+static void
 s390_dump_pool (struct constant_pool *pool, bool remote_label)
 {
   struct constant *c;
-  rtx insn;
+  rtx insn = pool->pool_insn;
   int i;
 
-  /* Pool start insn switches to proper section
-     and guarantees necessary alignment.  */
+  /* Switch to rodata section.  */
+  if (TARGET_CPU_ZARCH)
+    {
+      insn = emit_insn_after (gen_pool_section_start (), insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+    }
+
+  /* Ensure minimum pool alignment.  */
   if (TARGET_CPU_ZARCH)
-    insn = emit_insn_after (gen_pool_start_64 (), pool->pool_insn);
+    insn = emit_insn_after (gen_pool_align (GEN_INT (8)), insn);
   else
-    insn = emit_insn_after (gen_pool_start_31 (), pool->pool_insn);
+    insn = emit_insn_after (gen_pool_align (GEN_INT (4)), insn);
   INSN_ADDRESSES_NEW (insn, -1);
 
+  /* Emit pool base label.  */
   if (!remote_label)
     {
       insn = emit_label_after (pool->label, insn);
@@ -4618,64 +5622,56 @@ s390_dump_pool (struct constant_pool *pool, bool remote_label)
        INSN_ADDRESSES_NEW (insn, -1);
       }
 
-  /* Pool end insn switches back to previous section
-     and guarantees necessary alignment.  */
-  if (TARGET_CPU_ZARCH)
-    insn = emit_insn_after (gen_pool_end_64 (), insn);
-  else
-    insn = emit_insn_after (gen_pool_end_31 (), insn);
+  /* Ensure minimum alignment for instructions.  */
+  insn = emit_insn_after (gen_pool_align (GEN_INT (2)), insn);
   INSN_ADDRESSES_NEW (insn, -1);
 
+  /* Output in-pool execute template insns.  */
+  for (c = pool->execute; c; c = c->next)
+    {
+      insn = emit_label_after (c->label, insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+
+      insn = emit_insn_after (s390_execute_target (c->value), insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+    }
+
+  /* Switch back to previous section.  */
+  if (TARGET_CPU_ZARCH)
+    {
+      insn = emit_insn_after (gen_pool_section_end (), insn);
+      INSN_ADDRESSES_NEW (insn, -1);
+    }
+
   insn = emit_barrier_after (insn);
   INSN_ADDRESSES_NEW (insn, -1);
 
   /* Remove placeholder insn.  */
   remove_insn (pool->pool_insn);
-
-  return insn;
 }
 
-/* Allocate new constant_pool structure.  */
+/* Free all memory used by POOL.  */
 
-static struct constant_pool *
-s390_alloc_pool (void)
+static void
+s390_free_pool (struct constant_pool *pool)
 {
-  struct constant_pool *pool;
+  struct constant *c, *next;
   int i;
 
-  pool = (struct constant_pool *) xmalloc (sizeof *pool);
-  pool->next = NULL;
   for (i = 0; i < NR_C_MODES; i++)
-    pool->constants[i] = NULL;
-
-  pool->label = gen_label_rtx ();
-  pool->first_insn = NULL_RTX;
-  pool->pool_insn = NULL_RTX;
-  pool->insns = BITMAP_XMALLOC ();
-  pool->size = 0;
+    for (c = pool->constants[i]; c; c = next)
+      {
+       next = c->next;
+       free (c);
+      }
 
-  return pool;
-}
-
-/* Free all memory used by POOL.  */
-
-static void
-s390_free_pool (struct constant_pool *pool)
-{
-  int i;
-
-  for (i = 0; i < NR_C_MODES; i++)
+  for (c = pool->execute; c; c = next)
     {
-      struct constant *c = pool->constants[i];
-      while (c != NULL)
-       {
-         struct constant *next = c->next;
-         free (c);
-         c = next;
-       }
+      next = c->next;
+      free (c);
     }
 
-  BITMAP_XFREE (pool->insns);
+  BITMAP_FREE (pool->insns);
   free (pool);
 }
 
@@ -4697,12 +5693,15 @@ 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 (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+      if (!TARGET_CPU_ZARCH && s390_execute_label (insn))
+       {
+         s390_add_execute (pool, insn);
+       }
+      else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
        {
          rtx pool_ref = NULL_RTX;
          find_constant_pool_ref (PATTERN (insn), &pool_ref);
@@ -4715,8 +5714,7 @@ s390_mainpool_start (void)
        }
     }
 
-  if (!pool->pool_insn)
-    abort ();
+  gcc_assert (pool->pool_insn || pool->size == 0);
 
   if (pool->size >= 4096)
     {
@@ -4738,13 +5736,17 @@ s390_mainpool_start (void)
 static void
 s390_mainpool_finish (struct constant_pool *pool)
 {
-  rtx base_reg = SET_DEST (PATTERN (pool->pool_insn));
+  rtx base_reg = cfun->machine->base_reg;
   rtx insn;
 
   /* If the pool is empty, we're done.  */
   if (pool->size == 0)
     {
-      remove_insn (pool->pool_insn);
+      /* We don't actually need a base register after all.  */
+      cfun->machine->base_reg = NULL_RTX;
+
+      if (pool->pool_insn)
+       remove_insn (pool->pool_insn);
       s390_free_pool (pool);
       return;
     }
@@ -4826,8 +5828,12 @@ s390_mainpool_finish (struct constant_pool *pool)
           find_constant_pool_ref (PATTERN (insn), &pool_ref);
           if (pool_ref)
             {
-              addr = s390_find_constant (pool, get_pool_constant (pool_ref),
-                                               get_pool_mode (pool_ref));
+             if (s390_execute_label (insn))
+               addr = s390_find_execute (pool, insn);
+             else
+               addr = s390_find_constant (pool, get_pool_constant (pool_ref),
+                                                get_pool_mode (pool_ref));
+
               replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
               INSN_CODE (insn) = -1;
             }
@@ -4883,14 +5889,20 @@ 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 (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+      if (!TARGET_CPU_ZARCH && s390_execute_label (insn))
+       {
+         if (!curr_pool)
+           curr_pool = s390_start_pool (&pool_list, insn);
+
+         s390_add_execute (curr_pool, insn);
+         s390_add_pool_insn (curr_pool, insn);
+       }
+      else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
        {
          rtx pool_ref = NULL_RTX;
          find_constant_pool_ref (PATTERN (insn), &pool_ref);
@@ -4911,8 +5923,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;
                }
            }
@@ -4923,8 +5934,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
@@ -5002,14 +6012,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))
     {
@@ -5106,7 +6114,7 @@ s390_chunkify_start (void)
       }
 
 
-  BITMAP_XFREE (far_labels);
+  BITMAP_FREE (far_labels);
 
 
   /* Recompute insn addresses.  */
@@ -5145,8 +6153,13 @@ s390_chunkify_finish (struct constant_pool *pool_list)
           find_constant_pool_ref (PATTERN (insn), &pool_ref);
           if (pool_ref)
             {
-              addr = s390_find_constant (curr_pool, get_pool_constant (pool_ref),
-                                                    get_pool_mode (pool_ref));
+             if (s390_execute_label (insn))
+               addr = s390_find_execute (curr_pool, insn);
+             else
+               addr = s390_find_constant (curr_pool,
+                                          get_pool_constant (pool_ref),
+                                          get_pool_mode (pool_ref));
+
               replace_constant_pool_ref (&PATTERN (insn), pool_ref, addr);
               INSN_CODE (insn) = -1;
             }
@@ -5239,8 +6252,8 @@ s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align)
   switch (GET_MODE_CLASS (mode))
     {
     case MODE_FLOAT:
-      if (GET_CODE (exp) != CONST_DOUBLE)
-       abort ();
+    case MODE_DECIMAL_FLOAT:
+      gcc_assert (GET_CODE (exp) == CONST_DOUBLE);
 
       REAL_VALUE_FROM_CONST_DOUBLE (r, exp);
       assemble_real (r, mode, align);
@@ -5251,395 +6264,715 @@ s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align)
       break;
 
     default:
-      abort ();
+      gcc_unreachable ();
     }
 }
 
 
-/* Rework the prolog/epilog to avoid saving/restoring
-   registers unnecessarily.  BASE_USED specifies whether
-   the literal pool base register needs to be saved.  */
+/* 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.  */
 
-static void
-s390_optimize_prolog (bool base_used)
+rtx
+s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED)
 {
-  rtx insn, new_insn, next_insn;
-
-  /* Do a final recompute of the frame-related data.  */
-
-  s390_frame_info (base_used, cfun->machine->save_return_addr_p);
-  regs_ever_live[BASE_REGNUM] = base_used;
-  regs_ever_live[RETURN_REGNUM] = cfun->machine->save_return_addr_p;
-  regs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
+  int offset;
+  rtx addr;
 
-  /* If all special registers are in fact used, there's nothing we
-     can do, so no point in walking the insn list.  */
+  /* Without backchain, we fail for all but the current frame.  */
 
-  if (cfun->machine->first_save_gpr <= BASE_REGNUM 
-      && cfun->machine->last_save_gpr >= BASE_REGNUM
-      && (TARGET_CPU_ZARCH 
-          || (cfun->machine->first_save_gpr <= RETURN_REGNUM 
-              && cfun->machine->last_save_gpr >= RETURN_REGNUM)))
-    return;
+  if (!TARGET_BACKCHAIN && count > 0)
+    return NULL_RTX;
 
-  /* Search for prolog/epilog insns and replace them.  */
+  /* For the current frame, we need to make sure the initial
+     value of RETURN_REGNUM is actually saved.  */
 
-  for (insn = get_insns (); insn; insn = next_insn)
+  if (count == 0)
     {
-      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))
+      /* On non-z architectures branch splitting could overwrite r14.  */
+      if (TARGET_CPU_ZARCH)
+       return get_hard_reg_initial_val (Pmode, RETURN_REGNUM);
+      else
        {
-         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) - first * UNITS_PER_WORD;
+         cfun_frame_layout.save_return_addr_p = true;
+         return gen_rtx_MEM (Pmode, return_address_pointer_rtx);
+       }
+    }
 
-         if (GET_CODE (base) != REG || off < 0)
-           continue;
-         if (first > BASE_REGNUM || last < BASE_REGNUM)
-           continue;
+  if (TARGET_PACKED_STACK)
+    offset = -2 * UNITS_PER_WORD;
+  else
+    offset = RETURN_REGNUM * UNITS_PER_WORD;
 
-         if (cfun->machine->first_save_gpr != -1)
-           {
-             new_insn = save_gprs (base, off, cfun->machine->first_save_gpr,
-                                   cfun->machine->last_save_gpr);
-             new_insn = emit_insn_before (new_insn, insn);
-             INSN_ADDRESSES_NEW (new_insn, -1);
-           }
+  addr = plus_constant (frame, offset);
+  addr = memory_address (Pmode, addr);
+  return gen_rtx_MEM (Pmode, addr);
+}
 
-         remove_insn (insn);
-         continue;
-       }
+/* Return an RTL expression representing the back chain stored in
+   the current stack frame.  */
 
-      if (GET_CODE (PATTERN (insn)) == SET
-         && GET_CODE (SET_SRC (PATTERN (insn))) == REG
-         && REGNO (SET_SRC (PATTERN (insn))) == BASE_REGNUM
-         && GET_CODE (SET_DEST (PATTERN (insn))) == MEM)
-       {
-         set = PATTERN (insn);
-         offset = const0_rtx;
-         base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
-         off = INTVAL (offset) - BASE_REGNUM * UNITS_PER_WORD;
+rtx
+s390_back_chain_rtx (void)
+{
+  rtx chain;
 
-         if (GET_CODE (base) != REG || off < 0)
-           continue;
+  gcc_assert (TARGET_BACKCHAIN);
 
-         if (cfun->machine->first_save_gpr != -1)
-           {
-             new_insn = save_gprs (base, off, cfun->machine->first_save_gpr,
-                                   cfun->machine->last_save_gpr);
-             new_insn = emit_insn_before (new_insn, insn);
-             INSN_ADDRESSES_NEW (new_insn, -1);
-           }
+  if (TARGET_PACKED_STACK)
+    chain = plus_constant (stack_pointer_rtx,
+                          STACK_POINTER_OFFSET - UNITS_PER_WORD);
+  else
+    chain = stack_pointer_rtx;
 
-         remove_insn (insn);
-         continue;
-       }
+  chain = gen_rtx_MEM (Pmode, chain);
+  return chain;
+}
 
-      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) - first * UNITS_PER_WORD;
+/* 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.  */
 
-         if (GET_CODE (base) != REG || off < 0)
-           continue;
-         if (first > BASE_REGNUM || last < BASE_REGNUM)
-           continue;
+static int
+find_unused_clobbered_reg (void)
+{
+  int i;
+  for (i = 0; i < 6; i++)
+    if (!df_regs_ever_live_p (i))
+      return i;
+  return 0;
+}
 
-         if (cfun->machine->first_restore_gpr != -1)
-           {
-             new_insn = restore_gprs (base, off, cfun->machine->first_restore_gpr,
-                                      cfun->machine->last_restore_gpr);
-             new_insn = emit_insn_before (new_insn, insn);
-             INSN_ADDRESSES_NEW (new_insn, -1);
-           }
 
-         remove_insn (insn);
-         continue;
-       }
+/* Helper function for s390_regs_ever_clobbered.  Sets the fields in DATA for all 
+   clobbered hard regs in SETREG.  */
 
-      if (GET_CODE (PATTERN (insn)) == SET
-         && GET_CODE (SET_DEST (PATTERN (insn))) == REG
-         && REGNO (SET_DEST (PATTERN (insn))) == BASE_REGNUM
-         && GET_CODE (SET_SRC (PATTERN (insn))) == MEM)
-       {
-         set = PATTERN (insn);
-         offset = const0_rtx;
-         base = eliminate_constant_term (XEXP (SET_SRC (set), 0), &offset);
-         off = INTVAL (offset) - BASE_REGNUM * UNITS_PER_WORD;
+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 (base) != REG || off < 0)
-           continue;
+  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;
 
-         if (cfun->machine->first_restore_gpr != -1)
-           {
-             new_insn = restore_gprs (base, off, cfun->machine->first_restore_gpr,
-                                      cfun->machine->last_restore_gpr);
-             new_insn = emit_insn_before (new_insn, insn);
-             INSN_ADDRESSES_NEW (new_insn, -1);
-           }
+  for (i = regno;
+       i < regno + HARD_REGNO_NREGS (regno, mode);
+       i++)
+    regs_ever_clobbered[i] = 1;
+}
 
-         remove_insn (insn);
-         continue;
+/* 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 
+             && df_regs_ever_live_p (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);
        }
     }
 }
 
-/* Perform machine-dependent processing.  */
+/* 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_reorg (void)
+s390_frame_area (int *area_bottom, int *area_top)
 {
-  bool base_used = false;
-  bool pool_overflow = false;
-
-  /* Make sure all splits have been performed; splits after
-     machine_dependent_reorg might confuse insn length counts.  */
-  split_all_insns_noflow ();
-
-
-  /* Install the main literal pool and the associated base
-     register load insns.
-
-     In addition, there are two problematic situations we need
-     to correct:
+  int b, t;
+  int i;
 
-     - the literal pool might be > 4096 bytes in size, so that
-       some of its elements cannot be directly accessed
+  b = INT_MAX;
+  t = INT_MIN;
 
-     - a branch target might be > 64K away from the branch, so that
-       it is not possible to use a PC-relative instruction.
+  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;
+    }
 
-     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.
+  if (TARGET_64BIT && cfun_save_high_fprs_p)
+    {
+      b = MIN (b, cfun_frame_layout.f8_offset);
+      t = MAX (t, (cfun_frame_layout.f8_offset
+                  + cfun_frame_layout.high_fprs * 8));
+    }
 
-     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.
+  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;
+}
 
-     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.)  */
+/* Fill cfun->machine with info about register usage of current function.
+   Return in CLOBBERED_REGS which GPRs are currently considered set.  */
 
-  for (;;)
-    {
-      struct constant_pool *pool = NULL;
+static void
+s390_register_info (int clobbered_regs[])
+{
+  int i, j;
 
-      /* Collect the literal pool.  */
-      if (!pool_overflow)
+  /* 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 (df_regs_ever_live_p (i) && !global_regs[i])
        {
-         pool = s390_mainpool_start ();
-         if (!pool)
-           pool_overflow = true;
+         cfun_set_fpr_bit (i - 16);
+         cfun_frame_layout.high_fprs++;
        }
 
-      /* If literal pool overflowed, start to chunkify it.  */
-      if (pool_overflow)
-        pool = s390_chunkify_start ();
+  /* Find first and last gpr to be saved.  We trust regs_ever_live
+     data, except that we don't save and restore global registers.
 
-      /* 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);
+     Also, all registers with special meaning to the compiler need
+     to be handled extra.  */
 
-          continue;
-        }
+  s390_regs_ever_clobbered (clobbered_regs);
 
-      /* If we made it up to here, both conditions are satisfied.
-        Finish up literal pool related changes.  */
-      if ((pool_overflow || pool->size > 0)
-          && REGNO (cfun->machine->base_reg) == BASE_REGNUM)
-       base_used = true;
+  for (i = 0; i < 16; i++)
+    clobbered_regs[i] = clobbered_regs[i] && !global_regs[i] && !fixed_regs[i];
 
-      if (pool_overflow)
-       s390_chunkify_finish (pool);
-      else
-       s390_mainpool_finish (pool);
+  if (frame_pointer_needed)
+    clobbered_regs[HARD_FRAME_POINTER_REGNUM] = 1;
+
+  if (flag_pic)
+    clobbered_regs[PIC_OFFSET_TABLE_REGNUM] 
+      |= df_regs_ever_live_p (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);
 
+  for (i = 6; i < 16; i++)
+    if (df_regs_ever_live_p (i) || clobbered_regs[i])
+      break;
+  for (j = 15; j > i; j--)
+    if (df_regs_ever_live_p (j) || clobbered_regs[j])
       break;
-    }
 
-  s390_optimize_prolog (base_used);
-}
+  if (i == 16)
+    {
+      /* 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;
 
+      for (i = cfun_frame_layout.first_save_gpr_slot; 
+          i < cfun_frame_layout.last_save_gpr_slot + 1; 
+          i++)
+       if (clobbered_regs[i])
+         break;
 
-/* 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.  */
+      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;
+       }
+    }
 
-rtx
-s390_return_addr_rtx (int count, rtx frame)
+  if (current_function_stdarg)
+    {
+      /* 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;
+
+         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;
+           }
+
+         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;
+           }
+       }
+
+      /* 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 (df_regs_ever_live_p (i + 16) && !global_regs[i + 16])
+       cfun_set_fpr_bit (i);
+}
+
+/* Fill cfun->machine with info about frame of current function.  */
+
+static void
+s390_frame_info (void)
 {
-  rtx addr;
+  int i;
 
-  /* Without backchain, we fail for all but the current frame.  */
+  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)
+    {
+      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)
+       {
+         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 (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 (!TARGET_BACKCHAIN && count > 0)
-    return NULL_RTX;
+  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;
 
-  /* For the current frame, we need to make sure the initial
-     value of RETURN_REGNUM is actually saved.  */
+      /* 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 (count == 0)
-    cfun->machine->save_return_addr_p = true;
+      cfun_frame_layout.frame_size += cfun_frame_layout.high_fprs * 8;
 
-  /* To retrieve the return address we read the stack slot where the
-     corresponding RETURN_REGNUM value was saved.  */
+      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));
 
-  addr = plus_constant (frame, RETURN_REGNUM * UNITS_PER_WORD);
-  addr = memory_address (Pmode, addr);
-  return gen_rtx_MEM (Pmode, addr);
+      cfun_frame_layout.frame_size += current_function_outgoing_args_size;
+    }
 }
 
-/* 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.  */
+/* 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.  */
 
-static int
-find_unused_clobbered_reg (void)
+static void
+s390_init_frame_layout (void)
 {
-  int i;
-  for (i = 0; i < 6; i++)
-    if (!regs_ever_live[i])
-      return i;
-  return 0;
+  HOST_WIDE_INT frame_size;
+  int base_used;
+  int clobbered_regs[16];
+
+  /* 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;
+
+  do
+    {
+      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_K (frame_size));
+
+      /* 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 && !df_regs_ever_live_p (5))
+       cfun->machine->base_reg = gen_rtx_REG (Pmode, 5);
+      else
+       cfun->machine->base_reg = gen_rtx_REG (Pmode, BASE_REGNUM);
+
+      s390_register_info (clobbered_regs);
+      s390_frame_info ();
+    }
+  while (frame_size != cfun_frame_layout.frame_size);
 }
 
-/* Fill cfun->machine with info about frame of current function.  
-   BASE_USED and RETURN_ADDR_USED specify whether we assume the
-   base and return address register will need to be saved.  */
+/* 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_frame_info (int base_used, int return_addr_used)
+s390_update_frame_layout (void)
 {
-  int live_regs[16];
-  int i, j;
-  HOST_WIDE_INT fsize = get_frame_size ();
+  int clobbered_regs[16];
 
-  if (!TARGET_64BIT && fsize > 0x7fff0000)
-    fatal_error ("Total size of local variables exceeds architecture limit.");
+  s390_register_info (clobbered_regs);
 
-  /* fprs 8 - 15 are caller saved for 64 Bit ABI.  */
-  cfun->machine->save_fprs_p = 0;
-  if (TARGET_64BIT)
-    for (i = 24; i < 32; i++)
-      if (regs_ever_live[i] && !global_regs[i])
-       {
-          cfun->machine->save_fprs_p = 1;
-         break;
-       }
+  df_set_regs_ever_live (BASE_REGNUM, 
+                        clobbered_regs[BASE_REGNUM] ? true : false);
+  df_set_regs_ever_live (RETURN_REGNUM, 
+                        clobbered_regs[RETURN_REGNUM] ? true : false);
+  df_set_regs_ever_live (STACK_POINTER_REGNUM, 
+                        clobbered_regs[STACK_POINTER_REGNUM] ? true : false);
 
-  cfun->machine->frame_size = fsize + cfun->machine->save_fprs_p * 64;
+  if (cfun->machine->base_reg)
+    df_set_regs_ever_live (REGNO (cfun->machine->base_reg), true);
+}
 
-  /* Does function need to setup frame and save area.  */
+/* Return true if it is legal to put a value with MODE into REGNO.  */
 
-  if (!current_function_is_leaf
-      || TARGET_TPF_PROFILING
-      || cfun->machine->frame_size > 0
-      || current_function_calls_alloca
-      || current_function_stdarg)
-    cfun->machine->frame_size += STARTING_FRAME_OFFSET;
+bool
+s390_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
+{
+  switch (REGNO_REG_CLASS (regno))
+    {
+    case FP_REGS:
+      if (REGNO_PAIR_OK (regno, mode))
+       {
+         if (mode == SImode || mode == DImode)
+           return true;
 
-  /* Find first and last gpr to be saved.  We trust regs_ever_live
-     data, except that we don't save and restore global registers.
+         if (FLOAT_MODE_P (mode) && GET_MODE_CLASS (mode) != MODE_VECTOR_FLOAT)
+           return true;
+       }
+      break;
+    case ADDR_REGS:
+      if (FRAME_REGNO_P (regno) && mode == Pmode)
+       return true;
 
-     Also, all registers with special meaning to the compiler need
-     to be handled extra.  */
+      /* fallthrough */
+    case GENERAL_REGS:
+      if (REGNO_PAIR_OK (regno, mode))
+       {
+         if (TARGET_64BIT 
+             || (mode != TFmode && mode != TCmode && mode != TDmode))
+           return true;
+       }         
+      break;
+    case CC_REGS:
+      if (GET_MODE_CLASS (mode) == MODE_CC)
+       return true;
+      break;
+    case ACCESS_REGS:
+      if (REGNO_PAIR_OK (regno, mode))
+       {
+         if (mode == SImode || mode == Pmode)
+           return true;
+       }
+      break;
+    default:
+      return false;
+    }
+  
+  return false;
+}
 
-  for (i = 0; i < 16; i++)
-    live_regs[i] = regs_ever_live[i] && !global_regs[i];
+/* Return nonzero if register OLD_REG can be renamed to register NEW_REG.  */
 
-  if (flag_pic)
-    live_regs[PIC_OFFSET_TABLE_REGNUM] = 
-    regs_ever_live[PIC_OFFSET_TABLE_REGNUM];
+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;
 
-  live_regs[BASE_REGNUM] = base_used;
-  live_regs[RETURN_REGNUM] = return_addr_used;
-  live_regs[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
+  return true;
+}
 
-  for (i = 6; i < 16; i++)
-    if (live_regs[i])
-      break;
-  for (j = 15; j > i; j--)
-    if (live_regs[j])
-      break;
+/* Maximum number of registers to represent a value of mode MODE
+   in a register of class CLASS.  */
 
-  if (i == 16)
-    {
-      /* Nothing to save/restore.  */
-      cfun->machine->first_save_gpr = -1;
-      cfun->machine->first_restore_gpr = -1;
-      cfun->machine->last_save_gpr = -1;
-      cfun->machine->last_restore_gpr = -1;
-    }
-  else
+bool
+s390_class_max_nregs (enum reg_class class, enum machine_mode mode)
+{
+  switch (class)
     {
-      /* Save / Restore from gpr i to j.  */
-      cfun->machine->first_save_gpr = i;
-      cfun->machine->first_restore_gpr = i;
-      cfun->machine->last_save_gpr = j;
-      cfun->machine->last_restore_gpr = j;
+    case FP_REGS:
+      if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
+       return 2 * ((GET_MODE_SIZE (mode) / 2 + 8 - 1) / 8);
+      else
+       return (GET_MODE_SIZE (mode) + 8 - 1) / 8;
+    case ACCESS_REGS:
+      return (GET_MODE_SIZE (mode) + 4 - 1) / 4;
+    default:
+      break;
     }
+  return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+}
 
-  /* Varargs functions need to save gprs 2 to 6.  */
-  if (current_function_stdarg)
+/* Return true if register FROM can be eliminated via register TO.  */
+
+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)
     {
-      if (cfun->machine->first_save_gpr == -1
-          || cfun->machine->first_save_gpr > 2)
-        cfun->machine->first_save_gpr = 2;
+      if (TARGET_CPU_ZARCH)
+       {
+         s390_init_frame_layout ();
+         return cfun->machine->base_reg == NULL_RTX;
+       }
 
-      if (cfun->machine->last_save_gpr == -1
-          || cfun->machine->last_save_gpr < 6)
-        cfun->machine->last_save_gpr = 6;
+      return false;
     }
+
+  /* Everything else must point into the stack frame.  */
+  gcc_assert (to == STACK_POINTER_REGNUM
+             || to == HARD_FRAME_POINTER_REGNUM);
+
+  gcc_assert (from == FRAME_POINTER_REGNUM
+             || from == ARG_POINTER_REGNUM
+             || from == RETURN_ADDRESS_POINTER_REGNUM);
+
+  /* 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;
+
+  return true;
 }
 
-/* Return offset between argument pointer and frame pointer
-   initially after prologue.  */
+/* Return offset between register FROM and TO initially after prolog.  */
 
 HOST_WIDE_INT
-s390_arg_frame_offset (void)
+s390_initial_elimination_offset (int from, int to)
 {
-  /* See the comment in s390_emit_prologue about the assumptions we make
-     whether or not the base and return address register need to be saved.  */
-  int return_addr_used = !current_function_is_leaf
-                        || TARGET_TPF_PROFILING
-                        || regs_ever_live[RETURN_REGNUM]
-                        || cfun->machine->save_return_addr_p;
+  HOST_WIDE_INT offset;
+  int index;
+
+  /* ??? Why are we called for non-eliminable pairs?  */
+  if (!s390_can_eliminate (from, to))
+    return 0;
+
+  switch (from)
+    {
+    case FRAME_POINTER_REGNUM:
+      offset = (get_frame_size() 
+               + STACK_POINTER_OFFSET
+               + current_function_outgoing_args_size);
+      break;
+
+    case ARG_POINTER_REGNUM:
+      s390_init_frame_layout ();
+      offset = cfun_frame_layout.frame_size + STACK_POINTER_OFFSET;
+      break;
+
+    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;
+
+    case BASE_REGNUM:
+      offset = 0;
+      break;
+
+    default:
+      gcc_unreachable ();
+    }
 
-  s390_frame_info (1, !TARGET_CPU_ZARCH || return_addr_used);
-  return cfun->machine->frame_size + STACK_POINTER_OFFSET;
+  return offset;
 }
 
 /* Emit insn to save fpr REGNUM at offset OFFSET relative
@@ -5650,7 +6983,11 @@ 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);
+
+  if (regnum >= 16 && regnum <= (16 + FP_ARG_NUM_REG))
+    set_mem_alias_set (addr, get_varargs_alias_set ());
+  else
+    set_mem_alias_set (addr, get_frame_alias_set ());
 
   return emit_move_insn (addr, gen_rtx_REG (DFmode, regnum));
 }
@@ -5663,7 +7000,7 @@ 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);
+  set_mem_alias_set (addr, get_frame_alias_set ());
 
   return emit_move_insn (gen_rtx_REG (DFmode, regnum), addr);
 }
@@ -5678,9 +7015,10 @@ save_gprs (rtx base, int offset, int first, int last)
   rtx addr, insn, note;
   int i;
 
-  addr = plus_constant (base, offset + first * UNITS_PER_WORD);
+  addr = plus_constant (base, offset);
   addr = gen_rtx_MEM (Pmode, addr);
-  set_mem_alias_set (addr, s390_sr_alias_set);
+
+  set_mem_alias_set (addr, get_frame_alias_set ());
 
   /* Special-case single register.  */
   if (first == last)
@@ -5699,6 +7037,14 @@ save_gprs (rtx base, int offset, int first, int last)
                             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.
@@ -5706,7 +7052,7 @@ save_gprs (rtx base, int offset, int first, int last)
      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 the
+     ??? 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
@@ -5724,7 +7070,7 @@ save_gprs (rtx base, int offset, int first, int last)
     }
   else if (last >= 6)
     {
-      addr = plus_constant (base, offset + 6 * UNITS_PER_WORD);
+      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));
@@ -5753,9 +7099,9 @@ restore_gprs (rtx base, int offset, int first, int last)
 {
   rtx addr, insn;
 
-  addr = plus_constant (base, offset + first * UNITS_PER_WORD);
+  addr = plus_constant (base, offset);
   addr = gen_rtx_MEM (Pmode, addr);
-  set_mem_alias_set (addr, s390_sr_alias_set);
+  set_mem_alias_set (addr, get_frame_alias_set ());
 
   /* Special-case single register.  */
   if (first == last)
@@ -5825,41 +7171,12 @@ s390_emit_prologue (void)
   rtx insn, addr;
   rtx temp_reg;
   int i;
+  int offset;
+  int next_fpr = 0;
 
-  /* At this point, we decide whether we'll need to save/restore the
-     return address register.  This decision is final on zSeries machines;
-     on S/390 it can still be overridden in s390_split_branches.  */
-
-  if (!current_function_is_leaf
-      || TARGET_TPF_PROFILING
-      || regs_ever_live[RETURN_REGNUM])
-    cfun->machine->save_return_addr_p = 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 (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);
-
-  regs_ever_live[REGNO (cfun->machine->base_reg)] = 1;
-
-  /* Compute frame info.  Note that at this point, we assume the base 
-     register and -on S/390- the return register always need to be saved.
-     This is done because the usage of these registers might change even 
-     after the prolog was emitted.  If it turns out later that we really 
-     don't need them, the prolog/epilog code is modified again.  */
-
-  s390_frame_info (1, !TARGET_CPU_ZARCH || cfun->machine->save_return_addr_p);
-
-  /* We need to update regs_ever_live to avoid data-flow problems.  */
+  /* Complete frame layout.  */
 
-  regs_ever_live[BASE_REGNUM] = 1;
-  regs_ever_live[RETURN_REGNUM] = !TARGET_CPU_ZARCH 
-                                 || cfun->machine->save_return_addr_p;
-  regs_ever_live[STACK_POINTER_REGNUM] = cfun->machine->frame_size > 0;
+  s390_update_frame_layout ();
 
   /* Annotate all constant pool references to let the scheduler know
      they implicitly use the base register.  */
@@ -5868,70 +7185,169 @@ s390_emit_prologue (void)
 
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     if (INSN_P (insn))
-      annotate_constant_pool_refs (&PATTERN (insn));
+      {
+       annotate_constant_pool_refs (&PATTERN (insn));
+       df_insn_rescan (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 (!current_function_is_leaf
+  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.  */
-
-  insn = save_gprs (stack_pointer_rtx, 0,
-                   cfun->machine->first_save_gpr, cfun->machine->last_save_gpr);
-  emit_insn (insn);
+  if (cfun_frame_layout.first_save_gpr != -1)
+    {
+      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);
+    }
 
   /* Dummy insn to mark literal pool slot.  */
 
-  emit_insn (gen_main_pool (cfun->machine->base_reg));
-
-  /* Save fprs for variable args.  */
+  if (cfun->machine->base_reg)
+    emit_insn (gen_main_pool (cfun->machine->base_reg));
 
-  if (current_function_stdarg)
-    for (i = 16; i < (TARGET_64BIT ? 20 : 18); i++)
-      save_fpr (stack_pointer_rtx, 16*UNITS_PER_WORD + 8*(i-16), i);
+  offset = cfun_frame_layout.f0_offset;
 
-  /* Save fprs 4 and 6 if used (31 bit ABI).  */
+  /* Save f0 and f2.  */
+  for (i = 0; i < 2; i++)
+    {
+      if (cfun_fpr_bit_p (i))
+       {
+         save_fpr (stack_pointer_rtx, offset, i + 16);
+         offset += 8;
+       }
+      else if (!TARGET_PACKED_STACK)
+         offset += 8;
+    }
 
-  if (!TARGET_64BIT)
-    for (i = 18; i < 20; i++)
-      if (regs_ever_live[i] && !global_regs[i])
+  /* Save f4 and f6.  */
+  offset = cfun_frame_layout.f4_offset;
+  for (i = 2; i < 4; i++)
+    {
+      if (cfun_fpr_bit_p (i))
        {
-         insn = save_fpr (stack_pointer_rtx, 16*UNITS_PER_WORD + 8*(i-16), i);
-         RTX_FRAME_RELATED_P (insn) = 1;
+         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;
+    }
+
+  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);
+
+      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;
 
   /* Decrement stack pointer.  */
 
-  if (cfun->machine->frame_size > 0)
+  if (cfun_frame_layout.frame_size > 0)
     {
-      rtx frame_off = GEN_INT (-cfun->machine->frame_size);
+      rtx frame_off = GEN_INT (-cfun_frame_layout.frame_size);
 
-      /* Save incoming stack pointer into temp reg.  */
+      if (s390_stack_size)
+       {
+         HOST_WIDE_INT stack_guard;
 
-      if (TARGET_BACKCHAIN || cfun->machine->save_fprs_p)
-       {
-         insn = emit_insn (gen_move_insn (temp_reg, stack_pointer_rtx));
-       }
+         if (s390_stack_guard)
+           stack_guard = s390_stack_guard;
+         else
+           {
+             /* If no value for stack guard is provided the smallest power of 2
+                larger than the current frame size is chosen.  */
+             stack_guard = 1;
+             while (stack_guard < cfun_frame_layout.frame_size)
+               stack_guard <<= 1;
+           }
+
+         if (cfun_frame_layout.frame_size >= s390_stack_size)
+           {
+             warning (0, "frame size of function %qs is "
+                      HOST_WIDE_INT_PRINT_DEC
+                      " bytes exceeding user provided stack limit of "
+                      HOST_WIDE_INT_PRINT_DEC " bytes.  "
+                      "An unconditional trap is added.",
+                      current_function_name(), cfun_frame_layout.frame_size,
+                      s390_stack_size);
+             emit_insn (gen_trap ());
+           }
+         else
+           {
+             HOST_WIDE_INT stack_check_mask = ((s390_stack_size - 1)
+                                               & ~(stack_guard - 1));
+             rtx t = gen_rtx_AND (Pmode, stack_pointer_rtx,
+                                  GEN_INT (stack_check_mask));
+             if (TARGET_64BIT)
+               gen_cmpdi (t, const0_rtx);
+             else
+               gen_cmpsi (t, const0_rtx);
+
+             emit_insn (gen_conditional_trap (gen_rtx_EQ (CCmode,
+                                                          gen_rtx_REG (CCmode,
+                                                                    CC_REGNUM),
+                                                          const0_rtx),
+                                              const0_rtx));
+           }
+       }
+
+      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);
+
+      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,
+                             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"))
+         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));
@@ -5942,16 +7358,21 @@ s390_emit_prologue (void)
       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->machine->frame_size))),
+                            gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+                              GEN_INT (-cfun_frame_layout.frame_size))),
                           REG_NOTES (insn));
 
       /* Set backchain.  */
 
       if (TARGET_BACKCHAIN)
        {
-         addr = gen_rtx_MEM (Pmode, stack_pointer_rtx);
-         set_mem_alias_set (addr, s390_sr_alias_set);
+         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));
        }
 
@@ -5968,24 +7389,30 @@ s390_emit_prologue (void)
 
   /* Save fprs 8 - 15 (64 bit ABI).  */
 
-  if (cfun->machine->save_fprs_p)
+  if (cfun_save_high_fprs_p && next_fpr)
     {
-      insn = emit_insn (gen_add2_insn (temp_reg, GEN_INT(-64)));
+      insn = emit_insn (gen_add2_insn (temp_reg, 
+                                      GEN_INT (cfun_frame_layout.f8_offset)));
 
-      for (i = 24; i < 32; i++)
-       if (regs_ever_live[i] && !global_regs[i])
+      offset = 0;
+
+      for (i = 24; i <= next_fpr; i++)
+       if (cfun_fpr_bit_p (i - 16))
          {
            rtx addr = plus_constant (stack_pointer_rtx,
-                                     cfun->machine->frame_size - 64 + (i-24)*8);
-
-           insn = save_fpr (temp_reg, (i-24)*8, i);
+                                     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));
+                                gen_rtx_SET (VOIDmode,
+                                             gen_rtx_MEM (DFmode, addr),
+                                             gen_rtx_REG (DFmode, i)),
+                                REG_NOTES (insn));
          }
     }
 
@@ -5999,17 +7426,12 @@ s390_emit_prologue (void)
 
   /* Set up got pointer, if needed.  */
 
-  if (flag_pic && regs_ever_live[PIC_OFFSET_TABLE_REGNUM])
+  if (flag_pic && df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM))
     {
       rtx insns = s390_load_got ();
 
       for (insn = insns; insn; insn = NEXT_INSN (insn))
-       {
-         annotate_constant_pool_refs (&PATTERN (insn));
-
-         REG_NOTES (insn) = gen_rtx_EXPR_LIST (REG_MAYBE_DEAD, NULL_RTX,
-                                               REG_NOTES (insn));
-       }
+       annotate_constant_pool_refs (&PATTERN (insn));
 
       emit_insn (insns);
     }
@@ -6034,6 +7456,7 @@ s390_emit_epilogue (bool sibcall)
 {
   rtx frame_pointer, return_reg;
   int area_bottom, area_top, offset = 0;
+  int next_offset;
   rtvec p;
   int i;
 
@@ -6053,43 +7476,10 @@ s390_emit_epilogue (bool sibcall)
 
   /* Check whether to use frame or stack pointer for restore.  */
 
-  frame_pointer = frame_pointer_needed ?
-    hard_frame_pointer_rtx : stack_pointer_rtx;
-
-  /* Compute which parts of the save area we need to access.  */
-
-  if (cfun->machine->first_restore_gpr != -1)
-    {
-      area_bottom = cfun->machine->first_restore_gpr * UNITS_PER_WORD;
-      area_top = (cfun->machine->last_restore_gpr + 1) * UNITS_PER_WORD;
-    }
-  else
-    {
-      area_bottom = INT_MAX;
-      area_top = INT_MIN;
-    }
+  frame_pointer = (frame_pointer_needed 
+                  ? hard_frame_pointer_rtx : stack_pointer_rtx);
 
-  if (TARGET_64BIT)
-    {
-      if (cfun->machine->save_fprs_p)
-       {
-         if (area_bottom > -64)
-           area_bottom = -64;
-         if (area_top < 0)
-           area_top = 0;
-       }
-    }
-  else
-    {
-      for (i = 18; i < 20; i++)
-       if (regs_ever_live[i] && !global_regs[i])
-         {
-           if (area_bottom > 16*UNITS_PER_WORD + 8*(i-16))
-             area_bottom = 16*UNITS_PER_WORD + 8*(i-16);
-           if (area_top < 16*UNITS_PER_WORD + 8*(i-16) + 8)
-             area_top = 16*UNITS_PER_WORD + 8*(i-16) + 8;
-         }
-    }
+  s390_frame_area (&area_bottom, &area_top);
 
   /* Check whether we can access the register save area.
      If not, increment the frame pointer as required.  */
@@ -6098,18 +7488,18 @@ s390_emit_epilogue (bool sibcall)
     {
       /* Nothing to restore.  */
     }
-  else if (DISP_IN_RANGE (cfun->machine->frame_size + area_bottom)
-           && DISP_IN_RANGE (cfun->machine->frame_size + area_top-1))
+  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->machine->frame_size;
+      offset = cfun_frame_layout.frame_size;
     }
   else
     {
       rtx insn, frame_off;
 
       offset = area_bottom < 0 ? -area_bottom : 0;
-      frame_off = GEN_INT (cfun->machine->frame_size - offset);
+      frame_off = GEN_INT (cfun_frame_layout.frame_size - offset);
 
       if (DISP_IN_RANGE (INTVAL (frame_off)))
        {
@@ -6119,7 +7509,7 @@ s390_emit_epilogue (bool sibcall)
        }
       else
        {
-         if (!CONST_OK_FOR_CONSTRAINT_P (INTVAL (frame_off), 'K', "K"))
+         if (!CONST_OK_FOR_K (INTVAL (frame_off)))
            frame_off = force_const_mem (Pmode, frame_off);
 
          insn = emit_insn (gen_add2_insn (frame_pointer, frame_off));
@@ -6131,18 +7521,36 @@ s390_emit_epilogue (bool sibcall)
 
   if (TARGET_64BIT)
     {
-      if (cfun->machine->save_fprs_p)
-       for (i = 24; i < 32; i++)
-         if (regs_ever_live[i] && !global_regs[i])
-           restore_fpr (frame_pointer,
-                        offset - 64 + (i-24) * 8, i);
+      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 (regs_ever_live[i] && !global_regs[i])
-         restore_fpr (frame_pointer,
-                      offset + 16*UNITS_PER_WORD + 8*(i-16), 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;
+       }
+      
     }
 
   /* Return register.  */
@@ -6151,7 +7559,7 @@ s390_emit_epilogue (bool sibcall)
 
   /* Restore call saved gprs.  */
 
-  if (cfun->machine->first_restore_gpr != -1)
+  if (cfun_frame_layout.first_restore_gpr != -1)
     {
       rtx insn, addr;
       int i;
@@ -6159,8 +7567,8 @@ s390_emit_epilogue (bool sibcall)
       /* Check for global register and save them
         to stack location from where they get restored.  */
 
-      for (i = cfun->machine->first_restore_gpr;
-          i <= cfun->machine->last_restore_gpr;
+      for (i = cfun_frame_layout.first_restore_gpr;
+          i <= cfun_frame_layout.last_restore_gpr;
           i++)
        {
          /* These registers are special and need to be
@@ -6174,9 +7582,11 @@ s390_emit_epilogue (bool sibcall)
          if (global_regs[i])
            {
              addr = plus_constant (frame_pointer,
-                    offset + i * UNITS_PER_WORD);
+                                   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, s390_sr_alias_set);
+             set_mem_alias_set (addr, get_frame_alias_set ());
              emit_move_insn (addr, gen_rtx_REG (Pmode, i));
            }
        }
@@ -6186,9 +7596,9 @@ s390_emit_epilogue (bool sibcall)
          /* Fetch return address from stack before load multiple,
             this will do good for scheduling.  */
 
-         if (cfun->machine->save_return_addr_p
-             || (cfun->machine->first_restore_gpr < BASE_REGNUM
-                 && cfun->machine->last_restore_gpr > RETURN_REGNUM))
+         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)
@@ -6196,16 +7606,23 @@ s390_emit_epilogue (bool sibcall)
              return_reg = gen_rtx_REG (Pmode, return_regnum);
 
              addr = plus_constant (frame_pointer,
-                                   offset + RETURN_REGNUM * UNITS_PER_WORD);
+                                   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, s390_sr_alias_set);
+             set_mem_alias_set (addr, get_frame_alias_set ());
              emit_move_insn (return_reg, addr);
            }
        }
 
-      insn = restore_gprs (frame_pointer, offset,
-                          cfun->machine->first_restore_gpr,
-                          cfun->machine->last_restore_gpr);
+      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);
     }
 
@@ -6238,7 +7655,7 @@ s390_function_arg_size (enum machine_mode mode, tree type)
     return GET_MODE_SIZE (mode);
 
   /* If we have neither type nor mode, abort */
-  abort ();
+  gcc_unreachable ();
 }
 
 /* Return true if a function argument of type TYPE and mode MODE
@@ -6257,7 +7674,7 @@ s390_function_arg_float (enum machine_mode mode, tree type)
 
   /* No type info available for some library calls ...  */
   if (!type)
-    return mode == SFmode || mode == DFmode;
+    return mode == SFmode || mode == DFmode || mode == SDmode || mode == DDmode;
 
   /* The ABI says that record types with a single member are treated
      just like that member would be.  */
@@ -6299,7 +7716,7 @@ s390_function_arg_integer (enum machine_mode mode, tree type)
   /* 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);
+          || (TARGET_SOFT_FLOAT &&  SCALAR_FLOAT_MODE_P (mode));
 
   /* We accept small integral (and similar) types.  */
   if (INTEGRAL_TYPE_P (type)
@@ -6366,7 +7783,7 @@ s390_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
       cum->gprs += ((size + UNITS_PER_WORD-1) / UNITS_PER_WORD);
     }
   else
-    abort ();
+    gcc_unreachable ();
 }
 
 /* Define where to put the arguments to a function.
@@ -6394,7 +7811,7 @@ s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
 {
   if (s390_function_arg_float (mode, type))
     {
-      if (cum->fprs + 1 > (TARGET_64BIT? 4 : 2))
+      if (cum->fprs + 1 > FP_ARG_NUM_REG)
        return 0;
       else
        return gen_rtx_REG (mode, cum->fprs + 16);
@@ -6404,7 +7821,7 @@ s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
       int size = s390_function_arg_size (mode, type);
       int n_gprs = (size + UNITS_PER_WORD-1) / UNITS_PER_WORD;
 
-      if (cum->gprs + n_gprs > 5)
+      if (cum->gprs + n_gprs > GP_ARG_NUM_REG)
        return 0;
       else
        return gen_rtx_REG (mode, cum->gprs + 2);
@@ -6418,7 +7835,7 @@ s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
   else if (type == void_type_node)
     return const0_rtx;
 
-  abort ();
+  gcc_unreachable ();
 }
 
 /* Return true if return values of type TYPE should be returned
@@ -6461,13 +7878,10 @@ s390_function_value (tree type, enum machine_mode mode)
       mode = promote_mode (type, TYPE_MODE (type), &unsignedp, 1);
     }
 
-  if (GET_MODE_CLASS (mode) != MODE_INT
-      && GET_MODE_CLASS (mode) != MODE_FLOAT)
-    abort ();
-  if (GET_MODE_SIZE (mode) > 8)
-    abort ();
+  gcc_assert (GET_MODE_CLASS (mode) == MODE_INT || SCALAR_FLOAT_MODE_P (mode));
+  gcc_assert (GET_MODE_SIZE (mode) <= 8);
 
-  if (TARGET_HARD_FLOAT && GET_MODE_CLASS (mode) == MODE_FLOAT)
+  if (TARGET_HARD_FLOAT && SCALAR_FLOAT_MODE_P (mode))
     return gen_rtx_REG (mode, 16);
   else
     return gen_rtx_REG (mode, 2);
@@ -6514,6 +7928,9 @@ s390_build_builtin_va_list (void)
   f_sav = build_decl (FIELD_DECL, get_identifier ("__reg_save_area"),
                      ptr_type_node);
 
+  va_list_gpr_counter_field = f_gpr;
+  va_list_fpr_counter_field = f_fpr;
+
   DECL_FIELD_CONTEXT (f_gpr) = record;
   DECL_FIELD_CONTEXT (f_fpr) = record;
   DECL_FIELD_CONTEXT (f_ovf) = record;
@@ -6558,47 +7975,64 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
   f_ovf = TREE_CHAIN (f_fpr);
   f_sav = TREE_CHAIN (f_ovf);
 
-  valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), 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);
+  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);
 
   /* Count number of gp and fp argument registers used.  */
 
   n_gpr = current_function_args_info.gprs;
   n_fpr = current_function_args_info.fprs;
 
-  t = build (MODIFY_EXPR, TREE_TYPE (gpr), gpr, build_int_2 (n_gpr, 0));
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  if (cfun->va_list_gpr_size)
+    {
+      t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (gpr), gpr,
+                 build_int_cst (NULL_TREE, n_gpr));
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
 
-  t = build (MODIFY_EXPR, TREE_TYPE (fpr), fpr, build_int_2 (n_fpr, 0));
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  if (cfun->va_list_fpr_size)
+    {
+      t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (fpr), fpr,
+                 build_int_cst (NULL_TREE, n_fpr));
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
 
   /* Find the overflow area.  */
-  t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx);
+  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);
 
-  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);
+      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);
 
-  t = build (PLUS_EXPR, TREE_TYPE (ovf), t, build_int_2 (off, 0));
+      t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovf), t, size_int (off));
 
-  t = build (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+      t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (ovf), ovf, t);
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
 
   /* Find the register save area.  */
-  t = make_tree (TREE_TYPE (sav), virtual_incoming_args_rtx);
-  t = build (PLUS_EXPR, TREE_TYPE (sav), t,
-            build_int_2 (-STACK_POINTER_OFFSET, -1));
-  t = build (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
-  TREE_SIDE_EFFECTS (t) = 1;
-  expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+  if ((cfun->va_list_gpr_size && n_gpr < GP_ARG_NUM_REG)
+      || (cfun->va_list_fpr_size && n_fpr < FP_ARG_NUM_REG))
+    {
+      t = make_tree (TREE_TYPE (sav), return_address_pointer_rtx);
+      t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (sav), t,
+                 size_int (-RETURN_REGNUM * UNITS_PER_WORD));
+  
+      t = build2 (GIMPLE_MODIFY_STMT, TREE_TYPE (sav), sav, t);
+      TREE_SIDE_EFFECTS (t) = 1;
+      expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
+    }
 }
 
 /* Implement va_arg by updating the va_list structure
@@ -6625,7 +8059,7 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
        ret = **args.overflow_arg_area++;
    } */
 
-tree
+static tree
 s390_gimplify_va_arg (tree valist, tree type, tree *pre_p, 
                      tree *post_p ATTRIBUTE_UNUSED)
 {
@@ -6639,11 +8073,11 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
   f_ovf = TREE_CHAIN (f_fpr);
   f_sav = TREE_CHAIN (f_ovf);
 
-  valist = build1 (INDIRECT_REF, TREE_TYPE (TREE_TYPE (valist)), 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);
+  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);
 
@@ -6659,10 +8093,14 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
       indirect_p = 1;
       reg = gpr;
       n_reg = 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 = 4;
+      max_reg = GP_ARG_NUM_REG - n_reg;
     }
   else if (s390_function_arg_float (TYPE_MODE (type), type))
     {
@@ -6678,8 +8116,7 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
       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;
+      max_reg = FP_ARG_NUM_REG - n_reg;
     }
   else
     {
@@ -6693,16 +8130,17 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
       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;
+      max_reg = GP_ARG_NUM_REG - n_reg;
     }
 
   /* Pull the value out of the saved registers ...  */
@@ -6710,6 +8148,7 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
   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 ();
 
   t = fold_convert (TREE_TYPE (reg), size_int (max_reg));
   t = build2 (GT_EXPR, boolean_type_node, reg, t);
@@ -6717,13 +8156,13 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
   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)));
+  t = build2 (POINTER_PLUS_EXPR, ptr_type_node, sav, 
+             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 (POINTER_PLUS_EXPR, ptr_type_node, t, fold_convert (sizetype, u));
 
-  t = build2 (MODIFY_EXPR, void_type_node, addr, t);
+  t = build2 (GIMPLE_MODIFY_STMT, void_type_node, addr, t);
   gimplify_and_add (t, pre_p);
 
   t = build1 (GOTO_EXPR, void_type_node, lab_over);
@@ -6737,17 +8176,17 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
 
   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)));
+    t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, 
+               size_int (UNITS_PER_WORD - size));
 
   gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue);
 
-  u = build2 (MODIFY_EXPR, void_type_node, addr, t);
+  u = build2 (GIMPLE_MODIFY_STMT, void_type_node, addr, t);
   gimplify_and_add (u, pre_p);
 
-  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);
+  t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t, 
+             size_int (size));
+  t = build2 (GIMPLE_MODIFY_STMT, ptr_type_node, ovf, t);
   gimplify_and_add (t, pre_p);
 
   t = build1 (LABEL_EXPR, void_type_node, lab_over);
@@ -6764,7 +8203,7 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
     {
       t = build_pointer_type (build_pointer_type (type));
       addr = fold_convert (t, addr);
-      addr = build_fold_indirect_ref (addr);
+      addr = build_va_arg_indirect_ref (addr);
     }
   else
     {
@@ -6772,7 +8211,7 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
       addr = fold_convert (t, addr);
     }
 
-  return build_fold_indirect_ref (addr);
+  return build_va_arg_indirect_ref (addr);
 }
 
 
@@ -6802,14 +8241,14 @@ s390_init_builtins (void)
   tree ftype;
 
   ftype = build_function_type (ptr_type_node, void_list_node);
-  builtin_function ("__builtin_thread_pointer", ftype,
-                   S390_BUILTIN_THREAD_POINTER, BUILT_IN_MD,
-                   NULL, NULL_TREE);
+  add_builtin_function ("__builtin_thread_pointer", ftype,
+                       S390_BUILTIN_THREAD_POINTER, BUILT_IN_MD,
+                       NULL, NULL_TREE);
 
   ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
-  builtin_function ("__builtin_set_thread_pointer", ftype,
-                   S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD,
-                   NULL, NULL_TREE);
+  add_builtin_function ("__builtin_set_thread_pointer", ftype,
+                       S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD,
+                       NULL, NULL_TREE);
 }
 
 /* Expand an expression EXP that calls a built-in function,
@@ -6828,13 +8267,14 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
   unsigned int const *code_for_builtin =
     TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31;
 
-  tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+  tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 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;
+  tree arg;
+  call_expr_arg_iterator iter;
 
   if (fcode >= S390_BUILTIN_max)
     internal_error ("bad builtin fcode");
@@ -6844,13 +8284,11 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
 
   nonvoid = TREE_TYPE (TREE_TYPE (fndecl)) != void_type_node;
 
-  for (arglist = TREE_OPERAND (exp, 1), arity = 0;
-       arglist;
-       arglist = TREE_CHAIN (arglist), arity++)
+  arity = 0;
+  FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
     {
       const struct insn_operand_data *insn_op;
 
-      tree arg = TREE_VALUE (arglist);
       if (arg == error_mark_node)
        return NULL_RTX;
       if (arity > MAX_ARGS)
@@ -6862,6 +8300,7 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
 
       if (!(*insn_op->predicate) (op[arity], insn_op->mode))
        op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]);
+      arity++;
     }
 
   if (nonvoid)
@@ -6888,7 +8327,7 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
       pat = GEN_FCN (icode) (target, op[0], op[1]);
       break;
     default:
-      abort ();
+      gcc_unreachable ();
     }
   if (!pat)
     return NULL_RTX;
@@ -6910,23 +8349,23 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
 void
 s390_trampoline_template (FILE *file)
 {
+  rtx op[2];
+  op[0] = gen_rtx_REG (Pmode, 0);
+  op[1] = gen_rtx_REG (Pmode, 1);
+
   if (TARGET_64BIT)
     {
-      fprintf (file, "larl\t%s,0f\n", reg_names[1]);
-      fprintf (file, "lg\t%s,0(%s)\n", reg_names[0], reg_names[1]);
-      fprintf (file, "lg\t%s,8(%s)\n", reg_names[1], reg_names[1]);
-      fprintf (file, "br\t%s\n", reg_names[1]);
-      fprintf (file, "0:\t.quad\t0\n");
-      fprintf (file, ".quad\t0\n");
+      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));
     }
   else
     {
-      fprintf (file, "basr\t%s,0\n", reg_names[1]);
-      fprintf (file, "l\t%s,10(%s)\n", reg_names[0], reg_names[1]);
-      fprintf (file, "l\t%s,14(%s)\n", reg_names[1], reg_names[1]);
-      fprintf (file, "br\t%s\n", reg_names[1]);
-      fprintf (file, ".long\t0\n");
-      fprintf (file, ".long\t0\n");
+      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));
     }
 }
 
@@ -6939,36 +8378,14 @@ s390_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt)
 {
   emit_move_insn (gen_rtx_MEM (Pmode,
                   memory_address (Pmode,
-                  plus_constant (addr, (TARGET_64BIT ? 20 : 12) ))), cxt);
+                  plus_constant (addr, (TARGET_64BIT ? 16 : 8)))), cxt);
   emit_move_insn (gen_rtx_MEM (Pmode,
                   memory_address (Pmode,
-                  plus_constant (addr, (TARGET_64BIT ? 28 : 16) ))), fnaddr);
+                  plus_constant (addr, (TARGET_64BIT ? 24 : 12)))), fnaddr);
 }
 
-/* Return rtx for 64-bit constant formed from the 32-bit subwords
-   LOW and HIGH, independent of the host word size.  */
-
-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;
-
-  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
-}
-
-/* Output assembler code to FILE to increment profiler label # LABELNO
-   for profiling a function entry.  */
+/* Output assembler code to FILE to increment profiler label # LABELNO
+   for profiling a function entry.  */
 
 void
 s390_function_profiler (FILE *file, int labelno)
@@ -7035,20 +8452,6 @@ s390_function_profiler (FILE *file, int labelno)
     }
 }
 
-/* Select section for constant in constant pool.  In 32-bit mode,
-   constants go in the function section; in 64-bit mode in .rodata.  */
-
-static void
-s390_select_rtx_section (enum machine_mode mode ATTRIBUTE_UNUSED,
-                        rtx x ATTRIBUTE_UNUSED,
-                        unsigned HOST_WIDE_INT align ATTRIBUTE_UNUSED)
-{
-  if (TARGET_CPU_ZARCH)
-    readonly_data_section ();
-  else
-    function_section (current_function_decl);
-}
-
 /* Encode symbol attributes (local vs. global, tls model) of a SYMBOL_REF
    into its SYMBOL_REF_FLAGS.  */
 
@@ -7117,9 +8520,11 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
     {
       /* Setup literal pool pointer if required.  */
       if ((!DISP_IN_RANGE (delta)
-          && !CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K"))
+          && !CONST_OK_FOR_K (delta)
+          && !CONST_OK_FOR_Os (delta))
          || (!DISP_IN_RANGE (vcall_offset)
-             && !CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K")))
+             && !CONST_OK_FOR_K (vcall_offset)
+             && !CONST_OK_FOR_Os (vcall_offset)))
        {
          op[5] = gen_label_rtx ();
          output_asm_insn ("larl\t%4,%5", op);
@@ -7128,12 +8533,14 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
       /* Add DELTA to this pointer.  */
       if (delta)
        {
-         if (CONST_OK_FOR_CONSTRAINT_P (delta, 'J', "J"))
+         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_CONSTRAINT_P (delta, 'K', "K"))
+         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 ();
@@ -7149,12 +8556,18 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
              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"))
+         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 ();
@@ -7192,9 +8605,11 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
       /* Setup base pointer if required.  */
       if (!vcall_offset
          || (!DISP_IN_RANGE (delta)
-              && !CONST_OK_FOR_CONSTRAINT_P (delta, 'K', "K"))
+              && !CONST_OK_FOR_K (delta)
+             && !CONST_OK_FOR_Os (delta))
          || (!DISP_IN_RANGE (delta)
-              && !CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'K', "K")))
+              && !CONST_OK_FOR_K (vcall_offset)
+             && !CONST_OK_FOR_Os (vcall_offset)))
        {
          op[5] = gen_label_rtx ();
          output_asm_insn ("basr\t%4,0", op);
@@ -7205,12 +8620,14 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
       /* Add DELTA to this pointer.  */
       if (delta)
        {
-         if (CONST_OK_FOR_CONSTRAINT_P (delta, 'J', "J"))
+         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_CONSTRAINT_P (delta, 'K', "K"))
+         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 ();
@@ -7221,22 +8638,28 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
       /* Perform vcall adjustment.  */
       if (vcall_offset)
         {
-         if (CONST_OK_FOR_CONSTRAINT_P (vcall_offset, 'J', "J"))
+         if (CONST_OK_FOR_J (vcall_offset))
            {
-             output_asm_insn ("lg\t%4,0(%1)", op);
+             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 ("lg\t%4,0(%1)", op);
+             output_asm_insn ("l\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"))
+         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 ();
@@ -7309,55 +8732,44 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
     }
 }
 
-bool
+static bool
 s390_valid_pointer_mode (enum machine_mode mode)
 {
   return (mode == SImode || (TARGET_64BIT && mode == DImode));
 }
 
-/* How to allocate a 'struct machine_function'.  */
-
-static struct machine_function *
-s390_init_machine_status (void)
-{
-  return ggc_alloc_cleared (sizeof (struct machine_function));
-}
-
-/* Checks whether the given ARGUMENT_LIST would use a caller
+/* Checks whether the given CALL_EXPR would use a caller
    saved register.  This is used to decide whether sibling call
    optimization could be performed on the respective function
    call.  */
 
 static bool
-s390_call_saved_register_used (tree argument_list)
+s390_call_saved_register_used (tree call_expr)
 {
   CUMULATIVE_ARGS cum;
   tree parameter;
   enum machine_mode mode;
   tree type;
   rtx parm_rtx;
-  int reg;
+  int reg, i;
 
   INIT_CUMULATIVE_ARGS (cum, NULL, NULL, 0, 0);
 
-  while (argument_list)
+  for (i = 0; i < call_expr_nargs (call_expr); i++)
     {
-      parameter = TREE_VALUE (argument_list);
-      argument_list = TREE_CHAIN (argument_list);
-
-      if (!parameter)
-       abort();
+      parameter = CALL_EXPR_ARG (call_expr, i);
+      gcc_assert (parameter);
 
       /* For an undeclared variable passed as parameter we will get
         an ERROR_MARK node here.  */
       if (TREE_CODE (parameter) == ERROR_MARK)
        return true;
 
-      if (! (type = TREE_TYPE (parameter)))
-       abort();
+      type = TREE_TYPE (parameter);
+      gcc_assert (type);
 
-      if (! (mode = TYPE_MODE (TREE_TYPE (parameter))))
-       abort();
+      mode = TYPE_MODE (type);
+      gcc_assert (mode);
 
       if (pass_by_reference (&cum, mode, type, true))
        {
@@ -7395,16 +8807,23 @@ s390_function_ok_for_sibcall (tree decl, tree exp)
 
   /* 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))
+  if (!TARGET_64BIT && flag_pic && decl && !targetm.binds_local_p (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 !s390_call_saved_register_used (exp);
+}
 
+/* Return the fixed registers used for condition codes.  */
+
+static bool
+s390_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2)
+{
+  *p1 = CC_REGNUM;
+  *p2 = INVALID_REGNUM;
   return true;
 }
 
@@ -7490,8 +8909,7 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
     {
       /* s390_function_ok_for_sibcall should
         have denied sibcalls in this case.  */
-      if (retaddr_reg == NULL_RTX)
-       abort ();
+      gcc_assert (retaddr_reg != NULL_RTX);
 
       use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
     }
@@ -7512,6 +8930,8 @@ s390_conditional_register_usage (void)
     }
   if (TARGET_CPU_ZARCH)
     {
+      fixed_regs[BASE_REGNUM] = 0;
+      call_used_regs[BASE_REGNUM] = 0;
       fixed_regs[RETURN_REGNUM] = 0;
       call_used_regs[RETURN_REGNUM] = 0;
     }
@@ -7525,7 +8945,420 @@ s390_conditional_register_usage (void)
       for (i = 18; i < 20; i++)
        call_used_regs[i] = call_really_used_regs[i] = 0;
     }
+
+  if (TARGET_SOFT_FLOAT)
+    {
+      for (i = 16; i < 32; i++)
+       call_used_regs[i] = fixed_regs[i] = 1;
+    }
+}
+
+/* Corresponding function to eh_return expander.  */
+
+static GTY(()) rtx s390_tpf_eh_return_symbol;
+void
+s390_emit_tpf_eh_return (rtx target)
+{
+  rtx insn, reg;
+
+  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);
 }
 
+/* Rework the prologue/epilogue to avoid saving/restoring
+   registers unnecessarily.  */
+
+static void
+s390_optimize_prologue (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.  */
+
+  for (insn = get_insns (); insn; insn = next_insn)
+    {
+      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))
+       {
+         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);
+
+         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)
+           {
+             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);
+           }
+
+         remove_insn (insn);
+         continue;
+       }
+
+      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)
+       {
+         set = PATTERN (insn);
+         first = REGNO (SET_SRC (set));
+         offset = const0_rtx;
+         base = eliminate_constant_term (XEXP (SET_DEST (set), 0), &offset);
+         off = INTVAL (offset);
+
+         if (GET_CODE (base) != REG || off < 0)
+           continue;
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
+
+         remove_insn (insn);
+         continue;
+       }
+
+      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);
+
+         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 (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;
+       }
+
+      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);
+
+         if (GET_CODE (base) != REG || off < 0)
+           continue;
+         if (REGNO (base) != STACK_POINTER_REGNUM
+             && REGNO (base) != HARD_FRAME_POINTER_REGNUM)
+           continue;
+
+         remove_insn (insn);
+         continue;
+       }
+    }
+}
+
+/* Perform machine-dependent processing.  */
+
+static void
+s390_reorg (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 ();
+
+  /* From here on decomposed literal pool addresses must be accepted.  */
+  cfun->machine->decomposed_literal_pool_addresses_ok_p = true;
+
+  /* Install the main literal pool and the associated base
+     register load insns.
+
+     In addition, there are two problematic situations we need
+     to correct:
+
+     - the literal pool might be > 4096 bytes in size, so that
+       some of its elements cannot be directly accessed
+
+     - a branch target might be > 64K away from the branch, so that
+       it is not possible to use a PC-relative instruction.
+
+     To fix those, we split the single literal pool into multiple
+     pool chunks, reloading the pool base register at various
+     points throughout the function to ensure it always points to
+     the pool chunk the following code expects, and / or replace
+     PC-relative branches by absolute branches.
+
+     However, the two problems are interdependent: splitting the
+     literal pool can move a branch further away from its target,
+     causing the 64K limit to overflow, and on the other hand,
+     replacing a PC-relative branch by an absolute branch means
+     we need to put the branch target address into the literal
+     pool, possibly causing it to overflow.
+
+     So, we loop trying to fix up both problems until we manage
+     to satisfy both conditions at the same time.  Note that the
+     loop is guaranteed to terminate as every pass of the loop
+     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.)  */
+
+  for (;;)
+    {
+      struct constant_pool *pool = NULL;
+
+      /* Collect the literal pool.  */
+      if (!pool_overflow)
+       {
+         pool = s390_mainpool_start ();
+         if (!pool)
+           pool_overflow = true;
+       }
+
+      /* If literal pool overflowed, start to chunkify it.  */
+      if (pool_overflow)
+        pool = s390_chunkify_start ();
+
+      /* 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);
+
+          continue;
+        }
+
+      /* 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);
+
+      /* We're done splitting branches.  */
+      cfun->machine->split_branches_pending_p = false;
+      break;
+    }
+
+  /* Generate out-of-pool execute target insns.  */
+  if (TARGET_CPU_ZARCH)
+    {
+      rtx insn, label, target;
+
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+       {
+         label = s390_execute_label (insn);
+         if (!label)
+           continue;
+
+         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);
+       }
+    }
+
+  /* Try to optimize prologue and epilogue further.  */
+  s390_optimize_prologue ();
+}
+
+
+/* Initialize GCC target structure.  */
+
+#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_DEFAULT_TARGET_FLAGS
+#define TARGET_DEFAULT_TARGET_FLAGS (TARGET_DEFAULT | MASK_FUSED_MADD)
+#undef TARGET_HANDLE_OPTION
+#define TARGET_HANDLE_OPTION s390_handle_option
+
+#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
+
+#undef TARGET_INVALID_WITHIN_DOLOOP
+#define TARGET_INVALID_WITHIN_DOLOOP hook_constcharptr_rtx_null
+
+#ifdef HAVE_AS_TLS
+#undef TARGET_ASM_OUTPUT_DWARF_DTPREL
+#define TARGET_ASM_OUTPUT_DWARF_DTPREL s390_output_dwarf_dtprel
+#endif
+
+#ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
+#undef TARGET_MANGLE_TYPE
+#define TARGET_MANGLE_TYPE s390_mangle_type
+#endif
+
+#undef TARGET_SCALAR_MODE_SUPPORTED_P
+#define TARGET_SCALAR_MODE_SUPPORTED_P s390_scalar_mode_supported_p
+
+#undef TARGET_SECONDARY_RELOAD
+#define TARGET_SECONDARY_RELOAD s390_secondary_reload
+
+#undef TARGET_LIBGCC_CMP_RETURN_MODE
+#define TARGET_LIBGCC_CMP_RETURN_MODE s390_libgcc_cmp_return_mode
+
+#undef TARGET_LIBGCC_SHIFT_COUNT_MODE
+#define TARGET_LIBGCC_SHIFT_COUNT_MODE s390_libgcc_shift_count_mode
+
+struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-s390.h"