s390.c: Replace F*_REGNUM with FPR*_REGNUM.
[gcc.git] / gcc / config / s390 / s390.c
index b6aeee1129326aa9fe62fa2d7522f5380ab0eda6..7ce0c30c8838e06e404d01e8dd67d9bdb18aa946 100644 (file)
@@ -1,14 +1,14 @@
 /* Subroutines used for code generation on IBM S/390 and zSeries
-   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006
-   Free Software Foundation, Inc.
+   Copyright (C) 1999-2013 Free Software Foundation, Inc.
    Contributed by Hartmut Penner (hpenner@de.ibm.com) and
-                  Ulrich Weigand (uweigand@de.ibm.com).
+                  Ulrich Weigand (uweigand@de.ibm.com) and
+                  Andreas Krebbel (Andreas.Krebbel@de.ibm.com).
 
 This file is part of GCC.
 
 GCC is free software; you can redistribute it and/or modify it under
 the terms of the GNU General Public License as published by the Free
-Software Foundation; either version 2, or (at your option) any later
+Software Foundation; either version 3, or (at your option) any later
 version.
 
 GCC is distributed in the hope that it will be useful, but WITHOUT ANY
@@ -17,9 +17,8 @@ FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
 for more details.
 
 You should have received a copy of the GNU General Public License
-along with GCC; see the file COPYING.  If not, write to the Free
-Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
-02110-1301, USA.  */
+along with GCC; see the file COPYING3.  If not see
+<http://www.gnu.org/licenses/>.  */
 
 #include "config.h"
 #include "system.h"
@@ -30,7 +29,6 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "tm_p.h"
 #include "regs.h"
 #include "hard-reg-set.h"
-#include "real.h"
 #include "insn-config.h"
 #include "conditions.h"
 #include "output.h"
@@ -41,21 +39,23 @@ Software Foundation, 51 Franklin Street, Fifth Floor, Boston, MA
 #include "recog.h"
 #include "expr.h"
 #include "reload.h"
-#include "toplev.h"
+#include "diagnostic-core.h"
 #include "basic-block.h"
-#include "integrate.h"
 #include "ggc.h"
 #include "target.h"
 #include "target-def.h"
 #include "debug.h"
 #include "langhooks.h"
 #include "optabs.h"
-#include "tree-gimple.h"
-
+#include "gimple.h"
+#include "df.h"
+#include "params.h"
+#include "cfgloop.h"
+#include "opts.h"
 
 /* Define the specific costs for a given cpu.  */
 
-struct processor_costs 
+struct processor_costs
 {
   /* multiplication */
   const int m;        /* cost of an M instruction.  */
@@ -81,11 +81,8 @@ struct processor_costs
   const int maebr;    /* cost of multiply and add in SFmode.  */
   /* division */
   const int dxbr;
-  const int dxr;
   const int ddbr;
-  const int ddr;
   const int debr;
-  const int der;
   const int dlgr;
   const int dlr;
   const int dr;
@@ -96,7 +93,7 @@ struct processor_costs
 const struct processor_costs *s390_cost;
 
 static const
-struct processor_costs z900_cost = 
+struct processor_costs z900_cost =
 {
   COSTS_N_INSNS (5),     /* M     */
   COSTS_N_INSNS (10),    /* MGHI  */
@@ -118,11 +115,8 @@ struct processor_costs z900_cost =
   COSTS_N_INSNS (18),    /* MADBR */
   COSTS_N_INSNS (13),    /* MAEBR */
   COSTS_N_INSNS (134),   /* DXBR */
-  COSTS_N_INSNS (135),   /* DXR */
   COSTS_N_INSNS (30),    /* DDBR */
-  COSTS_N_INSNS (30),    /* DDR  */
   COSTS_N_INSNS (27),    /* DEBR */
-  COSTS_N_INSNS (26),    /* DER  */
   COSTS_N_INSNS (220),   /* DLGR */
   COSTS_N_INSNS (34),    /* DLR */
   COSTS_N_INSNS (34),    /* DR */
@@ -131,7 +125,7 @@ struct processor_costs z900_cost =
 };
 
 static const
-struct processor_costs z990_cost = 
+struct processor_costs z990_cost =
 {
   COSTS_N_INSNS (4),     /* M     */
   COSTS_N_INSNS (2),     /* MGHI  */
@@ -153,11 +147,8 @@ struct processor_costs z990_cost =
   COSTS_N_INSNS (1),     /* MADBR */
   COSTS_N_INSNS (1),     /* MAEBR */
   COSTS_N_INSNS (60),    /* DXBR */
-  COSTS_N_INSNS (72),    /* DXR */
   COSTS_N_INSNS (40),    /* DDBR */
-  COSTS_N_INSNS (44),    /* DDR  */
-  COSTS_N_INSNS (26),    /* DDBR */
-  COSTS_N_INSNS (28),    /* DER  */
+  COSTS_N_INSNS (26),    /* DEBR */
   COSTS_N_INSNS (176),   /* DLGR */
   COSTS_N_INSNS (31),    /* DLR */
   COSTS_N_INSNS (31),    /* DR */
@@ -166,7 +157,7 @@ struct processor_costs z990_cost =
 };
 
 static const
-struct processor_costs z9_109_cost = 
+struct processor_costs z9_109_cost =
 {
   COSTS_N_INSNS (4),     /* M     */
   COSTS_N_INSNS (2),     /* MGHI  */
@@ -188,11 +179,8 @@ struct processor_costs z9_109_cost =
   COSTS_N_INSNS (1),     /* MADBR */
   COSTS_N_INSNS (1),     /* MAEBR */
   COSTS_N_INSNS (60),    /* DXBR */
-  COSTS_N_INSNS (72),    /* DXR */
   COSTS_N_INSNS (40),    /* DDBR */
-  COSTS_N_INSNS (37),    /* DDR  */
-  COSTS_N_INSNS (26),    /* DDBR */
-  COSTS_N_INSNS (28),    /* DER  */
+  COSTS_N_INSNS (26),    /* DEBR */
   COSTS_N_INSNS (30),    /* DLGR */
   COSTS_N_INSNS (23),    /* DLR */
   COSTS_N_INSNS (23),    /* DR */
@@ -200,15 +188,106 @@ struct processor_costs z9_109_cost =
   COSTS_N_INSNS (24),    /* DSGR */
 };
 
-extern int reload_completed;
+static const
+struct processor_costs z10_cost =
+{
+  COSTS_N_INSNS (10),    /* M     */
+  COSTS_N_INSNS (10),    /* MGHI  */
+  COSTS_N_INSNS (10),    /* MH    */
+  COSTS_N_INSNS (10),    /* MHI   */
+  COSTS_N_INSNS (10),    /* ML    */
+  COSTS_N_INSNS (10),    /* MR    */
+  COSTS_N_INSNS (10),    /* MS    */
+  COSTS_N_INSNS (10),    /* MSG   */
+  COSTS_N_INSNS (10),    /* MSGF  */
+  COSTS_N_INSNS (10),    /* MSGFR */
+  COSTS_N_INSNS (10),    /* MSGR  */
+  COSTS_N_INSNS (10),    /* MSR   */
+  COSTS_N_INSNS (1) ,    /* multiplication in DFmode */
+  COSTS_N_INSNS (50),    /* MXBR */
+  COSTS_N_INSNS (120),   /* SQXBR */
+  COSTS_N_INSNS (52),    /* SQDBR */
+  COSTS_N_INSNS (38),    /* SQEBR */
+  COSTS_N_INSNS (1),     /* MADBR */
+  COSTS_N_INSNS (1),     /* MAEBR */
+  COSTS_N_INSNS (111),   /* DXBR */
+  COSTS_N_INSNS (39),    /* DDBR */
+  COSTS_N_INSNS (32),    /* DEBR */
+  COSTS_N_INSNS (160),   /* DLGR */
+  COSTS_N_INSNS (71),    /* DLR */
+  COSTS_N_INSNS (71),    /* DR */
+  COSTS_N_INSNS (71),    /* DSGFR */
+  COSTS_N_INSNS (71),    /* DSGR */
+};
+
+static const
+struct processor_costs z196_cost =
+{
+  COSTS_N_INSNS (7),     /* M     */
+  COSTS_N_INSNS (5),     /* MGHI  */
+  COSTS_N_INSNS (5),     /* MH    */
+  COSTS_N_INSNS (5),     /* MHI   */
+  COSTS_N_INSNS (7),     /* ML    */
+  COSTS_N_INSNS (7),     /* MR    */
+  COSTS_N_INSNS (6),     /* MS    */
+  COSTS_N_INSNS (8),     /* MSG   */
+  COSTS_N_INSNS (6),     /* MSGF  */
+  COSTS_N_INSNS (6),     /* MSGFR */
+  COSTS_N_INSNS (8),     /* MSGR  */
+  COSTS_N_INSNS (6),     /* MSR   */
+  COSTS_N_INSNS (1) ,    /* multiplication in DFmode */
+  COSTS_N_INSNS (40),    /* MXBR B+40 */
+  COSTS_N_INSNS (100),   /* SQXBR B+100 */
+  COSTS_N_INSNS (42),    /* SQDBR B+42 */
+  COSTS_N_INSNS (28),    /* SQEBR B+28 */
+  COSTS_N_INSNS (1),     /* MADBR B */
+  COSTS_N_INSNS (1),     /* MAEBR B */
+  COSTS_N_INSNS (101),   /* DXBR B+101 */
+  COSTS_N_INSNS (29),    /* DDBR */
+  COSTS_N_INSNS (22),    /* DEBR */
+  COSTS_N_INSNS (160),   /* DLGR cracked */
+  COSTS_N_INSNS (160),   /* DLR cracked */
+  COSTS_N_INSNS (160),   /* DR expanded */
+  COSTS_N_INSNS (160),   /* DSGFR cracked */
+  COSTS_N_INSNS (160),   /* DSGR cracked */
+};
+
+static const
+struct processor_costs zEC12_cost =
+{
+  COSTS_N_INSNS (7),     /* M     */
+  COSTS_N_INSNS (5),     /* MGHI  */
+  COSTS_N_INSNS (5),     /* MH    */
+  COSTS_N_INSNS (5),     /* MHI   */
+  COSTS_N_INSNS (7),     /* ML    */
+  COSTS_N_INSNS (7),     /* MR    */
+  COSTS_N_INSNS (6),     /* MS    */
+  COSTS_N_INSNS (8),     /* MSG   */
+  COSTS_N_INSNS (6),     /* MSGF  */
+  COSTS_N_INSNS (6),     /* MSGFR */
+  COSTS_N_INSNS (8),     /* MSGR  */
+  COSTS_N_INSNS (6),     /* MSR   */
+  COSTS_N_INSNS (1) ,    /* multiplication in DFmode */
+  COSTS_N_INSNS (40),    /* MXBR B+40 */
+  COSTS_N_INSNS (100),   /* SQXBR B+100 */
+  COSTS_N_INSNS (42),    /* SQDBR B+42 */
+  COSTS_N_INSNS (28),    /* SQEBR B+28 */
+  COSTS_N_INSNS (1),     /* MADBR B */
+  COSTS_N_INSNS (1),     /* MAEBR B */
+  COSTS_N_INSNS (131),   /* DXBR B+131 */
+  COSTS_N_INSNS (29),    /* DDBR */
+  COSTS_N_INSNS (22),    /* DEBR */
+  COSTS_N_INSNS (160),   /* DLGR cracked */
+  COSTS_N_INSNS (160),   /* DLR cracked */
+  COSTS_N_INSNS (160),   /* DR expanded */
+  COSTS_N_INSNS (160),   /* DSGFR cracked */
+  COSTS_N_INSNS (160),   /* DSGR cracked */
+};
 
-/* Save information from a "cmpxx" operation until the branch or scc is
-   emitted.  */
-rtx s390_compare_op0, s390_compare_op1;
+extern int reload_completed;
 
-/* Save the result of a compare_and_swap  until the branch or scc is
-   emitted.  */
-rtx s390_compare_emitted = NULL_RTX;
+/* Kept up to date using the SCHED_VARIABLE_ISSUE hook.  */
+static rtx last_scheduled_insn;
 
 /* Structure used to hold the components of a S/390 memory
    address.  A legitimate address on S/390 is of the general
@@ -228,21 +307,10 @@ struct s390_address
   bool literal_pool;
 };
 
-/* Which cpu are we tuning for.  */
-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;
-
-HOST_WIDE_INT s390_warn_framesize = 0;
-HOST_WIDE_INT s390_stack_size = 0;
-HOST_WIDE_INT s390_stack_guard = 0;
-
-/* The following structure is embedded in the machine 
+/* The following structure is embedded in the machine
    specific part of struct function.  */
 
-struct s390_frame_layout GTY (())
+struct GTY (()) s390_frame_layout
 {
   /* Offset within stack frame.  */
   HOST_WIDE_INT gprs_offset;
@@ -262,12 +330,12 @@ struct s390_frame_layout 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) 
+  /* 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  */
+     Bit 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
+     fpr 15 13 11  9 14 12 10  8  7  5  3  1  6  4  2  0
+     reg 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16  */
   unsigned int fpr_bitmap;
 
   /* Number of floating point registers f8-f15 which must be saved.  */
@@ -285,7 +353,7 @@ struct s390_frame_layout GTY (())
 
 /* Define the structure for the machine field in struct function.  */
 
-struct machine_function GTY(())
+struct GTY(()) machine_function
 {
   struct s390_frame_layout frame_layout;
 
@@ -295,13 +363,14 @@ struct machine_function GTY(())
   /* 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;
+
+  /* True if the current function may contain a tbegin clobbering
+     FPRs.  */
+  bool tbegin_p;
 };
 
 /* Few accessor macros for struct cfun->machine->s390_frame_layout.  */
@@ -309,11 +378,11 @@ struct machine_function GTY(())
 #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))))
+  cfun_frame_layout.first_save_gpr_slot + 1) * UNITS_PER_LONG)
+#define cfun_set_fpr_save(REGNO) (cfun->machine->frame_layout.fpr_bitmap |=    \
+  (1 << (REGNO - FPR0_REGNUM)))
+#define cfun_fpr_save_p(REGNO) (!!(cfun->machine->frame_layout.fpr_bitmap &    \
+  (1 << (REGNO - FPR0_REGNUM))))
 
 /* Number of GPRs and FPRs used for argument passing.  */
 #define GP_ARG_NUM_REG 5
@@ -334,14 +403,67 @@ struct machine_function GTY(())
 #define REGNO_PAIR_OK(REGNO, MODE)                               \
   (HARD_REGNO_NREGS ((REGNO), (MODE)) == 1 || !((REGNO) & 1))
 
+/* That's the read ahead of the dynamic branch prediction unit in
+   bytes on a z10 (or higher) CPU.  */
+#define PREDICT_DISTANCE (TARGET_Z10 ? 384 : 2048)
+
+/* Return the alignment for LABEL.  We default to the -falign-labels
+   value except for the literal pool base label.  */
+int
+s390_label_align (rtx label)
+{
+  rtx prev_insn = prev_active_insn (label);
+
+  if (prev_insn == NULL_RTX)
+    goto old;
+
+  prev_insn = single_set (prev_insn);
+
+  if (prev_insn == NULL_RTX)
+    goto old;
+
+  prev_insn = SET_SRC (prev_insn);
+
+  /* Don't align literal pool base labels.  */
+  if (GET_CODE (prev_insn) == UNSPEC
+      && XINT (prev_insn, 1) == UNSPEC_MAIN_BASE)
+    return 0;
+
+ old:
+  return align_labels_log;
+}
+
+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;
+}
+
+static enum machine_mode
+s390_unwind_word_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)
 {
+  /* In contrast to the default implementation reject TImode constants on 31bit
+     TARGET_ZARCH for ABI compliance.  */
+  if (!TARGET_64BIT && TARGET_ZARCH && mode == TImode)
+    return false;
+
   if (DECIMAL_FLOAT_MODE_P (mode))
-    return true;
-  else
-    return default_scalar_mode_supported_p (mode);
+    return default_decimal_float_supported_p ();
+
+  return default_scalar_mode_supported_p (mode);
 }
 
 /* Set the has_landing_pad_p flag in struct machine_function to VALUE.  */
@@ -378,7 +500,7 @@ s390_cc_modes_compatible (enum machine_mode m1, enum machine_mode m2)
     case CCZ1mode:
       if (m2 == CCZmode)
        return m1;
-      
+
       return VOIDmode;
 
     default:
@@ -488,7 +610,7 @@ s390_tm_ccmode (rtx op1, rtx op2, bool mixed)
   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;
@@ -560,7 +682,7 @@ s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
       case GT:
        /* 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. 
+          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)
@@ -569,10 +691,13 @@ s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
        /* If constants are involved in an add instruction it is possible to use
           the resulting cc for comparisons with zero. Knowing the sign of the
           constant the overflow behavior gets predictable. e.g.:
-            int a, b; if ((b = a + c) > 0)  
+            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))))
+           && (CONST_OK_FOR_K (INTVAL (XEXP (op0, 1)))
+               || (CONST_OK_FOR_CONSTRAINT_P (INTVAL (XEXP (op0, 1)), 'O', "Os")
+                   /* Avoid INT32_MIN on 32 bit.  */
+                   && (!TARGET_ZARCH || INTVAL (XEXP (op0, 1)) != -0x7fffffff - 1))))
          {
            if (INTVAL (XEXP((op0), 1)) < 0)
              return CCANmode;
@@ -623,9 +748,13 @@ s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
 /* 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)
+static void
+s390_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
+                             bool op0_preserve_value)
 {
+  if (op0_preserve_value)
+    return;
+
   /* Convert ZERO_EXTRACT back to AND to enable TM patterns.  */
   if ((*code == EQ || *code == NE)
       && *op1 == const0_rtx
@@ -692,17 +821,16 @@ s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1)
       && GET_CODE (*op1) == CONST_INT
       && INTVAL (*op1) == 0xffff
       && SCALAR_INT_MODE_P (GET_MODE (*op0))
-      && (nonzero_bits (*op0, 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_CMPINT conversions if possible.  */
+  /* Remove redundant UNSPEC_STRCMPCC_TO_INT conversions if possible.  */
   if (GET_CODE (*op0) == UNSPEC
-      && XINT (*op0, 1) == UNSPEC_CMPINT
+      && XINT (*op0, 1) == UNSPEC_STRCMPCC_TO_INT
       && XVECLEN (*op0, 0) == 1
       && GET_MODE (XVECEXP (*op0, 0, 0)) == CCUmode
       && GET_CODE (XVECEXP (*op0, 0, 0)) == REG
@@ -728,6 +856,40 @@ s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1)
        }
     }
 
+  /* Remove redundant UNSPEC_CC_TO_INT conversions if possible.  */
+  if (GET_CODE (*op0) == UNSPEC
+      && XINT (*op0, 1) == UNSPEC_CC_TO_INT
+      && XVECLEN (*op0, 0) == 1
+      && GET_CODE (XVECEXP (*op0, 0, 0)) == REG
+      && REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM
+      && CONST_INT_P (*op1))
+    {
+      enum rtx_code new_code = UNKNOWN;
+      switch (GET_MODE (XVECEXP (*op0, 0, 0)))
+       {
+       case CCZmode:
+       case CCRAWmode:
+         switch (*code)
+           {
+           case EQ: new_code = EQ;  break;
+           case NE: new_code = NE;  break;
+           default: break;
+           }
+         break;
+       default: break;
+       }
+
+      if (new_code != UNKNOWN)
+       {
+         /* For CCRAWmode put the required cc mask into the second
+            operand.  */
+         if (GET_MODE (XVECEXP (*op0, 0, 0)) == CCRAWmode)
+           *op1 = gen_rtx_CONST_INT (VOIDmode, 1 << (3 - INTVAL (*op1)));
+         *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)
@@ -749,7 +911,7 @@ s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1)
   if (MEM_P (*op0) && REG_P (*op1))
     {
       rtx tem = *op0; *op0 = *op1; *op1 = tem;
-      *code = swap_condition (*code);
+      *code = (int)swap_condition ((enum rtx_code)*code);
     }
 }
 
@@ -761,29 +923,44 @@ rtx
 s390_emit_compare (enum rtx_code code, rtx op0, rtx op1)
 {
   enum machine_mode mode = s390_select_ccmode (code, op0, op1);
-  rtx ret = NULL_RTX;
+  rtx cc;
 
   /* 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); 
+  if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC)
+    {
+      gcc_assert (s390_cc_modes_compatible (GET_MODE (op0), mode)
+                 == GET_MODE (op0));
+      cc = op0;
+    }
   else
     {
-      rtx cc = gen_rtx_REG (mode, CC_REGNUM);
-      
+      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;
+
+  return gen_rtx_fmt_ee (code, VOIDmode, cc, const0_rtx);
 }
 
-/* Emit a jump instruction to TARGET.  If COND is NULL_RTX, emit an
-   unconditional jump, else a conditional jump under condition COND.  */
+/* Emit a SImode compare and swap instruction setting MEM to NEW_RTX if OLD
+   matches CMP.
+   Return the correct condition RTL to be placed in the IF_THEN_ELSE of the
+   conditional branch testing the result.  */
 
-void
+static rtx
+s390_emit_compare_and_swap (enum rtx_code code, rtx old, rtx mem,
+                           rtx cmp, rtx new_rtx)
+{
+  emit_insn (gen_atomic_compare_and_swapsi_internal (old, mem, cmp, new_rtx));
+  return s390_emit_compare (code, gen_rtx_REG (CCZ1mode, CC_REGNUM),
+                           const0_rtx);
+}
+
+/* Emit a jump instruction to TARGET and return it.  If COND is
+   NULL_RTX, emit an unconditional jump, else a conditional jump under
+   condition COND.  */
+
+rtx
 s390_emit_jump (rtx target, rtx cond)
 {
   rtx insn;
@@ -793,7 +970,7 @@ s390_emit_jump (rtx target, rtx cond)
     target = gen_rtx_IF_THEN_ELSE (VOIDmode, cond, target, pc_rtx);
 
   insn = gen_rtx_SET (VOIDmode, pc_rtx, target);
-  emit_jump_insn (insn);
+  return emit_jump_insn (insn);
 }
 
 /* Return branch condition mask to implement a branch
@@ -809,7 +986,10 @@ s390_branch_condition_mask (rtx code)
 
   gcc_assert (GET_CODE (XEXP (code, 0)) == REG);
   gcc_assert (REGNO (XEXP (code, 0)) == CC_REGNUM);
-  gcc_assert (XEXP (code, 1) == const0_rtx);
+  gcc_assert (XEXP (code, 1) == const0_rtx
+             || (GET_MODE (XEXP (code, 0)) == CCRAWmode
+                 && CONST_INT_P (XEXP (code, 1))));
+
 
   switch (GET_MODE (XEXP (code, 0)))
     {
@@ -983,11 +1163,57 @@ s390_branch_condition_mask (rtx code)
         }
       break;
 
+    case CCRAWmode:
+      switch (GET_CODE (code))
+       {
+       case EQ:
+         return INTVAL (XEXP (code, 1));
+       case NE:
+         return (INTVAL (XEXP (code, 1))) ^ 0xf;
+       default:
+         gcc_unreachable ();
+       }
+
     default:
       return -1;
     }
 }
 
+
+/* Return branch condition mask to implement a compare and branch
+   specified by CODE.  Return -1 for invalid comparisons.  */
+
+int
+s390_compare_and_branch_condition_mask (rtx code)
+{
+  const int CC0 = 1 << 3;
+  const int CC1 = 1 << 2;
+  const int CC2 = 1 << 1;
+
+  switch (GET_CODE (code))
+    {
+    case EQ:
+      return CC0;
+    case NE:
+      return CC1 | CC2;
+    case LT:
+    case LTU:
+      return CC1;
+    case GT:
+    case GTU:
+      return CC2;
+    case LE:
+    case LEU:
+      return CC0 | CC1;
+    case GE:
+    case GEU:
+      return CC0 | CC2;
+    default:
+      gcc_unreachable ();
+    }
+  return -1;
+}
+
 /* If INV is false, return assembler mnemonic string to implement
    a branch specified by CODE.  If INV is true, return mnemonic
    for the corresponding inverted branch.  */
@@ -995,6 +1221,8 @@ s390_branch_condition_mask (rtx code)
 static const char *
 s390_branch_condition_mnemonic (rtx code, int inv)
 {
+  int mask;
+
   static const char *const mnemonic[16] =
     {
       NULL, "o", "h", "nle",
@@ -1003,7 +1231,15 @@ s390_branch_condition_mnemonic (rtx code, int inv)
       "le", "nh", "no", NULL
     };
 
-  int mask = s390_branch_condition_mask (code);
+  if (GET_CODE (XEXP (code, 0)) == REG
+      && REGNO (XEXP (code, 0)) == CC_REGNUM
+      && (XEXP (code, 1) == const0_rtx
+         || (GET_MODE (XEXP (code, 0)) == CCRAWmode
+             && CONST_INT_P (XEXP (code, 1)))))
+    mask = s390_branch_condition_mask (code);
+  else
+    mask = s390_compare_and_branch_condition_mask (code);
+
   gcc_assert (mask >= 0);
 
   if (inv)
@@ -1080,6 +1316,85 @@ s390_single_part (rtx op,
   return part == -1 ? -1 : n_parts - 1 - part;
 }
 
+/* Return true if IN contains a contiguous bitfield in the lower SIZE
+   bits and no other bits are set in IN.  POS and LENGTH can be used
+   to obtain the start position and the length of the bitfield.
+
+   POS gives the position of the first bit of the bitfield counting
+   from the lowest order bit starting with zero.  In order to use this
+   value for S/390 instructions this has to be converted to "bits big
+   endian" style.  */
+
+bool
+s390_contiguous_bitmask_p (unsigned HOST_WIDE_INT in, int size,
+                          int *pos, int *length)
+{
+  int tmp_pos = 0;
+  int tmp_length = 0;
+  int i;
+  unsigned HOST_WIDE_INT mask = 1ULL;
+  bool contiguous = false;
+
+  for (i = 0; i < size; mask <<= 1, i++)
+    {
+      if (contiguous)
+       {
+         if (mask & in)
+           tmp_length++;
+         else
+           break;
+       }
+      else
+       {
+         if (mask & in)
+           {
+             contiguous = true;
+             tmp_length++;
+           }
+         else
+           tmp_pos++;
+       }
+    }
+
+  if (!tmp_length)
+    return false;
+
+  /* Calculate a mask for all bits beyond the contiguous bits.  */
+  mask = (-1LL & ~(((1ULL << (tmp_length + tmp_pos - 1)) << 1) - 1));
+
+  if (mask & in)
+    return false;
+
+  if (tmp_length + tmp_pos - 1 > size)
+    return false;
+
+  if (length)
+    *length = tmp_length;
+
+  if (pos)
+    *pos = tmp_pos;
+
+  return true;
+}
+
+/* Check whether a rotate of ROTL followed by an AND of CONTIG is
+   equivalent to a shift followed by the AND.  In particular, CONTIG
+   should not overlap the (rotated) bit 0/bit 63 gap.  Negative values
+   for ROTL indicate a rotate to the right.  */
+
+bool
+s390_extzv_shift_ok (int bitsize, int rotl, unsigned HOST_WIDE_INT contig)
+{
+  int pos, len;
+  bool ok;
+
+  ok = s390_contiguous_bitmask_p (contig, bitsize, &pos, &len);
+  gcc_assert (ok);
+
+  return ((rotl >= 0 && rotl <= pos)
+         || (rotl < 0 && -rotl <= bitsize - len - pos));
+}
+
 /* Check whether we can (and want to) split a double-word
    move in mode MODE from SRC to DST into two single-word
    moves, moving the subword FIRST_SUBWORD first.  */
@@ -1135,9 +1450,9 @@ s390_overlap_p (rtx mem1, rtx mem2, HOST_WIDE_INT size)
 
   /* 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 
+     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 
+     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.  */
@@ -1273,105 +1588,11 @@ s390_narrow_logical_operator (enum rtx_code code, rtx *memop, rtx *immop)
 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.
-
-   LEVEL is the optimization level specified; 2 if `-O2' is
-   specified, 1 if `-O' is specified, and 0 if neither is specified.
-
-   SIZE is nonzero if `-Os' is specified and zero otherwise.  */
-
-void
-optimization_options (int level ATTRIBUTE_UNUSED, int size ATTRIBUTE_UNUSED)
-{
-  /* ??? There are apparently still problems with -fcaller-saves.  */
-  flag_caller_saves = 0;
-
-  /* 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;
-}
-
-/* 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)
-{
-  static struct pta
-    {
-      const char *const name;          /* processor name or nickname.  */
-      const enum processor_type processor;
-      const enum processor_flags flags;
-    }
-  const processor_alias_table[] =
-    {
-      {"g5", PROCESSOR_9672_G5, PF_IEEE_FLOAT},
-      {"g6", PROCESSOR_9672_G6, PF_IEEE_FLOAT},
-      {"z900", PROCESSOR_2064_Z900, PF_IEEE_FLOAT | PF_ZARCH},
-      {"z990", PROCESSOR_2084_Z990, PF_IEEE_FLOAT | PF_ZARCH
-                                   | PF_LONG_DISPLACEMENT},
-      {"z9-109", PROCESSOR_2094_Z9_109, PF_IEEE_FLOAT | PF_ZARCH
-                                       | PF_LONG_DISPLACEMENT | PF_EXTIMM},
-    };
-  size_t i;
-
-  for (i = 0; i < ARRAY_SIZE (processor_alias_table); i++)
-    if (strcmp (arg, processor_alias_table[i].name) == 0)
-      {
-       *type = processor_alias_table[i].processor;
-       *flags = processor_alias_table[i].flags;
-       return true;
-      }
-  return false;
-}
-
-/* Implement TARGET_HANDLE_OPTION.  */
-
-static bool
-s390_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
-{
-  switch (code)
-    {
-    case OPT_march_:
-      return s390_handle_arch_option (arg, &s390_arch, &s390_arch_flags);
-
-    case OPT_mstack_guard_:
-      if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_guard) != 1)
-       return false;
-      if (exact_log2 (s390_stack_guard) == -1)
-       error ("stack guard value must be an exact power of 2");
-      return true;
-
-    case OPT_mstack_size_:
-      if (sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_stack_size) != 1)
-       return false;
-      if (exact_log2 (s390_stack_size) == -1)
-       error ("stack size must be an exact power of 2");
-      return true;
-
-    case OPT_mtune_:
-      return s390_handle_arch_option (arg, &s390_tune, &s390_tune_flags);
-
-    case OPT_mwarn_framesize_:
-      return sscanf (arg, HOST_WIDE_INT_PRINT_DEC, &s390_warn_framesize) == 1;
-
-    default:
-      return true;
-    }
+  return ggc_alloc_cleared_machine_function ();
 }
 
-void
-override_options (void)
+static void
+s390_option_override (void)
 {
   /* Set up function hooks.  */
   init_machine_status = s390_init_machine_status;
@@ -1385,11 +1606,13 @@ override_options (void)
        target_flags &= ~MASK_ZARCH;
     }
 
-  /* Determine processor architectural level.  */
-  if (!s390_arch_string)
+  /* Set the march default in case it hasn't been specified on
+     cmdline.  */
+  if (s390_arch == PROCESSOR_max)
     {
       s390_arch_string = TARGET_ZARCH? "z900" : "g5";
-      s390_handle_arch_option (s390_arch_string, &s390_arch, &s390_arch_flags);
+      s390_arch = TARGET_ZARCH ? PROCESSOR_2064_Z900 : PROCESSOR_9672_G5;
+      s390_arch_flags = processor_flags_table[(int)s390_arch];
     }
 
   /* Determine processor to tune for.  */
@@ -1400,39 +1623,146 @@ override_options (void)
     }
 
   /* Sanity checks.  */
-  if (TARGET_ZARCH && !(s390_arch_flags & PF_ZARCH))
+  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");
 
+  /* Use hardware DFP if available and not explicitly disabled by
+     user. E.g. with -m31 -march=z10 -mzarch   */
+  if (!(target_flags_explicit & MASK_HARD_DFP) && TARGET_DFP)
+    target_flags |= MASK_HARD_DFP;
+
+  /* Enable hardware transactions if available and not explicitly
+     disabled by user.  E.g. with -m31 -march=zEC12 -mzarch */
+  if (!(target_flags_explicit & MASK_OPT_HTM) && TARGET_CPU_HTM && TARGET_ZARCH)
+    target_flags |= MASK_OPT_HTM;
+
+  if (TARGET_HARD_DFP && !TARGET_DFP)
+    {
+      if (target_flags_explicit & MASK_HARD_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_HARD_DFP;
+    }
+
+  if ((target_flags_explicit & MASK_SOFT_FLOAT) && TARGET_SOFT_FLOAT)
+    {
+      if ((target_flags_explicit & MASK_HARD_DFP) && TARGET_HARD_DFP)
+       error ("-mhard-dfp can%'t be used in conjunction with -msoft-float");
+
+      target_flags &= ~MASK_HARD_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;
-  
+  switch (s390_tune)
+    {
+    case PROCESSOR_2084_Z990:
+      s390_cost = &z990_cost;
+      break;
+    case PROCESSOR_2094_Z9_109:
+      s390_cost = &z9_109_cost;
+      break;
+    case PROCESSOR_2097_Z10:
+      s390_cost = &z10_cost;
+      break;
+    case PROCESSOR_2817_Z196:
+      s390_cost = &z196_cost;
+      break;
+    case PROCESSOR_2827_ZEC12:
+      s390_cost = &zEC12_cost;
+      break;
+    default:
+      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)
     {
-      if (!s390_stack_guard)
-       error ("-mstack-size implies use of -mstack-guard");
-      else if (s390_stack_guard >= s390_stack_size)
+      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"); 
+    error ("-mstack-guard implies use of -mstack-size");
 
 #ifdef TARGET_DEFAULT_LONG_DOUBLE_128
   if (!(target_flags_explicit & MASK_LONG_DOUBLE_128))
     target_flags |= MASK_LONG_DOUBLE_128;
 #endif
+
+  if (s390_tune == PROCESSOR_2097_Z10
+      || s390_tune == PROCESSOR_2817_Z196
+      || s390_tune == PROCESSOR_2827_ZEC12)
+    {
+      maybe_set_param_value (PARAM_MAX_UNROLLED_INSNS, 100,
+                            global_options.x_param_values,
+                            global_options_set.x_param_values);
+      maybe_set_param_value (PARAM_MAX_UNROLL_TIMES, 32,
+                            global_options.x_param_values,
+                            global_options_set.x_param_values);
+      maybe_set_param_value (PARAM_MAX_COMPLETELY_PEELED_INSNS, 2000,
+                            global_options.x_param_values,
+                            global_options_set.x_param_values);
+      maybe_set_param_value (PARAM_MAX_COMPLETELY_PEEL_TIMES, 64,
+                            global_options.x_param_values,
+                            global_options_set.x_param_values);
+    }
+
+  maybe_set_param_value (PARAM_MAX_PENDING_LIST_LENGTH, 256,
+                        global_options.x_param_values,
+                        global_options_set.x_param_values);
+  /* values for loop prefetching */
+  maybe_set_param_value (PARAM_L1_CACHE_LINE_SIZE, 256,
+                        global_options.x_param_values,
+                        global_options_set.x_param_values);
+  maybe_set_param_value (PARAM_L1_CACHE_SIZE, 128,
+                        global_options.x_param_values,
+                        global_options_set.x_param_values);
+  /* s390 has more than 2 levels and the size is much larger.  Since
+     we are always running virtualized assume that we only get a small
+     part of the caches above l1.  */
+  maybe_set_param_value (PARAM_L2_CACHE_SIZE, 1500,
+                        global_options.x_param_values,
+                        global_options_set.x_param_values);
+  maybe_set_param_value (PARAM_PREFETCH_MIN_INSN_TO_MEM_RATIO, 2,
+                        global_options.x_param_values,
+                        global_options_set.x_param_values);
+  maybe_set_param_value (PARAM_SIMULTANEOUS_PREFETCHES, 6,
+                        global_options.x_param_values,
+                        global_options_set.x_param_values);
+
+  /* This cannot reside in s390_option_optimization_table since HAVE_prefetch
+     requires the arch flags to be evaluated already.  Since prefetching
+     is beneficial on s390, we enable it if available.  */
+  if (flag_prefetch_loop_arrays < 0 && HAVE_prefetch && optimize >= 3)
+    flag_prefetch_loop_arrays = 1;
+
+  /* Use the alternative scheduling-pressure algorithm by default.  */
+  maybe_set_param_value (PARAM_SCHED_PRESSURE_ALGORITHM, 2,
+                         global_options.x_param_values,
+                         global_options_set.x_param_values);
+
+  if (TARGET_TPF)
+    {
+      /* Don't emit DWARF3/4 unless specifically selected.  The TPF
+        debuggers do not yet support DWARF 3/4.  */
+      if (!global_options_set.x_dwarf_strict) 
+       dwarf_strict = 1;
+      if (!global_options_set.x_dwarf_version)
+       dwarf_version = 2;
+    }
 }
 
 /* Map for smallest class containing reg regno.  */
@@ -1470,6 +1800,11 @@ s390_short_displacement (rtx disp)
   if (!disp)
     return true;
 
+  /* Without the long displacement facility we don't need to
+     distingiush between long and short displacement.  */
+  if (!TARGET_LONG_DISPLACEMENT)
+    return true;
+
   /* Integer displacement in range.  */
   if (GET_CODE (disp) == CONST_INT)
     return INTVAL (disp) >= 0 && INTVAL (disp) < 4096;
@@ -1610,7 +1945,7 @@ s390_decompose_address (rtx addr, struct s390_address *out)
          {
          case UNSPEC_LTREF:
            if (!disp)
-             disp = gen_rtx_UNSPEC (Pmode, 
+             disp = gen_rtx_UNSPEC (Pmode,
                                     gen_rtvec (1, XVECEXP (base, 0, 0)),
                                     UNSPEC_LTREL_OFFSET);
            else
@@ -1630,8 +1965,8 @@ s390_decompose_address (rtx addr, struct s390_address *out)
            return false;
          }
 
-      if (!REG_P (base) 
-         || (GET_MODE (base) != SImode 
+      if (!REG_P (base)
+         || (GET_MODE (base) != SImode
              && GET_MODE (base) != Pmode))
        return false;
 
@@ -1658,7 +1993,7 @@ s390_decompose_address (rtx addr, struct s390_address *out)
          {
          case UNSPEC_LTREF:
            if (!disp)
-             disp = gen_rtx_UNSPEC (Pmode, 
+             disp = gen_rtx_UNSPEC (Pmode,
                                     gen_rtvec (1, XVECEXP (indx, 0, 0)),
                                     UNSPEC_LTREL_OFFSET);
            else
@@ -1678,7 +2013,7 @@ s390_decompose_address (rtx addr, struct s390_address *out)
            return false;
          }
 
-      if (!REG_P (indx) 
+      if (!REG_P (indx)
          || (GET_MODE (indx) != SImode
              && GET_MODE (indx) != Pmode))
        return false;
@@ -1710,22 +2045,26 @@ s390_decompose_address (rtx addr, struct s390_address *out)
   /* 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 
+      /* 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)
+      /* LRA maintains always displacements up to date and we need to
+        know the displacement is right during all LRA not only at the
+        final elimination.  */
+      if (lra_in_progress
+         || (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;
     }
@@ -1744,20 +2083,25 @@ s390_decompose_address (rtx addr, struct s390_address *out)
          ;
         }
 
-      /* 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 pool label offsets.  */
+      else if (GET_CODE (disp) == UNSPEC
+              && XINT (disp, 1) == UNSPEC_POOL_OFFSET)
+       ;
 
       /* Accept literal pool references.  */
       else if (GET_CODE (disp) == UNSPEC
               && XINT (disp, 1) == UNSPEC_LTREL_OFFSET)
         {
+         /* In case CSE pulled a non literal pool reference out of
+            the pool we have to reject the address.  This is
+            especially important when loading the GOT pointer on non
+            zarch CPUs.  In this case the literal pool contains an lt
+            relative offset to the _GLOBAL_OFFSET_TABLE_ label which
+            will most likely exceed the displacement.  */
+         if (GET_CODE (XVECEXP (disp, 0, 0)) != SYMBOL_REF
+             || !CONSTANT_POOL_ADDRESS_P (XVECEXP (disp, 0, 0)))
+           return false;
+
          orig_disp = gen_rtx_CONST (Pmode, disp);
          if (offset)
            {
@@ -1767,7 +2111,7 @@ s390_decompose_address (rtx addr, struct s390_address *out)
              if (offset >= GET_MODE_SIZE (get_pool_mode (sym)))
                return false;
 
-              orig_disp = plus_constant (orig_disp, offset);
+              orig_disp = plus_constant (Pmode, orig_disp, offset);
            }
         }
 
@@ -1843,79 +2187,100 @@ s390_legitimate_address_without_index_p (rtx op)
 }
 
 
-/* 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.  */
+/* Return TRUE if ADDR is an operand valid for a load/store relative
+   instruction.  Be aware that the alignment of the operand needs to
+   be checked separately.
+   Valid addresses are single references or a sum of a reference and a
+   constant integer. Return these parts in SYMREF and ADDEND.  You can
+   pass NULL in REF and/or ADDEND if you are not interested in these
+   values.  Literal pool references are *not* considered symbol
+   references.  */
 
-int
-s390_mem_constraint (const char *str, rtx op)
+static bool
+s390_loadrelative_operand_p (rtx addr, rtx *symref, HOST_WIDE_INT *addend)
 {
-  struct s390_address addr;
-  char c = str[0];
+  HOST_WIDE_INT tmpaddend = 0;
 
-  /* Check for offsettable variants of memory constraints.  */
-  if (c == 'A')
+  if (GET_CODE (addr) == CONST)
+    addr = XEXP (addr, 0);
+
+  if (GET_CODE (addr) == PLUS)
     {
-      /* Only accept non-volatile MEMs.  */
-      if (!MEM_P (op) || MEM_VOLATILE_P (op))
-       return 0;
+      if (!CONST_INT_P (XEXP (addr, 1)))
+       return false;
 
-      if ((reload_completed || reload_in_progress)
-         ? !offsettable_memref_p (op) : !offsettable_nonstrict_memref_p (op))
-       return 0;
+      tmpaddend = INTVAL (XEXP (addr, 1));
+      addr = XEXP (addr, 0);
+    }
+
+  if ((GET_CODE (addr) == SYMBOL_REF && !CONSTANT_POOL_ADDRESS_P (addr))
+      || (GET_CODE (addr) == UNSPEC
+         && (XINT (addr, 1) == UNSPEC_GOTENT
+             || (TARGET_CPU_ZARCH && XINT (addr, 1) == UNSPEC_PLT))))
+    {
+      if (symref)
+       *symref = addr;
+      if (addend)
+       *addend = tmpaddend;
 
-      c = str[1];
+      return true;
     }
+  return false;
+}
+
+/* Return true if the address in OP is valid for constraint letter C
+   if wrapped in a MEM rtx.  Set LIT_POOL_OK to true if it literal
+   pool MEMs should be accepted.  Only the Q, R, S, T constraint
+   letters are allowed for C.  */
 
-  /* Check for non-literal-pool variants of memory constraints.  */
-  else if (c == 'B')
+static int
+s390_check_qrst_address (char c, rtx op, bool lit_pool_ok)
+{
+  struct s390_address addr;
+  bool decomposed = false;
+
+  /* This check makes sure that no symbolic address (except literal
+     pool references) are accepted by the R or T constraints.  */
+  if (s390_loadrelative_operand_p (op, NULL, NULL))
+    return 0;
+
+  /* Ensure literal pool references are only accepted if LIT_POOL_OK.  */
+  if (!lit_pool_ok)
     {
-      if (GET_CODE (op) != MEM)
-       return 0;
-      if (!s390_decompose_address (XEXP (op, 0), &addr))
+      if (!s390_decompose_address (op, &addr))
        return 0;
       if (addr.literal_pool)
        return 0;
-
-      c = str[1];
+      decomposed = true;
     }
 
   switch (c)
     {
-    case 'Q':
-      if (GET_CODE (op) != MEM)
-       return 0;
-      if (!s390_decompose_address (XEXP (op, 0), &addr))
+    case 'Q': /* no index short displacement */
+      if (!decomposed && !s390_decompose_address (op, &addr))
        return 0;
       if (addr.indx)
        return 0;
-
-      if (TARGET_LONG_DISPLACEMENT)
-       {
-         if (!s390_short_displacement (addr.disp))
-           return 0;
-       }
-      break;
-
-    case 'R':
-      if (GET_CODE (op) != MEM)
+      if (!s390_short_displacement (addr.disp))
        return 0;
+      break;
 
+    case 'R': /* with index short displacement */
       if (TARGET_LONG_DISPLACEMENT)
        {
-         if (!s390_decompose_address (XEXP (op, 0), &addr))
+         if (!decomposed && !s390_decompose_address (op, &addr))
            return 0;
          if (!s390_short_displacement (addr.disp))
            return 0;
        }
+      /* Any invalid address here will be fixed up by reload,
+        so accept it for the most generic constraint.  */
       break;
 
-    case 'S':
+    case 'S': /* no index long displacement */
       if (!TARGET_LONG_DISPLACEMENT)
        return 0;
-      if (GET_CODE (op) != MEM)
-       return 0;
-      if (!s390_decompose_address (XEXP (op, 0), &addr))
+      if (!decomposed && !s390_decompose_address (op, &addr))
        return 0;
       if (addr.indx)
        return 0;
@@ -1923,54 +2288,74 @@ s390_mem_constraint (const char *str, rtx op)
        return 0;
       break;
 
-    case 'T':
+    case 'T': /* with index long displacement */
       if (!TARGET_LONG_DISPLACEMENT)
        return 0;
-      if (GET_CODE (op) != MEM)
-       return 0;
       /* Any invalid address here will be fixed up by reload,
         so accept it for the most generic constraint.  */
-      if (s390_decompose_address (XEXP (op, 0), &addr)
+      if ((decomposed || s390_decompose_address (op, &addr))
          && s390_short_displacement (addr.disp))
        return 0;
       break;
+    default:
+      return 0;
+    }
+  return 1;
+}
 
-    case 'U':
-      if (TARGET_LONG_DISPLACEMENT)
-       {
-         if (!s390_decompose_address (op, &addr))
-           return 0;
-         if (!s390_short_displacement (addr.disp))
-           return 0;
-       }
-      break;
 
-    case 'W':
-      if (!TARGET_LONG_DISPLACEMENT)
+/* Evaluates constraint strings described by the regular expression
+   ([A|B|Z](Q|R|S|T))|U|W|Y and returns 1 if OP is a valid operand for
+   the constraint given in STR, or 0 else.  */
+
+int
+s390_mem_constraint (const char *str, rtx op)
+{
+  char c = str[0];
+
+  switch (c)
+    {
+    case 'A':
+      /* Check for offsettable variants of memory constraints.  */
+      if (!MEM_P (op) || MEM_VOLATILE_P (op))
        return 0;
-      /* Any invalid address here will be fixed up by reload,
-        so accept it for the most generic constraint.  */
-      if (s390_decompose_address (op, &addr)
-         && s390_short_displacement (addr.disp))
+      if ((reload_completed || reload_in_progress)
+         ? !offsettable_memref_p (op) : !offsettable_nonstrict_memref_p (op))
        return 0;
-      break;
-
+      return s390_check_qrst_address (str[1], XEXP (op, 0), true);
+    case 'B':
+      /* Check for non-literal-pool variants of memory constraints.  */
+      if (!MEM_P (op))
+       return 0;
+      return s390_check_qrst_address (str[1], XEXP (op, 0), false);
+    case 'Q':
+    case 'R':
+    case 'S':
+    case 'T':
+      if (GET_CODE (op) != MEM)
+       return 0;
+      return s390_check_qrst_address (c, XEXP (op, 0), true);
+    case 'U':
+      return (s390_check_qrst_address ('Q', op, true)
+             || s390_check_qrst_address ('R', op, true));
+    case 'W':
+      return (s390_check_qrst_address ('S', op, true)
+             || s390_check_qrst_address ('T', op, true));
     case 'Y':
       /* Simply check for the basic form of a shift count.  Reload will
         take care of making sure we have a proper base register.  */
       if (!s390_decompose_shift_count (op, NULL, NULL))
        return 0;
       break;
-
+    case 'Z':
+      return s390_check_qrst_address (str[1], op, true);
     default:
       return 0;
     }
-
   return 1;
 }
 
 
-
 /* 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
@@ -1992,8 +2377,7 @@ s390_O_constraint_str (const char c, HOST_WIDE_INT value)
        || s390_single_part (GEN_INT (value), DImode, SImode, 0) == 1;
 
     case 'n':
-      return value == -1
-       || s390_single_part (GEN_INT (value), DImode, SImode, -1) == 1;
+      return s390_single_part (GEN_INT (value - 1), DImode, SImode, -1) == 1;
 
     default:
       gcc_unreachable ();
@@ -2082,15 +2466,43 @@ s390_float_const_zero_p (rtx value)
          && value == CONST0_RTX (GET_MODE (value)));
 }
 
+/* Implement TARGET_REGISTER_MOVE_COST.  */
+
+static int
+s390_register_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+                         reg_class_t from, reg_class_t to)
+{
+  /* On s390, copy between fprs and gprs is expensive as long as no
+     ldgr/lgdr can be used.  */
+  if ((!TARGET_Z10 || GET_MODE_SIZE (mode) != 8)
+      && ((reg_classes_intersect_p (from, GENERAL_REGS)
+          && reg_classes_intersect_p (to, FP_REGS))
+         || (reg_classes_intersect_p (from, FP_REGS)
+             && reg_classes_intersect_p (to, GENERAL_REGS))))
+    return 10;
+
+  return 1;
+}
+
+/* Implement TARGET_MEMORY_MOVE_COST.  */
+
+static int
+s390_memory_move_cost (enum machine_mode mode ATTRIBUTE_UNUSED,
+                      reg_class_t rclass ATTRIBUTE_UNUSED,
+                      bool in ATTRIBUTE_UNUSED)
+{
+  return 1;
+}
 
 /* Compute a (partial) cost for rtx X.  Return true if the complete
    cost has been computed, and false if subexpressions should be
-   scanned.  In either case, *TOTAL contains the cost result.  
-   CODE contains GET_CODE (x), OUTER_CODE contains the code 
+   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)
+s390_rtx_costs (rtx x, int code, int outer_code, int opno ATTRIBUTE_UNUSED,
+               int *total, bool speed ATTRIBUTE_UNUSED)
 {
   switch (code)
     {
@@ -2118,25 +2530,10 @@ s390_rtx_costs (rtx x, int code, int outer_code, int *total)
 
     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_IEEE_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:      
+    case MULT:
       switch (GET_MODE (x))
        {
        case SImode:
@@ -2156,7 +2553,7 @@ s390_rtx_costs (rtx x, int code, int outer_code, int *total)
          {
            rtx left = XEXP (x, 0);
            rtx right = XEXP (x, 1);
-           if (TARGET_64BIT)
+           if (TARGET_ZARCH)
              {
                if (GET_CODE (right) == CONST_INT
                    && CONST_OK_FOR_K (INTVAL (right)))
@@ -2195,6 +2592,28 @@ s390_rtx_costs (rtx x, int code, int outer_code, int *total)
        }
       return false;
 
+    case FMA:
+      switch (GET_MODE (x))
+       {
+       case DFmode:
+         *total = s390_cost->madbr;
+         break;
+       case SFmode:
+         *total = s390_cost->maebr;
+         break;
+       default:
+         return false;
+       }
+      /* Negate in the third argument is free: FMSUB.  */
+      if (GET_CODE (XEXP (x, 2)) == NEG)
+       {
+         *total += (rtx_cost (XEXP (x, 0), FMA, 0, speed)
+                    + rtx_cost (XEXP (x, 1), FMA, 1, speed)
+                    + rtx_cost (XEXP (XEXP (x, 2), 0), FMA, 2, speed));
+         return true;
+       }
+      return false;
+
     case UDIV:
     case UMOD:
       if (GET_MODE (x) == TImode)             /* 128 bit division */
@@ -2217,7 +2636,7 @@ s390_rtx_costs (rtx x, int code, int outer_code, int *total)
        {
          rtx right = XEXP (x, 1);
          if (GET_CODE (right) == ZERO_EXTEND) /* 64 by 32 bit division */
-           if (TARGET_64BIT)
+           if (TARGET_ZARCH)
              *total = s390_cost->dsgfr;
            else
              *total = s390_cost->dr;
@@ -2228,24 +2647,15 @@ s390_rtx_costs (rtx x, int code, int outer_code, int *total)
        *total = s390_cost->dlr;
       else if (GET_MODE (x) == SFmode)
        {
-         if (TARGET_IEEE_FLOAT)
-           *total = s390_cost->debr;
-         else /* TARGET_IBM_FLOAT */
-           *total = s390_cost->der;
+         *total = s390_cost->debr;
        }
       else if (GET_MODE (x) == DFmode)
        {
-         if (TARGET_IEEE_FLOAT)
-           *total = s390_cost->ddbr;
-         else /* TARGET_IBM_FLOAT */
-           *total = s390_cost->ddr;
+         *total = s390_cost->ddbr;
        }
       else if (GET_MODE (x) == TFmode)
        {
-         if (TARGET_IEEE_FLOAT)
-           *total = s390_cost->dxbr;
-         else /* TARGET_IBM_FLOAT */
-           *total = s390_cost->dxr;
+         *total = s390_cost->dxbr;
        }
       return false;
 
@@ -2293,7 +2703,9 @@ s390_rtx_costs (rtx x, int code, int outer_code, int *total)
 /* Return the cost of an address rtx ADDR.  */
 
 static int
-s390_address_cost (rtx addr)
+s390_address_cost (rtx addr, enum machine_mode mode ATTRIBUTE_UNUSED,
+                  addr_space_t as ATTRIBUTE_UNUSED,
+                  bool speed ATTRIBUTE_UNUSED)
 {
   struct s390_address ad;
   if (!s390_decompose_address (addr, &ad))
@@ -2410,15 +2822,15 @@ legitimate_pic_operand_p (rtx op)
 /* Returns true if the constant value OP is a legitimate general operand.
    It is given that OP satisfies CONSTANT_P or is a CONST_DOUBLE.  */
 
-int
-legitimate_constant_p (rtx op)
+static bool
+s390_legitimate_constant_p (enum machine_mode mode, rtx op)
 {
   /* Accept all non-symbolic constants.  */
   if (!SYMBOLIC_CONST (op))
     return 1;
 
   /* Accept immediate LARL operands.  */
-  if (TARGET_CPU_ZARCH && larl_operand (op, VOIDmode))
+  if (TARGET_CPU_ZARCH && larl_operand (op, mode))
     return 1;
 
   /* Thread-local symbols are never legal constants.  This is
@@ -2443,7 +2855,7 @@ legitimate_constant_p (rtx op)
    not constant (TLS) or not known at final link time (PIC).  */
 
 static bool
-s390_cannot_force_const_mem (rtx x)
+s390_cannot_force_const_mem (enum machine_mode mode, rtx x)
 {
   switch (GET_CODE (x))
     {
@@ -2465,11 +2877,11 @@ s390_cannot_force_const_mem (rtx x)
        return flag_pic != 0;
 
     case CONST:
-      return s390_cannot_force_const_mem (XEXP (x, 0));
+      return s390_cannot_force_const_mem (mode, XEXP (x, 0));
     case PLUS:
     case MINUS:
-      return s390_cannot_force_const_mem (XEXP (x, 0))
-            || s390_cannot_force_const_mem (XEXP (x, 1));
+      return s390_cannot_force_const_mem (mode, XEXP (x, 0))
+            || s390_cannot_force_const_mem (mode, XEXP (x, 1));
 
     case UNSPEC:
       switch (XINT (x, 1))
@@ -2506,7 +2918,9 @@ s390_cannot_force_const_mem (rtx x)
    operand during and after reload.  The difference to
    legitimate_constant_p is that this function will not accept
    a constant that would need to be forced to the literal pool
-   before it can be used as operand.  */
+   before it can be used as operand.
+   This function accepts all constants which can be loaded directly
+   into a GPR.  */
 
 bool
 legitimate_reload_constant_p (rtx op)
@@ -2539,9 +2953,10 @@ legitimate_reload_constant_p (rtx op)
       && larl_operand (op, VOIDmode))
     return true;
 
-  /* Accept lzXX operands.  */
+  /* Accept floating-point zero operands that fit into a single GPR.  */
   if (GET_CODE (op) == CONST_DOUBLE
-      && CONST_DOUBLE_OK_FOR_CONSTRAINT_P (op, 'G', "G"))
+      && s390_float_const_zero_p (op)
+      && GET_MODE_SIZE (GET_MODE (op)) <= UNITS_PER_WORD)
     return true;
 
   /* Accept double-word operands that can be split.  */
@@ -2559,34 +2974,76 @@ legitimate_reload_constant_p (rtx op)
   return false;
 }
 
-/* Given an rtx OP being reloaded into a reg required to be in class CLASS,
+/* Returns true if the constant value OP is a legitimate fp operand
+   during and after reload.
+   This function accepts all constants which can be loaded directly
+   into an FPR.  */
+
+static bool
+legitimate_reload_fp_constant_p (rtx op)
+{
+  /* Accept floating-point zero operands if the load zero instruction
+     can be used.  Prior to z196 the load fp zero instruction caused a
+     performance penalty if the result is used as BFP number.  */
+  if (TARGET_Z196
+      && GET_CODE (op) == CONST_DOUBLE
+      && s390_float_const_zero_p (op))
+    return true;
+
+  return false;
+}
+
+/* Given an rtx OP being reloaded into a reg required to be in class RCLASS,
    return the class of reg to actually use.  */
 
-enum reg_class
-s390_preferred_reload_class (rtx op, enum reg_class class)
+static reg_class_t
+s390_preferred_reload_class (rtx op, reg_class_t rclass)
 {
   switch (GET_CODE (op))
     {
-      /* Constants we cannot reload must be forced into the
-        literal pool.  */
-
+      /* Constants we cannot reload into general registers
+        must be forced into the literal pool.  */
       case CONST_DOUBLE:
       case CONST_INT:
-       if (legitimate_reload_constant_p (op))
-         return class;
-       else
-         return NO_REGS;
+       if (reg_class_subset_p (GENERAL_REGS, rclass)
+           && legitimate_reload_constant_p (op))
+         return GENERAL_REGS;
+       else if (reg_class_subset_p (ADDR_REGS, rclass)
+                && legitimate_reload_constant_p (op))
+         return ADDR_REGS;
+       else if (reg_class_subset_p (FP_REGS, rclass)
+                && legitimate_reload_fp_constant_p (op))
+         return FP_REGS;
+       return NO_REGS;
 
       /* If a symbolic constant or a PLUS is reloaded,
         it is most likely being used as an address, so
         prefer ADDR_REGS.  If 'class' is not a superset
         of ADDR_REGS, e.g. FP_REGS, reject this reload.  */
-      case PLUS:
+      case CONST:
+       /* A larl operand with odd addend will get fixed via secondary
+          reload.  So don't request it to be pushed into literal
+          pool.  */
+       if (TARGET_CPU_ZARCH
+           && GET_CODE (XEXP (op, 0)) == PLUS
+           && GET_CODE (XEXP (XEXP(op, 0), 0)) == SYMBOL_REF
+           && GET_CODE (XEXP (XEXP(op, 0), 1)) == CONST_INT)
+         {
+           if (reg_class_subset_p (ADDR_REGS, rclass))
+             return ADDR_REGS;
+           else
+             return NO_REGS;
+         }
+       /* fallthrough */
       case LABEL_REF:
       case SYMBOL_REF:
-      case CONST:
-       if (reg_class_subset_p (ADDR_REGS, class))
-          return ADDR_REGS;
+       if (!legitimate_reload_constant_p (op))
+          return NO_REGS;
+       /* fallthrough */
+      case PLUS:
+       /* load address will be used.  */
+       if (reg_class_subset_p (ADDR_REGS, rclass))
+         return ADDR_REGS;
        else
          return NO_REGS;
 
@@ -2594,70 +3051,231 @@ s390_preferred_reload_class (rtx op, enum reg_class class)
        break;
     }
 
-  return class;
+  return rclass;
 }
 
-/* Return the register class of a scratch register needed to
-   load IN into a register of class CLASS in MODE.
+/* Return true if ADDR is SYMBOL_REF + addend with addend being a
+   multiple of ALIGNMENT and the SYMBOL_REF being naturally
+   aligned.  */
+
+bool
+s390_check_symref_alignment (rtx addr, HOST_WIDE_INT alignment)
+{
+  HOST_WIDE_INT addend;
+  rtx symref;
+
+  if (!s390_loadrelative_operand_p (addr, &symref, &addend))
+    return false;
+
+  if (addend & (alignment - 1))
+    return false;
+
+  if (GET_CODE (symref) == SYMBOL_REF
+      && !SYMBOL_REF_NOT_NATURALLY_ALIGNED_P (symref))
+    return true;
+
+  if (GET_CODE (symref) == UNSPEC
+      && alignment <= UNITS_PER_LONG)
+    return true;
+
+  return false;
+}
 
-   We need a temporary when loading a PLUS expression which
-   is not a legitimate operand of the LOAD ADDRESS instruction.  */
+/* ADDR is moved into REG using larl.  If ADDR isn't a valid larl
+   operand SCRATCH is used to reload the even part of the address and
+   adding one.  */
 
-enum reg_class
-s390_secondary_input_reload_class (enum reg_class class,
-                                  enum machine_mode mode, rtx in)
+void
+s390_reload_larl_operand (rtx reg, rtx addr, rtx scratch)
 {
-  if (s390_plus_operand (in, mode))
-    return ADDR_REGS;
+  HOST_WIDE_INT addend;
+  rtx symref;
 
-  if (reg_classes_intersect_p (FP_REGS, class)
-      && mode == TFmode
-      && GET_CODE (in) == MEM
-      && GET_CODE (XEXP (in, 0)) == PLUS
-      && GET_CODE (XEXP (XEXP (in, 0), 1)) == CONST_INT
-      && !DISP_IN_RANGE (INTVAL (XEXP (XEXP (in, 0), 1))
-                        + GET_MODE_SIZE (mode) - 1))
-    return ADDR_REGS;
+  if (!s390_loadrelative_operand_p (addr, &symref, &addend))
+    gcc_unreachable ();
 
-  if (reg_classes_intersect_p (CC_REGS, class))
-    return GENERAL_REGS;
+  if (!(addend & 1))
+    /* Easy case.  The addend is even so larl will do fine.  */
+    emit_move_insn (reg, addr);
+  else
+    {
+      /* We can leave the scratch register untouched if the target
+        register is a valid base register.  */
+      if (REGNO (reg) < FIRST_PSEUDO_REGISTER
+         && REGNO_REG_CLASS (REGNO (reg)) == ADDR_REGS)
+       scratch = reg;
 
-  return NO_REGS;
+      gcc_assert (REGNO (scratch) < FIRST_PSEUDO_REGISTER);
+      gcc_assert (REGNO_REG_CLASS (REGNO (scratch)) == ADDR_REGS);
+
+      if (addend != 1)
+       emit_move_insn (scratch,
+                       gen_rtx_CONST (Pmode,
+                                      gen_rtx_PLUS (Pmode, symref,
+                                                    GEN_INT (addend - 1))));
+      else
+       emit_move_insn (scratch, symref);
+
+      /* Increment the address using la in order to avoid clobbering cc.  */
+      s390_load_address (reg, gen_rtx_PLUS (Pmode, scratch, const1_rtx));
+    }
 }
 
-/* Return the register class of a scratch register needed to
-   store a register of class CLASS in MODE into OUT:
+/* Generate what is necessary to move between REG and MEM using
+   SCRATCH.  The direction is given by TOMEM.  */
+
+void
+s390_reload_symref_address (rtx reg, rtx mem, rtx scratch, bool tomem)
+{
+  /* Reload might have pulled a constant out of the literal pool.
+     Force it back in.  */
+  if (CONST_INT_P (mem) || GET_CODE (mem) == CONST_DOUBLE
+      || GET_CODE (mem) == CONST)
+    mem = force_const_mem (GET_MODE (reg), mem);
+
+  gcc_assert (MEM_P (mem));
+
+  /* For a load from memory we can leave the scratch register
+     untouched if the target register is a valid base register.  */
+  if (!tomem
+      && REGNO (reg) < FIRST_PSEUDO_REGISTER
+      && REGNO_REG_CLASS (REGNO (reg)) == ADDR_REGS
+      && GET_MODE (reg) == GET_MODE (scratch))
+    scratch = reg;
+
+  /* Load address into scratch register.  Since we can't have a
+     secondary reload for a secondary reload we have to cover the case
+     where larl would need a secondary reload here as well.  */
+  s390_reload_larl_operand (scratch, XEXP (mem, 0), scratch);
+
+  /* Now we can use a standard load/store to do the move.  */
+  if (tomem)
+    emit_move_insn (replace_equiv_address (mem, scratch), reg);
+  else
+    emit_move_insn (reg, replace_equiv_address (mem, scratch));
+}
 
-   We need a temporary when storing a double-word to a
-   non-offsettable memory address.  */
+/* Inform reload about cases where moving X with a mode MODE to a register in
+   RCLASS requires an extra scratch or immediate register.  Return the class
+   needed for the immediate register.  */
 
-enum reg_class
-s390_secondary_output_reload_class (enum reg_class class,
-                                   enum machine_mode mode, rtx out)
+static reg_class_t
+s390_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
+                      enum machine_mode mode, secondary_reload_info *sri)
 {
-  if ((TARGET_64BIT ? (mode == TImode || mode == TFmode)
-                    : (mode == DImode || mode == DFmode))
-      && reg_classes_intersect_p (GENERAL_REGS, class)
-      && GET_CODE (out) == MEM
-      && GET_CODE (XEXP (out, 0)) == PLUS
-      && GET_CODE (XEXP (XEXP (out, 0), 0)) == PLUS
-      && GET_CODE (XEXP (XEXP (out, 0), 1)) == CONST_INT
-      && !DISP_IN_RANGE (INTVAL (XEXP (XEXP (out, 0), 1))
-                        + GET_MODE_SIZE (mode) - 1))
-    return ADDR_REGS;
-
-  if (reg_classes_intersect_p (FP_REGS, class)
-      && mode == TFmode
-      && GET_CODE (out) == MEM
-      && GET_CODE (XEXP (out, 0)) == PLUS
-      && GET_CODE (XEXP (XEXP (out, 0), 1)) == CONST_INT
-      && !DISP_IN_RANGE (INTVAL (XEXP (XEXP (out, 0), 1))
-                        + GET_MODE_SIZE (mode) - 1))
-    return ADDR_REGS;
+  enum reg_class rclass = (enum reg_class) rclass_i;
 
-  if (reg_classes_intersect_p (CC_REGS, class))
+  /* Intermediate register needed.  */
+  if (reg_classes_intersect_p (CC_REGS, rclass))
     return GENERAL_REGS;
 
+  if (TARGET_Z10)
+    {
+      HOST_WIDE_INT offset;
+      rtx symref;
+
+      /* On z10 several optimizer steps may generate larl operands with
+        an odd addend.  */
+      if (in_p
+         && s390_loadrelative_operand_p (x, &symref, &offset)
+         && mode == Pmode
+         && !SYMBOL_REF_ALIGN1_P (symref)
+         && (offset & 1) == 1)
+       sri->icode = ((mode == DImode) ? CODE_FOR_reloaddi_larl_odd_addend_z10
+                     : CODE_FOR_reloadsi_larl_odd_addend_z10);
+
+      /* On z10 we need a scratch register when moving QI, TI or floating
+        point mode values from or to a memory location with a SYMBOL_REF
+        or if the symref addend of a SI or DI move is not aligned to the
+        width of the access.  */
+      if (MEM_P (x)
+         && s390_loadrelative_operand_p (XEXP (x, 0), NULL, NULL)
+         && (mode == QImode || mode == TImode || FLOAT_MODE_P (mode)
+             || (!TARGET_ZARCH && mode == DImode)
+             || ((mode == HImode || mode == SImode || mode == DImode)
+                 && (!s390_check_symref_alignment (XEXP (x, 0),
+                                                   GET_MODE_SIZE (mode))))))
+       {
+#define __SECONDARY_RELOAD_CASE(M,m)                                   \
+         case M##mode:                                                 \
+           if (TARGET_64BIT)                                           \
+             sri->icode = in_p ? CODE_FOR_reload##m##di_toreg_z10 :    \
+                                  CODE_FOR_reload##m##di_tomem_z10;    \
+           else                                                        \
+             sri->icode = in_p ? CODE_FOR_reload##m##si_toreg_z10 :    \
+                                  CODE_FOR_reload##m##si_tomem_z10;    \
+         break;
+
+         switch (GET_MODE (x))
+           {
+             __SECONDARY_RELOAD_CASE (QI, qi);
+             __SECONDARY_RELOAD_CASE (HI, hi);
+             __SECONDARY_RELOAD_CASE (SI, si);
+             __SECONDARY_RELOAD_CASE (DI, di);
+             __SECONDARY_RELOAD_CASE (TI, ti);
+             __SECONDARY_RELOAD_CASE (SF, sf);
+             __SECONDARY_RELOAD_CASE (DF, df);
+             __SECONDARY_RELOAD_CASE (TF, tf);
+             __SECONDARY_RELOAD_CASE (SD, sd);
+             __SECONDARY_RELOAD_CASE (DD, dd);
+             __SECONDARY_RELOAD_CASE (TD, td);
+
+           default:
+             gcc_unreachable ();
+           }
+#undef __SECONDARY_RELOAD_CASE
+       }
+    }
+
+  /* We need a scratch register when loading a PLUS expression which
+     is not a legitimate operand of the LOAD ADDRESS instruction.  */
+  /* LRA can deal with transformation of plus op very well -- so we
+     don't need to prompt LRA in this case.  */
+  if (! lra_in_progress && 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, rclass)
+          && 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, rclass)
+             && 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);
+       }
+    }
+
+  /* A scratch address register is needed when a symbolic constant is
+     copied to r0 compiling with -fPIC.  In other cases the target
+     register might be used as temporary (see legitimize_pic_address).  */
+  if (in_p && SYMBOLIC_CONST (x) && flag_pic == 2 && rclass != ADDR_REGS)
+    sri->icode = (TARGET_64BIT ?
+                 CODE_FOR_reloaddi_PIC_addr :
+                 CODE_FOR_reloadsi_PIC_addr);
+
+  /* Either scratch or no register needed.  */
   return NO_REGS;
 }
 
@@ -2724,11 +3342,17 @@ s390_expand_plus_operand (rtx target, rtx src,
 /* Return true if ADDR is a valid memory address.
    STRICT specifies whether strict register checking applies.  */
 
-bool
-legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
-                     rtx addr, int strict)
+static bool
+s390_legitimate_address_p (enum machine_mode mode, rtx addr, bool strict)
 {
   struct s390_address ad;
+
+  if (TARGET_Z10
+      && larl_operand (addr, VOIDmode)
+      && (mode == VOIDmode
+         || s390_check_symref_alignment (addr, GET_MODE_SIZE (mode))))
+    return true;
+
   if (!s390_decompose_address (addr, &ad))
     return false;
 
@@ -2742,11 +3366,11 @@ legitimate_address_p (enum machine_mode mode ATTRIBUTE_UNUSED,
     }
   else
     {
-      if (ad.base 
+      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))
@@ -2787,6 +3411,13 @@ preferred_la_operand_p (rtx op1, rtx op2)
   if (addr.indx && !REGNO_OK_FOR_INDEX_P (REGNO (addr.indx)))
     return false;
 
+  /* Avoid LA instructions with index register on z196; it is
+     preferable to use regular add instructions when possible.
+     Starting with zEC12 the la with index register is "uncracked"
+     again.  */
+  if (addr.indx && s390_tune == PROCESSOR_2817_Z196)
+    return false;
+
   if (!TARGET_64BIT && !addr.pointer)
     return false;
 
@@ -2828,273 +3459,281 @@ s390_load_address (rtx dst, rtx src)
       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
+   TARGET_LEGITIMIZE_ADDRESS_P 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;
+  rtx addend = const0_rtx;
+  rtx new_rtx = orig;
 
   gcc_assert (!TLS_SYMBOLIC_CONST (addr));
 
-  if (GET_CODE (addr) == LABEL_REF
-      || (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (addr)))
+  if (GET_CODE (addr) == CONST)
+    addr = XEXP (addr, 0);
+
+  if (GET_CODE (addr) == PLUS)
     {
-      /* 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 ...  */
-        }
+      addend = XEXP (addr, 1);
+      addr = XEXP (addr, 0);
+    }
+
+  if ((GET_CODE (addr) == LABEL_REF
+       || (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (addr))
+       || (GET_CODE (addr) == UNSPEC &&
+          (XINT (addr, 1) == UNSPEC_GOTENT
+           || (TARGET_CPU_ZARCH && XINT (addr, 1) == UNSPEC_PLT))))
+      && GET_CODE (addend) == CONST_INT)
+    {
+      /* This can be locally addressed.  */
+
+      /* larl_operand requires UNSPECs to be wrapped in a const rtx.  */
+      rtx const_addr = (GET_CODE (addr) == UNSPEC ?
+                       gen_rtx_CONST (Pmode, addr) : addr);
+
+      if (TARGET_CPU_ZARCH
+         && larl_operand (const_addr, VOIDmode)
+         && INTVAL (addend) < (HOST_WIDE_INT)1 << 31
+         && INTVAL (addend) >= -((HOST_WIDE_INT)1 << 31))
+       {
+         if (INTVAL (addend) & 1)
+           {
+             /* LARL can't handle odd offsets, so emit a pair of LARL
+                and LA.  */
+             rtx temp = reg? reg : gen_reg_rtx (Pmode);
+
+             if (!DISP_IN_RANGE (INTVAL (addend)))
+               {
+                 HOST_WIDE_INT even = INTVAL (addend) - 1;
+                 addr = gen_rtx_PLUS (Pmode, addr, GEN_INT (even));
+                 addr = gen_rtx_CONST (Pmode, addr);
+                 addend = const1_rtx;
+               }
+
+             emit_move_insn (temp, addr);
+             new_rtx = gen_rtx_PLUS (Pmode, temp, addend);
+
+             if (reg != 0)
+               {
+                 s390_load_address (reg, new_rtx);
+                 new_rtx = reg;
+               }
+           }
+         else
+           {
+             /* If the offset is even, we can just use LARL.  This
+                will happen automatically.  */
+           }
+       }
       else
-        {
-          /* Access local symbols relative to the GOT.  */
+       {
+         /* No larl - Access local symbols relative to the GOT.  */
 
-          rtx temp = reg? reg : gen_reg_rtx (Pmode);
+         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, addr), UNSPEC_GOTOFF);
-          addr = gen_rtx_CONST (Pmode, addr);
-          addr = force_const_mem (Pmode, addr);
+         addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTOFF);
+         if (addend != const0_rtx)
+           addr = gen_rtx_PLUS (Pmode, addr, addend);
+         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)
-            {
-              s390_load_address (reg, new);
-              new = reg;
-            }
-        }
+         new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
+         if (reg != 0)
+           {
+             s390_load_address (reg, new_rtx);
+             new_rtx = reg;
+           }
+       }
     }
-  else if (GET_CODE (addr) == SYMBOL_REF)
+  else if (GET_CODE (addr) == SYMBOL_REF && addend == const0_rtx)
     {
+      /* A non-local symbol reference without addend.
+
+        The symbol ref is wrapped into an UNSPEC to make sure the
+        proper operand modifier (@GOT or @GOTENT) will be emitted.
+        This will tell the linker to put the symbol into the GOT.
+
+        Additionally the code dereferencing the GOT slot is emitted here.
+
+        An addend to the symref needs to be added afterwards.
+        legitimize_pic_address calls itself recursively to handle
+        that case.  So no need to do it here.  */
+
       if (reg == 0)
         reg = gen_reg_rtx (Pmode);
 
-      if (flag_pic == 1)
+      if (TARGET_Z10)
+       {
+         /* Use load relative if possible.
+            lgrl <target>, sym@GOTENT  */
+         new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTENT);
+         new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+         new_rtx = gen_const_mem (GET_MODE (reg), new_rtx);
+
+         emit_move_insn (reg, new_rtx);
+         new_rtx = reg;
+       }
+      else if (flag_pic == 1)
         {
-          /* Assume GOT offset < 4k.  This is handled the same way
-             in both 31- and 64-bit code (@GOT).  */
+          /* Assume GOT offset is a valid displacement operand (< 4k
+             or < 512k with z990).  This is handled the same way in
+             both 31- and 64-bit code (@GOT).
+             lg <target>, sym@GOT(r12)  */
 
          if (reload_in_progress || reload_completed)
-           regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
-
-          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_const_mem (Pmode, new);
-          emit_move_insn (reg, new);
-          new = reg;
+           df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
+
+          new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOT);
+          new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+          new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new_rtx);
+          new_rtx = gen_const_mem (Pmode, new_rtx);
+          emit_move_insn (reg, new_rtx);
+          new_rtx = reg;
         }
       else if (TARGET_CPU_ZARCH)
         {
           /* If the GOT offset might be >= 4k, we determine the position
-             of the GOT entry via a PC-relative LARL (@GOTENT).  */
+             of the GOT entry via a PC-relative LARL (@GOTENT).
+            larl temp, sym@GOTENT
+             lg   <target>, 0(temp) */
 
-          rtx temp = gen_reg_rtx (Pmode);
+          rtx temp = reg ? reg : gen_reg_rtx (Pmode);
 
-          new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTENT);
-          new = gen_rtx_CONST (Pmode, new);
-          emit_move_insn (temp, new);
+         gcc_assert (REGNO (temp) >= FIRST_PSEUDO_REGISTER
+                     || REGNO_REG_CLASS (REGNO (temp)) == ADDR_REGS);
 
-          new = gen_const_mem (Pmode, temp);
-          emit_move_insn (reg, new);
-          new = reg;
+          new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTENT);
+          new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+         emit_move_insn (temp, new_rtx);
+
+         new_rtx = gen_const_mem (Pmode, temp);
+          emit_move_insn (reg, new_rtx);
+
+          new_rtx = reg;
         }
       else
         {
           /* If the GOT offset might be >= 4k, we have to load it
-             from the literal pool (@GOT).  */
+             from the literal pool (@GOT).
+
+            lg temp, lit-litbase(r13)
+             lg <target>, 0(temp)
+            lit:  .long sym@GOT  */
+
+          rtx temp = reg ? reg : gen_reg_rtx (Pmode);
 
-          rtx temp = gen_reg_rtx (Pmode);
+         gcc_assert (REGNO (temp) >= FIRST_PSEUDO_REGISTER
+                     || REGNO_REG_CLASS (REGNO (temp)) == ADDR_REGS);
 
          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);
           addr = force_const_mem (Pmode, addr);
           emit_move_insn (temp, addr);
 
-          new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
-          new = gen_const_mem (Pmode, new);
-          emit_move_insn (reg, new);
-          new = reg;
+          new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
+          new_rtx = gen_const_mem (Pmode, new_rtx);
+          emit_move_insn (reg, new_rtx);
+          new_rtx = reg;
         }
     }
-  else
+  else if (GET_CODE (addr) == UNSPEC && GET_CODE (addend) == CONST_INT)
     {
-      if (GET_CODE (addr) == CONST)
+      gcc_assert (XVECLEN (addr, 0) == 1);
+      switch (XINT (addr, 1))
        {
-         addr = XEXP (addr, 0);
-         if (GET_CODE (addr) == UNSPEC)
-           {
-             gcc_assert (XVECLEN (addr, 0) == 1);
-              switch (XINT (addr, 1))
-                {
-                  /* If someone moved a GOT-relative UNSPEC
-                     out of the literal pool, force them back in.  */
-                  case UNSPEC_GOTOFF:
-                  case UNSPEC_PLTOFF:
-                    new = force_const_mem (Pmode, orig);
-                    break;
-
-                  /* @GOT is OK as is if small.  */
-                 case UNSPEC_GOT:
-                   if (flag_pic == 2)
-                     new = force_const_mem (Pmode, orig);
-                   break;
+         /* These address symbols (or PLT slots) relative to the GOT
+            (not GOT slots!).  In general this will exceed the
+            displacement range so these value belong into the literal
+            pool.  */
+       case UNSPEC_GOTOFF:
+       case UNSPEC_PLTOFF:
+         new_rtx = force_const_mem (Pmode, orig);
+         break;
 
-                  /* @GOTENT is OK as is.  */
-                  case UNSPEC_GOTENT:
-                    break;
-
-                  /* @PLT is OK as is on 64-bit, must be converted to
-                     GOT-relative @PLTOFF on 31-bit.  */
-                  case UNSPEC_PLT:
-                    if (!TARGET_CPU_ZARCH)
-                      {
-                        rtx temp = reg? reg : gen_reg_rtx (Pmode);
-
-                       if (reload_in_progress || reload_completed)
-                         regs_ever_live[PIC_OFFSET_TABLE_REGNUM] = 1;
-
-                        addr = XVECEXP (addr, 0, 0);
-                        addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr),
-                                              UNSPEC_PLTOFF);
-                        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)
-                          {
-                            s390_load_address (reg, new);
-                            new = reg;
-                          }
-                      }
-                    break;
-
-                  /* Everything else cannot happen.  */
-                  default:
-                    gcc_unreachable ();
-                }
-           }
-         else 
-           gcc_assert (GET_CODE (addr) == PLUS);
-       }
-      if (GET_CODE (addr) == PLUS)
-       {
-         rtx op0 = XEXP (addr, 0), op1 = XEXP (addr, 1);
+         /* For -fPIC the GOT size might exceed the displacement
+            range so make sure the value is in the literal pool.  */
+       case UNSPEC_GOT:
+         if (flag_pic == 2)
+           new_rtx = force_const_mem (Pmode, orig);
+         break;
 
-         gcc_assert (!TLS_SYMBOLIC_CONST (op0));
-         gcc_assert (!TLS_SYMBOLIC_CONST (op1));
+         /* For @GOTENT larl is used.  This is handled like local
+            symbol refs.  */
+       case UNSPEC_GOTENT:
+         gcc_unreachable ();
+         break;
 
-         /* 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)
+         /* @PLT is OK as is on 64-bit, must be converted to
+            GOT-relative @PLTOFF on 31-bit.  */
+       case UNSPEC_PLT:
+         if (!TARGET_CPU_ZARCH)
            {
-              if (TARGET_CPU_ZARCH && larl_operand (op0, VOIDmode))
-                {
-                  if (INTVAL (op1) & 1)
-                    {
-                      /* LARL can't handle odd offsets, so emit a
-                         pair of LARL and LA.  */
-                      rtx temp = reg? reg : gen_reg_rtx (Pmode);
-
-                      if (!DISP_IN_RANGE (INTVAL (op1)))
-                        {
-                          int even = INTVAL (op1) - 1;
-                          op0 = gen_rtx_PLUS (Pmode, op0, GEN_INT (even));
-                         op0 = gen_rtx_CONST (Pmode, op0);
-                          op1 = const1_rtx;
-                        }
-
-                      emit_move_insn (temp, op0);
-                      new = gen_rtx_PLUS (Pmode, temp, op1);
-
-                      if (reg != 0)
-                        {
-                          s390_load_address (reg, new);
-                          new = reg;
-                        }
-                    }
-                  else
-                    {
-                      /* If the offset is even, we can just use LARL.
-                         This will happen 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, op0),
-                                        UNSPEC_GOTOFF);
-                  addr = gen_rtx_PLUS (Pmode, addr, op1);
-                  addr = gen_rtx_CONST (Pmode, addr);
-                  addr = force_const_mem (Pmode, addr);
-                 emit_move_insn (temp, addr);
-
-                  new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
-                  if (reg != 0)
-                    {
-                      s390_load_address (reg, new);
-                      new = reg;
-                    }
-                }
+             rtx temp = reg? reg : gen_reg_rtx (Pmode);
+
+             if (reload_in_progress || reload_completed)
+               df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
+
+             addr = XVECEXP (addr, 0, 0);
+             addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr),
+                                    UNSPEC_PLTOFF);
+             if (addend != const0_rtx)
+               addr = gen_rtx_PLUS (Pmode, addr, addend);
+             addr = gen_rtx_CONST (Pmode, addr);
+             addr = force_const_mem (Pmode, addr);
+             emit_move_insn (temp, addr);
+
+             new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
+             if (reg != 0)
+               {
+                 s390_load_address (reg, new_rtx);
+                 new_rtx = reg;
+               }
            }
+         else
+           /* On 64 bit larl can be used.  This case is handled like
+              local symbol refs.  */
+           gcc_unreachable ();
+         break;
 
-          /* Now, check whether it is a GOT relative symbol plus offset
-             that was pulled out of the literal pool.  Force it back in.  */
-
-         else if (GET_CODE (op0) == UNSPEC
-                  && GET_CODE (op1) == CONST_INT
-                  && XINT (op0, 1) == UNSPEC_GOTOFF)
-            {
-             gcc_assert (XVECLEN (op0, 0) == 1);
-
-              new = force_const_mem (Pmode, orig);
-            }
+         /* Everything else cannot happen.  */
+       default:
+         gcc_unreachable ();
+       }
+    }
+  else if (addend != const0_rtx)
+    {
+      /* Otherwise, compute the sum.  */
 
-          /* Otherwise, compute the sum.  */
-         else
+      rtx base = legitimize_pic_address (addr, reg);
+      new_rtx  = legitimize_pic_address (addend,
+                                        base == reg ? NULL_RTX : reg);
+      if (GET_CODE (new_rtx) == CONST_INT)
+       new_rtx = plus_constant (Pmode, base, INTVAL (new_rtx));
+      else
+       {
+         if (GET_CODE (new_rtx) == PLUS && CONSTANT_P (XEXP (new_rtx, 1)))
            {
-             base = legitimize_pic_address (XEXP (addr, 0), reg);
-             new  = legitimize_pic_address (XEXP (addr, 1),
-                                            base == reg ? NULL_RTX : reg);
-             if (GET_CODE (new) == CONST_INT)
-               new = plus_constant (base, INTVAL (new));
-             else
-               {
-                 if (GET_CODE (new) == PLUS && CONSTANT_P (XEXP (new, 1)))
-                   {
-                     base = gen_rtx_PLUS (Pmode, base, XEXP (new, 0));
-                     new = XEXP (new, 1);
-                   }
-                 new = gen_rtx_PLUS (Pmode, base, new);
-               }
-
-             if (GET_CODE (new) == CONST)
-               new = XEXP (new, 0);
-              new = force_operand (new, 0);
+             base = gen_rtx_PLUS (Pmode, base, XEXP (new_rtx, 0));
+             new_rtx = XEXP (new_rtx, 1);
            }
+         new_rtx = gen_rtx_PLUS (Pmode, base, new_rtx);
        }
+
+      if (GET_CODE (new_rtx) == CONST)
+       new_rtx = XEXP (new_rtx, 0);
+      new_rtx = force_operand (new_rtx, 0);
     }
-  return new;
+
+  return new_rtx;
 }
 
 /* Load the thread pointer into a register.  */
@@ -3122,7 +3761,8 @@ s390_emit_tls_call_insn (rtx result_reg, rtx tls_call)
 {
   rtx insn;
 
-  gcc_assert (flag_pic);
+  if (!flag_pic)
+    emit_insn (s390_load_got ());
 
   if (!s390_tls_symbol)
     s390_tls_symbol = gen_rtx_SYMBOL_REF (Pmode, "__tls_get_offset");
@@ -3131,7 +3771,7 @@ s390_emit_tls_call_insn (rtx result_reg, rtx tls_call)
                         gen_rtx_REG (Pmode, RETURN_REGNUM));
 
   use_reg (&CALL_INSN_FUNCTION_USAGE (insn), result_reg);
-  CONST_OR_PURE_CALL_P (insn) = 1;
+  RTL_CONST_CALL_P (insn) = 1;
 }
 
 /* ADDR contains a thread-local SYMBOL_REF.  Generate code to compute
@@ -3140,7 +3780,7 @@ s390_emit_tls_call_insn (rtx result_reg, rtx tls_call)
 static rtx
 legitimize_tls_address (rtx addr, rtx reg)
 {
-  rtx new, tls_call, temp, base, r2, insn;
+  rtx new_rtx, tls_call, temp, base, r2, insn;
 
   if (GET_CODE (addr) == SYMBOL_REF)
     switch (tls_symbolic_operand (addr))
@@ -3149,22 +3789,22 @@ legitimize_tls_address (rtx addr, rtx reg)
        start_sequence ();
        r2 = gen_rtx_REG (Pmode, 2);
        tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_TLSGD);
-       new = gen_rtx_CONST (Pmode, tls_call);
-       new = force_const_mem (Pmode, new);
-       emit_move_insn (r2, new);
+       new_rtx = gen_rtx_CONST (Pmode, tls_call);
+       new_rtx = force_const_mem (Pmode, new_rtx);
+       emit_move_insn (r2, new_rtx);
        s390_emit_tls_call_insn (r2, tls_call);
        insn = get_insns ();
        end_sequence ();
 
-       new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF);
+       new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF);
        temp = gen_reg_rtx (Pmode);
-       emit_libcall_block (insn, temp, r2, new);
+       emit_libcall_block (insn, temp, r2, new_rtx);
 
-       new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
+       new_rtx = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
        if (reg != 0)
          {
-           s390_load_address (reg, new);
-           new = reg;
+           s390_load_address (reg, new_rtx);
+           new_rtx = reg;
          }
        break;
 
@@ -3172,32 +3812,32 @@ legitimize_tls_address (rtx addr, rtx reg)
        start_sequence ();
        r2 = gen_rtx_REG (Pmode, 2);
        tls_call = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM);
-       new = gen_rtx_CONST (Pmode, tls_call);
-       new = force_const_mem (Pmode, new);
-       emit_move_insn (r2, new);
+       new_rtx = gen_rtx_CONST (Pmode, tls_call);
+       new_rtx = force_const_mem (Pmode, new_rtx);
+       emit_move_insn (r2, new_rtx);
        s390_emit_tls_call_insn (r2, tls_call);
        insn = get_insns ();
        end_sequence ();
 
-       new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM_NTPOFF);
+       new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, const0_rtx), UNSPEC_TLSLDM_NTPOFF);
        temp = gen_reg_rtx (Pmode);
-       emit_libcall_block (insn, temp, r2, new);
+       emit_libcall_block (insn, temp, r2, new_rtx);
 
-       new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
+       new_rtx = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
        base = gen_reg_rtx (Pmode);
-       s390_load_address (base, new);
+       s390_load_address (base, new_rtx);
 
-       new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_DTPOFF);
-       new = gen_rtx_CONST (Pmode, new);
-       new = force_const_mem (Pmode, new);
+       new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_DTPOFF);
+       new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+       new_rtx = force_const_mem (Pmode, new_rtx);
        temp = gen_reg_rtx (Pmode);
-       emit_move_insn (temp, new);
+       emit_move_insn (temp, new_rtx);
 
-       new = gen_rtx_PLUS (Pmode, base, temp);
+       new_rtx = gen_rtx_PLUS (Pmode, base, temp);
        if (reg != 0)
          {
-           s390_load_address (reg, new);
-           new = reg;
+           s390_load_address (reg, new_rtx);
+           new_rtx = reg;
          }
        break;
 
@@ -3208,28 +3848,28 @@ 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_const_mem (Pmode, new);
+           new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF);
+           new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+           new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, new_rtx);
+           new_rtx = gen_const_mem (Pmode, new_rtx);
            temp = gen_reg_rtx (Pmode);
-           emit_move_insn (temp, new);
+           emit_move_insn (temp, new_rtx);
          }
        else if (TARGET_CPU_ZARCH)
          {
            /* If the GOT offset might be >= 4k, we determine the position
               of the GOT entry via a PC-relative LARL.  */
 
-           new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF);
-           new = gen_rtx_CONST (Pmode, new);
+           new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF);
+           new_rtx = gen_rtx_CONST (Pmode, new_rtx);
            temp = gen_reg_rtx (Pmode);
-           emit_move_insn (temp, new);
+           emit_move_insn (temp, new_rtx);
 
-           new = gen_const_mem (Pmode, temp);
+           new_rtx = gen_const_mem (Pmode, temp);
            temp = gen_reg_rtx (Pmode);
-           emit_move_insn (temp, new);
+           emit_move_insn (temp, new_rtx);
          }
        else if (flag_pic)
          {
@@ -3237,59 +3877,59 @@ 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);
-           new = force_const_mem (Pmode, new);
+           new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_GOTNTPOFF);
+           new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+           new_rtx = force_const_mem (Pmode, new_rtx);
            temp = gen_reg_rtx (Pmode);
-           emit_move_insn (temp, new);
+           emit_move_insn (temp, new_rtx);
 
-            new = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
-           new = gen_const_mem (Pmode, new);
+            new_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
+           new_rtx = gen_const_mem (Pmode, new_rtx);
 
-           new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD);
+           new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new_rtx, addr), UNSPEC_TLS_LOAD);
            temp = gen_reg_rtx (Pmode);
-           emit_insn (gen_rtx_SET (Pmode, temp, new));
+           emit_insn (gen_rtx_SET (Pmode, temp, new_rtx));
          }
        else
          {
            /* In position-dependent code, load the absolute address of
               the GOT entry from the literal pool.  */
 
-           new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF);
-           new = gen_rtx_CONST (Pmode, new);
-           new = force_const_mem (Pmode, new);
+           new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_INDNTPOFF);
+           new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+           new_rtx = force_const_mem (Pmode, new_rtx);
            temp = gen_reg_rtx (Pmode);
-           emit_move_insn (temp, new);
+           emit_move_insn (temp, new_rtx);
 
-           new = temp;
-           new = gen_const_mem (Pmode, new);
-           new = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new, addr), UNSPEC_TLS_LOAD);
+           new_rtx = temp;
+           new_rtx = gen_const_mem (Pmode, new_rtx);
+           new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, new_rtx, addr), UNSPEC_TLS_LOAD);
            temp = gen_reg_rtx (Pmode);
-           emit_insn (gen_rtx_SET (Pmode, temp, new));
+           emit_insn (gen_rtx_SET (Pmode, temp, new_rtx));
          }
 
-       new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
+       new_rtx = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
        if (reg != 0)
          {
-           s390_load_address (reg, new);
-           new = reg;
+           s390_load_address (reg, new_rtx);
+           new_rtx = reg;
          }
        break;
 
       case TLS_MODEL_LOCAL_EXEC:
-       new = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF);
-       new = gen_rtx_CONST (Pmode, new);
-       new = force_const_mem (Pmode, new);
+       new_rtx = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, addr), UNSPEC_NTPOFF);
+       new_rtx = gen_rtx_CONST (Pmode, new_rtx);
+       new_rtx = force_const_mem (Pmode, new_rtx);
         temp = gen_reg_rtx (Pmode);
-       emit_move_insn (temp, new);
+       emit_move_insn (temp, new_rtx);
 
-       new = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
+       new_rtx = gen_rtx_PLUS (Pmode, s390_get_thread_pointer (), temp);
        if (reg != 0)
          {
-           s390_load_address (reg, new);
-           new = reg;
+           s390_load_address (reg, new_rtx);
+           new_rtx = reg;
          }
        break;
 
@@ -3303,7 +3943,7 @@ legitimize_tls_address (rtx addr, rtx reg)
        {
        case UNSPEC_INDNTPOFF:
          gcc_assert (TARGET_CPU_ZARCH);
-         new = addr;
+         new_rtx = addr;
          break;
 
        default:
@@ -3314,27 +3954,31 @@ legitimize_tls_address (rtx addr, rtx reg)
   else if (GET_CODE (addr) == CONST && GET_CODE (XEXP (addr, 0)) == PLUS
           && GET_CODE (XEXP (XEXP (addr, 0), 1)) == CONST_INT)
     {
-      new = XEXP (XEXP (addr, 0), 0);
-      if (GET_CODE (new) != SYMBOL_REF)
-       new = gen_rtx_CONST (Pmode, new);
+      new_rtx = XEXP (XEXP (addr, 0), 0);
+      if (GET_CODE (new_rtx) != SYMBOL_REF)
+       new_rtx = gen_rtx_CONST (Pmode, new_rtx);
 
-      new = legitimize_tls_address (new, reg);
-      new = plus_constant (new, INTVAL (XEXP (XEXP (addr, 0), 1)));
-      new = force_operand (new, 0);
+      new_rtx = legitimize_tls_address (new_rtx, reg);
+      new_rtx = plus_constant (Pmode, new_rtx,
+                              INTVAL (XEXP (XEXP (addr, 0), 1)));
+      new_rtx = force_operand (new_rtx, 0);
     }
 
   else
     gcc_unreachable ();  /* for now ... */
 
-  return new;
+  return new_rtx;
 }
 
-/* Emit insns to move operands[1] into operands[0].  */
+/* Emit insns making the address in operands[1] valid for a standard
+   move to operands[0].  operands[1] is replaced by an address which
+   should be used instead of the former RTX to emit the move
+   pattern.  */
 
 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]);
@@ -3355,9 +3999,9 @@ emit_symbolic_move (rtx *operands)
    When -fpic is used, special handling is needed for symbolic references.
    See comments by legitimize_pic_address for details.  */
 
-rtx
-legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
-                   enum machine_mode mode ATTRIBUTE_UNUSED)
+static rtx
+s390_legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
+                        enum machine_mode mode ATTRIBUTE_UNUSED)
 {
   rtx constant_term = const0_rtx;
 
@@ -3365,11 +4009,11 @@ legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
     {
       x = legitimize_tls_address (x, 0);
 
-      if (legitimate_address_p (mode, x, FALSE))
+      if (s390_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, 0))
               || TLS_SYMBOLIC_CONST (XEXP (x, 1))))
     {
       return x;
@@ -3382,7 +4026,7 @@ legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
                   || SYMBOLIC_CONST (XEXP (x, 1)))))
          x = legitimize_pic_address (x, 0);
 
-      if (legitimate_address_p (mode, x, FALSE))
+      if (s390_legitimate_address_p (mode, x, FALSE))
        return x;
     }
 
@@ -3443,12 +4087,12 @@ legitimize_address (rtx x, rtx oldx ATTRIBUTE_UNUSED,
 }
 
 /* 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.
+   operand.  If we find one, push the reload 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 
+rtx
 legitimize_reload_address (rtx ad, enum machine_mode mode ATTRIBUTE_UNUSED,
                           int opnum, int type)
 {
@@ -3470,19 +4114,19 @@ legitimize_reload_address (rtx ad, enum machine_mode mode ATTRIBUTE_UNUSED,
     {
       HOST_WIDE_INT lower = INTVAL (XEXP (ad, 1)) & 0xfff;
       HOST_WIDE_INT upper = INTVAL (XEXP (ad, 1)) ^ lower;
-      rtx cst, tem, new;
+      rtx cst, tem, new_rtx;
 
       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));
+      new_rtx = 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, 
+                  BASE_REG_CLASS, Pmode, VOIDmode, 0, 0,
                   opnum, (enum reload_type) type);
-      return new;
+      return new_rtx;
     }
 
   return NULL_RTX;
@@ -3490,9 +4134,16 @@ legitimize_reload_address (rtx ad, enum machine_mode mode ATTRIBUTE_UNUSED,
 
 /* Emit code to move LEN bytes from DST to SRC.  */
 
-void
+bool
 s390_expand_movmem (rtx dst, rtx src, rtx len)
 {
+  /* When tuning for z10 or higher we rely on the Glibc functions to
+     do the right thing. Only for constant lengths below 64k we will
+     generate inline code.  */
+  if (s390_tune >= PROCESSOR_2097_Z10
+      && (GET_CODE (len) != CONST_INT || INTVAL (len) > (1<<16)))
+    return false;
+
   if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256)
     {
       if (INTVAL (len) > 0)
@@ -3530,11 +4181,13 @@ s390_expand_movmem (rtx dst, rtx src, rtx len)
       dst = change_address (dst, VOIDmode, dst_addr);
       src = change_address (src, VOIDmode, src_addr);
 
-      temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
+      temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1,
+                          OPTAB_DIRECT);
       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,
+                          OPTAB_DIRECT);
       if (temp != blocks)
         emit_move_insn (blocks, temp);
 
@@ -3543,13 +4196,32 @@ s390_expand_movmem (rtx dst, rtx src, rtx len)
 
       emit_label (loop_start_label);
 
+      if (TARGET_Z10
+         && (GET_CODE (len) != CONST_INT || INTVAL (len) > 768))
+       {
+         rtx prefetch;
+
+         /* Issue a read prefetch for the +3 cache line.  */
+         prefetch = gen_prefetch (gen_rtx_PLUS (Pmode, src_addr, GEN_INT (768)),
+                                  const0_rtx, const0_rtx);
+         PREFETCH_SCHEDULE_BARRIER_P (prefetch) = true;
+         emit_insn (prefetch);
+
+         /* Issue a write prefetch for the +3 cache line.  */
+         prefetch = gen_prefetch (gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (768)),
+                                  const1_rtx, const0_rtx);
+         PREFETCH_SCHEDULE_BARRIER_P (prefetch) = true;
+         emit_insn (prefetch);
+       }
+
       emit_insn (gen_movmem_short (dst, src, GEN_INT (255)));
       s390_load_address (dst_addr,
                         gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256)));
       s390_load_address (src_addr,
                         gen_rtx_PLUS (Pmode, src_addr, GEN_INT (256)));
 
-      temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
+      temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1,
+                          OPTAB_DIRECT);
       if (temp != blocks)
         emit_move_insn (blocks, temp);
 
@@ -3563,6 +4235,7 @@ s390_expand_movmem (rtx dst, rtx src, rtx len)
                                   convert_to_mode (Pmode, count, 1)));
       emit_label (end_label);
     }
+  return true;
 }
 
 /* Emit code to set LEN bytes at DST to VAL.
@@ -3571,10 +4244,12 @@ s390_expand_movmem (rtx dst, rtx src, rtx len)
 void
 s390_expand_setmem (rtx dst, rtx len, rtx val)
 {
-  gcc_assert (GET_CODE (len) != CONST_INT || INTVAL (len) > 0);
+  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) <= 257)
+
+  if (GET_CODE (len) == CONST_INT && INTVAL (len) > 0 && INTVAL (len) <= 257)
     {
       if (val == const0_rtx && INTVAL (len) <= 256)
         emit_insn (gen_clrmem_short (dst, GEN_INT (INTVAL (len) - 1)));
@@ -3582,7 +4257,7 @@ s390_expand_setmem (rtx dst, rtx len, rtx val)
        {
          /* 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.
@@ -3591,9 +4266,9 @@ s390_expand_setmem (rtx dst, rtx len, rtx val)
                 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);
+             set_mem_size (dst, 1);
 
-             emit_insn (gen_movmem_short (dstp1, dst, 
+             emit_insn (gen_movmem_short (dstp1, dst,
                                           GEN_INT (INTVAL (len) - 2)));
            }
        }
@@ -3607,7 +4282,7 @@ s390_expand_setmem (rtx dst, rtx len, rtx val)
 
   else
     {
-      rtx dst_addr, src_addr, count, blocks, temp, dstp1 = NULL_RTX;
+      rtx dst_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 ();
@@ -3618,7 +4293,6 @@ s390_expand_setmem (rtx dst, rtx len, rtx val)
         mode = Pmode;
 
       dst_addr = gen_reg_rtx (Pmode);
-      src_addr = gen_reg_rtx (Pmode);
       count = gen_reg_rtx (mode);
       blocks = gen_reg_rtx (mode);
 
@@ -3630,25 +4304,28 @@ s390_expand_setmem (rtx dst, rtx len, rtx val)
       dst = change_address (dst, VOIDmode, dst_addr);
 
       if (val == const0_rtx)
-        temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
+        temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1,
+                            OPTAB_DIRECT);
       else
        {
          dstp1 = adjust_address (dst, VOIDmode, 1);
-         set_mem_size (dst, const1_rtx);
+         set_mem_size (dst, 1);
 
          /* 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);
+         temp = expand_binop (mode, add_optab, count, GEN_INT (-2), count, 1,
+                              OPTAB_DIRECT);
        }
       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,
+                          OPTAB_DIRECT);
       if (temp != blocks)
         emit_move_insn (blocks, temp);
 
@@ -3657,6 +4334,17 @@ s390_expand_setmem (rtx dst, rtx len, rtx val)
 
       emit_label (loop_start_label);
 
+      if (TARGET_Z10
+         && (GET_CODE (len) != CONST_INT || INTVAL (len) > 1024))
+       {
+         /* Issue a write prefetch for the +4 cache line.  */
+         rtx prefetch = gen_prefetch (gen_rtx_PLUS (Pmode, dst_addr,
+                                                    GEN_INT (1024)),
+                                      const1_rtx, const0_rtx);
+         emit_insn (prefetch);
+         PREFETCH_SCHEDULE_BARRIER_P (prefetch) = true;
+       }
+
       if (val == const0_rtx)
        emit_insn (gen_clrmem_short (dst, GEN_INT (255)));
       else
@@ -3664,7 +4352,8 @@ s390_expand_setmem (rtx dst, rtx len, rtx val)
       s390_load_address (dst_addr,
                         gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256)));
 
-      temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
+      temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1,
+                          OPTAB_DIRECT);
       if (temp != blocks)
         emit_move_insn (blocks, temp);
 
@@ -3685,12 +4374,19 @@ s390_expand_setmem (rtx dst, rtx len, rtx val)
 /* Emit code to compare LEN bytes at OP0 with those at OP1,
    and return the result in TARGET.  */
 
-void
+bool
 s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
 {
   rtx ccreg = gen_rtx_REG (CCUmode, CC_REGNUM);
   rtx tmp;
 
+  /* When tuning for z10 or higher we rely on the Glibc functions to
+     do the right thing. Only for constant lengths below 64k we will
+     generate inline code.  */
+  if (s390_tune >= PROCESSOR_2097_Z10
+      && (GET_CODE (len) != CONST_INT || INTVAL (len) > (1<<16)))
+    return false;
+
   /* As the result of CMPINT is inverted compared to what we need,
      we have to swap the operands.  */
   tmp = op0; op0 = op1; op1 = tmp;
@@ -3736,11 +4432,13 @@ s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
       op0 = change_address (op0, VOIDmode, addr0);
       op1 = change_address (op1, VOIDmode, addr1);
 
-      temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1, 0);
+      temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1,
+                          OPTAB_DIRECT);
       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,
+                          OPTAB_DIRECT);
       if (temp != blocks)
         emit_move_insn (blocks, temp);
 
@@ -3749,6 +4447,24 @@ s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
 
       emit_label (loop_start_label);
 
+      if (TARGET_Z10
+         && (GET_CODE (len) != CONST_INT || INTVAL (len) > 512))
+       {
+         rtx prefetch;
+
+         /* Issue a read prefetch for the +2 cache line of operand 1.  */
+         prefetch = gen_prefetch (gen_rtx_PLUS (Pmode, addr0, GEN_INT (512)),
+                                  const0_rtx, const0_rtx);
+         emit_insn (prefetch);
+         PREFETCH_SCHEDULE_BARRIER_P (prefetch) = true;
+
+         /* Issue a read prefetch for the +2 cache line of operand 2.  */
+         prefetch = gen_prefetch (gen_rtx_PLUS (Pmode, addr1, GEN_INT (512)),
+                                  const0_rtx, const0_rtx);
+         emit_insn (prefetch);
+         PREFETCH_SCHEDULE_BARRIER_P (prefetch) = true;
+       }
+
       emit_insn (gen_cmpmem_short (op0, op1, GEN_INT (255)));
       temp = gen_rtx_NE (VOIDmode, ccreg, const0_rtx);
       temp = gen_rtx_IF_THEN_ELSE (VOIDmode, temp,
@@ -3761,7 +4477,8 @@ s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
       s390_load_address (addr1,
                         gen_rtx_PLUS (Pmode, addr1, GEN_INT (256)));
 
-      temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1, 0);
+      temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1,
+                          OPTAB_DIRECT);
       if (temp != blocks)
         emit_move_insn (blocks, temp);
 
@@ -3777,6 +4494,7 @@ s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
 
       emit_insn (gen_cmpint (target, ccreg));
     }
+  return true;
 }
 
 
@@ -3864,7 +4582,7 @@ 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.  */
-      ret = insn_invalid_p (emit_insn (insn));
+      ret = insn_invalid_p (emit_insn (insn), false);
       gcc_assert (!ret);
 
       /* Emit ALC instruction pattern.  */
@@ -3877,14 +4595,14 @@ s390_expand_addcc (enum rtx_code cmp_code, rtx cmp_op0, rtx cmp_op1,
          if (!register_operand (src, GET_MODE (dst)))
            src = force_reg (GET_MODE (dst), src);
 
-         src = gen_rtx_PLUS (GET_MODE (dst), src, const0_rtx);
-         op_res = gen_rtx_PLUS (GET_MODE (dst), src, op_res);
+         op_res = gen_rtx_PLUS (GET_MODE (dst), op_res, src);
+         op_res = gen_rtx_PLUS (GET_MODE (dst), op_res, const0_rtx);
        }
 
       p = rtvec_alloc (2);
-      RTVEC_ELT (p, 0) = 
+      RTVEC_ELT (p, 0) =
         gen_rtx_SET (VOIDmode, dst, op_res);
-      RTVEC_ELT (p, 1) = 
+      RTVEC_ELT (p, 1) =
        gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
       emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
 
@@ -3936,22 +4654,22 @@ 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.  */
-      ret = insn_invalid_p (emit_insn (insn));
+      ret = insn_invalid_p (emit_insn (insn), false);
       gcc_assert (!ret);
 
       /* Emit SLB instruction pattern.  */
       if (!register_operand (src, GET_MODE (dst)))
        src = force_reg (GET_MODE (dst), src);
 
-      op_res = gen_rtx_MINUS (GET_MODE (dst), 
-                             gen_rtx_MINUS (GET_MODE (dst), src, const0_rtx), 
-                             gen_rtx_fmt_ee (cmp_code, GET_MODE (dst), 
-                                             gen_rtx_REG (cc_mode, CC_REGNUM), 
+      op_res = gen_rtx_MINUS (GET_MODE (dst),
+                             gen_rtx_MINUS (GET_MODE (dst), src, const0_rtx),
+                             gen_rtx_fmt_ee (cmp_code, GET_MODE (dst),
+                                             gen_rtx_REG (cc_mode, CC_REGNUM),
                                              const0_rtx));
       p = rtvec_alloc (2);
-      RTVEC_ELT (p, 0) = 
+      RTVEC_ELT (p, 0) =
         gen_rtx_SET (VOIDmode, dst, op_res);
-      RTVEC_ELT (p, 1) = 
+      RTVEC_ELT (p, 1) =
        gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
       emit_insn (gen_rtx_PARALLEL (VOIDmode, p));
 
@@ -3961,96 +4679,160 @@ 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.  */
+/* Expand code for the insv template. Return true if successful.  */
 
-bool 
+bool
 s390_expand_insv (rtx dest, rtx op1, rtx op2, rtx src)
 {
   int bitsize = INTVAL (op1);
   int bitpos = INTVAL (op2);
+  enum machine_mode mode = GET_MODE (dest);
+  enum machine_mode smode;
+  int smode_bsize, mode_bsize;
+  rtx op, clobber;
 
-  /* We need byte alignment.  */
-  if (bitsize % BITS_PER_UNIT)
+  if (bitsize + bitpos > GET_MODE_SIZE (mode))
     return false;
 
+  /* Generate INSERT IMMEDIATE (IILL et al).  */
+  /* (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;
+    }
+
+  smode = smallest_mode_for_size (bitsize, MODE_INT);
+  smode_bsize = GET_MODE_BITSIZE (smode);
+  mode_bsize = GET_MODE_BITSIZE (mode);
+
+  /* Generate STORE CHARACTERS UNDER MASK (STCM et al).  */
   if (bitpos == 0
-      && memory_operand (dest, VOIDmode)
+      && (bitsize % BITS_PER_UNIT) == 0
+      && MEM_P (dest)
       && (register_operand (src, word_mode)
          || const_int_operand (src, VOIDmode)))
     {
       /* Emit standard pattern if possible.  */
-      enum machine_mode mode = smallest_mode_for_size (bitsize, MODE_INT);
-      if (GET_MODE_BITSIZE (mode) == bitsize)
-       emit_move_insn (adjust_address (dest, mode, 0), gen_lowpart (mode, src));
+      if (smode_bsize == bitsize)
+       {
+         emit_move_insn (adjust_address (dest, smode, 0),
+                         gen_lowpart (smode, src));
+         return true;
+       }
 
       /* (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);
+         rtx src_mem = adjust_address (force_const_mem (word_mode, src),
+                                       BLKmode,
+                                       UNITS_PER_WORD - size);
 
          dest = adjust_address (dest, BLKmode, 0);
-         set_mem_size (dest, GEN_INT (size));
+         set_mem_size (dest, size);
          s390_expand_movmem (dest, src_mem, GEN_INT (size));
+         return true;
        }
-         
+
       /* (set (ze (mem)) (reg)).  */
       else if (register_operand (src, word_mode))
        {
-         if (bitsize <= GET_MODE_BITSIZE (SImode))
+         if (bitsize <= 32)
            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 stcmh_width = bitsize - 32;
              int size = stcmh_width / BITS_PER_UNIT;
 
-             emit_move_insn (adjust_address (dest, SImode, size), 
+             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))));
+             set_mem_size (dest, 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 (32)));
            }
+         return true;
        }
-      else
-       return false;
+    }
 
-      return true;
+  /* Generate INSERT CHARACTERS UNDER MASK (IC, ICM et al).  */
+  if ((bitpos % BITS_PER_UNIT) == 0
+      && (bitsize % BITS_PER_UNIT) == 0
+      && (bitpos & 32) == ((bitpos + bitsize - 1) & 32)
+      && MEM_P (src)
+      && (mode == DImode || mode == SImode)
+      && register_operand (dest, mode))
+    {
+      /* Emit a strict_low_part pattern if possible.  */
+      if (smode_bsize == bitsize && bitpos == mode_bsize - smode_bsize)
+       {
+         op = gen_rtx_STRICT_LOW_PART (VOIDmode, gen_lowpart (smode, dest));
+         op = gen_rtx_SET (VOIDmode, op, gen_lowpart (smode, src));
+         clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
+         emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clobber)));
+         return true;
+       }
+
+      /* ??? There are more powerful versions of ICM that are not
+        completely represented in the md file.  */
     }
 
-  /* (set (ze (reg)) (const_int)).  */
-  if (TARGET_ZARCH
-      && register_operand (dest, word_mode) 
-      && (bitpos % 16) == 0
-      && (bitsize % 16) == 0
-      && const_int_operand (src, VOIDmode))
+  /* For z10, generate ROTATE THEN INSERT SELECTED BITS (RISBG et al).  */
+  if (TARGET_Z10 && (mode == DImode || mode == SImode))
     {
-      HOST_WIDE_INT val = INTVAL (src);
-      int regpos = bitpos + bitsize;
+      enum machine_mode mode_s = GET_MODE (src);
 
-      while (regpos > bitpos)
+      if (mode_s == VOIDmode)
        {
-         enum machine_mode putmode;
-         int putsize;
+         /* Assume const_int etc already in the proper mode.  */
+         src = force_reg (mode, src);
+       }
+      else if (mode_s != mode)
+       {
+         gcc_assert (GET_MODE_BITSIZE (mode_s) >= bitsize);
+         src = force_reg (mode_s, src);
+         src = gen_lowpart (mode, src);
+       }
 
-         if (TARGET_EXTIMM && (regpos % 32 == 0) && (regpos >= bitpos + 32))
-           putmode = SImode;
-         else
-           putmode = HImode;
+      op = gen_rtx_ZERO_EXTRACT (mode, dest, op1, op2),
+      op = gen_rtx_SET (VOIDmode, op, src);
 
-         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;
+      if (!TARGET_ZEC12)
+       {
+         clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
+         op = gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clobber));
        }
-      gcc_assert (regpos == bitpos);
+      emit_insn (op);
+
       return true;
     }
 
@@ -4065,16 +4847,16 @@ 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, 
+  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.  */ 
+   in HImode and QImode.  */
 
 struct alignment_context
 {
-  rtx memsi;     /* SI aligned memory location.  */ 
+  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 */
@@ -4120,96 +4902,141 @@ init_alignment_context (struct alignment_context *ac, rtx mem,
       /* 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);
+  ac->shift = expand_simple_binop (SImode, ASHIFT, ac->shift, GEN_INT (3),
+                                  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);
+  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);
+}
+
+/* A subroutine of s390_expand_cs_hqi.  Insert INS into VAL.  If possible,
+   use a single insv insn into SEQ2.  Otherwise, put prep insns in SEQ1 and
+   perform the merge in SEQ2.  */
+
+static rtx
+s390_two_part_insv (struct alignment_context *ac, rtx *seq1, rtx *seq2,
+                   enum machine_mode mode, rtx val, rtx ins)
+{
+  rtx tmp;
+
+  if (ac->aligned)
+    {
+      start_sequence ();
+      tmp = copy_to_mode_reg (SImode, val);
+      if (s390_expand_insv (tmp, GEN_INT (GET_MODE_BITSIZE (mode)),
+                           const0_rtx, ins))
+       {
+         *seq1 = NULL;
+         *seq2 = get_insns ();
+         end_sequence ();
+         return tmp;
+       }
+      end_sequence ();
+    }
+
+  /* Failed to use insv.  Generate a two part shift and mask.  */
+  start_sequence ();
+  tmp = s390_expand_mask_and_shift (ins, mode, ac->shift);
+  *seq1 = get_insns ();
+  end_sequence ();
+
+  start_sequence ();
+  tmp = expand_simple_binop (SImode, IOR, tmp, val, NULL_RTX, 1, OPTAB_DIRECT);
+  *seq2 = get_insns ();
+  end_sequence ();
+
+  return tmp;
 }
 
 /* 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.  */
+   the memory location, CMP the old value to compare MEM with and NEW_RTX the
+   value to set if CMP == MEM.  */
 
 void
-s390_expand_cs_hqi (enum machine_mode mode, rtx target, rtx mem, rtx cmp, rtx new)
+s390_expand_cs_hqi (enum machine_mode mode, rtx btarget, rtx vtarget, rtx mem,
+                   rtx cmp, rtx new_rtx, bool is_weak)
 {
   struct alignment_context ac;
-  rtx cmpv, newv, val, resv, cc;
+  rtx cmpv, newv, val, cc, seq0, seq1, seq2, seq3;
   rtx res = gen_reg_rtx (SImode);
-  rtx csloop = gen_label_rtx ();
-  rtx csend = gen_label_rtx ();
+  rtx csloop = NULL, csend = NULL;
 
-  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);
 
+  /* Prepare insertions of cmp and new_rtx into the loaded value.  When
+     possible, we try to use insv to make this happen efficiently.  If
+     that fails we'll generate code both inside and outside the loop.  */
+  cmpv = s390_two_part_insv (&ac, &seq0, &seq2, mode, val, cmp);
+  newv = s390_two_part_insv (&ac, &seq1, &seq3, mode, val, new_rtx);
+
+  if (seq0)
+    emit_insn (seq0);
+  if (seq1)
+    emit_insn (seq1);
+
   /* Start CS loop.  */
-  emit_label (csloop);
-  /* val = "<mem>00..0<mem>" 
+  if (!is_weak)
+    {
+      /* Begin assuming success.  */
+      emit_move_insn (btarget, const1_rtx);
+
+      csloop = gen_label_rtx ();
+      csend = gen_label_rtx ();
+      emit_label (csloop);
+    }
+
+  /* val = "<mem>00..0<mem>"
    * cmp = "00..0<cmp>00..0"
-   * new = "00..0<new>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);
-    }
+  emit_insn (seq2);
+  emit_insn (seq3);
+
+  cc = s390_emit_compare_and_swap (EQ, res, ac.memsi, cmpv, newv);
+  if (is_weak)
+    emit_insn (gen_cstorecc4 (btarget, cc, XEXP (cc, 0), XEXP (cc, 1)));
   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));
+      rtx tmp;
 
-  /* Emit compare_and_swap pattern.  */
-  emit_insn (gen_sync_compare_and_swap_ccsi (res, ac.memsi, cmpv, newv));
-      
-  /* Jump to end if we're done (likely?).  */
-  s390_emit_jump (csend, s390_emit_compare (EQ, cmpv, ac.memsi));
+      /* Jump to end if we're done (likely?).  */
+      s390_emit_jump (csend, cc);
 
-  /* 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);
+      /* Check for changes outside mode, and loop internal if so.
+        Arrange the moves so that the compare is adjacent to the
+        branch so that we can generate CRJ.  */
+      tmp = copy_to_reg (val);
+      force_expand_binop (SImode, and_optab, res, ac.modemaski, val,
+                         1, OPTAB_DIRECT);
+      cc = s390_emit_compare (NE, val, tmp);
+      s390_emit_jump (csloop, cc);
+
+      /* Failed.  */
+      emit_move_insn (btarget, const0_rtx);
+      emit_label (csend);
+    }
 
-  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);
+  convert_move (vtarget, 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 the value
+   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.  */
@@ -4220,7 +5047,7 @@ s390_expand_atomic (enum machine_mode mode, enum rtx_code code,
 {
   struct alignment_context ac;
   rtx cmp;
-  rtx new = gen_reg_rtx (SImode);
+  rtx new_rtx = gen_reg_rtx (SImode);
   rtx orig = gen_reg_rtx (SImode);
   rtx csloop = gen_label_rtx ();
 
@@ -4246,56 +5073,53 @@ s390_expand_atomic (enum machine_mode mode, enum rtx_code code,
 
   /* Start CS loop.  */
   emit_label (csloop);
-  emit_move_insn (new, cmp);
+  emit_move_insn (new_rtx, cmp);
 
   /* Patch new with val at correct position.  */
   switch (code)
     {
     case PLUS:
     case MINUS:
-      val = expand_simple_binop (SImode, code, new, orig,
+      val = expand_simple_binop (SImode, code, new_rtx, orig,
                                 NULL_RTX, 1, OPTAB_DIRECT);
       val = expand_simple_binop (SImode, AND, val, ac.modemask,
                                 NULL_RTX, 1, OPTAB_DIRECT);
       /* FALLTHRU */
-    case SET: 
+    case SET:
       if (ac.aligned && MEM_P (val))
-       store_bit_field (new, GET_MODE_BITSIZE (mode), 0, SImode, val);
+       store_bit_field (new_rtx, GET_MODE_BITSIZE (mode), 0,
+                        0, 0, SImode, val);
       else
        {
-         new = expand_simple_binop (SImode, AND, new, ac.modemaski,
+         new_rtx = expand_simple_binop (SImode, AND, new_rtx, ac.modemaski,
                                     NULL_RTX, 1, OPTAB_DIRECT);
-         new = expand_simple_binop (SImode, IOR, new, val,
+         new_rtx = expand_simple_binop (SImode, IOR, new_rtx, val,
                                     NULL_RTX, 1, OPTAB_DIRECT);
        }
       break;
     case AND:
     case IOR:
     case XOR:
-      new = expand_simple_binop (SImode, code, new, val,
+      new_rtx = expand_simple_binop (SImode, code, new_rtx, val,
                                 NULL_RTX, 1, OPTAB_DIRECT);
       break;
     case MULT: /* NAND */
-      new = expand_simple_binop (SImode, XOR, new, ac.modemask,
+      new_rtx = expand_simple_binop (SImode, AND, new_rtx, val,
                                 NULL_RTX, 1, OPTAB_DIRECT);
-      new = expand_simple_binop (SImode, AND, new, val,
+      new_rtx = expand_simple_binop (SImode, XOR, new_rtx, ac.modemask,
                                 NULL_RTX, 1, OPTAB_DIRECT);
       break;
     default:
       gcc_unreachable ();
     }
-  /* Emit compare_and_swap pattern.  */
-  emit_insn (gen_sync_compare_and_swap_ccsi (cmp, ac.memsi, cmp, new));
 
-  /* Loop until swapped (unlikely?).  */
-  s390_emit_jump (csloop, gen_rtx_fmt_ee (NE, CCZ1mode,
-                                         gen_rtx_REG (CCZ1mode, CC_REGNUM),
-                                         const0_rtx));
+  s390_emit_jump (csloop, s390_emit_compare_and_swap (NE, cmp,
+                                                     ac.memsi, cmp, new_rtx));
 
   /* Return the correct part of the bitfield.  */
   if (target)
     convert_move (target, expand_simple_binop (SImode, LSHIFTRT,
-                                              after ? new : cmp, ac.shift,
+                                              after ? new_rtx : cmp, ac.shift,
                                               NULL_RTX, 1, OPTAB_DIRECT), 1);
 }
 
@@ -4323,10 +5147,10 @@ s390_output_dwarf_dtprel (FILE *file, int size, rtx x)
 }
 
 #ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
-/* Implement TARGET_MANGLE_FUNDAMENTAL_TYPE.  */
+/* Implement TARGET_MANGLE_TYPE.  */
 
 static const char *
-s390_mangle_fundamental_type (tree type)
+s390_mangle_type (const_tree type)
 {
   if (TYPE_MAIN_VARIANT (type) == long_double_type_node
       && TARGET_LONG_DOUBLE_128)
@@ -4344,7 +5168,42 @@ s390_mangle_fundamental_type (tree type)
 static rtx
 s390_delegitimize_address (rtx orig_x)
 {
-  rtx x = orig_x, y;
+  rtx x, y;
+
+  orig_x = delegitimize_mem_from_attrs (orig_x);
+  x = orig_x;
+
+  /* Extract the symbol ref from:
+     (plus:SI (reg:SI 12 %r12)
+              (const:SI (unspec:SI [(symbol_ref/f:SI ("*.LC0"))]
+                                   UNSPEC_GOTOFF/PLTOFF)))
+     and
+     (plus:SI (reg:SI 12 %r12)
+              (const:SI (plus:SI (unspec:SI [(symbol_ref:SI ("L"))]
+                                             UNSPEC_GOTOFF/PLTOFF)
+                                (const_int 4 [0x4]))))  */
+  if (GET_CODE (x) == PLUS
+      && REG_P (XEXP (x, 0))
+      && REGNO (XEXP (x, 0)) == PIC_OFFSET_TABLE_REGNUM
+      && GET_CODE (XEXP (x, 1)) == CONST)
+    {
+      HOST_WIDE_INT offset = 0;
+
+      /* The const operand.  */
+      y = XEXP (XEXP (x, 1), 0);
+
+      if (GET_CODE (y) == PLUS
+         && GET_CODE (XEXP (y, 1)) == CONST_INT)
+       {
+         offset = INTVAL (XEXP (y, 1));
+         y = XEXP (y, 0);
+       }
+
+      if (GET_CODE (y) == UNSPEC
+         && (XINT (y, 1) == UNSPEC_GOTOFF
+             || XINT (y, 1) == UNSPEC_PLTOFF))
+       return plus_constant (Pmode, XVECEXP (y, 0, 0), offset);
+    }
 
   if (GET_CODE (x) != MEM)
     return orig_x;
@@ -4358,20 +5217,36 @@ s390_delegitimize_address (rtx orig_x)
       y = XEXP (XEXP (x, 1), 0);
       if (GET_CODE (y) == UNSPEC
          && XINT (y, 1) == UNSPEC_GOT)
-       return XVECEXP (y, 0, 0);
-      return orig_x;
+       y = XVECEXP (y, 0, 0);
+      else
+       return orig_x;
     }
-
-  if (GET_CODE (x) == CONST)
+  else if (GET_CODE (x) == CONST)
     {
+      /* Extract the symbol ref from:
+        (mem:QI (const:DI (unspec:DI [(symbol_ref:DI ("foo"))]
+                                      UNSPEC_PLT/GOTENT)))  */
+
       y = XEXP (x, 0);
       if (GET_CODE (y) == UNSPEC
-         && XINT (y, 1) == UNSPEC_GOTENT)
-       return XVECEXP (y, 0, 0);
-      return orig_x;
+         && (XINT (y, 1) == UNSPEC_GOTENT
+             || XINT (y, 1) == UNSPEC_PLT))
+       y = XVECEXP (y, 0, 0);
+      else
+       return orig_x;
     }
+  else
+    return orig_x;
 
-  return orig_x;
+  if (GET_MODE (orig_x) != Pmode)
+    {
+      if (GET_MODE (orig_x) == BLKmode)
+       return orig_x;
+      y = lowpart_subreg (GET_MODE (orig_x), y, Pmode);
+      if (y == NULL_RTX)
+       return orig_x;
+    }
+  return y;
 }
 
 /* Output operand OP to stdio stream FILE.
@@ -4448,7 +5323,7 @@ get_some_local_dynamic_name (void)
    in assembler syntax to stdio stream FILE.  Returns true if the
    constant X could be recognized, false otherwise.  */
 
-bool
+static bool
 s390_output_addr_const_extra (FILE *file, rtx x)
 {
   if (GET_CODE (x) == UNSPEC && XVECLEN (x, 0) == 1)
@@ -4500,6 +5375,14 @@ s390_output_addr_const_extra (FILE *file, rtx x)
        return true;
       }
 
+  if (GET_CODE (x) == UNSPEC && XVECLEN (x, 0) == 2)
+    switch (XINT (x, 1))
+      {
+      case UNSPEC_POOL_OFFSET:
+       x = gen_rtx_MINUS (GET_MODE (x), XVECEXP (x, 0, 0), XVECEXP (x, 0, 1));
+       output_addr_const (file, x);
+       return true;
+      }
   return false;
 }
 
@@ -4511,6 +5394,18 @@ print_operand_address (FILE *file, rtx addr)
 {
   struct s390_address ad;
 
+  if (s390_loadrelative_operand_p (addr, NULL, NULL))
+    {
+      if (!TARGET_Z10)
+       {
+         output_operand_lossage ("symbolic memory references are "
+                                 "only supported on z10 or later");
+         return;
+       }
+      output_addr_const (file, addr);
+      return;
+    }
+
   if (!s390_decompose_address (addr, &ad)
       || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
       || (ad.indx && !REGNO_OK_FOR_INDEX_P (REGNO (ad.indx))))
@@ -4534,27 +5429,36 @@ 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
+    'E': print opcode suffix for branch on index instruction.
     'G': print the size of the operand in bytes.
+    'J': print tls_load/tls_gdcall/tls_ldcall suffix
+    'M': print the second word of a TImode operand.
+    'N': print the second word of a DImode operand.
     '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 halfword.
+    'c': print integer X as if it's an signed byte.
+    'e': "end" of DImode contiguous bitmask X.
+    'f': "end" of SImode contiguous bitmask X.
     '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 -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.  */
+    'o': print integer X as if it's an unsigned 32bit word.
+    's': "start" of DImode contiguous bitmask X.
+    't': "start" of SImode contiguous bitmask X.
+    'x': print integer X as if it's an unsigned halfword.
+*/
 
 void
 print_operand (FILE *file, rtx x, int code)
 {
+  HOST_WIDE_INT ival;
+
   switch (code)
     {
     case 'C':
@@ -4565,6 +5469,16 @@ print_operand (FILE *file, rtx x, int code)
       fprintf (file, s390_branch_condition_mnemonic (x, TRUE));
       return;
 
+    case 'E':
+      if (GET_CODE (x) == LE)
+       fprintf (file, "l");
+      else if (GET_CODE (x) == GT)
+       fprintf (file, "h");
+      else
+       output_operand_lossage ("invalid comparison operator "
+                               "for 'E' output modifier");
+      return;
+
     case 'J':
       if (GET_CODE (x) == SYMBOL_REF)
        {
@@ -4582,7 +5496,7 @@ print_operand (FILE *file, rtx x, int code)
          assemble_name (file, get_some_local_dynamic_name ());
        }
       else
-       gcc_unreachable ();
+       output_operand_lossage ("invalid reference for 'J' output modifier");
       return;
 
     case 'G':
@@ -4594,11 +5508,22 @@ print_operand (FILE *file, rtx x, int code)
         struct s390_address ad;
        int ret;
 
-        gcc_assert (GET_CODE (x) == MEM);
+       if (!MEM_P (x))
+         {
+           output_operand_lossage ("memory reference expected for "
+                                   "'O' output modifier");
+           return;
+         }
+
        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 (!ret
+           || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
+           || ad.indx)
+         {
+           output_operand_lossage ("invalid address for 'O' output modifier");
+           return;
+         }
 
         if (ad.disp)
           output_addr_const (file, ad.disp);
@@ -4612,11 +5537,22 @@ print_operand (FILE *file, rtx x, int code)
         struct s390_address ad;
        int ret;
 
-        gcc_assert (GET_CODE (x) == MEM);
+       if (!MEM_P (x))
+         {
+           output_operand_lossage ("memory reference expected for "
+                                   "'R' output modifier");
+           return;
+         }
+
        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 (!ret
+           || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
+           || ad.indx)
+         {
+           output_operand_lossage ("invalid address for 'R' output modifier");
+           return;
+         }
 
         if (ad.base)
           fprintf (file, "%s", reg_names[REGNO (ad.base)]);
@@ -4630,11 +5566,21 @@ print_operand (FILE *file, rtx x, int code)
        struct s390_address ad;
        int ret;
 
-        gcc_assert (GET_CODE (x) == MEM);
+       if (!MEM_P (x))
+         {
+           output_operand_lossage ("memory reference expected for "
+                                   "'S' output modifier");
+           return;
+         }
        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 (!ret
+           || (ad.base && !REGNO_OK_FOR_BASE_P (REGNO (ad.base)))
+           || ad.indx)
+         {
+           output_operand_lossage ("invalid address for 'S' output modifier");
+           return;
+         }
 
        if (ad.disp)
          output_addr_const (file, ad.disp);
@@ -4650,18 +5596,22 @@ print_operand (FILE *file, rtx x, int code)
       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));
+       x = change_address (x, VOIDmode,
+                           plus_constant (Pmode, XEXP (x, 0), 4));
       else
-        gcc_unreachable ();
+       output_operand_lossage ("register or memory expression expected "
+                               "for 'N' output modifier");
       break;
 
     case 'M':
       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), 8));
+       x = change_address (x, VOIDmode,
+                           plus_constant (Pmode, XEXP (x, 0), 8));
       else
-        gcc_unreachable ();
+       output_operand_lossage ("register or memory expression expected "
+                               "for 'M' output modifier");
       break;
 
     case 'Y':
@@ -4687,28 +5637,57 @@ print_operand (FILE *file, rtx x, int code)
       break;
 
     case CONST_INT:
-      if (code == 'b')
-        fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xff);
-      else if (code == 'x')
-        fprintf (file, HOST_WIDE_INT_PRINT_DEC, INTVAL (x) & 0xffff);
-      else if (code == 'h')
-        fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((INTVAL (x) & 0xffff) ^ 0x8000) - 0x8000);
-      else if (code == 'i')
-       fprintf (file, HOST_WIDE_INT_PRINT_DEC,
-                s390_extract_part (x, HImode, 0));
-      else if (code == 'j')
-       fprintf (file, HOST_WIDE_INT_PRINT_DEC,
-                s390_extract_part (x, HImode, -1));
-      else 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));
+      ival = INTVAL (x);
+      switch (code)
+       {
+       case 0:
+         break;
+       case 'b':
+         ival &= 0xff;
+         break;
+       case 'c':
+         ival = ((ival & 0xff) ^ 0x80) - 0x80;
+         break;
+       case 'x':
+         ival &= 0xffff;
+         break;
+       case 'h':
+         ival = ((ival & 0xffff) ^ 0x8000) - 0x8000;
+         break;
+       case 'i':
+         ival = s390_extract_part (x, HImode, 0);
+         break;
+       case 'j':
+         ival = s390_extract_part (x, HImode, -1);
+         break;
+       case 'k':
+         ival = s390_extract_part (x, SImode, 0);
+         break;
+       case 'm':
+         ival = s390_extract_part (x, SImode, -1);
+         break;
+       case 'o':
+         ival &= 0xffffffff;
+         break;
+       case 'e': case 'f':
+       case 's': case 't':
+         {
+           int pos, len;
+           bool ok;
+
+           len = (code == 's' || code == 'e' ? 64 : 32);
+           ok = s390_contiguous_bitmask_p (ival, len, &pos, &len);
+           gcc_assert (ok);
+           if (code == 's' || code == 't')
+             ival = 64 - pos - len;
+           else
+             ival = 64 - 1 - pos;
+         }
+         break;
+       default:
+         output_operand_lossage ("invalid constant for output modifier '%c'", code);
+       }
+      fprintf (file, HOST_WIDE_INT_PRINT_DEC, ival);
       break;
 
     case CONST_DOUBLE:
@@ -4718,13 +5697,26 @@ print_operand (FILE *file, rtx x, int code)
       else if (code == 'x')
         fprintf (file, HOST_WIDE_INT_PRINT_DEC, CONST_DOUBLE_LOW (x) & 0xffff);
       else if (code == 'h')
-        fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((CONST_DOUBLE_LOW (x) & 0xffff) ^ 0x8000) - 0x8000);
+        fprintf (file, HOST_WIDE_INT_PRINT_DEC,
+                ((CONST_DOUBLE_LOW (x) & 0xffff) ^ 0x8000) - 0x8000);
       else
-        gcc_unreachable ();
+       {
+         if (code == 0)
+           output_operand_lossage ("invalid constant - try using "
+                                   "an output modifier");
+         else
+           output_operand_lossage ("invalid constant for output modifier '%c'",
+                                   code);
+       }
       break;
 
     default:
-      fatal_insn ("UNKNOWN in print_operand !?", x);
+      if (code == 0)
+       output_operand_lossage ("invalid expression - try using "
+                               "an output modifier");
+      else
+       output_operand_lossage ("invalid expression for output "
+                               "modifier '%c'", code);
       break;
     }
 }
@@ -4793,8 +5785,8 @@ addr_generation_dependency_p (rtx dep_rtx, rtx insn)
 {
   rtx target, pat;
 
-  if (GET_CODE (dep_rtx) == INSN)
-      dep_rtx = PATTERN (dep_rtx);
+  if (NONJUMP_INSN_P (dep_rtx))
+    dep_rtx = PATTERN (dep_rtx);
 
   if (GET_CODE (dep_rtx) == SET)
     {
@@ -4848,6 +5840,7 @@ s390_agen_dep_p (rtx dep_insn, rtx insn)
   return 0;
 }
 
+
 /* 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
@@ -4855,7 +5848,6 @@ s390_agen_dep_p (rtx dep_insn, rtx insn)
 
    A STD instruction should be scheduled earlier,
    in order to use the bypass.  */
-
 static int
 s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
 {
@@ -4863,7 +5855,10 @@ s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
     return priority;
 
   if (s390_tune != PROCESSOR_2084_Z990
-      && s390_tune != PROCESSOR_2094_Z9_109)
+      && s390_tune != PROCESSOR_2094_Z9_109
+      && s390_tune != PROCESSOR_2097_Z10
+      && s390_tune != PROCESSOR_2817_Z196
+      && s390_tune != PROCESSOR_2827_ZEC12)
     return priority;
 
   switch (s390_safe_attr_type (insn))
@@ -4882,15 +5877,24 @@ s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
   return priority;
 }
 
+
 /* The number of instructions that can be issued per cycle.  */
 
 static int
 s390_issue_rate (void)
 {
-  if (s390_tune == PROCESSOR_2084_Z990
-      || s390_tune == PROCESSOR_2094_Z9_109)
-    return 3;
-  return 1;
+  switch (s390_tune)
+    {
+    case PROCESSOR_2084_Z990:
+    case PROCESSOR_2094_Z9_109:
+    case PROCESSOR_2817_Z196:
+      return 3;
+    case PROCESSOR_2097_Z10:
+    case PROCESSOR_2827_ZEC12:
+      return 2;
+    default:
+      return 1;
+    }
 }
 
 static int
@@ -4899,7 +5903,6 @@ s390_first_cycle_multipass_dfa_lookahead (void)
   return 4;
 }
 
-
 /* Annotate every literal pool reference in X by an UNSPEC_LTREF expression.
    Fix up MEMs as required.  */
 
@@ -4940,7 +5943,7 @@ annotate_constant_pool_refs (rtx *x)
          rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, sym, base),
                                     UNSPEC_LTREF);
 
-         *x = replace_equiv_address (*x, plus_constant (addr, off));
+         *x = replace_equiv_address (*x, plus_constant (Pmode, addr, off));
          return;
        }
     }
@@ -4973,7 +5976,7 @@ annotate_constant_pool_refs (rtx *x)
          rtx addr = gen_rtx_UNSPEC (Pmode, gen_rtvec (2, sym, base),
                                     UNSPEC_LTREF);
 
-         SET_SRC (*x) = plus_constant (addr, off);
+         SET_SRC (*x) = plus_constant (Pmode, addr, off);
          return;
        }
     }
@@ -5022,7 +6025,7 @@ s390_split_branches (void)
 
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
-      if (GET_CODE (insn) != JUMP_INSN)
+      if (! JUMP_P (insn))
        continue;
 
       pat = PATTERN (insn);
@@ -5089,8 +6092,8 @@ s390_split_branches (void)
 }
 
 
-/* Find an annotated literal pool symbol referenced in RTX X, 
-   and store it at REF.  Will abort if X contains references to 
+/* Find an annotated literal pool symbol referenced in RTX X,
+   and store it at REF.  Will abort if X contains references to
    more than one such pool symbol; multiple references to the same
    symbol are allowed, however.
 
@@ -5123,7 +6126,7 @@ find_constant_pool_ref (rtx x, rtx *ref)
 
       if (*ref == NULL_RTX)
        *ref = sym;
-      else 
+      else
        gcc_assert (*ref == sym);
 
       return;
@@ -5144,7 +6147,7 @@ find_constant_pool_ref (rtx x, rtx *ref)
     }
 }
 
-/* Replace every reference to the annotated literal pool 
+/* Replace every reference to the annotated literal pool
    symbol REF in X by its base plus OFFSET.  */
 
 static void
@@ -5170,7 +6173,7 @@ replace_constant_pool_ref (rtx *x, rtx ref, rtx offset)
       && XVECEXP (XEXP (*x, 0), 0, 0) == ref)
     {
       rtx addr = gen_rtx_PLUS (Pmode, XVECEXP (XEXP (*x, 0), 0, 1), offset);
-      *x = plus_constant (addr, INTVAL (XEXP (*x, 1)));
+      *x = plus_constant (Pmode, addr, INTVAL (XEXP (*x, 1)));
       return;
     }
 
@@ -5282,6 +6285,7 @@ struct constant_pool
   rtx first_insn;
   rtx pool_insn;
   bitmap insns;
+  rtx emit_pool_after;
 
   struct constant *constants[NR_C_MODES];
   struct constant *execute;
@@ -5308,6 +6312,7 @@ s390_alloc_pool (void)
   pool->pool_insn = NULL_RTX;
   pool->insns = BITMAP_ALLOC (NULL);
   pool->size = 0;
+  pool->emit_pool_after = NULL_RTX;
 
   return pool;
 }
@@ -5395,6 +6400,20 @@ s390_add_constant (struct constant_pool *pool, rtx val, enum machine_mode mode)
     }
 }
 
+/* Return an rtx that represents the offset of X from the start of
+   pool POOL.  */
+
+static rtx
+s390_pool_offset (struct constant_pool *pool, rtx x)
+{
+  rtx label;
+
+  label = gen_rtx_LABEL_REF (GET_MODE (x), pool->label);
+  x = gen_rtx_UNSPEC (GET_MODE (x), gen_rtvec (2, x, label),
+                     UNSPEC_POOL_OFFSET);
+  return gen_rtx_CONST (GET_MODE (x), x);
+}
+
 /* Find constant VAL of mode MODE in the constant pool POOL.
    Return an RTX describing the distance from the start of
    the pool to the location of the new constant.  */
@@ -5404,7 +6423,6 @@ s390_find_constant (struct constant_pool *pool, rtx val,
                    enum machine_mode mode)
 {
   struct constant *c;
-  rtx offset;
   int i;
 
   for (i = 0; i < NR_C_MODES; i++)
@@ -5418,10 +6436,7 @@ s390_find_constant (struct constant_pool *pool, rtx val,
 
   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;
+  return s390_pool_offset (pool, gen_rtx_LABEL_REF (Pmode, c->label));
 }
 
 /* Check whether INSN is an execute.  Return the label_ref to its
@@ -5430,7 +6445,7 @@ s390_find_constant (struct constant_pool *pool, rtx val,
 static rtx
 s390_execute_label (rtx insn)
 {
-  if (GET_CODE (insn) == INSN
+  if (NONJUMP_INSN_P (insn)
       && GET_CODE (PATTERN (insn)) == PARALLEL
       && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC
       && XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE)
@@ -5469,7 +6484,6 @@ 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))
@@ -5477,10 +6491,7 @@ s390_find_execute (struct constant_pool *pool, rtx insn)
 
   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;
+  return s390_pool_offset (pool, gen_rtx_LABEL_REF (Pmode, c->label));
 }
 
 /* For an execute INSN, extract the execute target template.  */
@@ -5556,16 +6567,12 @@ s390_dump_pool (struct constant_pool *pool, bool remote_label)
     for (c = pool->constants[i]; c; c = c->next)
       {
        /* Convert UNSPEC_LTREL_OFFSET unspecs to pool-relative references.  */
-       rtx value = c->value;
+       rtx value = copy_rtx (c->value);
        if (GET_CODE (value) == CONST
            && GET_CODE (XEXP (value, 0)) == UNSPEC
            && XINT (XEXP (value, 0), 1) == UNSPEC_LTREL_OFFSET
            && XVECLEN (XEXP (value, 0), 0) == 1)
-         {
-           value = gen_rtx_MINUS (Pmode, XVECEXP (XEXP (value, 0), 0, 0),
-                                  gen_rtx_LABEL_REF (VOIDmode, pool->label));
-           value = gen_rtx_CONST (VOIDmode, value);
-         }
+         value = s390_pool_offset (pool, XVECEXP (XEXP (value, 0), 0, 0));
 
        insn = emit_label_after (c->label, insn);
        INSN_ADDRESSES_NEW (insn, -1);
@@ -5643,7 +6650,7 @@ s390_mainpool_start (void)
 
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
-      if (GET_CODE (insn) == INSN
+      if (NONJUMP_INSN_P (insn)
          && GET_CODE (PATTERN (insn)) == SET
          && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC_VOLATILE
          && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPECV_MAIN_POOL)
@@ -5656,7 +6663,7 @@ s390_mainpool_start (void)
        {
          s390_add_execute (pool, insn);
        }
-      else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+      else if (NONJUMP_INSN_P (insn) || CALL_P (insn))
        {
          rtx pool_ref = NULL_RTX;
          find_constant_pool_ref (PATTERN (insn), &pool_ref);
@@ -5667,6 +6674,17 @@ s390_mainpool_start (void)
              s390_add_constant (pool, constant, mode);
            }
        }
+
+      /* If hot/cold partitioning is enabled we have to make sure that
+        the literal pool is emitted in the same section where the
+        initialization of the literal pool base pointer takes place.
+        emit_pool_after is only used in the non-overflow case on non
+        Z cpus where we can emit the literal pool at the end of the
+        function body within the text section.  */
+      if (NOTE_P (insn)
+         && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS
+         && !pool->emit_pool_after)
+       pool->emit_pool_after = PREV_INSN (insn);
     }
 
   gcc_assert (pool->pool_insn || pool->size == 0);
@@ -5681,6 +6699,11 @@ s390_mainpool_start (void)
       pool = NULL;
     }
 
+  /* If the functions ends with the section where the literal pool
+     should be emitted set the marker to its end.  */
+  if (pool && !pool->emit_pool_after)
+    pool->emit_pool_after = get_last_insn ();
+
   return pool;
 }
 
@@ -5728,7 +6751,7 @@ s390_mainpool_finish (struct constant_pool *pool)
   /* On S/390, if the total size of the function's code plus literal pool
      does not exceed 4096 bytes, we use BASR to set up a function base
      pointer, and emit the literal pool at the end of the function.  */
-  else if (INSN_ADDRESSES (INSN_UID (get_last_insn ()))
+  else if (INSN_ADDRESSES (INSN_UID (pool->emit_pool_after))
           + pool->size + 8 /* alignment slop */ < 4096)
     {
       insn = gen_main_base_31_small (base_reg, pool->label);
@@ -5739,7 +6762,11 @@ s390_mainpool_finish (struct constant_pool *pool)
       insn = emit_label_after (pool->label, insn);
       INSN_ADDRESSES_NEW (insn, -1);
 
-      insn = get_last_insn ();
+      /* emit_pool_after will be set by s390_mainpool_start to the
+        last insn of the section where the literal pool should be
+        emitted.  */
+      insn = pool->emit_pool_after;
+
       pool->pool_insn = emit_insn_after (gen_pool (const0_rtx), insn);
       INSN_ADDRESSES_NEW (pool->pool_insn, -1);
 
@@ -5753,7 +6780,8 @@ s390_mainpool_finish (struct constant_pool *pool)
       rtx pool_end = gen_label_rtx ();
 
       insn = gen_main_base_31_large (base_reg, pool->label, pool_end);
-      insn = emit_insn_after (insn, pool->pool_insn);
+      insn = emit_jump_insn_after (insn, pool->pool_insn);
+      JUMP_LABEL (insn) = pool_end;
       INSN_ADDRESSES_NEW (insn, -1);
       remove_insn (pool->pool_insn);
 
@@ -5777,7 +6805,7 @@ s390_mainpool_finish (struct constant_pool *pool)
       if (INSN_P (insn))
        replace_ltrel_base (&PATTERN (insn));
 
-      if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+      if (NONJUMP_INSN_P (insn) || CALL_P (insn))
         {
           rtx addr, pool_ref = NULL_RTX;
           find_constant_pool_ref (PATTERN (insn), &pool_ref);
@@ -5838,6 +6866,8 @@ s390_chunkify_start (void)
 
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
     {
+      bool section_switch_p = false;
+
       /* Check for pending LTREL_BASE.  */
       if (INSN_P (insn))
        {
@@ -5857,7 +6887,7 @@ s390_chunkify_start (void)
          s390_add_execute (curr_pool, insn);
          s390_add_pool_insn (curr_pool, insn);
        }
-      else if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+      else if (NONJUMP_INSN_P (insn) || CALL_P (insn))
        {
          rtx pool_ref = NULL_RTX;
          find_constant_pool_ref (PATTERN (insn), &pool_ref);
@@ -5884,7 +6914,7 @@ s390_chunkify_start (void)
            }
        }
 
-      if (GET_CODE (insn) == JUMP_INSN || GET_CODE (insn) == CODE_LABEL)
+      if (JUMP_P (insn) || JUMP_TABLE_DATA_P (insn) || LABEL_P (insn))
        {
          if (curr_pool)
            s390_add_pool_insn (curr_pool, insn);
@@ -5892,10 +6922,23 @@ s390_chunkify_start (void)
          gcc_assert (!pending_ltrel);
        }
 
-      if (!curr_pool
-         || INSN_ADDRESSES_SIZE () <= (size_t) INSN_UID (insn)
-          || INSN_ADDRESSES (INSN_UID (insn)) == -1)
-       continue;
+      if (NOTE_P (insn))
+       switch (NOTE_KIND (insn))
+         {
+         case NOTE_INSN_SWITCH_TEXT_SECTIONS:
+           section_switch_p = true;
+           break;
+         case NOTE_INSN_VAR_LOCATION:
+         case NOTE_INSN_CALL_ARG_LOCATION:
+           continue;
+         default:
+           break;
+         }
+
+      if (!curr_pool
+         || INSN_ADDRESSES_SIZE () <= (size_t) INSN_UID (insn)
+          || INSN_ADDRESSES (INSN_UID (insn)) == -1)
+       continue;
 
       if (TARGET_CPU_ZARCH)
        {
@@ -5915,15 +6958,16 @@ s390_chunkify_start (void)
             Those will have an effect on code size, which we need to
             consider here.  This calculation makes rather pessimistic
             worst-case assumptions.  */
-         if (GET_CODE (insn) == CODE_LABEL)
+         if (LABEL_P (insn))
            extra_size += 6;
 
          if (chunk_size < S390_POOL_CHUNK_MIN
-             && curr_pool->size < S390_POOL_CHUNK_MIN)
+             && curr_pool->size < S390_POOL_CHUNK_MIN
+             && !section_switch_p)
            continue;
 
          /* Pool chunks can only be inserted after BARRIERs ...  */
-         if (GET_CODE (insn) == BARRIER)
+         if (BARRIER_P (insn))
            {
              s390_end_pool (curr_pool, insn);
              curr_pool = NULL;
@@ -5931,24 +6975,53 @@ s390_chunkify_start (void)
            }
 
          /* ... so if we don't find one in time, create one.  */
-          else if ((chunk_size > S390_POOL_CHUNK_MAX
-                  || curr_pool->size > S390_POOL_CHUNK_MAX))
+          else if (chunk_size > S390_POOL_CHUNK_MAX
+                  || curr_pool->size > S390_POOL_CHUNK_MAX
+                  || section_switch_p)
            {
-              rtx label, jump, barrier;
+             rtx label, jump, barrier, next, prev;
 
-             /* We can insert the barrier only after a 'real' insn.  */
-             if (GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_INSN)
-               continue;
-             if (get_attr_length (insn) == 0)
-               continue;
+             if (!section_switch_p)
+               {
+                 /* We can insert the barrier only after a 'real' insn.  */
+                 if (! NONJUMP_INSN_P (insn) && ! CALL_P (insn))
+                   continue;
+                 if (get_attr_length (insn) == 0)
+                   continue;
+                 /* Don't separate LTREL_BASE from the corresponding
+                    LTREL_OFFSET load.  */
+                 if (pending_ltrel)
+                   continue;
+                 next = insn;
+                 do
+                   {
+                     insn = next;
+                     next = NEXT_INSN (insn);
+                   }
+                 while (next
+                        && NOTE_P (next)
+                        && (NOTE_KIND (next) == NOTE_INSN_VAR_LOCATION
+                            || NOTE_KIND (next) == NOTE_INSN_CALL_ARG_LOCATION));
+               }
+             else
+               {
+                 gcc_assert (!pending_ltrel);
 
-             /* Don't separate LTREL_BASE from the corresponding
-                LTREL_OFFSET load.  */
-             if (pending_ltrel)
-               continue;
+                 /* The old pool has to end before the section switch
+                    note in order to make it part of the current
+                    section.  */
+                 insn = PREV_INSN (insn);
+               }
 
              label = gen_label_rtx ();
-             jump = emit_jump_insn_after (gen_jump (label), insn);
+             prev = insn;
+             if (prev && NOTE_P (prev))
+               prev = prev_nonnote_insn (prev);
+             if (prev)
+               jump = emit_jump_insn_after_setloc (gen_jump (label), insn,
+                                                   INSN_LOCATION (prev));
+             else
+               jump = emit_jump_insn_after_noloc (gen_jump (label), insn);
              barrier = emit_barrier_after (jump);
              insn = emit_label_after (label, barrier);
              JUMP_LABEL (jump) = label;
@@ -5983,23 +7056,21 @@ s390_chunkify_start (void)
         Don't do that, however, if it is the label before
         a jump table.  */
 
-      if (GET_CODE (insn) == CODE_LABEL
+      if (LABEL_P (insn)
          && (LABEL_PRESERVE_P (insn) || LABEL_NAME (insn)))
        {
-         rtx vec_insn = next_real_insn (insn);
-         rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ?
-                       PATTERN (vec_insn) : NULL_RTX;
-         if (!vec_pat
-             || !(GET_CODE (vec_pat) == ADDR_VEC
-                  || GET_CODE (vec_pat) == ADDR_DIFF_VEC))
+         rtx vec_insn = NEXT_INSN (insn);
+         if (! vec_insn || ! JUMP_TABLE_DATA_P (vec_insn))
            bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (insn));
        }
 
       /* If we have a direct jump (conditional or unconditional)
         or a casesi jump, check all potential targets.  */
-      else if (GET_CODE (insn) == JUMP_INSN)
+      else if (JUMP_P (insn))
        {
           rtx pat = PATTERN (insn);
+          rtx table;
+
          if (GET_CODE (pat) == PARALLEL && XVECLEN (pat, 0) > 2)
            pat = XVECEXP (pat, 0, 0);
 
@@ -6013,31 +7084,18 @@ s390_chunkify_start (void)
                    bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label));
                }
             }
-         else if (GET_CODE (pat) == PARALLEL
-                  && XVECLEN (pat, 0) == 2
-                  && GET_CODE (XVECEXP (pat, 0, 0)) == SET
-                  && GET_CODE (XVECEXP (pat, 0, 1)) == USE
-                  && GET_CODE (XEXP (XVECEXP (pat, 0, 1), 0)) == LABEL_REF)
-           {
-             /* Find the jump table used by this casesi jump.  */
-             rtx vec_label = XEXP (XEXP (XVECEXP (pat, 0, 1), 0), 0);
-             rtx vec_insn = next_real_insn (vec_label);
-             rtx vec_pat = vec_insn && GET_CODE (vec_insn) == JUMP_INSN ?
-                           PATTERN (vec_insn) : NULL_RTX;
-             if (vec_pat
-                 && (GET_CODE (vec_pat) == ADDR_VEC
-                     || GET_CODE (vec_pat) == ADDR_DIFF_VEC))
-               {
-                 int i, diff_p = GET_CODE (vec_pat) == ADDR_DIFF_VEC;
-
-                 for (i = 0; i < XVECLEN (vec_pat, diff_p); i++)
-                   {
-                     rtx label = XEXP (XVECEXP (vec_pat, diff_p, i), 0);
-
-                     if (s390_find_pool (pool_list, label)
-                         != s390_find_pool (pool_list, insn))
-                       bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label));
-                   }
+         else if (tablejump_p (insn, NULL, &table))
+           {
+             rtx vec_pat = PATTERN (table);
+             int i, diff_p = GET_CODE (vec_pat) == ADDR_DIFF_VEC;
+
+             for (i = 0; i < XVECLEN (vec_pat, diff_p); i++)
+               {
+                 rtx label = XEXP (XVECEXP (vec_pat, diff_p, i), 0);
+
+                 if (s390_find_pool (pool_list, label)
+                     != s390_find_pool (pool_list, insn))
+                   bitmap_set_bit (far_labels, CODE_LABEL_NUMBER (label));
                }
            }
         }
@@ -6047,7 +7105,7 @@ s390_chunkify_start (void)
 
   for (curr_pool = pool_list; curr_pool; curr_pool = curr_pool->next)
     {
-      rtx new_insn = gen_reload_base (cfun->machine->base_reg, 
+      rtx new_insn = gen_reload_base (cfun->machine->base_reg,
                                      curr_pool->label);
       rtx insn = curr_pool->first_insn;
       INSN_ADDRESSES_NEW (emit_insn_before (new_insn, insn), -1);
@@ -6056,13 +7114,13 @@ s390_chunkify_start (void)
   /* Insert base register reload insns at every far label.  */
 
   for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
-    if (GET_CODE (insn) == CODE_LABEL
+    if (LABEL_P (insn)
         && bitmap_bit_p (far_labels, CODE_LABEL_NUMBER (insn)))
       {
        struct constant_pool *pool = s390_find_pool (pool_list, insn);
        if (pool)
          {
-           rtx new_insn = gen_reload_base (cfun->machine->base_reg, 
+           rtx new_insn = gen_reload_base (cfun->machine->base_reg,
                                            pool->label);
            INSN_ADDRESSES_NEW (emit_insn_after (new_insn, insn), -1);
          }
@@ -6102,7 +7160,7 @@ s390_chunkify_finish (struct constant_pool *pool_list)
       if (!curr_pool)
        continue;
 
-      if (GET_CODE (insn) == INSN || GET_CODE (insn) == CALL_INSN)
+      if (NONJUMP_INSN_P (insn) || CALL_P (insn))
         {
           rtx addr, pool_ref = NULL_RTX;
           find_constant_pool_ref (PATTERN (insn), &pool_ref);
@@ -6155,9 +7213,9 @@ s390_chunkify_cancel (struct constant_pool *pool_list)
       rtx jump = barrier? PREV_INSN (barrier) : NULL_RTX;
       rtx label = NEXT_INSN (curr_pool->pool_insn);
 
-      if (jump && GET_CODE (jump) == JUMP_INSN
-         && barrier && GET_CODE (barrier) == BARRIER
-         && label && GET_CODE (label) == CODE_LABEL
+      if (jump && JUMP_P (jump)
+         && barrier && BARRIER_P (barrier)
+         && label && LABEL_P (label)
          && GET_CODE (PATTERN (jump)) == SET
          && SET_DEST (PATTERN (jump)) == pc_rtx
          && GET_CODE (SET_SRC (PATTERN (jump))) == LABEL_REF
@@ -6177,7 +7235,7 @@ s390_chunkify_cancel (struct constant_pool *pool_list)
     {
       rtx next_insn = NEXT_INSN (insn);
 
-      if (GET_CODE (insn) == INSN
+      if (NONJUMP_INSN_P (insn)
          && GET_CODE (PATTERN (insn)) == SET
          && GET_CODE (SET_SRC (PATTERN (insn))) == UNSPEC
          && XINT (SET_SRC (PATTERN (insn)), 1) == UNSPEC_RELOAD_BASE)
@@ -6196,7 +7254,6 @@ s390_chunkify_cancel (struct constant_pool *pool_list)
     }
 }
 
-
 /* Output the constant pool entry EXP in mode MODE with alignment ALIGN.  */
 
 void
@@ -6216,6 +7273,7 @@ s390_output_pool_entry (rtx exp, enum machine_mode mode, unsigned int align)
 
     case MODE_INT:
       assemble_integer (exp, GET_MODE_SIZE (mode), align, 1);
+      mark_symbol_refs_as_used (exp);
       break;
 
     default:
@@ -6255,11 +7313,11 @@ s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED)
     }
 
   if (TARGET_PACKED_STACK)
-    offset = -2 * UNITS_PER_WORD;
+    offset = -2 * UNITS_PER_LONG;
   else
-    offset = RETURN_REGNUM * UNITS_PER_WORD;
+    offset = RETURN_REGNUM * UNITS_PER_LONG;
 
-  addr = plus_constant (frame, offset);
+  addr = plus_constant (Pmode, frame, offset);
   addr = memory_address (Pmode, addr);
   return gen_rtx_MEM (Pmode, addr);
 }
@@ -6275,8 +7333,8 @@ s390_back_chain_rtx (void)
   gcc_assert (TARGET_BACKCHAIN);
 
   if (TARGET_PACKED_STACK)
-    chain = plus_constant (stack_pointer_rtx,
-                          STACK_POINTER_OFFSET - UNITS_PER_WORD);
+    chain = plus_constant (Pmode, stack_pointer_rtx,
+                          STACK_POINTER_OFFSET - UNITS_PER_LONG);
   else
     chain = stack_pointer_rtx;
 
@@ -6293,17 +7351,17 @@ find_unused_clobbered_reg (void)
 {
   int i;
   for (i = 0; i < 6; i++)
-    if (!regs_ever_live[i])
+    if (!df_regs_ever_live_p (i))
       return i;
   return 0;
 }
 
 
-/* Helper function for s390_regs_ever_clobbered.  Sets the fields in DATA for all 
+/* Helper function for s390_regs_ever_clobbered.  Sets the fields in DATA for all
    clobbered hard regs in SETREG.  */
 
 static void
-s390_reg_clobbered_rtx (rtx setreg, rtx set_insn ATTRIBUTE_UNUSED, void *data)
+s390_reg_clobbered_rtx (rtx setreg, const_rtx set_insn ATTRIBUTE_UNUSED, void *data)
 {
   int *regs_ever_clobbered = (int *)data;
   unsigned int i, regno;
@@ -6312,11 +7370,11 @@ s390_reg_clobbered_rtx (rtx setreg, rtx set_insn ATTRIBUTE_UNUSED, void *data)
   if (GET_CODE (setreg) == SUBREG)
     {
       rtx inner = SUBREG_REG (setreg);
-      if (!GENERAL_REG_P (inner))
+      if (!GENERAL_REG_P (inner) && !FP_REG_P (inner))
        return;
       regno = subreg_regno (setreg);
     }
-  else if (GENERAL_REG_P (setreg))
+  else if (GENERAL_REG_P (setreg) || FP_REG_P (setreg))
     regno = REGNO (setreg);
   else
     return;
@@ -6339,13 +7397,13 @@ s390_regs_ever_clobbered (int *regs_ever_clobbered)
   rtx cur_insn;
   unsigned int i;
 
-  memset (regs_ever_clobbered, 0, 16 * sizeof (int));
+  memset (regs_ever_clobbered, 0, 32 * sizeof (int));
 
   /* For non-leaf functions we have to consider all call clobbered regs to be
      clobbered.  */
-  if (!current_function_is_leaf)
+  if (!crtl->is_leaf)
     {
-      for (i = 0; i < 16; i++)
+      for (i = 0; i < 32; i++)
        regs_ever_clobbered[i] = call_really_used_regs[i];
     }
 
@@ -6355,19 +7413,19 @@ s390_regs_ever_clobbered (int *regs_ever_clobbered)
      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)
+  if (crtl->calls_eh_return || cfun->machine->has_landing_pad_p)
     for (i = 0; EH_RETURN_DATA_REGNO (i) != INVALID_REGNUM ; i++)
-      if (current_function_calls_eh_return 
-         || (cfun->machine->has_landing_pad_p 
-             && regs_ever_live [EH_RETURN_DATA_REGNO (i)]))
+      if (crtl->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 (cfun->has_nonlocal_label)
+    for (i = 0; i < 32; i++)
       if (!call_really_used_regs[i])
        regs_ever_clobbered[i] = 1;
 
@@ -6377,23 +7435,22 @@ s390_regs_ever_clobbered (int *regs_ever_clobbered)
        {
          if (INSN_P (cur_insn))
            note_stores (PATTERN (cur_insn),
-                        s390_reg_clobbered_rtx, 
+                        s390_reg_clobbered_rtx,
                         regs_ever_clobbered);
        }
     }
 }
 
-/* Determine the frame area which actually has to be accessed 
-   in the function epilogue. The values are stored at the 
+/* 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 
+   address) and AREA_TOP (address of the first item which does
    not belong to the stack frame).  */
 
 static void
 s390_frame_area (int *area_bottom, int *area_top)
 {
   int b, t;
-  int i;
 
   b = INT_MAX;
   t = INT_MIN;
@@ -6401,9 +7458,9 @@ s390_frame_area (int *area_bottom, int *area_top)
   if (cfun_frame_layout.first_restore_gpr != -1)
     {
       b = (cfun_frame_layout.gprs_offset
-          + cfun_frame_layout.first_restore_gpr * UNITS_PER_WORD);
+          + cfun_frame_layout.first_restore_gpr * UNITS_PER_LONG);
       t = b + (cfun_frame_layout.last_restore_gpr
-              - cfun_frame_layout.first_restore_gpr + 1) * UNITS_PER_WORD;
+              - cfun_frame_layout.first_restore_gpr + 1) * UNITS_PER_LONG;
     }
 
   if (TARGET_64BIT && cfun_save_high_fprs_p)
@@ -6414,13 +7471,18 @@ s390_frame_area (int *area_bottom, int *area_top)
     }
 
   if (!TARGET_64BIT)
-    for (i = 2; i < 4; i++)
-      if (cfun_fpr_bit_p (i))
+    {
+      if (cfun_fpr_save_p (FPR4_REGNUM))
+       {
+         b = MIN (b, cfun_frame_layout.f4_offset);
+         t = MAX (t, cfun_frame_layout.f4_offset + 8);
+       }
+      if (cfun_fpr_save_p (FPR6_REGNUM))
        {
-         b = MIN (b, cfun_frame_layout.f4_offset + (i - 2) * 8);
-         t = MAX (t, cfun_frame_layout.f4_offset + (i - 1) * 8);
+         b = MIN (b, cfun_frame_layout.f4_offset + 8);
+         t = MAX (t, cfun_frame_layout.f4_offset + 16);
        }
-  
+    }
   *area_bottom = b;
   *area_top = t;
 }
@@ -6433,17 +7495,6 @@ s390_register_info (int clobbered_regs[])
 {
   int i, j;
 
-  /* fprs 8 - 15 are call saved for 64 Bit ABI.  */
-  cfun_frame_layout.fpr_bitmap = 0;
-  cfun_frame_layout.high_fprs = 0;
-  if (TARGET_64BIT)
-    for (i = 24; i < 32; i++)
-      if (regs_ever_live[i] && !global_regs[i])
-       {
-         cfun_set_fpr_bit (i - 16);
-         cfun_frame_layout.high_fprs++;
-       }
-
   /* Find first and last gpr to be saved.  We trust regs_ever_live
      data, except that we don't save and restore global registers.
 
@@ -6452,6 +7503,29 @@ s390_register_info (int clobbered_regs[])
 
   s390_regs_ever_clobbered (clobbered_regs);
 
+  /* fprs 8 - 15 are call saved for 64 Bit ABI.  */
+  if (!epilogue_completed)
+    {
+      cfun_frame_layout.fpr_bitmap = 0;
+      cfun_frame_layout.high_fprs = 0;
+      if (TARGET_64BIT)
+       for (i = FPR8_REGNUM; i <= FPR15_REGNUM; i++)
+         /* During reload we have to use the df_regs_ever_live infos
+            since reload is marking FPRs used as spill slots there as
+            live before actually making the code changes.  Without
+            this we fail during elimination offset verification.  */
+         if ((clobbered_regs[i]
+              || (df_regs_ever_live_p (i)
+                  && (lra_in_progress
+                      || reload_in_progress
+                      || crtl->saves_all_registers)))
+             && !global_regs[i])
+           {
+             cfun_set_fpr_save (i);
+             cfun_frame_layout.high_fprs++;
+           }
+    }
+
   for (i = 0; i < 16; i++)
     clobbered_regs[i] = clobbered_regs[i] && !global_regs[i] && !fixed_regs[i];
 
@@ -6459,34 +7533,34 @@ s390_register_info (int clobbered_regs[])
     clobbered_regs[HARD_FRAME_POINTER_REGNUM] = 1;
 
   if (flag_pic)
-    clobbered_regs[PIC_OFFSET_TABLE_REGNUM] 
-      |= regs_ever_live[PIC_OFFSET_TABLE_REGNUM];
+    clobbered_regs[PIC_OFFSET_TABLE_REGNUM]
+      |= df_regs_ever_live_p (PIC_OFFSET_TABLE_REGNUM);
 
-  clobbered_regs[BASE_REGNUM] 
+  clobbered_regs[BASE_REGNUM]
     |= (cfun->machine->base_reg
         && REGNO (cfun->machine->base_reg) == BASE_REGNUM);
 
   clobbered_regs[RETURN_REGNUM]
-    |= (!current_function_is_leaf
+    |= (!crtl->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);
+       || crtl->calls_eh_return
+       || cfun->stdarg);
 
   clobbered_regs[STACK_POINTER_REGNUM]
-    |= (!current_function_is_leaf
+    |= (!crtl->is_leaf
        || TARGET_TPF_PROFILING
        || cfun_save_high_fprs_p
        || get_frame_size () > 0
-       || current_function_calls_alloca
-       || current_function_stdarg);
+       || cfun->calls_alloca
+       || cfun->stdarg);
 
   for (i = 6; i < 16; i++)
-    if (regs_ever_live[i] || clobbered_regs[i])
+    if (df_regs_ever_live_p (i) || clobbered_regs[i])
       break;
   for (j = 15; j > i; j--)
-    if (regs_ever_live[j] || clobbered_regs[j])
+    if (df_regs_ever_live_p (j) || clobbered_regs[j])
       break;
 
   if (i == 16)
@@ -6505,8 +7579,8 @@ s390_register_info (int clobbered_regs[])
       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; 
+      for (i = cfun_frame_layout.first_save_gpr_slot;
+          i < cfun_frame_layout.last_save_gpr_slot + 1;
           i++)
        if (clobbered_regs[i])
          break;
@@ -6514,7 +7588,7 @@ s390_register_info (int clobbered_regs[])
       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.  */
@@ -6533,13 +7607,13 @@ s390_register_info (int clobbered_regs[])
        }
     }
 
-  if (current_function_stdarg)
+  if (cfun->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)
+         && crtl->args.info.gprs < GP_ARG_NUM_REG)
        {
-         int min_gpr = current_function_args_info.gprs;
+         int min_gpr = crtl->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;
@@ -6561,9 +7635,9 @@ s390_register_info (int clobbered_regs[])
 
       /* 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)
+         && crtl->args.info.fprs < FP_ARG_NUM_REG)
        {
-         int min_fpr = current_function_args_info.fprs;
+         int min_fpr = crtl->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;
@@ -6574,14 +7648,17 @@ s390_register_info (int clobbered_regs[])
            min_fpr = 0;
 
          for (i = min_fpr; i < max_fpr; i++)
-           cfun_set_fpr_bit (i);
+           cfun_set_fpr_save (i + FPR0_REGNUM);
        }
     }
 
   if (!TARGET_64BIT)
-    for (i = 2; i < 4; i++)
-      if (regs_ever_live[i + 16] && !global_regs[i + 16])
-       cfun_set_fpr_bit (i);
+    {
+      if (df_regs_ever_live_p (FPR4_REGNUM) && !global_regs[FPR4_REGNUM])
+       cfun_set_fpr_save (FPR4_REGNUM);
+      if (df_regs_ever_live_p (FPR6_REGNUM) && !global_regs[FPR6_REGNUM])
+       cfun_set_fpr_save (FPR6_REGNUM);
+    }
 }
 
 /* Fill cfun->machine with info about frame of current function.  */
@@ -6594,81 +7671,87 @@ s390_frame_info (void)
   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.f0_offset = 16 * UNITS_PER_LONG;
       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);
+                                      * UNITS_PER_LONG);
     }
   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 
+                                           - UNITS_PER_LONG);
+      cfun_frame_layout.gprs_offset
+       = (cfun_frame_layout.backchain_offset
           - (STACK_POINTER_REGNUM - cfun_frame_layout.first_save_gpr_slot + 1)
-          * UNITS_PER_WORD);
-         
+          * UNITS_PER_LONG);
+
       if (TARGET_64BIT)
        {
-         cfun_frame_layout.f4_offset 
+         cfun_frame_layout.f4_offset
            = (cfun_frame_layout.gprs_offset
-              - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
-         
-         cfun_frame_layout.f0_offset 
-           = (cfun_frame_layout.f4_offset 
-              - 8 * (cfun_fpr_bit_p (0) + cfun_fpr_bit_p (1)));
+              - 8 * (cfun_fpr_save_p (FPR4_REGNUM)
+                     + cfun_fpr_save_p (FPR6_REGNUM)));
+
+         cfun_frame_layout.f0_offset
+           = (cfun_frame_layout.f4_offset
+              - 8 * (cfun_fpr_save_p (FPR0_REGNUM)
+                     + cfun_fpr_save_p (FPR2_REGNUM)));
        }
       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 
+         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 
+              - 8 * (cfun_fpr_save_p (FPR0_REGNUM)
+                     + cfun_fpr_save_p (FPR2_REGNUM)));
+
+         cfun_frame_layout.f4_offset
            = (cfun_frame_layout.f0_offset
-              - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
+              - 8 * (cfun_fpr_save_p (FPR4_REGNUM)
+                     + cfun_fpr_save_p (FPR6_REGNUM)));
        }
     }
   else /* no backchain */
     {
-      cfun_frame_layout.f4_offset 
+      cfun_frame_layout.f4_offset
        = (STACK_POINTER_OFFSET
-          - 8 * (cfun_fpr_bit_p (2) + cfun_fpr_bit_p (3)));
-      
-      cfun_frame_layout.f0_offset 
+          - 8 * (cfun_fpr_save_p (FPR4_REGNUM)
+                 + cfun_fpr_save_p (FPR6_REGNUM)));
+
+      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 
+          - 8 * (cfun_fpr_save_p (FPR0_REGNUM)
+                 + cfun_fpr_save_p (FPR2_REGNUM)));
+
+      cfun_frame_layout.gprs_offset
        = cfun_frame_layout.f0_offset - cfun_gprs_save_area_size;
     }
 
-  if (current_function_is_leaf
+  if (crtl->is_leaf
       && !TARGET_TPF_PROFILING
       && cfun_frame_layout.frame_size == 0
       && !cfun_save_high_fprs_p
-      && !current_function_calls_alloca
-      && !current_function_stdarg)
+      && !cfun->calls_alloca
+      && !cfun->stdarg)
     return;
 
   if (!TARGET_PACKED_STACK)
     cfun_frame_layout.frame_size += (STACK_POINTER_OFFSET
-                                    + current_function_outgoing_args_size
+                                    + crtl->outgoing_args_size
                                     + cfun_frame_layout.high_fprs * 8);
   else
     {
       if (TARGET_BACKCHAIN)
-       cfun_frame_layout.frame_size += UNITS_PER_WORD;
+       cfun_frame_layout.frame_size += UNITS_PER_LONG;
 
-      /* No alignment trouble here because f8-f15 are only saved under 
+      /* 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),
@@ -6677,19 +7760,19 @@ s390_frame_info (void)
 
       cfun_frame_layout.frame_size += cfun_frame_layout.high_fprs * 8;
 
-      for (i = 0; i < 8; i++)
-       if (cfun_fpr_bit_p (i))
+      for (i = FPR0_REGNUM; i <= FPR7_REGNUM; i++)
+       if (cfun_fpr_save_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));
 
-      cfun_frame_layout.frame_size += current_function_outgoing_args_size;
+      cfun_frame_layout.frame_size += crtl->outgoing_args_size;
     }
 }
 
@@ -6702,7 +7785,7 @@ s390_init_frame_layout (void)
 {
   HOST_WIDE_INT frame_size;
   int base_used;
-  int clobbered_regs[16];
+  int clobbered_regs[32];
 
   /* On S/390 machines, we may need to perform branch splitting, which
      will require both base and return address register.  We have no
@@ -6717,16 +7800,16 @@ s390_init_frame_layout (void)
 
       /* 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));
+                 || crtl->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 && !regs_ever_live[5])
+      else if (crtl->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);
@@ -6737,6 +7820,157 @@ s390_init_frame_layout (void)
   while (frame_size != cfun_frame_layout.frame_size);
 }
 
+/* Remove the FPR clobbers from a tbegin insn if it can be proven that
+   the TX is nonescaping.  A transaction is considered escaping if
+   there is at least one path from tbegin returning CC0 to the
+   function exit block without an tend.
+
+   The check so far has some limitations:
+   - only single tbegin/tend BBs are supported
+   - the first cond jump after tbegin must separate the CC0 path from ~CC0
+   - when CC is copied to a GPR and the CC0 check is done with the GPR
+     this is not supported
+*/
+
+static void
+s390_optimize_nonescaping_tx (void)
+{
+  const unsigned int CC0 = 1 << 3;
+  basic_block tbegin_bb = NULL;
+  basic_block tend_bb = NULL;
+  basic_block bb;
+  rtx insn;
+  bool result = true;
+  int bb_index;
+  rtx tbegin_insn = NULL_RTX;
+
+  if (!cfun->machine->tbegin_p)
+    return;
+
+  for (bb_index = 0; bb_index < n_basic_blocks; bb_index++)
+    {
+      bb = BASIC_BLOCK (bb_index);
+
+      FOR_BB_INSNS (bb, insn)
+       {
+         rtx ite, cc, pat, target;
+         unsigned HOST_WIDE_INT mask;
+
+         if (!INSN_P (insn) || INSN_CODE (insn) <= 0)
+           continue;
+
+         pat = PATTERN (insn);
+
+         if (GET_CODE (pat) == PARALLEL)
+           pat = XVECEXP (pat, 0, 0);
+
+         if (GET_CODE (pat) != SET
+             || GET_CODE (SET_SRC (pat)) != UNSPEC_VOLATILE)
+           continue;
+
+         if (XINT (SET_SRC (pat), 1) == UNSPECV_TBEGIN)
+           {
+             rtx tmp;
+
+             tbegin_insn = insn;
+
+             /* Just return if the tbegin doesn't have clobbers.  */
+             if (GET_CODE (PATTERN (insn)) != PARALLEL)
+               return;
+
+             if (tbegin_bb != NULL)
+               return;
+
+             /* Find the next conditional jump.  */
+             for (tmp = NEXT_INSN (insn);
+                  tmp != NULL_RTX;
+                  tmp = NEXT_INSN (tmp))
+               {
+                 if (reg_set_p (gen_rtx_REG (CCmode, CC_REGNUM), tmp))
+                   return;
+                 if (!JUMP_P (tmp))
+                   continue;
+
+                 ite = SET_SRC (PATTERN (tmp));
+                 if (GET_CODE (ite) != IF_THEN_ELSE)
+                   continue;
+
+                 cc = XEXP (XEXP (ite, 0), 0);
+                 if (!REG_P (cc) || !CC_REGNO_P (REGNO (cc))
+                     || GET_MODE (cc) != CCRAWmode
+                     || GET_CODE (XEXP (XEXP (ite, 0), 1)) != CONST_INT)
+                   return;
+
+                 if (bb->succs->length () != 2)
+                   return;
+
+                 mask = INTVAL (XEXP (XEXP (ite, 0), 1));
+                 if (GET_CODE (XEXP (ite, 0)) == NE)
+                   mask ^= 0xf;
+
+                 if (mask == CC0)
+                   target = XEXP (ite, 1);
+                 else if (mask == (CC0 ^ 0xf))
+                   target = XEXP (ite, 2);
+                 else
+                   return;
+
+                 {
+                   edge_iterator ei;
+                   edge e1, e2;
+
+                   ei = ei_start (bb->succs);
+                   e1 = ei_safe_edge (ei);
+                   ei_next (&ei);
+                   e2 = ei_safe_edge (ei);
+
+                   if (e2->flags & EDGE_FALLTHRU)
+                     {
+                       e2 = e1;
+                       e1 = ei_safe_edge (ei);
+                     }
+
+                   if (!(e1->flags & EDGE_FALLTHRU))
+                     return;
+
+                   tbegin_bb = (target == pc_rtx) ? e1->dest : e2->dest;
+                 }
+                 if (tmp == BB_END (bb))
+                   break;
+               }
+           }
+
+         if (XINT (SET_SRC (pat), 1) == UNSPECV_TEND)
+           {
+             if (tend_bb != NULL)
+               return;
+             tend_bb = bb;
+           }
+       }
+    }
+
+  /* Either we successfully remove the FPR clobbers here or we are not
+     able to do anything for this TX.  Both cases don't qualify for
+     another look.  */
+  cfun->machine->tbegin_p = false;
+
+  if (tbegin_bb == NULL || tend_bb == NULL)
+    return;
+
+  calculate_dominance_info (CDI_POST_DOMINATORS);
+  result = dominated_by_p (CDI_POST_DOMINATORS, tbegin_bb, tend_bb);
+  free_dominance_info (CDI_POST_DOMINATORS);
+
+  if (!result)
+    return;
+
+  PATTERN (tbegin_insn) = XVECEXP (PATTERN (tbegin_insn), 0, 0);
+  INSN_CODE (tbegin_insn) = -1;
+  df_insn_rescan (tbegin_insn);
+
+  return;
+}
+
 /* 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
@@ -6745,16 +7979,19 @@ s390_init_frame_layout (void)
 static void
 s390_update_frame_layout (void)
 {
-  int clobbered_regs[16];
+  int clobbered_regs[32];
 
   s390_register_info (clobbered_regs);
 
-  regs_ever_live[BASE_REGNUM] = clobbered_regs[BASE_REGNUM];
-  regs_ever_live[RETURN_REGNUM] = clobbered_regs[RETURN_REGNUM];
-  regs_ever_live[STACK_POINTER_REGNUM] = clobbered_regs[STACK_POINTER_REGNUM];
+  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);
 
   if (cfun->machine->base_reg)
-    regs_ever_live[REGNO (cfun->machine->base_reg)] = 1;
+    df_set_regs_ever_live (REGNO (cfun->machine->base_reg), true);
 }
 
 /* Return true if it is legal to put a value with MODE into REGNO.  */
@@ -6782,10 +8019,10 @@ s390_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
     case GENERAL_REGS:
       if (REGNO_PAIR_OK (regno, mode))
        {
-         if (TARGET_64BIT 
+         if (TARGET_ZARCH
              || (mode != TFmode && mode != TCmode && mode != TDmode))
            return true;
-       }         
+       }
       break;
     case CC_REGS:
       if (GET_MODE_CLASS (mode) == MODE_CC)
@@ -6801,7 +8038,7 @@ s390_hard_regno_mode_ok (unsigned int regno, enum machine_mode mode)
     default:
       return false;
     }
-  
+
   return false;
 }
 
@@ -6821,12 +8058,12 @@ s390_hard_regno_rename_ok (unsigned int old_reg, unsigned int new_reg)
 }
 
 /* Maximum number of registers to represent a value of mode MODE
-   in a register of class CLASS.  */
+   in a register of class RCLASS.  */
 
-bool
-s390_class_max_nregs (enum reg_class class, enum machine_mode mode)
+int
+s390_class_max_nregs (enum reg_class rclass, enum machine_mode mode)
 {
-  switch (class)
+  switch (rclass)
     {
     case FP_REGS:
       if (GET_MODE_CLASS (mode) == MODE_COMPLEX_FLOAT)
@@ -6841,10 +8078,17 @@ s390_class_max_nregs (enum reg_class class, enum machine_mode mode)
   return (GET_MODE_SIZE (mode) + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
 }
 
+/* Return true if we use LRA instead of reload pass.  */
+static bool
+s390_lra_p (void)
+{
+  return s390_lra_flag;
+}
+
 /* Return true if register FROM can be eliminated via register TO.  */
 
-bool
-s390_can_eliminate (int from, int to)
+static bool
+s390_can_eliminate (const int from, const int to)
 {
   /* On zSeries machines, we have not marked the base register as fixed.
      Instead, we have an elimination rule BASE_REGNUM -> BASE_REGNUM.
@@ -6875,8 +8119,8 @@ s390_can_eliminate (int from, int to)
 
   /* Make sure we actually saved the return address.  */
   if (from == RETURN_ADDRESS_POINTER_REGNUM)
-    if (!current_function_calls_eh_return
-       && !current_function_stdarg
+    if (!crtl->calls_eh_return
+       && !cfun->stdarg
        && !cfun_frame_layout.save_return_addr_p)
       return false;
 
@@ -6898,9 +8142,9 @@ s390_initial_elimination_offset (int from, int to)
   switch (from)
     {
     case FRAME_POINTER_REGNUM:
-      offset = (get_frame_size() 
+      offset = (get_frame_size()
                + STACK_POINTER_OFFSET
-               + current_function_outgoing_args_size);
+               + crtl->outgoing_args_size);
       break;
 
     case ARG_POINTER_REGNUM:
@@ -6913,7 +8157,7 @@ s390_initial_elimination_offset (int from, int to)
       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;
+      offset += index * UNITS_PER_LONG;
       break;
 
     case BASE_REGNUM:
@@ -6934,7 +8178,7 @@ static rtx
 save_fpr (rtx base, int offset, int regnum)
 {
   rtx addr;
-  addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
+  addr = gen_rtx_MEM (DFmode, plus_constant (Pmode, base, offset));
 
   if (regnum >= 16 && regnum <= (16 + FP_ARG_NUM_REG))
     set_mem_alias_set (addr, get_varargs_alias_set ());
@@ -6951,12 +8195,27 @@ static rtx
 restore_fpr (rtx base, int offset, int regnum)
 {
   rtx addr;
-  addr = gen_rtx_MEM (DFmode, plus_constant (base, offset));
+  addr = gen_rtx_MEM (DFmode, plus_constant (Pmode, base, offset));
   set_mem_alias_set (addr, get_frame_alias_set ());
 
   return emit_move_insn (gen_rtx_REG (DFmode, regnum), addr);
 }
 
+/* Return true if REGNO is a global register, but not one
+   of the special ones that need to be saved/restored in anyway.  */
+
+static inline bool
+global_not_special_regno_p (int regno)
+{
+  return (global_regs[regno]
+         /* These registers are special and need to be
+            restored in any case.  */
+         && !(regno == STACK_POINTER_REGNUM
+              || regno == RETURN_REGNUM
+              || regno == BASE_REGNUM
+              || (flag_pic && regno == (int)PIC_OFFSET_TABLE_REGNUM)));
+}
+
 /* Generate insn to save registers FIRST to LAST into
    the register save area located at offset OFFSET
    relative to register BASE.  */
@@ -6967,7 +8226,7 @@ save_gprs (rtx base, int offset, int first, int last)
   rtx addr, insn, note;
   int i;
 
-  addr = plus_constant (base, offset);
+  addr = plus_constant (Pmode, base, offset);
   addr = gen_rtx_MEM (Pmode, addr);
 
   set_mem_alias_set (addr, get_frame_alias_set ());
@@ -6980,7 +8239,8 @@ save_gprs (rtx base, int offset, int first, int last)
       else
         insn = gen_movsi (addr, gen_rtx_REG (Pmode, first));
 
-      RTX_FRAME_RELATED_P (insn) = 1;
+      if (!global_not_special_regno_p (first))
+       RTX_FRAME_RELATED_P (insn) = 1;
       return insn;
     }
 
@@ -6989,11 +8249,11 @@ 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)
+  if (first <= 6 && cfun->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 ());
       }
@@ -7010,30 +8270,42 @@ save_gprs (rtx base, int offset, int first, int last)
      set, even if it does not.  Therefore we emit a new pattern
      without those registers as REG_FRAME_RELATED_EXPR note.  */
 
-  if (first >= 6)
+  if (first >= 6 && !global_not_special_regno_p (first))
     {
       rtx pat = PATTERN (insn);
 
       for (i = 0; i < XVECLEN (pat, 0); i++)
-       if (GET_CODE (XVECEXP (pat, 0, i)) == SET)
+       if (GET_CODE (XVECEXP (pat, 0, i)) == SET
+           && !global_not_special_regno_p (REGNO (SET_SRC (XVECEXP (pat,
+                                                                    0, i)))))
          RTX_FRAME_RELATED_P (XVECEXP (pat, 0, i)) = 1;
 
       RTX_FRAME_RELATED_P (insn) = 1;
     }
   else if (last >= 6)
     {
-      addr = plus_constant (base, offset + (6 - first) * UNITS_PER_WORD);
+      int start;
+
+      for (start = first >= 6 ? first : 6; start <= last; start++)
+       if (!global_not_special_regno_p (start))
+         break;
+
+      if (start > last)
+       return insn;
+
+      addr = plus_constant (Pmode, base,
+                           offset + (start - first) * UNITS_PER_LONG);
       note = gen_store_multiple (gen_rtx_MEM (Pmode, addr),
-                                gen_rtx_REG (Pmode, 6),
-                                GEN_INT (last - 6 + 1));
+                                gen_rtx_REG (Pmode, start),
+                                GEN_INT (last - start + 1));
       note = PATTERN (note);
 
-      REG_NOTES (insn) =
-       gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                          note, REG_NOTES (insn));
+      add_reg_note (insn, REG_FRAME_RELATED_EXPR, note);
 
       for (i = 0; i < XVECLEN (note, 0); i++)
-       if (GET_CODE (XVECEXP (note, 0, i)) == SET)
+       if (GET_CODE (XVECEXP (note, 0, i)) == SET
+           && !global_not_special_regno_p (REGNO (SET_SRC (XVECEXP (note,
+                                                                    0, i)))))
          RTX_FRAME_RELATED_P (XVECEXP (note, 0, i)) = 1;
 
       RTX_FRAME_RELATED_P (insn) = 1;
@@ -7051,7 +8323,7 @@ restore_gprs (rtx base, int offset, int first, int last)
 {
   rtx addr, insn;
 
-  addr = plus_constant (base, offset);
+  addr = plus_constant (Pmode, base, offset);
   addr = gen_rtx_MEM (Pmode, addr);
   set_mem_alias_set (addr, get_frame_alias_set ());
 
@@ -7080,6 +8352,12 @@ s390_load_got (void)
 {
   rtx insns;
 
+  /* We cannot use pic_offset_table_rtx here since we use this
+     function also for non-pic if __tls_get_offset is called and in
+     that case PIC_OFFSET_TABLE_REGNUM as well as pic_offset_table_rtx
+     aren't usable.  */
+  rtx got_rtx = gen_rtx_REG (Pmode, 12);
+
   if (!got_symbol)
     {
       got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
@@ -7090,7 +8368,7 @@ s390_load_got (void)
 
   if (TARGET_CPU_ZARCH)
     {
-      emit_move_insn (pic_offset_table_rtx, got_symbol);
+      emit_move_insn (got_rtx, got_symbol);
     }
   else
     {
@@ -7101,13 +8379,13 @@ s390_load_got (void)
       offset = gen_rtx_CONST (Pmode, offset);
       offset = force_const_mem (Pmode, offset);
 
-      emit_move_insn (pic_offset_table_rtx, offset);
+      emit_move_insn (got_rtx, offset);
 
       offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, XEXP (offset, 0)),
                               UNSPEC_LTREL_BASE);
-      offset = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, offset);
+      offset = gen_rtx_PLUS (Pmode, got_rtx, offset);
 
-      emit_move_insn (pic_offset_table_rtx, offset);
+      emit_move_insn (got_rtx, offset);
     }
 
   insns = get_insns ();
@@ -7115,6 +8393,18 @@ s390_load_got (void)
   return insns;
 }
 
+/* This ties together stack memory (MEM with an alias set of frame_alias_set)
+   and the change to the stack pointer.  */
+
+static void
+s390_emit_stack_tie (void)
+{
+  rtx mem = gen_frame_mem (BLKmode,
+                          gen_rtx_REG (Pmode, STACK_POINTER_REGNUM));
+
+  emit_insn (gen_stack_tie (mem));
+}
+
 /* Expand the prologue into a bunch of separate insns.  */
 
 void
@@ -7126,8 +8416,10 @@ s390_emit_prologue (void)
   int offset;
   int next_fpr = 0;
 
-  /* Complete frame layout.  */
+  /* Try to get rid of the FPR clobbers.  */
+  s390_optimize_nonescaping_tx ();
 
+  /* Complete frame layout.  */
   s390_update_frame_layout ();
 
   /* Annotate all constant pool references to let the scheduler know
@@ -7137,15 +8429,18 @@ 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 (!has_hard_reg_initial_val (Pmode, RETURN_REGNUM) 
-      && !current_function_is_leaf 
+  if (!has_hard_reg_initial_val (Pmode, RETURN_REGNUM)
+      && !crtl->is_leaf
       && !TARGET_TPF_PROFILING)
     temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
   else
@@ -7154,11 +8449,11 @@ s390_emit_prologue (void)
   /* Save call saved gprs.  */
   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 
+      insn = save_gprs (stack_pointer_rtx,
+                       cfun_frame_layout.gprs_offset +
+                       UNITS_PER_LONG * (cfun_frame_layout.first_save_gpr
                                          - cfun_frame_layout.first_save_gpr_slot),
-                       cfun_frame_layout.first_save_gpr, 
+                       cfun_frame_layout.first_save_gpr,
                        cfun_frame_layout.last_save_gpr);
       emit_insn (insn);
     }
@@ -7171,11 +8466,11 @@ s390_emit_prologue (void)
   offset = cfun_frame_layout.f0_offset;
 
   /* Save f0 and f2.  */
-  for (i = 0; i < 2; i++)
+  for (i = FPR0_REGNUM; i <= FPR0_REGNUM + 1; i++)
     {
-      if (cfun_fpr_bit_p (i))
+      if (cfun_fpr_save_p (i))
        {
-         save_fpr (stack_pointer_rtx, offset, i + 16);
+         save_fpr (stack_pointer_rtx, offset, i);
          offset += 8;
        }
       else if (!TARGET_PACKED_STACK)
@@ -7184,16 +8479,16 @@ s390_emit_prologue (void)
 
   /* Save f4 and f6.  */
   offset = cfun_frame_layout.f4_offset;
-  for (i = 2; i < 4; i++)
+  for (i = FPR4_REGNUM; i <= FPR4_REGNUM + 1; i++)
     {
-      if (cfun_fpr_bit_p (i))
+      if (cfun_fpr_save_p (i))
        {
-         insn = save_fpr (stack_pointer_rtx, offset, i + 16);
+         insn = save_fpr (stack_pointer_rtx, offset, i);
          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])
+         if (!call_really_used_regs[i])
            RTX_FRAME_RELATED_P (insn) = 1;
        }
       else if (!TARGET_PACKED_STACK)
@@ -7207,49 +8502,92 @@ s390_emit_prologue (void)
       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))
+      for (i = FPR15_REGNUM; i >= FPR8_REGNUM && offset >= 0; i--)
+       if (cfun_fpr_save_p (i))
          {
-           insn = save_fpr (stack_pointer_rtx, offset, i + 16);
-                      
+           insn = save_fpr (stack_pointer_rtx, offset, i);
+
            RTX_FRAME_RELATED_P (insn) = 1;
            offset -= 8;
          }
       if (offset >= cfun_frame_layout.f8_offset)
-       next_fpr = i + 16;
+       next_fpr = i;
     }
-  
+
   if (!TARGET_PACKED_STACK)
-    next_fpr = cfun_save_high_fprs_p ? 31 : 0;
+    next_fpr = cfun_save_high_fprs_p ? FPR15_REGNUM : 0;
+
+  if (flag_stack_usage_info)
+    current_function_static_stack_size = cfun_frame_layout.frame_size;
 
   /* Decrement stack pointer.  */
 
   if (cfun_frame_layout.frame_size > 0)
     {
       rtx frame_off = GEN_INT (-cfun_frame_layout.frame_size);
+      rtx real_frame_off;
 
       if (s390_stack_size)
        {
-         HOST_WIDE_INT stack_check_mask = ((s390_stack_size - 1)
-                                           & ~(s390_stack_guard - 1));
-         rtx t = gen_rtx_AND (Pmode, stack_pointer_rtx,
-                              GEN_INT (stack_check_mask));
+         HOST_WIDE_INT stack_guard;
 
-         if (TARGET_64BIT)
-           gen_cmpdi (t, const0_rtx);
+         if (s390_stack_guard)
+           stack_guard = s390_stack_guard;
          else
-           gen_cmpsi (t, const0_rtx);
+           {
+             /* 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;
+           }
 
-         emit_insn (gen_conditional_trap (gen_rtx_EQ (CCmode, 
-                                                      gen_rtx_REG (CCmode, 
-                                                                   CC_REGNUM),
-                                                      const0_rtx),
-                                          const0_rtx));
+         if (cfun_frame_layout.frame_size >= s390_stack_size)
+           {
+             warning (0, "frame size of function %qs is %wd"
+                      " bytes exceeding user provided stack limit of "
+                      "%d bytes.  "
+                      "An unconditional trap is added.",
+                      current_function_name(), cfun_frame_layout.frame_size,
+                      s390_stack_size);
+             emit_insn (gen_trap ());
+           }
+         else
+           {
+             /* stack_guard has to be smaller than s390_stack_size.
+                Otherwise we would emit an AND with zero which would
+                not match the test under mask pattern.  */
+             if (stack_guard >= s390_stack_size)
+               {
+                 warning (0, "frame size of function %qs is %wd"
+                          " bytes which is more than half the stack size. "
+                          "The dynamic check would not be reliable. "
+                          "No check emitted for this function.",
+                          current_function_name(),
+                          cfun_frame_layout.frame_size);
+               }
+             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)
+                   emit_insn (gen_ctrapdi4 (gen_rtx_EQ (VOIDmode,
+                                                        t, const0_rtx),
+                                            t, const0_rtx, const0_rtx));
+                 else
+                   emit_insn (gen_ctrapsi4 (gen_rtx_EQ (VOIDmode,
+                                                        t, const0_rtx),
+                                            t, const0_rtx, const0_rtx));
+               }
+           }
        }
 
-      if (s390_warn_framesize > 0 
+      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", 
+       warning (0, "frame size of %qs is %wd bytes",
                 current_function_name (), cfun_frame_layout.frame_size);
 
       if (s390_warn_dynamicstack_p && cfun->calls_alloca)
@@ -7264,7 +8602,7 @@ s390_emit_prologue (void)
       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);
        }
@@ -7278,35 +8616,33 @@ s390_emit_prologue (void)
        }
 
       RTX_FRAME_RELATED_P (insn) = 1;
-      REG_NOTES (insn) =
-       gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                          gen_rtx_SET (VOIDmode, stack_pointer_rtx,
-                            gen_rtx_PLUS (Pmode, stack_pointer_rtx,
-                              GEN_INT (-cfun_frame_layout.frame_size))),
-                          REG_NOTES (insn));
+      real_frame_off = GEN_INT (-cfun_frame_layout.frame_size);
+      add_reg_note (insn, REG_FRAME_RELATED_EXPR,
+                   gen_rtx_SET (VOIDmode, stack_pointer_rtx,
+                                gen_rtx_PLUS (Pmode, stack_pointer_rtx,
+                                              real_frame_off)));
 
       /* Set backchain.  */
 
       if (TARGET_BACKCHAIN)
        {
          if (cfun_frame_layout.backchain_offset)
-           addr = gen_rtx_MEM (Pmode, 
-                               plus_constant (stack_pointer_rtx, 
+           addr = gen_rtx_MEM (Pmode,
+                               plus_constant (Pmode, stack_pointer_rtx,
                                  cfun_frame_layout.backchain_offset));
          else
-           addr = gen_rtx_MEM (Pmode, stack_pointer_rtx);  
+           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));
        }
 
-      /* If we support asynchronous exceptions (e.g. for Java),
+      /* If we support non-call exceptions (e.g. for Java),
         we need to make sure the backchain pointer is set up
         before any possibly trapping memory access.  */
-
-      if (TARGET_BACKCHAIN && flag_non_call_exceptions)
+      if (TARGET_BACKCHAIN && cfun->can_throw_non_call_exceptions)
        {
          addr = gen_rtx_MEM (BLKmode, gen_rtx_SCRATCH (VOIDmode));
-         emit_insn (gen_rtx_CLOBBER (VOIDmode, addr));
+         emit_clobber (addr);
        }
     }
 
@@ -7314,28 +8650,31 @@ s390_emit_prologue (void)
 
   if (cfun_save_high_fprs_p && next_fpr)
     {
-      insn = emit_insn (gen_add2_insn (temp_reg, 
+      /* If the stack might be accessed through a different register
+        we have to make sure that the stack pointer decrement is not
+        moved below the use of the stack slots.  */
+      s390_emit_stack_tie ();
+
+      insn = emit_insn (gen_add2_insn (temp_reg,
                                       GEN_INT (cfun_frame_layout.f8_offset)));
 
       offset = 0;
 
-      for (i = 24; i <= next_fpr; i++)
-       if (cfun_fpr_bit_p (i - 16))
+      for (i = FPR8_REGNUM; i <= next_fpr; i++)
+       if (cfun_fpr_save_p (i))
          {
-           rtx addr = plus_constant (stack_pointer_rtx,
+           rtx addr = plus_constant (Pmode, stack_pointer_rtx,
                                      cfun_frame_layout.frame_size
                                      + cfun_frame_layout.f8_offset
                                      + offset);
-           
+
            insn = save_fpr (temp_reg, offset, i);
            offset += 8;
            RTX_FRAME_RELATED_P (insn) = 1;
-           REG_NOTES (insn) =
-             gen_rtx_EXPR_LIST (REG_FRAME_RELATED_EXPR,
-                                gen_rtx_SET (VOIDmode,
-                                             gen_rtx_MEM (DFmode, addr),
-                                             gen_rtx_REG (DFmode, i)),
-                                REG_NOTES (insn));
+           add_reg_note (insn, REG_FRAME_RELATED_EXPR,
+                         gen_rtx_SET (VOIDmode,
+                                      gen_rtx_MEM (DFmode, addr),
+                                      gen_rtx_REG (DFmode, i)));
          }
     }
 
@@ -7349,17 +8688,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);
     }
@@ -7382,7 +8716,7 @@ s390_emit_prologue (void)
 void
 s390_emit_epilogue (bool sibcall)
 {
-  rtx frame_pointer, return_reg;
+  rtx frame_pointer, return_reg, cfa_restores = NULL_RTX;
   int area_bottom, area_top, offset = 0;
   int next_offset;
   rtvec p;
@@ -7404,7 +8738,7 @@ s390_emit_epilogue (bool sibcall)
 
   /* Check whether to use frame or stack pointer for restore.  */
 
-  frame_pointer = (frame_pointer_needed 
+  frame_pointer = (frame_pointer_needed
                   ? hard_frame_pointer_rtx : stack_pointer_rtx);
 
   s390_frame_area (&area_bottom, &area_top);
@@ -7424,11 +8758,13 @@ s390_emit_epilogue (bool sibcall)
     }
   else
     {
-      rtx insn, frame_off;
+      rtx insn, frame_off, cfa;
 
       offset = area_bottom < 0 ? -area_bottom : 0;
       frame_off = GEN_INT (cfun_frame_layout.frame_size - offset);
 
+      cfa = gen_rtx_SET (VOIDmode, frame_pointer,
+                        gen_rtx_PLUS (Pmode, frame_pointer, frame_off));
       if (DISP_IN_RANGE (INTVAL (frame_off)))
        {
          insn = gen_rtx_SET (VOIDmode, frame_pointer,
@@ -7443,6 +8779,8 @@ s390_emit_epilogue (bool sibcall)
          insn = emit_insn (gen_add2_insn (frame_pointer, frame_off));
          annotate_constant_pool_refs (&PATTERN (insn));
        }
+      add_reg_note (insn, REG_CFA_ADJUST_CFA, cfa);
+      RTX_FRAME_RELATED_P (insn) = 1;
     }
 
   /* Restore call saved fprs.  */
@@ -7452,33 +8790,40 @@ s390_emit_epilogue (bool sibcall)
       if (cfun_save_high_fprs_p)
        {
          next_offset = cfun_frame_layout.f8_offset;
-         for (i = 24; i < 32; i++)
+         for (i = FPR8_REGNUM; i <= FPR15_REGNUM; i++)
            {
-             if (cfun_fpr_bit_p (i - 16))
+             if (cfun_fpr_save_p (i))
                {
                  restore_fpr (frame_pointer,
                               offset + next_offset, i);
+                 cfa_restores
+                   = alloc_reg_note (REG_CFA_RESTORE,
+                                     gen_rtx_REG (DFmode, i), cfa_restores);
                  next_offset += 8;
                }
            }
        }
-             
+
     }
   else
     {
       next_offset = cfun_frame_layout.f4_offset;
-      for (i = 18; i < 20; i++)
+      /* f4, f6 */
+      for (i = FPR4_REGNUM; i <= FPR4_REGNUM + 1; i++)
        {
-         if (cfun_fpr_bit_p (i - 16))
+         if (cfun_fpr_save_p (i))
            {
              restore_fpr (frame_pointer,
                           offset + next_offset, i);
+             cfa_restores
+               = alloc_reg_note (REG_CFA_RESTORE,
+                                 gen_rtx_REG (DFmode, i), cfa_restores);
              next_offset += 8;
            }
          else if (!TARGET_PACKED_STACK)
            next_offset += 8;
        }
-      
+
     }
 
   /* Return register.  */
@@ -7499,24 +8844,20 @@ s390_emit_epilogue (bool sibcall)
           i <= cfun_frame_layout.last_restore_gpr;
           i++)
        {
-         /* These registers are special and need to be
-            restored in any case.  */
-         if (i == STACK_POINTER_REGNUM
-              || i == RETURN_REGNUM
-              || i == BASE_REGNUM
-              || (flag_pic && i == (int)PIC_OFFSET_TABLE_REGNUM))
-           continue;
-
-         if (global_regs[i])
+         if (global_not_special_regno_p (i))
            {
-             addr = plus_constant (frame_pointer,
-                                   offset + cfun_frame_layout.gprs_offset 
+             addr = plus_constant (Pmode, frame_pointer,
+                                   offset + cfun_frame_layout.gprs_offset
                                    + (i - cfun_frame_layout.first_save_gpr_slot)
-                                   * UNITS_PER_WORD);
+                                   * UNITS_PER_LONG);
              addr = gen_rtx_MEM (Pmode, addr);
              set_mem_alias_set (addr, get_frame_alias_set ());
              emit_move_insn (addr, gen_rtx_REG (Pmode, i));
            }
+         else
+           cfa_restores
+             = alloc_reg_note (REG_CFA_RESTORE,
+                               gen_rtx_REG (Pmode, i), cfa_restores);
        }
 
       if (! sibcall)
@@ -7533,11 +8874,11 @@ s390_emit_epilogue (bool sibcall)
                return_regnum = 4;
              return_reg = gen_rtx_REG (Pmode, return_regnum);
 
-             addr = plus_constant (frame_pointer,
+             addr = plus_constant (Pmode, frame_pointer,
                                    offset + cfun_frame_layout.gprs_offset
-                                   + (RETURN_REGNUM 
+                                   + (RETURN_REGNUM
                                       - cfun_frame_layout.first_save_gpr_slot)
-                                   * UNITS_PER_WORD);
+                                   * UNITS_PER_LONG);
              addr = gen_rtx_MEM (Pmode, addr);
              set_mem_alias_set (addr, get_frame_alias_set ());
              emit_move_insn (return_reg, addr);
@@ -7546,12 +8887,17 @@ s390_emit_epilogue (bool sibcall)
 
       insn = restore_gprs (frame_pointer,
                           offset + cfun_frame_layout.gprs_offset
-                          + (cfun_frame_layout.first_restore_gpr 
+                          + (cfun_frame_layout.first_restore_gpr
                              - cfun_frame_layout.first_save_gpr_slot)
-                          * UNITS_PER_WORD,
+                          * UNITS_PER_LONG,
                           cfun_frame_layout.first_restore_gpr,
                           cfun_frame_layout.last_restore_gpr);
-      emit_insn (insn);
+      insn = emit_insn (insn);
+      REG_NOTES (insn) = cfa_restores;
+      add_reg_note (insn, REG_CFA_DEF_CFA,
+                   plus_constant (Pmode, stack_pointer_rtx,
+                                  STACK_POINTER_OFFSET));
+      RTX_FRAME_RELATED_P (insn) = 1;
     }
 
   if (! sibcall)
@@ -7561,7 +8907,7 @@ s390_emit_epilogue (bool sibcall)
 
       p = rtvec_alloc (2);
 
-      RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode);
+      RTVEC_ELT (p, 0) = ret_rtx;
       RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg);
       emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
     }
@@ -7573,7 +8919,7 @@ s390_emit_epilogue (bool sibcall)
    MODE must be specified.  */
 
 static int
-s390_function_arg_size (enum machine_mode mode, tree type)
+s390_function_arg_size (enum machine_mode mode, const_tree type)
 {
   if (type)
     return int_size_in_bytes (type);
@@ -7590,7 +8936,7 @@ s390_function_arg_size (enum machine_mode mode, tree type)
    is to be passed in a floating-point register, if available.  */
 
 static bool
-s390_function_arg_float (enum machine_mode mode, tree type)
+s390_function_arg_float (enum machine_mode mode, const_tree type)
 {
   int size = s390_function_arg_size (mode, type);
   if (size > 8)
@@ -7610,7 +8956,7 @@ s390_function_arg_float (enum machine_mode mode, tree type)
     {
       tree field, single = NULL_TREE;
 
-      for (field = TYPE_FIELDS (type); field; field = TREE_CHAIN (field))
+      for (field = TYPE_FIELDS (type); field; field = DECL_CHAIN (field))
        {
          if (TREE_CODE (field) != FIELD_DECL)
            continue;
@@ -7635,7 +8981,7 @@ s390_function_arg_float (enum machine_mode mode, tree type)
    registers, if available.  */
 
 static bool
-s390_function_arg_integer (enum machine_mode mode, tree type)
+s390_function_arg_integer (enum machine_mode mode, const_tree type)
 {
   int size = s390_function_arg_size (mode, type);
   if (size > 8)
@@ -7649,6 +8995,7 @@ s390_function_arg_integer (enum machine_mode mode, tree type)
   /* We accept small integral (and similar) types.  */
   if (INTEGRAL_TYPE_P (type)
       || POINTER_TYPE_P (type)
+      || TREE_CODE (type) == NULLPTR_TYPE
       || TREE_CODE (type) == OFFSET_TYPE
       || (TARGET_SOFT_FLOAT && TREE_CODE (type) == REAL_TYPE))
     return true;
@@ -7670,8 +9017,8 @@ s390_function_arg_integer (enum machine_mode mode, tree type)
    reference.  */
 
 static bool
-s390_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
-                       enum machine_mode mode, tree type,
+s390_pass_by_reference (cumulative_args_t ca ATTRIBUTE_UNUSED,
+                       enum machine_mode mode, const_tree type,
                        bool named ATTRIBUTE_UNUSED)
 {
   int size = s390_function_arg_size (mode, type);
@@ -7697,10 +9044,12 @@ s390_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
    argument is a named argument (as opposed to an unnamed argument
    matching an ellipsis).  */
 
-void
-s390_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
-                          tree type, int named ATTRIBUTE_UNUSED)
+static void
+s390_function_arg_advance (cumulative_args_t cum_v, enum machine_mode mode,
+                          const_tree type, bool named ATTRIBUTE_UNUSED)
 {
+  CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+
   if (s390_function_arg_float (mode, type))
     {
       cum->fprs += 1;
@@ -7708,7 +9057,7 @@ s390_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
   else if (s390_function_arg_integer (mode, type))
     {
       int size = s390_function_arg_size (mode, type);
-      cum->gprs += ((size + UNITS_PER_WORD-1) / UNITS_PER_WORD);
+      cum->gprs += ((size + UNITS_PER_LONG - 1) / UNITS_PER_LONG);
     }
   else
     gcc_unreachable ();
@@ -7733,10 +9082,12 @@ s390_function_arg_advance (CUMULATIVE_ARGS *cum, enum machine_mode mode,
    to pass floating point arguments.  All remaining arguments
    are pushed to the stack.  */
 
-rtx
-s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
-                  int named ATTRIBUTE_UNUSED)
+static rtx
+s390_function_arg (cumulative_args_t cum_v, enum machine_mode mode,
+                  const_tree type, bool named ATTRIBUTE_UNUSED)
 {
+  CUMULATIVE_ARGS *cum = get_cumulative_args (cum_v);
+
   if (s390_function_arg_float (mode, type))
     {
       if (cum->fprs + 1 > FP_ARG_NUM_REG)
@@ -7747,12 +9098,25 @@ s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
   else if (s390_function_arg_integer (mode, type))
     {
       int size = s390_function_arg_size (mode, type);
-      int n_gprs = (size + UNITS_PER_WORD-1) / UNITS_PER_WORD;
+      int n_gprs = (size + UNITS_PER_LONG - 1) / UNITS_PER_LONG;
 
       if (cum->gprs + n_gprs > GP_ARG_NUM_REG)
        return 0;
-      else
+      else if (n_gprs == 1 || UNITS_PER_WORD == UNITS_PER_LONG)
        return gen_rtx_REG (mode, cum->gprs + 2);
+      else if (n_gprs == 2)
+       {
+         rtvec p = rtvec_alloc (2);
+
+         RTVEC_ELT (p, 0)
+           = gen_rtx_EXPR_LIST (SImode, gen_rtx_REG (SImode, cum->gprs + 2),
+                                        const0_rtx);
+         RTVEC_ELT (p, 1)
+           = gen_rtx_EXPR_LIST (SImode, gen_rtx_REG (SImode, cum->gprs + 3),
+                                        GEN_INT (4));
+
+         return gen_rtx_PARALLEL (mode, p);
+       }
     }
 
   /* After the real arguments, expand_call calls us once again
@@ -7771,7 +9135,7 @@ s390_function_arg (CUMULATIVE_ARGS *cum, enum machine_mode mode, tree type,
    hidden first argument.  */
 
 static bool
-s390_return_in_memory (tree type, tree fundecl ATTRIBUTE_UNUSED)
+s390_return_in_memory (const_tree type, const_tree fundecl ATTRIBUTE_UNUSED)
 {
   /* We accept small integral (and similar) types.  */
   if (INTEGRAL_TYPE_P (type)
@@ -7793,17 +9157,42 @@ s390_return_in_memory (tree type, tree fundecl ATTRIBUTE_UNUSED)
   return true;
 }
 
-/* Define where to return a (scalar) value of type TYPE.
-   If TYPE is null, define where to return a (scalar)
+/* Function arguments and return values are promoted to word size.  */
+
+static enum machine_mode
+s390_promote_function_mode (const_tree type, enum machine_mode mode,
+                            int *punsignedp,
+                            const_tree fntype ATTRIBUTE_UNUSED,
+                            int for_return ATTRIBUTE_UNUSED)
+{
+  if (INTEGRAL_MODE_P (mode)
+      && GET_MODE_SIZE (mode) < UNITS_PER_LONG)
+    {
+      if (type != NULL_TREE && POINTER_TYPE_P (type))
+       *punsignedp = POINTERS_EXTEND_UNSIGNED;
+      return Pmode;
+    }
+
+  return mode;
+}
+
+/* Define where to return a (scalar) value of type RET_TYPE.
+   If RET_TYPE is null, define where to return a (scalar)
    value of mode MODE from a libcall.  */
 
-rtx
-s390_function_value (tree type, enum machine_mode mode)
+static rtx
+s390_function_and_libcall_value (enum machine_mode mode,
+                                const_tree ret_type,
+                                const_tree fntype_or_decl,
+                                bool outgoing ATTRIBUTE_UNUSED)
 {
-  if (type)
+  /* For normal functions perform the promotion as
+     promote_function_mode would do.  */
+  if (ret_type)
     {
-      int unsignedp = TYPE_UNSIGNED (type);
-      mode = promote_mode (type, TYPE_MODE (type), &unsignedp, 1);
+      int unsignedp = TYPE_UNSIGNED (ret_type);
+      mode = promote_function_mode (ret_type, mode, &unsignedp,
+                                   fntype_or_decl, 1);
     }
 
   gcc_assert (GET_MODE_CLASS (mode) == MODE_INT || SCALAR_FLOAT_MODE_P (mode));
@@ -7811,8 +9200,46 @@ s390_function_value (tree type, enum machine_mode mode)
 
   if (TARGET_HARD_FLOAT && SCALAR_FLOAT_MODE_P (mode))
     return gen_rtx_REG (mode, 16);
-  else
+  else if (GET_MODE_SIZE (mode) <= UNITS_PER_LONG
+          || UNITS_PER_LONG == UNITS_PER_WORD)
     return gen_rtx_REG (mode, 2);
+  else if (GET_MODE_SIZE (mode) == 2 * UNITS_PER_LONG)
+    {
+      /* This case is triggered when returning a 64 bit value with
+        -m31 -mzarch.  Although the value would fit into a single
+        register it has to be forced into a 32 bit register pair in
+        order to match the ABI.  */
+      rtvec p = rtvec_alloc (2);
+
+      RTVEC_ELT (p, 0)
+       = gen_rtx_EXPR_LIST (SImode, gen_rtx_REG (SImode, 2), const0_rtx);
+      RTVEC_ELT (p, 1)
+       = gen_rtx_EXPR_LIST (SImode, gen_rtx_REG (SImode, 3), GEN_INT (4));
+
+      return gen_rtx_PARALLEL (mode, p);
+    }
+
+  gcc_unreachable ();
+}
+
+/* Define where to return a scalar return value of type RET_TYPE.  */
+
+static rtx
+s390_function_value (const_tree ret_type, const_tree fn_decl_or_type,
+                    bool outgoing)
+{
+  return s390_function_and_libcall_value (TYPE_MODE (ret_type), ret_type,
+                                         fn_decl_or_type, outgoing);
+}
+
+/* Define where to return a scalar libcall return value of mode
+   MODE.  */
+
+static rtx
+s390_libcall_value (enum machine_mode mode, const_rtx fun ATTRIBUTE_UNUSED)
+{
+  return s390_function_and_libcall_value (mode, NULL_TREE,
+                                         NULL_TREE, true);
 }
 
 
@@ -7845,15 +9272,20 @@ s390_build_builtin_va_list (void)
   record = lang_hooks.types.make_type (RECORD_TYPE);
 
   type_decl =
-    build_decl (TYPE_DECL, get_identifier ("__va_list_tag"), record);
+    build_decl (BUILTINS_LOCATION,
+               TYPE_DECL, get_identifier ("__va_list_tag"), record);
 
-  f_gpr = build_decl (FIELD_DECL, get_identifier ("__gpr"),
+  f_gpr = build_decl (BUILTINS_LOCATION,
+                     FIELD_DECL, get_identifier ("__gpr"),
                      long_integer_type_node);
-  f_fpr = build_decl (FIELD_DECL, get_identifier ("__fpr"),
+  f_fpr = build_decl (BUILTINS_LOCATION,
+                     FIELD_DECL, get_identifier ("__fpr"),
                      long_integer_type_node);
-  f_ovf = build_decl (FIELD_DECL, get_identifier ("__overflow_arg_area"),
+  f_ovf = build_decl (BUILTINS_LOCATION,
+                     FIELD_DECL, get_identifier ("__overflow_arg_area"),
                      ptr_type_node);
-  f_sav = build_decl (FIELD_DECL, get_identifier ("__reg_save_area"),
+  f_sav = build_decl (BUILTINS_LOCATION,
+                     FIELD_DECL, get_identifier ("__reg_save_area"),
                      ptr_type_node);
 
   va_list_gpr_counter_field = f_gpr;
@@ -7864,12 +9296,12 @@ s390_build_builtin_va_list (void)
   DECL_FIELD_CONTEXT (f_ovf) = record;
   DECL_FIELD_CONTEXT (f_sav) = record;
 
-  TREE_CHAIN (record) = type_decl;
+  TYPE_STUB_DECL (record) = type_decl;
   TYPE_NAME (record) = type_decl;
   TYPE_FIELDS (record) = f_gpr;
-  TREE_CHAIN (f_gpr) = f_fpr;
-  TREE_CHAIN (f_fpr) = f_ovf;
-  TREE_CHAIN (f_ovf) = f_sav;
+  DECL_CHAIN (f_gpr) = f_fpr;
+  DECL_CHAIN (f_fpr) = f_ovf;
+  DECL_CHAIN (f_ovf) = f_sav;
 
   layout_type (record);
 
@@ -7884,13 +9316,13 @@ s390_build_builtin_va_list (void)
    The following global variables are used to initialize
    the va_list structure:
 
-     current_function_args_info:
+     crtl->args.info:
        holds number of gprs and fprs used for named arguments.
-     current_function_arg_offset_rtx:
+     crtl->args.arg_offset_rtx:
        holds the offset of the first anonymous stack argument
        (relative to the virtual arg pointer).  */
 
-void
+static void
 s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
 {
   HOST_WIDE_INT n_gpr, n_fpr;
@@ -7899,11 +9331,11 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
   tree gpr, fpr, ovf, sav, t;
 
   f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
-  f_fpr = TREE_CHAIN (f_gpr);
-  f_ovf = TREE_CHAIN (f_fpr);
-  f_sav = TREE_CHAIN (f_ovf);
+  f_fpr = DECL_CHAIN (f_gpr);
+  f_ovf = DECL_CHAIN (f_fpr);
+  f_sav = DECL_CHAIN (f_ovf);
 
-  valist = build_va_arg_indirect_ref (valist);
+  valist = build_simple_mem_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);
@@ -7911,13 +9343,13 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
 
   /* Count number of gp and fp argument registers used.  */
 
-  n_gpr = current_function_args_info.gprs;
-  n_fpr = current_function_args_info.fprs;
+  n_gpr = crtl->args.info.gprs;
+  n_fpr = crtl->args.info.fprs;
 
   if (cfun->va_list_gpr_size)
     {
       t = build2 (MODIFY_EXPR, TREE_TYPE (gpr), gpr,
-                 build_int_cst (NULL_TREE, n_gpr));
+                 build_int_cst (NULL_TREE, n_gpr));
       TREE_SIDE_EFFECTS (t) = 1;
       expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
     }
@@ -7936,13 +9368,13 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
     {
       t = make_tree (TREE_TYPE (ovf), virtual_incoming_args_rtx);
 
-      off = INTVAL (current_function_arg_offset_rtx);
+      off = INTVAL (crtl->args.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 = build2 (PLUS_EXPR, TREE_TYPE (ovf), t, build_int_cst (NULL_TREE, off));
+      t = fold_build_pointer_plus_hwi (t, off);
 
       t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
       TREE_SIDE_EFFECTS (t) = 1;
@@ -7954,9 +9386,8 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
       || (cfun->va_list_fpr_size && n_fpr < FP_ARG_NUM_REG))
     {
       t = make_tree (TREE_TYPE (sav), return_address_pointer_rtx);
-      t = build2 (PLUS_EXPR, TREE_TYPE (sav), t,
-                 build_int_cst (NULL_TREE, -RETURN_REGNUM * UNITS_PER_WORD));
-  
+      t = fold_build_pointer_plus_hwi (t, -RETURN_REGNUM * UNITS_PER_LONG);
+
       t = build2 (MODIFY_EXPR, TREE_TYPE (sav), sav, t);
       TREE_SIDE_EFFECTS (t) = 1;
       expand_expr (t, const0_rtx, VOIDmode, EXPAND_NORMAL);
@@ -7988,8 +9419,8 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
    } */
 
 static tree
-s390_gimplify_va_arg (tree valist, tree type, tree *pre_p, 
-                     tree *post_p ATTRIBUTE_UNUSED)
+s390_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
+                     gimple_seq *post_p ATTRIBUTE_UNUSED)
 {
   tree f_gpr, f_fpr, f_ovf, f_sav;
   tree gpr, fpr, ovf, sav, reg, t, u;
@@ -7997,16 +9428,20 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
   tree lab_false, lab_over, addr;
 
   f_gpr = TYPE_FIELDS (TREE_TYPE (va_list_type_node));
-  f_fpr = TREE_CHAIN (f_gpr);
-  f_ovf = TREE_CHAIN (f_fpr);
-  f_sav = TREE_CHAIN (f_ovf);
+  f_fpr = DECL_CHAIN (f_gpr);
+  f_ovf = DECL_CHAIN (f_fpr);
+  f_sav = DECL_CHAIN (f_ovf);
 
   valist = build_va_arg_indirect_ref (valist);
   gpr = build3 (COMPONENT_REF, TREE_TYPE (f_gpr), valist, f_gpr, NULL_TREE);
   fpr = build3 (COMPONENT_REF, TREE_TYPE (f_fpr), valist, f_fpr, NULL_TREE);
-  ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
   sav = build3 (COMPONENT_REF, TREE_TYPE (f_sav), valist, f_sav, NULL_TREE);
 
+  /* The tree for args* cannot be shared between gpr/fpr and ovf since
+     both appear on a lhs.  */
+  valist = unshare_expr (valist);
+  ovf = build3 (COMPONENT_REF, TREE_TYPE (f_ovf), valist, f_ovf, NULL_TREE);
+
   size = int_size_in_bytes (type);
 
   if (pass_by_reference (NULL, TYPE_MODE (type), type, false))
@@ -8025,9 +9460,9 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
       /* 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;
+      sav_ofs = 2 * UNITS_PER_LONG;
+      sav_scale = UNITS_PER_LONG;
+      size = UNITS_PER_LONG;
       max_reg = GP_ARG_NUM_REG - n_reg;
     }
   else if (s390_function_arg_float (TYPE_MODE (type), type))
@@ -8042,7 +9477,7 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
       indirect_p = 0;
       reg = fpr;
       n_reg = 1;
-      sav_ofs = 16 * UNITS_PER_WORD;
+      sav_ofs = 16 * UNITS_PER_LONG;
       sav_scale = 8;
       max_reg = FP_ARG_NUM_REG - n_reg;
     }
@@ -8057,26 +9492,25 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
       /* Otherwise into GP registers.  */
       indirect_p = 0;
       reg = gpr;
-      n_reg = (size + UNITS_PER_WORD - 1) / UNITS_PER_WORD;
+      n_reg = (size + UNITS_PER_LONG - 1) / UNITS_PER_LONG;
 
       /* 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_ofs = 2 * UNITS_PER_LONG;
 
-      if (size < UNITS_PER_WORD)
-       sav_ofs += UNITS_PER_WORD - size;
+      if (size < UNITS_PER_LONG)
+       sav_ofs += UNITS_PER_LONG - size;
 
-      sav_scale = UNITS_PER_WORD;
+      sav_scale = UNITS_PER_LONG;
       max_reg = GP_ARG_NUM_REG - n_reg;
     }
 
   /* Pull the value out of the saved registers ...  */
 
-  lab_false = create_artificial_label ();
-  lab_over = create_artificial_label ();
+  lab_false = create_artificial_label (UNKNOWN_LOCATION);
+  lab_over = create_artificial_label (UNKNOWN_LOCATION);
   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);
@@ -8084,41 +9518,32 @@ 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)));
-  u = build2 (MULT_EXPR, TREE_TYPE (reg), reg, 
+  t = fold_build_pointer_plus_hwi (sav, 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 = fold_build_pointer_plus (t, u);
 
-  t = build2 (MODIFY_EXPR, void_type_node, addr, t);
-  gimplify_and_add (t, pre_p);
+  gimplify_assign (addr, t, pre_p);
 
-  t = build1 (GOTO_EXPR, void_type_node, lab_over);
-  gimplify_and_add (t, pre_p);
+  gimple_seq_add_stmt (pre_p, gimple_build_goto (lab_over));
 
-  t = build1 (LABEL_EXPR, void_type_node, lab_false);
-  append_to_statement_list (t, pre_p);
+  gimple_seq_add_stmt (pre_p, gimple_build_label (lab_false));
 
 
   /* ... Otherwise out of the overflow area.  */
 
   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)));
+  if (size < UNITS_PER_LONG)
+    t = fold_build_pointer_plus_hwi (t, UNITS_PER_LONG - size);
 
   gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue);
 
-  u = build2 (MODIFY_EXPR, void_type_node, addr, t);
-  gimplify_and_add (u, pre_p);
+  gimplify_assign (addr, t, 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);
-  gimplify_and_add (t, pre_p);
+  t = fold_build_pointer_plus_hwi (t, size);
+  gimplify_assign (ovf, t, pre_p);
 
-  t = build1 (LABEL_EXPR, void_type_node, lab_over);
-  append_to_statement_list (t, pre_p);
+  gimple_seq_add_stmt (pre_p, gimple_build_label (lab_over));
 
 
   /* Increment register save count.  */
@@ -8129,54 +9554,193 @@ s390_gimplify_va_arg (tree valist, tree type, tree *pre_p,
 
   if (indirect_p)
     {
-      t = build_pointer_type (build_pointer_type (type));
+      t = build_pointer_type_for_mode (build_pointer_type (type),
+                                      ptr_mode, true);
       addr = fold_convert (t, addr);
       addr = build_va_arg_indirect_ref (addr);
     }
   else
     {
-      t = build_pointer_type (type);
+      t = build_pointer_type_for_mode (type, ptr_mode, true);
       addr = fold_convert (t, addr);
     }
 
   return build_va_arg_indirect_ref (addr);
 }
 
+/* Emit rtl for the tbegin or tbegin_retry (RETRY != NULL_RTX)
+   expanders.
+   DEST  - Register location where CC will be stored.
+   TDB   - Pointer to a 256 byte area where to store the transaction.
+           diagnostic block. NULL if TDB is not needed.
+   RETRY - Retry count value.  If non-NULL a retry loop for CC2
+           is emitted
+   CLOBBER_FPRS_P - If true clobbers for all FPRs are emitted as part
+                    of the tbegin instruction pattern.  */
+
+void
+s390_expand_tbegin (rtx dest, rtx tdb, rtx retry, bool clobber_fprs_p)
+{
+  const int CC0 = 1 << 3;
+  const int CC1 = 1 << 2;
+  const int CC3 = 1 << 0;
+  rtx abort_label = gen_label_rtx ();
+  rtx leave_label = gen_label_rtx ();
+  rtx retry_reg = gen_reg_rtx (SImode);
+  rtx retry_label = NULL_RTX;
+  rtx jump;
+  rtx very_unlikely = GEN_INT (REG_BR_PROB_BASE / 100 - 1);
+
+  if (retry != NULL_RTX)
+    {
+      emit_move_insn (retry_reg, retry);
+      retry_label = gen_label_rtx ();
+      emit_label (retry_label);
+    }
+
+  if (clobber_fprs_p)
+    emit_insn (gen_tbegin_1 (tdb,
+                gen_rtx_CONST_INT (VOIDmode, TBEGIN_MASK)));
+  else
+    emit_insn (gen_tbegin_nofloat_1 (tdb,
+                gen_rtx_CONST_INT (VOIDmode, TBEGIN_MASK)));
+
+  jump = s390_emit_jump (abort_label,
+                        gen_rtx_NE (VOIDmode,
+                                    gen_rtx_REG (CCRAWmode, CC_REGNUM),
+                                    gen_rtx_CONST_INT (VOIDmode, CC0)));
+
+  JUMP_LABEL (jump) = abort_label;
+  LABEL_NUSES (abort_label) = 1;
+  add_reg_note (jump, REG_BR_PROB, very_unlikely);
+
+  /* Initialize CC return value.  */
+  emit_move_insn (dest, const0_rtx);
+
+  s390_emit_jump (leave_label, NULL_RTX);
+  LABEL_NUSES (leave_label) = 1;
+  emit_barrier ();
+
+  /* Abort handler code.  */
+
+  emit_label (abort_label);
+  if (retry != NULL_RTX)
+    {
+      rtx count = gen_reg_rtx (SImode);
+      jump = s390_emit_jump (leave_label,
+                            gen_rtx_EQ (VOIDmode,
+                              gen_rtx_REG (CCRAWmode, CC_REGNUM),
+                              gen_rtx_CONST_INT (VOIDmode, CC1 | CC3)));
+      LABEL_NUSES (leave_label) = 2;
+      add_reg_note (jump, REG_BR_PROB, very_unlikely);
+
+      /* CC2 - transient failure. Perform retry with ppa.  */
+      emit_move_insn (count, retry);
+      emit_insn (gen_subsi3 (count, count, retry_reg));
+      emit_insn (gen_tx_assist (count));
+      jump = emit_jump_insn (gen_doloop_si64 (retry_label,
+                                             retry_reg,
+                                             retry_reg));
+      JUMP_LABEL (jump) = retry_label;
+      LABEL_NUSES (retry_label) = 1;
+    }
+
+  emit_move_insn (dest, gen_rtx_UNSPEC (SImode,
+                                       gen_rtvec (1, gen_rtx_REG (CCRAWmode,
+                                                                  CC_REGNUM)),
+                                       UNSPEC_CC_TO_INT));
+  emit_label (leave_label);
+}
 
 /* Builtins.  */
 
 enum s390_builtin
 {
-  S390_BUILTIN_THREAD_POINTER,
-  S390_BUILTIN_SET_THREAD_POINTER,
+  S390_BUILTIN_TBEGIN,
+  S390_BUILTIN_TBEGIN_NOFLOAT,
+  S390_BUILTIN_TBEGIN_RETRY,
+  S390_BUILTIN_TBEGIN_RETRY_NOFLOAT,
+  S390_BUILTIN_TBEGINC,
+  S390_BUILTIN_TEND,
+  S390_BUILTIN_TABORT,
+  S390_BUILTIN_NON_TX_STORE,
+  S390_BUILTIN_TX_NESTING_DEPTH,
+  S390_BUILTIN_TX_ASSIST,
 
   S390_BUILTIN_max
 };
 
-static unsigned int const code_for_builtin_64[S390_BUILTIN_max] = {
-  CODE_FOR_get_tp_64,
-  CODE_FOR_set_tp_64
-};
-
-static unsigned int const code_for_builtin_31[S390_BUILTIN_max] = {
-  CODE_FOR_get_tp_31,
-  CODE_FOR_set_tp_31
+static enum insn_code const code_for_builtin[S390_BUILTIN_max] = {
+  CODE_FOR_tbegin,
+  CODE_FOR_tbegin_nofloat,
+  CODE_FOR_tbegin_retry,
+  CODE_FOR_tbegin_retry_nofloat,
+  CODE_FOR_tbeginc,
+  CODE_FOR_tend,
+  CODE_FOR_tabort,
+  CODE_FOR_ntstg,
+  CODE_FOR_etnd,
+  CODE_FOR_tx_assist
 };
 
 static void
 s390_init_builtins (void)
 {
-  tree ftype;
-
-  ftype = build_function_type (ptr_type_node, void_list_node);
-  lang_hooks.builtin_function ("__builtin_thread_pointer", ftype,
-                              S390_BUILTIN_THREAD_POINTER, BUILT_IN_MD,
-                              NULL, NULL_TREE);
+  tree ftype, uint64_type;
+
+  /* void foo (void) */
+  ftype = build_function_type_list (void_type_node, NULL_TREE);
+  add_builtin_function ("__builtin_tbeginc", ftype, S390_BUILTIN_TBEGINC,
+                       BUILT_IN_MD, NULL, NULL_TREE);
+
+  /* void foo (int) */
+  ftype = build_function_type_list (void_type_node, integer_type_node,
+                                   NULL_TREE);
+  add_builtin_function ("__builtin_tabort", ftype,
+                       S390_BUILTIN_TABORT, BUILT_IN_MD, NULL, NULL_TREE);
+  add_builtin_function ("__builtin_tx_assist", ftype,
+                       S390_BUILTIN_TX_ASSIST, BUILT_IN_MD, NULL, NULL_TREE);
+
+  /* int foo (void *) */
+  ftype = build_function_type_list (integer_type_node, ptr_type_node, NULL_TREE);
+  add_builtin_function ("__builtin_tbegin", ftype, S390_BUILTIN_TBEGIN,
+                       BUILT_IN_MD, NULL, NULL_TREE);
+  add_builtin_function ("__builtin_tbegin_nofloat", ftype,
+                       S390_BUILTIN_TBEGIN_NOFLOAT,
+                       BUILT_IN_MD, NULL, NULL_TREE);
+
+  /* int foo (void *, int) */
+  ftype = build_function_type_list (integer_type_node, ptr_type_node,
+                                   integer_type_node, NULL_TREE);
+  add_builtin_function ("__builtin_tbegin_retry", ftype,
+                       S390_BUILTIN_TBEGIN_RETRY,
+                       BUILT_IN_MD,
+                       NULL, NULL_TREE);
+  add_builtin_function ("__builtin_tbegin_retry_nofloat", ftype,
+                       S390_BUILTIN_TBEGIN_RETRY_NOFLOAT,
+                       BUILT_IN_MD,
+                       NULL, NULL_TREE);
+
+  /* int foo (void) */
+  ftype = build_function_type_list (integer_type_node, NULL_TREE);
+  add_builtin_function ("__builtin_tx_nesting_depth", ftype,
+                       S390_BUILTIN_TX_NESTING_DEPTH,
+                       BUILT_IN_MD, NULL, NULL_TREE);
+  add_builtin_function ("__builtin_tend", ftype,
+                       S390_BUILTIN_TEND, BUILT_IN_MD, NULL, NULL_TREE);
+
+  /* void foo (uint64_t *, uint64_t) */
+  if (TARGET_64BIT)
+    uint64_type = long_unsigned_type_node;
+  else
+    uint64_type = long_long_unsigned_type_node;
 
-  ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
-  lang_hooks.builtin_function ("__builtin_set_thread_pointer", ftype,
-                              S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD,
-                              NULL, NULL_TREE);
+   ftype = build_function_type_list (void_type_node,
+                                   build_pointer_type (uint64_type),
+                                   uint64_type, NULL_TREE);
+  add_builtin_function ("__builtin_non_tx_store", ftype,
+                       S390_BUILTIN_NON_TX_STORE,
+                       BUILT_IN_MD, NULL, NULL_TREE);
 }
 
 /* Expand an expression EXP that calls a built-in function,
@@ -8192,16 +9756,14 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
 {
 #define MAX_ARGS 2
 
-  unsigned int const *code_for_builtin =
-    TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31;
-
-  tree fndecl = TREE_OPERAND (TREE_OPERAND (exp, 0), 0);
+  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");
@@ -8209,26 +9771,49 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
   if (icode == 0)
     internal_error ("bad builtin fcode");
 
+  if (!TARGET_ZEC12)
+    error ("Transactional execution builtins require zEC12 or later\n");
+
+  if (!TARGET_HTM && TARGET_ZEC12)
+    error ("Transactional execution builtins not enabled (-mtx)\n");
+
+  /* Set a flag in the machine specific cfun part in order to support
+     saving/restoring of FPRs.  */
+  if (fcode == S390_BUILTIN_TBEGIN || fcode == S390_BUILTIN_TBEGIN_RETRY)
+    cfun->machine->tbegin_p = true;
+
   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)
+      if (arity >= MAX_ARGS)
        return NULL_RTX;
 
       insn_op = &insn_data[icode].operand[arity + nonvoid];
 
-      op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, 0);
+      op[arity] = expand_expr (arg, NULL_RTX, insn_op->mode, EXPAND_NORMAL);
 
       if (!(*insn_op->predicate) (op[arity], insn_op->mode))
-       op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]);
+       {
+         if (insn_op->predicate == memory_operand)
+           {
+             /* Don't move a NULL pointer into a register. Otherwise
+                we have to rely on combine being able to move it back
+                in order to get an immediate 0 in the instruction.  */
+             if (op[arity] != const0_rtx)
+               op[arity] = copy_to_mode_reg (Pmode, op[arity]);
+             op[arity] = gen_rtx_MEM (insn_op->mode, op[arity]);
+           }
+         else
+           op[arity] = copy_to_mode_reg (insn_op->mode, op[arity]);
+       }
+
+      arity++;
     }
 
   if (nonvoid)
@@ -8252,8 +9837,11 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
        pat = GEN_FCN (icode) (op[0]);
       break;
     case 2:
-      pat = GEN_FCN (icode) (target, op[0], op[1]);
-      break;
+      if (nonvoid)
+       pat = GEN_FCN (icode) (target, op[0], op[1]);
+      else
+       pat = GEN_FCN (icode) (op[0], op[1]);
+      break;
     default:
       gcc_unreachable ();
     }
@@ -8274,8 +9862,8 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
    On S/390, we use gpr 1 internally in the trampoline code;
    gpr 0 is used to hold the static chain.  */
 
-void
-s390_trampoline_template (FILE *file)
+static void
+s390_asm_trampoline_template (FILE *file)
 {
   rtx op[2];
   op[0] = gen_rtx_REG (Pmode, 0);
@@ -8283,16 +9871,16 @@ s390_trampoline_template (FILE *file)
 
   if (TARGET_64BIT)
     {
-      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);
+      output_asm_insn ("basr\t%1,0", op);         /* 2 byte */
+      output_asm_insn ("lmg\t%0,%1,14(%1)", op);  /* 6 byte */
+      output_asm_insn ("br\t%1", op);             /* 2 byte */
       ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 10));
     }
   else
     {
-      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);
+      output_asm_insn ("basr\t%1,0", op);         /* 2 byte */
+      output_asm_insn ("lm\t%0,%1,6(%1)", op);    /* 4 byte */
+      output_asm_insn ("br\t%1", op);             /* 2 byte */
       ASM_OUTPUT_SKIP (file, (HOST_WIDE_INT)(TRAMPOLINE_SIZE - 8));
     }
 }
@@ -8301,37 +9889,19 @@ s390_trampoline_template (FILE *file)
    FNADDR is an RTX for the address of the function's pure code.
    CXT is an RTX for the static chain value for the function.  */
 
-void
-s390_initialize_trampoline (rtx addr, rtx fnaddr, rtx cxt)
+static void
+s390_trampoline_init (rtx m_tramp, tree fndecl, rtx cxt)
 {
-  emit_move_insn (gen_rtx_MEM (Pmode,
-                  memory_address (Pmode,
-                  plus_constant (addr, (TARGET_64BIT ? 16 : 8)))), cxt);
-  emit_move_insn (gen_rtx_MEM (Pmode,
-                  memory_address (Pmode,
-                  plus_constant (addr, (TARGET_64BIT ? 24 : 12)))), fnaddr);
-}
+  rtx fnaddr = XEXP (DECL_RTL (fndecl), 0);
+  rtx mem;
 
-/* Return rtx for 64-bit constant formed from the 32-bit subwords
-   LOW and HIGH, independent of the host word size.  */
+  emit_block_move (m_tramp, assemble_trampoline_template (),
+                  GEN_INT (2 * UNITS_PER_LONG), BLOCK_OP_NORMAL);
 
-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
-  gcc_unreachable ();
-#endif
-#endif
+  mem = adjust_address (m_tramp, Pmode, 2 * UNITS_PER_LONG);
+  emit_move_insn (mem, cxt);
+  mem = adjust_address (m_tramp, Pmode, 3 * UNITS_PER_LONG);
+  emit_move_insn (mem, fnaddr);
 }
 
 /* Output assembler code to FILE to increment profiler label # LABELNO
@@ -8349,7 +9919,7 @@ s390_function_profiler (FILE *file, int labelno)
 
   op[0] = gen_rtx_REG (Pmode, RETURN_REGNUM);
   op[1] = gen_rtx_REG (Pmode, STACK_POINTER_REGNUM);
-  op[1] = gen_rtx_MEM (Pmode, plus_constant (op[1], UNITS_PER_WORD));
+  op[1] = gen_rtx_MEM (Pmode, plus_constant (Pmode, op[1], UNITS_PER_LONG));
 
   op[2] = gen_rtx_REG (Pmode, 1);
   op[3] = gen_rtx_SYMBOL_REF (Pmode, label);
@@ -8410,11 +9980,31 @@ s390_encode_section_info (tree decl, rtx rtl, int first)
 {
   default_encode_section_info (decl, rtl, first);
 
-  /* If a variable has a forced alignment to < 2 bytes, mark it with
-     SYMBOL_FLAG_ALIGN1 to prevent it from being used as LARL operand.  */
-  if (TREE_CODE (decl) == VAR_DECL
-      && DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) < 16)
-    SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_ALIGN1;
+  if (TREE_CODE (decl) == VAR_DECL)
+    {
+      /* If a variable has a forced alignment to < 2 bytes, mark it
+        with SYMBOL_FLAG_ALIGN1 to prevent it from being used as LARL
+        operand.  */
+      if (DECL_USER_ALIGN (decl) && DECL_ALIGN (decl) < 16)
+       SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_ALIGN1;
+      if (!DECL_SIZE (decl)
+         || !DECL_ALIGN (decl)
+         || !host_integerp (DECL_SIZE (decl), 0)
+         || (DECL_ALIGN (decl) <= 64
+             && DECL_ALIGN (decl) != tree_low_cst (DECL_SIZE (decl), 0)))
+       SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_NOT_NATURALLY_ALIGNED;
+    }
+
+  /* Literal pool references don't have a decl so they are handled
+     differently here.  We rely on the information in the MEM_ALIGN
+     entry to decide upon natural alignment.  */
+  if (MEM_P (rtl)
+      && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF
+      && TREE_CONSTANT_POOL_ADDRESS_P (XEXP (rtl, 0))
+      && (MEM_ALIGN (rtl) == 0
+         || GET_MODE_BITSIZE (GET_MODE (rtl)) == 0
+         || MEM_ALIGN (rtl) < GET_MODE_BITSIZE (GET_MODE (rtl))))
+    SYMBOL_REF_FLAGS (XEXP (rtl, 0)) |= SYMBOL_FLAG_NOT_NATURALLY_ALIGNED;
 }
 
 /* Output thunk to FILE that implements a C++ virtual function call (with
@@ -8431,6 +10021,9 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
   rtx op[10];
   int nonlocal = 0;
 
+  /* Make sure unwind info is emitted for the thunk if needed.  */
+  final_start_function (emit_barrier (), file, 1);
+
   /* Operand 0 is the target function.  */
   op[0] = XEXP (DECL_RTL (function), 0);
   if (flag_pic && !SYMBOL_REF_LOCAL_P (op[0]))
@@ -8680,6 +10273,7 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
          output_asm_insn (".long\t%3", op);
        }
     }
+  final_end_function ();
 }
 
 static bool
@@ -8688,28 +10282,28 @@ s390_valid_pointer_mode (enum machine_mode mode)
   return (mode == SImode || (TARGET_64BIT && mode == DImode));
 }
 
-/* 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;
+  CUMULATIVE_ARGS cum_v;
+  cumulative_args_t 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);
+  INIT_CUMULATIVE_ARGS (cum_v, NULL, NULL, 0, 0);
+  cum = pack_cumulative_args (&cum_v);
 
-  while (argument_list)
+  for (i = 0; i < call_expr_nargs (call_expr); i++)
     {
-      parameter = TREE_VALUE (argument_list);
-      argument_list = TREE_CHAIN (argument_list);
-
+      parameter = CALL_EXPR_ARG (call_expr, i);
       gcc_assert (parameter);
 
       /* For an undeclared variable passed as parameter we will get
@@ -8723,24 +10317,46 @@ s390_call_saved_register_used (tree argument_list)
       mode = TYPE_MODE (type);
       gcc_assert (mode);
 
-      if (pass_by_reference (&cum, mode, type, true))
+      if (pass_by_reference (&cum_v, mode, type, true))
        {
          mode = Pmode;
          type = build_pointer_type (type);
        }
 
-       parm_rtx = s390_function_arg (&cum, mode, type, 0);
+       parm_rtx = s390_function_arg (cum, mode, type, 0);
 
-       s390_function_arg_advance (&cum, mode, type, 0);
+       s390_function_arg_advance (cum, mode, type, 0);
 
-       if (parm_rtx && REG_P (parm_rtx))
-        {
+       if (!parm_rtx)
+        continue;
+
+       if (REG_P (parm_rtx))
+        {
           for (reg = 0;
                reg < HARD_REGNO_NREGS (REGNO (parm_rtx), GET_MODE (parm_rtx));
                reg++)
-            if (! call_used_regs[reg + REGNO (parm_rtx)])
-              return true;
+            if (!call_used_regs[reg + REGNO (parm_rtx)])
+              return true;
+        }
+
+       if (GET_CODE (parm_rtx) == PARALLEL)
+        {
+          int i;
+
+          for (i = 0; i < XVECLEN (parm_rtx, 0); i++)
+            {
+              rtx r = XEXP (XVECEXP (parm_rtx, 0, i), 0);
+
+              gcc_assert (REG_P (r));
+
+              for (reg = 0;
+                   reg < HARD_REGNO_NREGS (REGNO (r), GET_MODE (r));
+                   reg++)
+                if (!call_used_regs[reg + REGNO (r)])
+                  return true;
+            }
         }
+
     }
   return false;
 }
@@ -8765,11 +10381,7 @@ s390_function_ok_for_sibcall (tree decl, tree exp)
   /* Register 6 on s390 is available as an argument register but unfortunately
      "caller saved". This makes functions needing this register for arguments
      not suitable for sibcalls.  */
-  if (TREE_OPERAND (exp, 1)
-      && s390_call_saved_register_used (TREE_OPERAND (exp, 1)))
-      return false;
-
-  return true;
+  return !s390_call_saved_register_used (exp);
 }
 
 /* Return the fixed registers used for condition codes.  */
@@ -8779,7 +10391,7 @@ s390_fixed_condition_code_regs (unsigned int *p1, unsigned int *p2)
 {
   *p1 = CC_REGNUM;
   *p2 = INVALID_REGNUM;
+
   return true;
 }
 
@@ -8810,11 +10422,25 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
          replace the symbol itself with the PLT stub.  */
       if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location))
         {
-         addr_location = gen_rtx_UNSPEC (Pmode,
-                                         gen_rtvec (1, addr_location),
-                                         UNSPEC_PLT);
-         addr_location = gen_rtx_CONST (Pmode, addr_location);
-         plt_call = true;
+         if (retaddr_reg != NULL_RTX)
+           {
+             addr_location = gen_rtx_UNSPEC (Pmode,
+                                             gen_rtvec (1, addr_location),
+                                             UNSPEC_PLT);
+             addr_location = gen_rtx_CONST (Pmode, addr_location);
+             plt_call = true;
+           }
+         else
+           /* For -fpic code the PLT entries might use r12 which is
+              call-saved.  Therefore we cannot do a sibcall when
+              calling directly using a symbol ref.  When reaching
+              this point we decided (in s390_function_ok_for_sibcall)
+              to do a sibcall for a function pointer but one of the
+              optimizers was able to get rid of the function pointer
+              by propagating the symbol ref into the call.  This
+              optimization is illegal for S/390 so we turn the direct
+              call into a indirect call again.  */
+           addr_location = force_reg (Pmode, addr_location);
         }
 
       /* Unless we can use the bras(l) insn, force the
@@ -8866,15 +10492,14 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
       /* s390_function_ok_for_sibcall should
         have denied sibcalls in this case.  */
       gcc_assert (retaddr_reg != NULL_RTX);
-
-      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), pic_offset_table_rtx);
+      use_reg (&CALL_INSN_FUNCTION_USAGE (insn), gen_rtx_REG (Pmode, 12));
     }
   return insn;
 }
 
-/* Implement CONDITIONAL_REGISTER_USAGE.  */
+/* Implement TARGET_CONDITIONAL_REGISTER_USAGE.  */
 
-void
+static void
 s390_conditional_register_usage (void)
 {
   int i;
@@ -8893,18 +10518,18 @@ s390_conditional_register_usage (void)
     }
   if (TARGET_64BIT)
     {
-      for (i = 24; i < 32; i++)
+      for (i = FPR8_REGNUM; i <= FPR15_REGNUM; i++)
        call_used_regs[i] = call_really_used_regs[i] = 0;
     }
   else
     {
-      for (i = 18; i < 20; i++)
-       call_used_regs[i] = call_really_used_regs[i] = 0;
+      call_used_regs[FPR4_REGNUM] = call_really_used_regs[FPR4_REGNUM] = 0;
+      call_used_regs[FPR6_REGNUM] = call_really_used_regs[FPR6_REGNUM] = 0;
     }
 
   if (TARGET_SOFT_FLOAT)
     {
-      for (i = 16; i < 32; i++)
+      for (i = FPR0_REGNUM; i <= FPR15_REGNUM; i++)
        call_used_regs[i] = fixed_regs[i] = 1;
     }
 }
@@ -8945,10 +10570,10 @@ s390_optimize_prologue (void)
   /* 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 
+  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 
+      && (TARGET_CPU_ZARCH
+          || (cfun_frame_layout.first_save_gpr <= RETURN_REGNUM
               && cfun_frame_layout.last_save_gpr >= RETURN_REGNUM)))
     return;
 
@@ -8961,7 +10586,7 @@ s390_optimize_prologue (void)
 
       next_insn = NEXT_INSN (insn);
 
-      if (GET_CODE (insn) != INSN)
+      if (! NONJUMP_INSN_P (insn))
        continue;
 
       if (GET_CODE (PATTERN (insn)) == PARALLEL
@@ -8988,9 +10613,9 @@ s390_optimize_prologue (void)
 
          if (cfun_frame_layout.first_save_gpr != -1)
            {
-             new_insn  = save_gprs (base, 
+             new_insn  = save_gprs (base,
                                     off + (cfun_frame_layout.first_save_gpr
-                                           - first) * UNITS_PER_WORD, 
+                                           - first) * UNITS_PER_LONG,
                                     cfun_frame_layout.first_save_gpr,
                                     cfun_frame_layout.last_save_gpr);
              new_insn = emit_insn_before (new_insn, insn);
@@ -9049,9 +10674,9 @@ s390_optimize_prologue (void)
 
          if (cfun_frame_layout.first_restore_gpr != -1)
            {
-             new_insn = restore_gprs (base, 
+             new_insn = restore_gprs (base,
                                       off + (cfun_frame_layout.first_restore_gpr
-                                             - first) * UNITS_PER_WORD, 
+                                             - first) * UNITS_PER_LONG,
                                       cfun_frame_layout.first_restore_gpr,
                                       cfun_frame_layout.last_restore_gpr);
              new_insn = emit_insn_before (new_insn, insn);
@@ -9088,6 +10713,232 @@ s390_optimize_prologue (void)
     }
 }
 
+/* On z10 and later the dynamic branch prediction must see the
+   backward jump within a certain windows.  If not it falls back to
+   the static prediction.  This function rearranges the loop backward
+   branch in a way which makes the static prediction always correct.
+   The function returns true if it added an instruction.  */
+static bool
+s390_fix_long_loop_prediction (rtx insn)
+{
+  rtx set = single_set (insn);
+  rtx code_label, label_ref, new_label;
+  rtx uncond_jump;
+  rtx cur_insn;
+  rtx tmp;
+  int distance;
+
+  /* This will exclude branch on count and branch on index patterns
+     since these are correctly statically predicted.  */
+  if (!set
+      || SET_DEST (set) != pc_rtx
+      || GET_CODE (SET_SRC(set)) != IF_THEN_ELSE)
+    return false;
+
+  label_ref = (GET_CODE (XEXP (SET_SRC (set), 1)) == LABEL_REF ?
+              XEXP (SET_SRC (set), 1) : XEXP (SET_SRC (set), 2));
+
+  gcc_assert (GET_CODE (label_ref) == LABEL_REF);
+
+  code_label = XEXP (label_ref, 0);
+
+  if (INSN_ADDRESSES (INSN_UID (code_label)) == -1
+      || INSN_ADDRESSES (INSN_UID (insn)) == -1
+      || (INSN_ADDRESSES (INSN_UID (insn))
+         - INSN_ADDRESSES (INSN_UID (code_label)) < PREDICT_DISTANCE))
+    return false;
+
+  for (distance = 0, cur_insn = PREV_INSN (insn);
+       distance < PREDICT_DISTANCE - 6;
+       distance += get_attr_length (cur_insn), cur_insn = PREV_INSN (cur_insn))
+    if (!cur_insn || JUMP_P (cur_insn) || LABEL_P (cur_insn))
+      return false;
+
+  new_label = gen_label_rtx ();
+  uncond_jump = emit_jump_insn_after (
+                 gen_rtx_SET (VOIDmode, pc_rtx,
+                              gen_rtx_LABEL_REF (VOIDmode, code_label)),
+                 insn);
+  emit_label_after (new_label, uncond_jump);
+
+  tmp = XEXP (SET_SRC (set), 1);
+  XEXP (SET_SRC (set), 1) = XEXP (SET_SRC (set), 2);
+  XEXP (SET_SRC (set), 2) = tmp;
+  INSN_CODE (insn) = -1;
+
+  XEXP (label_ref, 0) = new_label;
+  JUMP_LABEL (insn) = new_label;
+  JUMP_LABEL (uncond_jump) = code_label;
+
+  return true;
+}
+
+/* Returns 1 if INSN reads the value of REG for purposes not related
+   to addressing of memory, and 0 otherwise.  */
+static int
+s390_non_addr_reg_read_p (rtx reg, rtx insn)
+{
+  return reg_referenced_p (reg, PATTERN (insn))
+    && !reg_used_in_mem_p (REGNO (reg), PATTERN (insn));
+}
+
+/* Starting from INSN find_cond_jump looks downwards in the insn
+   stream for a single jump insn which is the last user of the
+   condition code set in INSN.  */
+static rtx
+find_cond_jump (rtx insn)
+{
+  for (; insn; insn = NEXT_INSN (insn))
+    {
+      rtx ite, cc;
+
+      if (LABEL_P (insn))
+       break;
+
+      if (!JUMP_P (insn))
+       {
+         if (reg_mentioned_p (gen_rtx_REG (CCmode, CC_REGNUM), insn))
+           break;
+         continue;
+       }
+
+      /* This will be triggered by a return.  */
+      if (GET_CODE (PATTERN (insn)) != SET)
+       break;
+
+      gcc_assert (SET_DEST (PATTERN (insn)) == pc_rtx);
+      ite = SET_SRC (PATTERN (insn));
+
+      if (GET_CODE (ite) != IF_THEN_ELSE)
+       break;
+
+      cc = XEXP (XEXP (ite, 0), 0);
+      if (!REG_P (cc) || !CC_REGNO_P (REGNO (cc)))
+       break;
+
+      if (find_reg_note (insn, REG_DEAD, cc))
+       return insn;
+      break;
+    }
+
+  return NULL_RTX;
+}
+
+/* Swap the condition in COND and the operands in OP0 and OP1 so that
+   the semantics does not change.  If NULL_RTX is passed as COND the
+   function tries to find the conditional jump starting with INSN.  */
+static void
+s390_swap_cmp (rtx cond, rtx *op0, rtx *op1, rtx insn)
+{
+  rtx tmp = *op0;
+
+  if (cond == NULL_RTX)
+    {
+      rtx jump = find_cond_jump (NEXT_INSN (insn));
+      jump = jump ? single_set (jump) : NULL_RTX;
+
+      if (jump == NULL_RTX)
+       return;
+
+      cond = XEXP (XEXP (jump, 1), 0);
+    }
+
+  *op0 = *op1;
+  *op1 = tmp;
+  PUT_CODE (cond, swap_condition (GET_CODE (cond)));
+}
+
+/* On z10, instructions of the compare-and-branch family have the
+   property to access the register occurring as second operand with
+   its bits complemented.  If such a compare is grouped with a second
+   instruction that accesses the same register non-complemented, and
+   if that register's value is delivered via a bypass, then the
+   pipeline recycles, thereby causing significant performance decline.
+   This function locates such situations and exchanges the two
+   operands of the compare.  The function return true whenever it
+   added an insn.  */
+static bool
+s390_z10_optimize_cmp (rtx insn)
+{
+  rtx prev_insn, next_insn;
+  bool insn_added_p = false;
+  rtx cond, *op0, *op1;
+
+  if (GET_CODE (PATTERN (insn)) == PARALLEL)
+    {
+      /* Handle compare and branch and branch on count
+        instructions.  */
+      rtx pattern = single_set (insn);
+
+      if (!pattern
+         || SET_DEST (pattern) != pc_rtx
+         || GET_CODE (SET_SRC (pattern)) != IF_THEN_ELSE)
+       return false;
+
+      cond = XEXP (SET_SRC (pattern), 0);
+      op0 = &XEXP (cond, 0);
+      op1 = &XEXP (cond, 1);
+    }
+  else if (GET_CODE (PATTERN (insn)) == SET)
+    {
+      rtx src, dest;
+
+      /* Handle normal compare instructions.  */
+      src = SET_SRC (PATTERN (insn));
+      dest = SET_DEST (PATTERN (insn));
+
+      if (!REG_P (dest)
+         || !CC_REGNO_P (REGNO (dest))
+         || GET_CODE (src) != COMPARE)
+       return false;
+
+      /* s390_swap_cmp will try to find the conditional
+        jump when passing NULL_RTX as condition.  */
+      cond = NULL_RTX;
+      op0 = &XEXP (src, 0);
+      op1 = &XEXP (src, 1);
+    }
+  else
+    return false;
+
+  if (!REG_P (*op0) || !REG_P (*op1))
+    return false;
+
+  if (GET_MODE_CLASS (GET_MODE (*op0)) != MODE_INT)
+    return false;
+
+  /* Swap the COMPARE arguments and its mask if there is a
+     conflicting access in the previous insn.  */
+  prev_insn = prev_active_insn (insn);
+  if (prev_insn != NULL_RTX && INSN_P (prev_insn)
+      && reg_referenced_p (*op1, PATTERN (prev_insn)))
+    s390_swap_cmp (cond, op0, op1, insn);
+
+  /* Check if there is a conflict with the next insn. If there
+     was no conflict with the previous insn, then swap the
+     COMPARE arguments and its mask.  If we already swapped
+     the operands, or if swapping them would cause a conflict
+     with the previous insn, issue a NOP after the COMPARE in
+     order to separate the two instuctions.  */
+  next_insn = next_active_insn (insn);
+  if (next_insn != NULL_RTX && INSN_P (next_insn)
+      && s390_non_addr_reg_read_p (*op1, next_insn))
+    {
+      if (prev_insn != NULL_RTX && INSN_P (prev_insn)
+         && s390_non_addr_reg_read_p (*op0, prev_insn))
+       {
+         if (REGNO (*op1) == 0)
+           emit_insn_after (gen_nop1 (), insn);
+         else
+           emit_insn_after (gen_nop (), insn);
+         insn_added_p = true;
+       }
+      else
+       s390_swap_cmp (cond, op0, op1, insn);
+    }
+  return insn_added_p;
+}
+
 /* Perform machine-dependent processing.  */
 
 static void
@@ -9099,9 +10950,6 @@ s390_reorg (void)
      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.
 
@@ -9200,8 +11048,428 @@ s390_reorg (void)
 
   /* Try to optimize prologue and epilogue further.  */
   s390_optimize_prologue ();
+
+  /* Walk over the insns and do some >=z10 specific changes.  */
+  if (s390_tune == PROCESSOR_2097_Z10
+      || s390_tune == PROCESSOR_2817_Z196
+      || s390_tune == PROCESSOR_2827_ZEC12)
+    {
+      rtx insn;
+      bool insn_added_p = false;
+
+      /* The insn lengths and addresses have to be up to date for the
+        following manipulations.  */
+      shorten_branches (get_insns ());
+
+      for (insn = get_insns (); insn; insn = NEXT_INSN (insn))
+       {
+         if (!INSN_P (insn) || INSN_CODE (insn) <= 0)
+           continue;
+
+         if (JUMP_P (insn))
+           insn_added_p |= s390_fix_long_loop_prediction (insn);
+
+         if ((GET_CODE (PATTERN (insn)) == PARALLEL
+              || GET_CODE (PATTERN (insn)) == SET)
+             && s390_tune == PROCESSOR_2097_Z10)
+           insn_added_p |= s390_z10_optimize_cmp (insn);
+       }
+
+      /* Adjust branches if we added new instructions.  */
+      if (insn_added_p)
+       shorten_branches (get_insns ());
+    }
+}
+
+/* Return true if INSN is a fp load insn writing register REGNO.  */
+static inline bool
+s390_fpload_toreg (rtx insn, unsigned int regno)
+{
+  rtx set;
+  enum attr_type flag = s390_safe_attr_type (insn);
+
+  if (flag != TYPE_FLOADSF && flag != TYPE_FLOADDF)
+    return false;
+
+  set = single_set (insn);
+
+  if (set == NULL_RTX)
+    return false;
+
+  if (!REG_P (SET_DEST (set)) || !MEM_P (SET_SRC (set)))
+    return false;
+
+  if (REGNO (SET_DEST (set)) != regno)
+    return false;
+
+  return true;
+}
+
+/* This value describes the distance to be avoided between an
+   aritmetic fp instruction and an fp load writing the same register.
+   Z10_EARLYLOAD_DISTANCE - 1 as well as Z10_EARLYLOAD_DISTANCE + 1 is
+   fine but the exact value has to be avoided. Otherwise the FP
+   pipeline will throw an exception causing a major penalty.  */
+#define Z10_EARLYLOAD_DISTANCE 7
+
+/* Rearrange the ready list in order to avoid the situation described
+   for Z10_EARLYLOAD_DISTANCE.  A problematic load instruction is
+   moved to the very end of the ready list.  */
+static void
+s390_z10_prevent_earlyload_conflicts (rtx *ready, int *nready_p)
+{
+  unsigned int regno;
+  int nready = *nready_p;
+  rtx tmp;
+  int i;
+  rtx insn;
+  rtx set;
+  enum attr_type flag;
+  int distance;
+
+  /* Skip DISTANCE - 1 active insns.  */
+  for (insn = last_scheduled_insn, distance = Z10_EARLYLOAD_DISTANCE - 1;
+       distance > 0 && insn != NULL_RTX;
+       distance--, insn = prev_active_insn (insn))
+    if (CALL_P (insn) || JUMP_P (insn))
+      return;
+
+  if (insn == NULL_RTX)
+    return;
+
+  set = single_set (insn);
+
+  if (set == NULL_RTX || !REG_P (SET_DEST (set))
+      || GET_MODE_CLASS (GET_MODE (SET_DEST (set))) != MODE_FLOAT)
+    return;
+
+  flag = s390_safe_attr_type (insn);
+
+  if (flag == TYPE_FLOADSF || flag == TYPE_FLOADDF)
+    return;
+
+  regno = REGNO (SET_DEST (set));
+  i = nready - 1;
+
+  while (!s390_fpload_toreg (ready[i], regno) && i > 0)
+    i--;
+
+  if (!i)
+    return;
+
+  tmp = ready[i];
+  memmove (&ready[1], &ready[0], sizeof (rtx) * i);
+  ready[0] = tmp;
+}
+
+
+/* The s390_sched_state variable tracks the state of the current or
+   the last instruction group.
+
+   0,1,2 number of instructions scheduled in the current group
+   3     the last group is complete - normal insns
+   4     the last group was a cracked/expanded insn */
+
+static int s390_sched_state;
+
+#define S390_OOO_SCHED_STATE_NORMAL  3
+#define S390_OOO_SCHED_STATE_CRACKED 4
+
+#define S390_OOO_SCHED_ATTR_MASK_CRACKED    0x1
+#define S390_OOO_SCHED_ATTR_MASK_EXPANDED   0x2
+#define S390_OOO_SCHED_ATTR_MASK_ENDGROUP   0x4
+#define S390_OOO_SCHED_ATTR_MASK_GROUPALONE 0x8
+
+static unsigned int
+s390_get_sched_attrmask (rtx insn)
+{
+  unsigned int mask = 0;
+
+  if (get_attr_ooo_cracked (insn))
+    mask |= S390_OOO_SCHED_ATTR_MASK_CRACKED;
+  if (get_attr_ooo_expanded (insn))
+    mask |= S390_OOO_SCHED_ATTR_MASK_EXPANDED;
+  if (get_attr_ooo_endgroup (insn))
+    mask |= S390_OOO_SCHED_ATTR_MASK_ENDGROUP;
+  if (get_attr_ooo_groupalone (insn))
+    mask |= S390_OOO_SCHED_ATTR_MASK_GROUPALONE;
+  return mask;
+}
+
+/* Return the scheduling score for INSN.  The higher the score the
+   better.  The score is calculated from the OOO scheduling attributes
+   of INSN and the scheduling state s390_sched_state.  */
+static int
+s390_sched_score (rtx insn)
+{
+  unsigned int mask = s390_get_sched_attrmask (insn);
+  int score = 0;
+
+  switch (s390_sched_state)
+    {
+    case 0:
+      /* Try to put insns into the first slot which would otherwise
+        break a group.  */
+      if ((mask & S390_OOO_SCHED_ATTR_MASK_CRACKED) != 0
+         || (mask & S390_OOO_SCHED_ATTR_MASK_EXPANDED) != 0)
+       score += 5;
+      if ((mask & S390_OOO_SCHED_ATTR_MASK_GROUPALONE) != 0)
+       score += 10;
+    case 1:
+      /* Prefer not cracked insns while trying to put together a
+        group.  */
+      if ((mask & S390_OOO_SCHED_ATTR_MASK_CRACKED) == 0
+         && (mask & S390_OOO_SCHED_ATTR_MASK_EXPANDED) == 0
+         && (mask & S390_OOO_SCHED_ATTR_MASK_GROUPALONE) == 0)
+       score += 10;
+      if ((mask & S390_OOO_SCHED_ATTR_MASK_ENDGROUP) == 0)
+       score += 5;
+      break;
+    case 2:
+      /* Prefer not cracked insns while trying to put together a
+        group.  */
+      if ((mask & S390_OOO_SCHED_ATTR_MASK_CRACKED) == 0
+         && (mask & S390_OOO_SCHED_ATTR_MASK_EXPANDED) == 0
+         && (mask & S390_OOO_SCHED_ATTR_MASK_GROUPALONE) == 0)
+       score += 10;
+      /* Prefer endgroup insns in the last slot.  */
+      if ((mask & S390_OOO_SCHED_ATTR_MASK_ENDGROUP) != 0)
+       score += 10;
+      break;
+    case S390_OOO_SCHED_STATE_NORMAL:
+      /* Prefer not cracked insns if the last was not cracked.  */
+      if ((mask & S390_OOO_SCHED_ATTR_MASK_CRACKED) == 0
+         && (mask & S390_OOO_SCHED_ATTR_MASK_EXPANDED) == 0)
+       score += 5;
+      if ((mask & S390_OOO_SCHED_ATTR_MASK_GROUPALONE) != 0)
+       score += 10;
+      break;
+    case S390_OOO_SCHED_STATE_CRACKED:
+      /* Try to keep cracked insns together to prevent them from
+        interrupting groups.  */
+      if ((mask & S390_OOO_SCHED_ATTR_MASK_CRACKED) != 0
+         || (mask & S390_OOO_SCHED_ATTR_MASK_EXPANDED) != 0)
+       score += 5;
+      break;
+    }
+  return score;
 }
 
+/* This function is called via hook TARGET_SCHED_REORDER before
+   issueing one insn from list READY which contains *NREADYP entries.
+   For target z10 it reorders load instructions to avoid early load
+   conflicts in the floating point pipeline  */
+static int
+s390_sched_reorder (FILE *file, int verbose,
+                   rtx *ready, int *nreadyp, int clock ATTRIBUTE_UNUSED)
+{
+  if (s390_tune == PROCESSOR_2097_Z10)
+    if (reload_completed && *nreadyp > 1)
+      s390_z10_prevent_earlyload_conflicts (ready, nreadyp);
+
+  if (s390_tune == PROCESSOR_2827_ZEC12
+      && reload_completed
+      && *nreadyp > 1)
+    {
+      int i;
+      int last_index = *nreadyp - 1;
+      int max_index = -1;
+      int max_score = -1;
+      rtx tmp;
+
+      /* Just move the insn with the highest score to the top (the
+        end) of the list.  A full sort is not needed since a conflict
+        in the hazard recognition cannot happen.  So the top insn in
+        the ready list will always be taken.  */
+      for (i = last_index; i >= 0; i--)
+       {
+         int score;
+
+         if (recog_memoized (ready[i]) < 0)
+           continue;
+
+         score = s390_sched_score (ready[i]);
+         if (score > max_score)
+           {
+             max_score = score;
+             max_index = i;
+           }
+       }
+
+      if (max_index != -1)
+       {
+         if (max_index != last_index)
+           {
+             tmp = ready[max_index];
+             ready[max_index] = ready[last_index];
+             ready[last_index] = tmp;
+
+             if (verbose > 5)
+               fprintf (file,
+                        "move insn %d to the top of list\n",
+                        INSN_UID (ready[last_index]));
+           }
+         else if (verbose > 5)
+           fprintf (file,
+                    "best insn %d already on top\n",
+                    INSN_UID (ready[last_index]));
+       }
+
+      if (verbose > 5)
+       {
+         fprintf (file, "ready list ooo attributes - sched state: %d\n",
+                  s390_sched_state);
+
+         for (i = last_index; i >= 0; i--)
+           {
+             if (recog_memoized (ready[i]) < 0)
+               continue;
+             fprintf (file, "insn %d score: %d: ", INSN_UID (ready[i]),
+                      s390_sched_score (ready[i]));
+#define PRINT_OOO_ATTR(ATTR) fprintf (file, "%s ", get_attr_##ATTR (ready[i]) ? #ATTR : "!" #ATTR);
+             PRINT_OOO_ATTR (ooo_cracked);
+             PRINT_OOO_ATTR (ooo_expanded);
+             PRINT_OOO_ATTR (ooo_endgroup);
+             PRINT_OOO_ATTR (ooo_groupalone);
+#undef PRINT_OOO_ATTR
+             fprintf (file, "\n");
+           }
+       }
+    }
+
+  return s390_issue_rate ();
+}
+
+
+/* This function is called via hook TARGET_SCHED_VARIABLE_ISSUE after
+   the scheduler has issued INSN.  It stores the last issued insn into
+   last_scheduled_insn in order to make it available for
+   s390_sched_reorder.  */
+static int
+s390_sched_variable_issue (FILE *file, int verbose, rtx insn, int more)
+{
+  last_scheduled_insn = insn;
+
+  if (s390_tune == PROCESSOR_2827_ZEC12
+      && reload_completed
+      && recog_memoized (insn) >= 0)
+    {
+      unsigned int mask = s390_get_sched_attrmask (insn);
+
+      if ((mask & S390_OOO_SCHED_ATTR_MASK_CRACKED) != 0
+         || (mask & S390_OOO_SCHED_ATTR_MASK_EXPANDED) != 0)
+       s390_sched_state = S390_OOO_SCHED_STATE_CRACKED;
+      else if ((mask & S390_OOO_SCHED_ATTR_MASK_ENDGROUP) != 0
+              || (mask & S390_OOO_SCHED_ATTR_MASK_GROUPALONE) != 0)
+       s390_sched_state = S390_OOO_SCHED_STATE_NORMAL;
+      else
+       {
+         /* Only normal insns are left (mask == 0).  */
+         switch (s390_sched_state)
+           {
+           case 0:
+           case 1:
+           case 2:
+           case S390_OOO_SCHED_STATE_NORMAL:
+             if (s390_sched_state == S390_OOO_SCHED_STATE_NORMAL)
+               s390_sched_state = 1;
+             else
+               s390_sched_state++;
+
+             break;
+           case S390_OOO_SCHED_STATE_CRACKED:
+             s390_sched_state = S390_OOO_SCHED_STATE_NORMAL;
+             break;
+           }
+       }
+      if (verbose > 5)
+       {
+         fprintf (file, "insn %d: ", INSN_UID (insn));
+#define PRINT_OOO_ATTR(ATTR)                                           \
+         fprintf (file, "%s ", get_attr_##ATTR (insn) ? #ATTR : "");
+         PRINT_OOO_ATTR (ooo_cracked);
+         PRINT_OOO_ATTR (ooo_expanded);
+         PRINT_OOO_ATTR (ooo_endgroup);
+         PRINT_OOO_ATTR (ooo_groupalone);
+#undef PRINT_OOO_ATTR
+         fprintf (file, "\n");
+         fprintf (file, "sched state: %d\n", s390_sched_state);
+       }
+    }
+
+  if (GET_CODE (PATTERN (insn)) != USE
+      && GET_CODE (PATTERN (insn)) != CLOBBER)
+    return more - 1;
+  else
+    return more;
+}
+
+static void
+s390_sched_init (FILE *file ATTRIBUTE_UNUSED,
+                int verbose ATTRIBUTE_UNUSED,
+                int max_ready ATTRIBUTE_UNUSED)
+{
+  last_scheduled_insn = NULL_RTX;
+  s390_sched_state = 0;
+}
+
+/* This function checks the whole of insn X for memory references. The
+   function always returns zero because the framework it is called
+   from would stop recursively analyzing the insn upon a return value
+   other than zero. The real result of this function is updating
+   counter variable MEM_COUNT.  */
+static int
+check_dpu (rtx *x, unsigned *mem_count)
+{
+  if (*x != NULL_RTX && MEM_P (*x))
+    (*mem_count)++;
+  return 0;
+}
+
+/* This target hook implementation for TARGET_LOOP_UNROLL_ADJUST calculates
+   a new number struct loop *loop should be unrolled if tuned for cpus with
+   a built-in stride prefetcher.
+   The loop is analyzed for memory accesses by calling check_dpu for
+   each rtx of the loop. Depending on the loop_depth and the amount of
+   memory accesses a new number <=nunroll is returned to improve the
+   behaviour of the hardware prefetch unit.  */
+static unsigned
+s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop)
+{
+  basic_block *bbs;
+  rtx insn;
+  unsigned i;
+  unsigned mem_count = 0;
+
+  if (s390_tune != PROCESSOR_2097_Z10
+      && s390_tune != PROCESSOR_2817_Z196
+      && s390_tune != PROCESSOR_2827_ZEC12)
+    return nunroll;
+
+  /* Count the number of memory references within the loop body.  */
+  bbs = get_loop_body (loop);
+  for (i = 0; i < loop->num_nodes; i++)
+    {
+      for (insn = BB_HEAD (bbs[i]); insn != BB_END (bbs[i]); insn = NEXT_INSN (insn))
+       if (INSN_P (insn) && INSN_CODE (insn) != -1)
+            for_each_rtx (&insn, (rtx_function) check_dpu, &mem_count);
+    }
+  free (bbs);
+
+  /* Prevent division by zero, and we do not need to adjust nunroll in this case.  */
+  if (mem_count == 0)
+    return nunroll;
+
+  switch (loop_depth(loop))
+    {
+    case 1:
+      return MIN (nunroll, 28 / mem_count);
+    case 2:
+      return MIN (nunroll, 22 / mem_count);
+    default:
+      return MIN (nunroll, 16 / mem_count);
+    }
+}
 
 /* Initialize GCC target structure.  */
 
@@ -9218,14 +11486,15 @@ s390_reorg (void)
 #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_OPTION_OVERRIDE
+#define TARGET_OPTION_OVERRIDE s390_option_override
 
 #undef TARGET_ENCODE_SECTION_INFO
 #define TARGET_ENCODE_SECTION_INFO s390_encode_section_info
 
+#undef TARGET_SCALAR_MODE_SUPPORTED_P
+#define TARGET_SCALAR_MODE_SUPPORTED_P s390_scalar_mode_supported_p
+
 #ifdef HAVE_AS_TLS
 #undef TARGET_HAVE_TLS
 #define TARGET_HAVE_TLS true
@@ -9236,6 +11505,9 @@ s390_reorg (void)
 #undef TARGET_DELEGITIMIZE_ADDRESS
 #define TARGET_DELEGITIMIZE_ADDRESS s390_delegitimize_address
 
+#undef TARGET_LEGITIMIZE_ADDRESS
+#define TARGET_LEGITIMIZE_ADDRESS s390_legitimize_address
+
 #undef TARGET_RETURN_IN_MEMORY
 #define TARGET_RETURN_IN_MEMORY s390_return_in_memory
 
@@ -9244,10 +11516,13 @@ s390_reorg (void)
 #undef  TARGET_EXPAND_BUILTIN
 #define TARGET_EXPAND_BUILTIN s390_expand_builtin
 
+#undef TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA
+#define TARGET_ASM_OUTPUT_ADDR_CONST_EXTRA s390_output_addr_const_extra
+
 #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
+#define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
 
 #undef  TARGET_SCHED_ADJUST_PRIORITY
 #define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority
@@ -9256,12 +11531,23 @@ s390_reorg (void)
 #undef TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD
 #define TARGET_SCHED_FIRST_CYCLE_MULTIPASS_DFA_LOOKAHEAD s390_first_cycle_multipass_dfa_lookahead
 
+#undef TARGET_SCHED_VARIABLE_ISSUE
+#define TARGET_SCHED_VARIABLE_ISSUE s390_sched_variable_issue
+#undef TARGET_SCHED_REORDER
+#define TARGET_SCHED_REORDER s390_sched_reorder
+#undef TARGET_SCHED_INIT
+#define TARGET_SCHED_INIT s390_sched_init
+
 #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_REGISTER_MOVE_COST
+#define TARGET_REGISTER_MOVE_COST s390_register_move_cost
+#undef TARGET_MEMORY_MOVE_COST
+#define TARGET_MEMORY_MOVE_COST s390_memory_move_cost
 
 #undef TARGET_MACHINE_DEPENDENT_REORG
 #define TARGET_MACHINE_DEPENDENT_REORG s390_reorg
@@ -9271,18 +11557,26 @@ s390_reorg (void)
 
 #undef TARGET_BUILD_BUILTIN_VA_LIST
 #define TARGET_BUILD_BUILTIN_VA_LIST s390_build_builtin_va_list
+#undef TARGET_EXPAND_BUILTIN_VA_START
+#define TARGET_EXPAND_BUILTIN_VA_START s390_va_start
 #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_PROMOTE_FUNCTION_MODE
+#define TARGET_PROMOTE_FUNCTION_MODE s390_promote_function_mode
 #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_FUNCTION_ARG
+#define TARGET_FUNCTION_ARG s390_function_arg
+#undef TARGET_FUNCTION_ARG_ADVANCE
+#define TARGET_FUNCTION_ARG_ADVANCE s390_function_arg_advance
+#undef TARGET_FUNCTION_VALUE
+#define TARGET_FUNCTION_VALUE s390_function_value
+#undef TARGET_LIBCALL_VALUE
+#define TARGET_LIBCALL_VALUE s390_libcall_value
 
 #undef TARGET_FIXED_CONDITION_CODE_REGS
 #define TARGET_FIXED_CONDITION_CODE_REGS s390_fixed_condition_code_regs
@@ -9291,7 +11585,7 @@ s390_reorg (void)
 #define TARGET_CC_MODES_COMPATIBLE s390_cc_modes_compatible
 
 #undef TARGET_INVALID_WITHIN_DOLOOP
-#define TARGET_INVALID_WITHIN_DOLOOP hook_constcharptr_rtx_null
+#define TARGET_INVALID_WITHIN_DOLOOP hook_constcharptr_const_rtx_null
 
 #ifdef HAVE_AS_TLS
 #undef TARGET_ASM_OUTPUT_DWARF_DTPREL
@@ -9299,13 +11593,54 @@ s390_reorg (void)
 #endif
 
 #ifdef TARGET_ALTERNATE_LONG_DOUBLE_MANGLING
-#undef TARGET_MANGLE_FUNDAMENTAL_TYPE
-#define TARGET_MANGLE_FUNDAMENTAL_TYPE s390_mangle_fundamental_type
+#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_PREFERRED_RELOAD_CLASS
+#define TARGET_PREFERRED_RELOAD_CLASS s390_preferred_reload_class
+
+#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
+
+#undef TARGET_LEGITIMATE_ADDRESS_P
+#define TARGET_LEGITIMATE_ADDRESS_P s390_legitimate_address_p
+
+#undef TARGET_LEGITIMATE_CONSTANT_P
+#define TARGET_LEGITIMATE_CONSTANT_P s390_legitimate_constant_p
+
+#undef TARGET_LRA_P
+#define TARGET_LRA_P s390_lra_p
+
+#undef TARGET_CAN_ELIMINATE
+#define TARGET_CAN_ELIMINATE s390_can_eliminate
+
+#undef TARGET_CONDITIONAL_REGISTER_USAGE
+#define TARGET_CONDITIONAL_REGISTER_USAGE s390_conditional_register_usage
+
+#undef TARGET_LOOP_UNROLL_ADJUST
+#define TARGET_LOOP_UNROLL_ADJUST s390_loop_unroll_adjust
+
+#undef TARGET_ASM_TRAMPOLINE_TEMPLATE
+#define TARGET_ASM_TRAMPOLINE_TEMPLATE s390_asm_trampoline_template
+#undef TARGET_TRAMPOLINE_INIT
+#define TARGET_TRAMPOLINE_INIT s390_trampoline_init
+
+#undef TARGET_UNWIND_WORD_MODE
+#define TARGET_UNWIND_WORD_MODE s390_unwind_word_mode
+
+#undef TARGET_CANONICALIZE_COMPARISON
+#define TARGET_CANONICALIZE_COMPARISON s390_canonicalize_comparison
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-s390.h"