s390.c: Replace F*_REGNUM with FPR*_REGNUM.
[gcc.git] / gcc / config / s390 / s390.c
index 7ff8cb870cd79119fe4bce741b7b9a9bc2d0ff1c..7ce0c30c8838e06e404d01e8dd67d9bdb18aa946 100644 (file)
@@ -1,6 +1,5 @@
 /* Subroutines used for code generation on IBM S/390 and zSeries
-   Copyright (C) 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006,
-   2007, 2008, 2009, 2010 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) and
                   Andreas Krebbel (Andreas.Krebbel@de.ibm.com).
@@ -40,9 +39,8 @@ along with GCC; see the file COPYING3.  If not see
 #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"
@@ -53,7 +51,7 @@ along with GCC; see the file COPYING3.  If not see
 #include "df.h"
 #include "params.h"
 #include "cfgloop.h"
-
+#include "opts.h"
 
 /* Define the specific costs for a given cpu.  */
 
@@ -222,6 +220,70 @@ struct processor_costs z10_cost =
   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 */
+};
+
 extern int reload_completed;
 
 /* Kept up to date using the SCHED_VARIABLE_ISSUE hook.  */
@@ -245,17 +307,6 @@ struct s390_address
   bool literal_pool;
 };
 
-/* Which cpu are we tuning for.  */
-enum processor_type s390_tune = PROCESSOR_max;
-int s390_tune_flags;
-/* Which instruction set architecture to use.  */
-enum processor_type s390_arch;
-int 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
    specific part of struct function.  */
 
@@ -282,9 +333,9 @@ struct GTY (()) s390_frame_layout
   /* 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.  */
@@ -316,6 +367,10 @@ struct GTY(()) machine_function
   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.  */
@@ -324,10 +379,10 @@ struct GTY(()) machine_function
 #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_LONG)
-#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))))
+#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
@@ -349,8 +404,34 @@ struct GTY(()) machine_function
   (HARD_REGNO_NREGS ((REGNO), (MODE)) == 1 || !((REGNO) & 1))
 
 /* That's the read ahead of the dynamic branch prediction unit in
-   bytes on a z10 CPU.  */
-#define Z10_PREDICT_DISTANCE 384
+   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)
@@ -613,7 +694,10 @@ s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
             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;
@@ -664,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
@@ -740,9 +828,9 @@ s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1)
       *op1 = constm1_rtx;
     }
 
-  /* Remove redundant UNSPEC_CCU_TO_INT conversions if possible.  */
+  /* Remove redundant UNSPEC_STRCMPCC_TO_INT conversions if possible.  */
   if (GET_CODE (*op0) == UNSPEC
-      && XINT (*op0, 1) == UNSPEC_CCU_TO_INT
+      && 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
@@ -768,25 +856,35 @@ s390_canonicalize_comparison (enum rtx_code *code, rtx *op0, rtx *op1)
        }
     }
 
-  /* Remove redundant UNSPEC_CCZ_TO_INT conversions if possible.  */
+  /* Remove redundant UNSPEC_CC_TO_INT conversions if possible.  */
   if (GET_CODE (*op0) == UNSPEC
-      && XINT (*op0, 1) == UNSPEC_CCZ_TO_INT
+      && XINT (*op0, 1) == UNSPEC_CC_TO_INT
       && XVECLEN (*op0, 0) == 1
-      && GET_MODE (XVECEXP (*op0, 0, 0)) == CCZmode
       && GET_CODE (XVECEXP (*op0, 0, 0)) == REG
       && REGNO (XVECEXP (*op0, 0, 0)) == CC_REGNUM
-      && *op1 == const0_rtx)
+      && CONST_INT_P (*op1))
     {
       enum rtx_code new_code = UNKNOWN;
-      switch (*code)
+      switch (GET_MODE (XVECEXP (*op0, 0, 0)))
        {
-         case EQ: new_code = EQ;  break;
-         case NE: new_code = NE;  break;
-         default: break;
+       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;
        }
@@ -813,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);
     }
 }
 
@@ -850,16 +948,19 @@ s390_emit_compare (enum rtx_code code, rtx op0, rtx op1)
    conditional branch testing the result.  */
 
 static rtx
-s390_emit_compare_and_swap (enum rtx_code code, rtx old, rtx mem, rtx cmp, rtx new_rtx)
+s390_emit_compare_and_swap (enum rtx_code code, rtx old, rtx mem,
+                           rtx cmp, rtx new_rtx)
 {
-  emit_insn (gen_sync_compare_and_swapsi (old, mem, cmp, new_rtx));
-  return s390_emit_compare (code, gen_rtx_REG (CCZ1mode, CC_REGNUM), const0_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.  If COND is NULL_RTX, emit an
-   unconditional jump, else a conditional jump under condition COND.  */
+/* 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.  */
 
-void
+rtx
 s390_emit_jump (rtx target, rtx cond)
 {
   rtx insn;
@@ -869,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
@@ -885,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)))
     {
@@ -1059,6 +1163,17 @@ 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;
     }
@@ -1118,7 +1233,9 @@ s390_branch_condition_mnemonic (rtx code, int inv)
 
   if (GET_CODE (XEXP (code, 0)) == REG
       && REGNO (XEXP (code, 0)) == CC_REGNUM
-      && XEXP (code, 1) == const0_rtx)
+      && (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);
@@ -1260,6 +1377,24 @@ s390_contiguous_bitmask_p (unsigned HOST_WIDE_INT in, int size,
   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.  */
@@ -1456,106 +1591,8 @@ s390_init_machine_status (void)
   return ggc_alloc_cleared_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,
-                        int *flags)
-{
-  static struct pta
-    {
-      const char *const name;          /* processor name or nickname.  */
-      const enum processor_type processor;
-      const int flags;                 /* From enum processor_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},
-      {"z9-ec", PROCESSOR_2094_Z9_109, PF_IEEE_FLOAT | PF_ZARCH
-                             | PF_LONG_DISPLACEMENT | PF_EXTIMM | PF_DFP },
-      {"z10", PROCESSOR_2097_Z10, PF_IEEE_FLOAT | PF_ZARCH
-                             | PF_LONG_DISPLACEMENT | PF_EXTIMM | PF_DFP | PF_Z10},
-    };
-  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;
-    }
-}
-
-void
-override_options (void)
+static void
+s390_option_override (void)
 {
   /* Set up function hooks.  */
   init_machine_status = s390_init_machine_status;
@@ -1569,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.  */
@@ -1589,15 +1628,25 @@ override_options (void)
   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"
+           error ("hardware decimal floating point instructions"
                   " not available on %s", s390_arch_string);
          if (!TARGET_ZARCH)
-           error ("Hardware decimal floating point instructions"
+           error ("hardware decimal floating point instructions"
                   " not available in ESA/390 mode");
        }
       else
@@ -1607,7 +1656,7 @@ override_options (void)
   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");
+       error ("-mhard-dfp can%'t be used in conjunction with -msoft-float");
 
       target_flags &= ~MASK_HARD_DFP;
     }
@@ -1624,6 +1673,12 @@ override_options (void)
     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;
     }
@@ -1647,37 +1702,67 @@ override_options (void)
     target_flags |= MASK_LONG_DOUBLE_128;
 #endif
 
-  if (s390_tune == PROCESSOR_2097_Z10)
-    {
-      if (!PARAM_SET_P (PARAM_MAX_UNROLLED_INSNS))
-       set_param_value ("max-unrolled-insns", 100);
-      if (!PARAM_SET_P (PARAM_MAX_UNROLL_TIMES))
-       set_param_value ("max-unroll-times", 32);
-      if (!PARAM_SET_P (PARAM_MAX_COMPLETELY_PEELED_INSNS))
-       set_param_value ("max-completely-peeled-insns", 2000);
-      if (!PARAM_SET_P (PARAM_MAX_COMPLETELY_PEEL_TIMES))
-       set_param_value ("max-completely-peel-times", 64);
-    }
-
-  set_param_value ("max-pending-list-length", 256);
+  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 */
-  set_param_value ("l1-cache-line-size", 256);
-  if (!PARAM_SET_P (PARAM_L1_CACHE_SIZE))
-    set_param_value ("l1-cache-size", 128);
+  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.  */
-  if (!PARAM_SET_P (PARAM_L2_CACHE_SIZE))
-    set_param_value ("l2-cache-size", 1500);
-  if (!PARAM_SET_P (PARAM_PREFETCH_MIN_INSN_TO_MEM_RATIO))
-    set_param_value ("prefetch-min-insn-to-mem-ratio", 2);
-  if (!PARAM_SET_P (PARAM_SIMULTANEOUS_PREFETCHES))
-    set_param_value ("simultaneous-prefetches", 6);
-
-  /* This cannot reside in optimization_options since HAVE_prefetch
-     requires the arch flags to be evaluated already.  */
-  if (HAVE_prefetch && optimize >= 3)
+  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.  */
@@ -1968,14 +2053,18 @@ s390_decompose_address (rtx addr, struct s390_address *out)
         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;
     }
@@ -2003,6 +2092,16 @@ s390_decompose_address (rtx addr, struct s390_address *out)
       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)
            {
@@ -2012,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);
            }
         }
 
@@ -2088,12 +2187,17 @@ s390_legitimate_address_without_index_p (rtx op)
 }
 
 
-/* Return true if ADDR is of kind symbol_ref or symbol_ref + const_int
-   and return these parts in SYMREF and ADDEND.  You can pass NULL in
-   SYMREF and/or ADDEND if you are not interested in these values.  */
+/* 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.  */
 
 static bool
-s390_symref_operand_p (rtx addr, rtx *symref, HOST_WIDE_INT *addend)
+s390_loadrelative_operand_p (rtx addr, rtx *symref, HOST_WIDE_INT *addend)
 {
   HOST_WIDE_INT tmpaddend = 0;
 
@@ -2102,28 +2206,28 @@ s390_symref_operand_p (rtx addr, rtx *symref, HOST_WIDE_INT *addend)
 
   if (GET_CODE (addr) == PLUS)
     {
-      if (GET_CODE (XEXP (addr, 0)) == SYMBOL_REF
-         && CONST_INT_P (XEXP (addr, 1)))
-       {
-         tmpaddend = INTVAL (XEXP (addr, 1));
-         addr = XEXP (addr, 0);
-       }
-      else
+      if (!CONST_INT_P (XEXP (addr, 1)))
        return false;
+
+      tmpaddend = INTVAL (XEXP (addr, 1));
+      addr = XEXP (addr, 0);
     }
-  else
-    if (GET_CODE (addr) != SYMBOL_REF)
-       return false;
 
-  if (symref)
-    *symref = addr;
-  if (addend)
-    *addend = tmpaddend;
+  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;
 
-  return true;
+      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
@@ -2137,13 +2241,15 @@ s390_check_qrst_address (char c, rtx op, bool lit_pool_ok)
 
   /* This check makes sure that no symbolic address (except literal
      pool references) are accepted by the R or T constraints.  */
-  if (s390_symref_operand_p (op, NULL, NULL))
+  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 (!lit_pool_ok)
-       return 0;
       if (!s390_decompose_address (op, &addr))
        return 0;
-      if (!addr.literal_pool)
+      if (addr.literal_pool)
        return 0;
       decomposed = true;
     }
@@ -2360,6 +2466,33 @@ 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
@@ -2368,8 +2501,8 @@ s390_float_const_zero_p (rtx value)
    of the superexpression of x.  */
 
 static bool
-s390_rtx_costs (rtx x, int code, int outer_code, int *total,
-               bool speed ATTRIBUTE_UNUSED)
+s390_rtx_costs (rtx x, int code, int outer_code, int opno ATTRIBUTE_UNUSED,
+               int *total, bool speed ATTRIBUTE_UNUSED)
 {
   switch (code)
     {
@@ -2397,21 +2530,6 @@ 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_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, speed)
-                    + rtx_cost (XEXP (XEXP (x, 0), 1), MULT, speed)
-                    + rtx_cost (XEXP (x, 1), (enum rtx_code) code, speed));
-         return true;  /* Do not do an additional recursive descent.  */
-       }
       *total = COSTS_N_INSNS (1);
       return false;
 
@@ -2474,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 */
@@ -2563,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, bool speed ATTRIBUTE_UNUSED)
+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))
@@ -2680,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
@@ -2713,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))
     {
@@ -2735,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))
@@ -2776,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)
@@ -2809,6 +2953,12 @@ legitimate_reload_constant_p (rtx op)
       && larl_operand (op, VOIDmode))
     return true;
 
+  /* Accept floating-point zero operands that fit into a single GPR.  */
+  if (GET_CODE (op) == CONST_DOUBLE
+      && 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.  */
   if (GET_CODE (op) == CONST_INT
       && trunc_int_for_mode (INTVAL (op), word_mode) != INTVAL (op))
@@ -2824,34 +2974,76 @@ legitimate_reload_constant_p (rtx op)
   return false;
 }
 
+/* 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 rclass)
+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 rclass;
-       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 (!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;
+         return ADDR_REGS;
        else
          return NO_REGS;
 
@@ -2872,11 +3064,21 @@ s390_check_symref_alignment (rtx addr, HOST_WIDE_INT alignment)
   HOST_WIDE_INT addend;
   rtx symref;
 
-  if (!s390_symref_operand_p (addr, &symref, &addend))
+  if (!s390_loadrelative_operand_p (addr, &symref, &addend))
+    return false;
+
+  if (addend & (alignment - 1))
     return false;
 
-  return (!SYMBOL_REF_NOT_NATURALLY_ALIGNED_P (symref)
-         && !(addend & (alignment - 1)));
+  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;
 }
 
 /* ADDR is moved into REG using larl.  If ADDR isn't a valid larl
@@ -2889,7 +3091,7 @@ s390_reload_larl_operand (rtx reg, rtx addr, rtx scratch)
   HOST_WIDE_INT addend;
   rtx symref;
 
-  if (!s390_symref_operand_p (addr, &symref, &addend))
+  if (!s390_loadrelative_operand_p (addr, &symref, &addend))
     gcc_unreachable ();
 
   if (!(addend & 1))
@@ -2915,7 +3117,7 @@ s390_reload_larl_operand (rtx reg, rtx addr, rtx scratch)
        emit_move_insn (scratch, symref);
 
       /* Increment the address using la in order to avoid clobbering cc.  */
-      emit_move_insn (reg, gen_rtx_PLUS (Pmode, scratch, const1_rtx));
+      s390_load_address (reg, gen_rtx_PLUS (Pmode, scratch, const1_rtx));
     }
 }
 
@@ -2969,12 +3171,16 @@ s390_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
 
   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_symref_operand_p (x, NULL, NULL)
+         && s390_loadrelative_operand_p (x, &symref, &offset)
          && mode == Pmode
-         && !s390_check_symref_alignment (x, 2))
+         && !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);
 
@@ -2983,7 +3189,7 @@ s390_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
         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_symref_operand_p (XEXP (x, 0), NULL, NULL)
+         && 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)
@@ -3023,7 +3229,9 @@ s390_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
 
   /* We need a scratch register when loading a PLUS expression which
      is not a legitimate operand of the LOAD ADDRESS instruction.  */
-  if (in_p && s390_plus_operand (x, mode))
+  /* 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);
 
@@ -3203,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;
 
@@ -3251,52 +3466,125 @@ rtx
 legitimize_pic_address (rtx orig, rtx reg)
 {
   rtx addr = orig;
+  rtx addend = const0_rtx;
   rtx new_rtx = orig;
-  rtx base;
 
   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)
            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_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
-          if (reg != 0)
-            {
-              s390_load_address (reg, new_rtx);
-              new_rtx = 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)
            df_set_regs_ever_live (PIC_OFFSET_TABLE_REGNUM, true);
@@ -3311,7 +3599,9 @@ legitimize_pic_address (rtx orig, 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 = reg ? reg : gen_reg_rtx (Pmode);
 
@@ -3320,16 +3610,21 @@ legitimize_pic_address (rtx orig, rtx 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);
+         emit_move_insn (temp, new_rtx);
 
-          new_rtx = gen_const_mem (Pmode, temp);
+         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);
 
@@ -3350,175 +3645,94 @@ legitimize_pic_address (rtx orig, rtx reg)
           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_rtx = force_const_mem (Pmode, orig);
-                    break;
-
-                  /* @GOT is OK as is if small.  */
-                 case UNSPEC_GOT:
-                   if (flag_pic == 2)
-                     new_rtx = 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)
-                         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);
-                        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;
-                          }
-                      }
-                    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)
-                 && INTVAL (op1) < (HOST_WIDE_INT)1 << 31
-                 && INTVAL (op1) >= -((HOST_WIDE_INT)1 << 31))
-                {
-                  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)))
-                        {
-                          HOST_WIDE_INT even = INTVAL (op1) - 1;
-                          op0 = gen_rtx_PLUS (Pmode, op0, GEN_INT (even));
-                         op0 = gen_rtx_CONST (Pmode, op0);
-                          op1 = const1_rtx;
-                        }
-
-                      emit_move_insn (temp, op0);
-                      new_rtx = gen_rtx_PLUS (Pmode, temp, op1);
-
-                      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.  */
-
-                  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 = 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_rtx = gen_rtx_PLUS (Pmode, pic_offset_table_rtx, temp);
-                  if (reg != 0)
-                    {
-                      s390_load_address (reg, new_rtx);
-                      new_rtx = 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_rtx = 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_rtx  = legitimize_pic_address (XEXP (addr, 1),
-                                            base == reg ? NULL_RTX : reg);
-             if (GET_CODE (new_rtx) == CONST_INT)
-               new_rtx = plus_constant (base, INTVAL (new_rtx));
-             else
-               {
-                 if (GET_CODE (new_rtx) == PLUS && CONSTANT_P (XEXP (new_rtx, 1)))
-                   {
-                     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);
+             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_rtx;
 }
 
@@ -3547,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");
@@ -3744,7 +3959,8 @@ legitimize_tls_address (rtx addr, rtx reg)
        new_rtx = gen_rtx_CONST (Pmode, new_rtx);
 
       new_rtx = legitimize_tls_address (new_rtx, reg);
-      new_rtx = plus_constant (new_rtx, INTVAL (XEXP (XEXP (addr, 0), 1)));
+      new_rtx = plus_constant (Pmode, new_rtx,
+                              INTVAL (XEXP (XEXP (addr, 0), 1)));
       new_rtx = force_operand (new_rtx, 0);
     }
 
@@ -3871,7 +4087,7 @@ s390_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.  */
@@ -3918,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)
@@ -4012,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.
@@ -4042,7 +4266,7 @@ 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,
                                           GEN_INT (INTVAL (len) - 2)));
@@ -4085,7 +4309,7 @@ s390_expand_setmem (rtx dst, rtx len, rtx val)
       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);
@@ -4150,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;
@@ -4263,6 +4494,7 @@ s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
 
       emit_insn (gen_cmpint (target, ccreg));
     }
+  return true;
 }
 
 
@@ -4350,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.  */
@@ -4422,7 +4654,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 SLB instruction pattern.  */
@@ -4454,106 +4686,153 @@ 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;
 
-  /* On z10 we can use the risbg instruction to implement insv.  */
-  if (TARGET_Z10
-      && ((GET_MODE (dest) == DImode && GET_MODE (src) == DImode)
-         || (GET_MODE (dest) == SImode && GET_MODE (src) == SImode)))
+  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))
     {
-      rtx op;
-      rtx clobber;
+      HOST_WIDE_INT val = INTVAL (src);
+      int regpos = bitpos + bitsize;
 
-      op = gen_rtx_SET (GET_MODE(src),
-                       gen_rtx_ZERO_EXTRACT (GET_MODE (dest), dest, op1, op2),
-                       src);
-      clobber = gen_rtx_CLOBBER (VOIDmode, gen_rtx_REG (CCmode, CC_REGNUM));
-      emit_insn (gen_rtx_PARALLEL (VOIDmode, gen_rtvec (2, op, clobber)));
+      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;
     }
 
-  /* We need byte alignment.  */
-  if (bitsize % BITS_PER_UNIT)
-    return false;
+  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),
                              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;
     }
 
@@ -4623,90 +4902,137 @@ 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);
+                                     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_RTX 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_rtx)
+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_rtx)))
-    new_rtx = s390_expand_mask_and_shift (new_rtx, 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);
+  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"
    */
 
-  /* 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_rtx))
     {
-      newv = force_reg (SImode, val);
-      store_bit_field (newv, GET_MODE_BITSIZE (mode), 0, SImode, new_rtx);
-    }
-  else
-    newv = force_reg (SImode, expand_simple_binop (SImode, IOR, new_rtx, val,
-                                                  NULL_RTX, 1, OPTAB_DIRECT));
+      rtx tmp;
 
-  /* Jump to end if we're done (likely?).  */
-  s390_emit_jump (csend, s390_emit_compare_and_swap (EQ, res, ac.memsi,
-                                                    cmpv, newv));
+      /* 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);
 
-  emit_label (csend);
+      /* Failed.  */
+      emit_move_insn (btarget, const0_rtx);
+      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
@@ -4761,7 +5087,8 @@ s390_expand_atomic (enum machine_mode mode, enum rtx_code code,
       /* FALLTHRU */
     case SET:
       if (ac.aligned && MEM_P (val))
-       store_bit_field (new_rtx, GET_MODE_BITSIZE (mode), 0, SImode, val);
+       store_bit_field (new_rtx, GET_MODE_BITSIZE (mode), 0,
+                        0, 0, SImode, val);
       else
        {
          new_rtx = expand_simple_binop (SImode, AND, new_rtx, ac.modemaski,
@@ -4845,6 +5172,39 @@ s390_delegitimize_address (rtx orig_x)
 
   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;
 
@@ -4857,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.
@@ -4947,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)
@@ -5018,9 +5394,14 @@ print_operand_address (FILE *file, rtx addr)
 {
   struct s390_address ad;
 
-  if (s390_symref_operand_p (addr, NULL, NULL))
+  if (s390_loadrelative_operand_p (addr, NULL, NULL))
     {
-      gcc_assert (TARGET_Z10);
+      if (!TARGET_Z10)
+       {
+         output_operand_lossage ("symbolic memory references are "
+                                 "only supported on z10 or later");
+         return;
+       }
       output_addr_const (file, addr);
       return;
     }
@@ -5049,28 +5430,35 @@ print_operand_address (FILE *file, rtx addr)
     'C': print opcode suffix for branch condition.
     'D': print opcode suffix for inverse branch condition.
     'E': print opcode suffix for branch on index instruction.
-    'J': print tls_load/tls_gdcall/tls_ldcall suffix
     '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.
     'c': print integer X as if it's an signed byte.
-    'x': print integer X as if it's an unsigned halfword.
+    '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':
@@ -5087,7 +5475,8 @@ print_operand (FILE *file, rtx x, int code)
       else if (GET_CODE (x) == GT)
        fprintf (file, "h");
       else
-       gcc_unreachable ();
+       output_operand_lossage ("invalid comparison operator "
+                               "for 'E' output modifier");
       return;
 
     case 'J':
@@ -5107,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':
@@ -5119,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);
@@ -5137,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)]);
@@ -5155,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);
@@ -5175,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':
@@ -5212,30 +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 == 'c')
-        fprintf (file, HOST_WIDE_INT_PRINT_DEC, ((INTVAL (x) & 0xff) ^ 0x80) - 0x80);
-      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:
@@ -5245,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;
     }
 }
@@ -5320,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)
     {
@@ -5383,8 +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)
 {
@@ -5393,7 +5856,9 @@ s390_adjust_priority (rtx insn ATTRIBUTE_UNUSED, int priority)
 
   if (s390_tune != PROCESSOR_2084_Z990
       && s390_tune != PROCESSOR_2094_Z9_109
-      && s390_tune != PROCESSOR_2097_Z10)
+      && s390_tune != PROCESSOR_2097_Z10
+      && s390_tune != PROCESSOR_2817_Z196
+      && s390_tune != PROCESSOR_2827_ZEC12)
     return priority;
 
   switch (s390_safe_attr_type (insn))
@@ -5422,8 +5887,10 @@ s390_issue_rate (void)
     {
     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;
@@ -5476,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;
        }
     }
@@ -5509,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;
        }
     }
@@ -5558,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);
@@ -5706,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;
     }
 
@@ -5978,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)
@@ -6183,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)
@@ -6196,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);
@@ -6313,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);
 
@@ -6337,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);
@@ -6419,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);
@@ -6446,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);
@@ -6454,8 +6922,18 @@ s390_chunkify_start (void)
          gcc_assert (!pending_ltrel);
        }
 
-      if (NOTE_P (insn) && NOTE_KIND (insn) == NOTE_INSN_SWITCH_TEXT_SECTIONS)
-       section_switch_p = true;
+      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)
@@ -6480,7 +6958,7 @@ 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
@@ -6489,7 +6967,7 @@ s390_chunkify_start (void)
            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;
@@ -6501,19 +6979,29 @@ s390_chunkify_start (void)
                   || curr_pool->size > S390_POOL_CHUNK_MAX
                   || section_switch_p)
            {
-              rtx label, jump, barrier;
+             rtx label, jump, barrier, next, prev;
 
              if (!section_switch_p)
                {
                  /* We can insert the barrier only after a 'real' insn.  */
-                 if (GET_CODE (insn) != INSN && GET_CODE (insn) != CALL_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.  */
+                    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
                {
@@ -6526,7 +7014,14 @@ s390_chunkify_start (void)
                }
 
              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;
@@ -6561,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);
 
@@ -6591,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));
                }
            }
         }
@@ -6634,7 +7114,7 @@ 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);
@@ -6680,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);
@@ -6733,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
@@ -6755,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)
@@ -6837,7 +7317,7 @@ s390_return_addr_rtx (int count, rtx frame ATTRIBUTE_UNUSED)
   else
     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);
 }
@@ -6853,7 +7333,7 @@ s390_back_chain_rtx (void)
   gcc_assert (TARGET_BACKCHAIN);
 
   if (TARGET_PACKED_STACK)
-    chain = plus_constant (stack_pointer_rtx,
+    chain = plus_constant (Pmode, stack_pointer_rtx,
                           STACK_POINTER_OFFSET - UNITS_PER_LONG);
   else
     chain = stack_pointer_rtx;
@@ -6890,11 +7370,11 @@ s390_reg_clobbered_rtx (rtx setreg, const_rtx set_insn ATTRIBUTE_UNUSED, void *d
   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;
@@ -6917,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];
     }
 
@@ -6945,7 +7425,7 @@ s390_regs_ever_clobbered (int *regs_ever_clobbered)
      See expand_builtin_unwind_init.  For regs_ever_live this is done by
      reload.  */
   if (cfun->has_nonlocal_label)
-    for (i = 0; i < 16; i++)
+    for (i = 0; i < 32; i++)
       if (!call_really_used_regs[i])
        regs_ever_clobbered[i] = 1;
 
@@ -6971,7 +7451,6 @@ static void
 s390_frame_area (int *area_bottom, int *area_top)
 {
   int b, t;
-  int i;
 
   b = INT_MAX;
   t = INT_MIN;
@@ -6992,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 + (i - 2) * 8);
-         t = MAX (t, cfun_frame_layout.f4_offset + (i - 1) * 8);
+         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 + 8);
+         t = MAX (t, cfun_frame_layout.f4_offset + 16);
+       }
+    }
   *area_bottom = b;
   *area_top = t;
 }
@@ -7011,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 (df_regs_ever_live_p (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.
 
@@ -7030,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];
 
@@ -7045,7 +7541,7 @@ s390_register_info (int clobbered_regs[])
         && 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
@@ -7053,7 +7549,7 @@ s390_register_info (int clobbered_regs[])
        || 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
@@ -7152,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 (df_regs_ever_live_p (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.  */
@@ -7195,11 +7694,13 @@ s390_frame_info (void)
        {
          cfun_frame_layout.f4_offset
            = (cfun_frame_layout.gprs_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)));
 
          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 (FPR0_REGNUM)
+                     + cfun_fpr_save_p (FPR2_REGNUM)));
        }
       else
        {
@@ -7208,28 +7709,32 @@ s390_frame_info (void)
          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)));
+              - 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
        = (STACK_POINTER_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)));
 
       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 (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
@@ -7255,8 +7760,8 @@ 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;
@@ -7280,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
@@ -7304,7 +7809,7 @@ s390_init_frame_layout (void)
         as base register to avoid save/restore overhead.  */
       if (!base_used)
        cfun->machine->base_reg = NULL_RTX;
-      else if (current_function_is_leaf && !df_regs_ever_live_p (5))
+      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);
@@ -7315,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
@@ -7323,7 +7979,7 @@ 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);
 
@@ -7404,7 +8060,7 @@ 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 RCLASS.  */
 
-bool
+int
 s390_class_max_nregs (enum reg_class rclass, enum machine_mode mode)
 {
   switch (rclass)
@@ -7422,6 +8078,13 @@ s390_class_max_nregs (enum reg_class rclass, 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.  */
 
 static bool
@@ -7515,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 ());
@@ -7532,7 +8195,7 @@ 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);
@@ -7563,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 ());
@@ -7630,7 +8293,8 @@ save_gprs (rtx base, int offset, int first, int last)
       if (start > last)
        return insn;
 
-      addr = plus_constant (base, offset + (start - first) * UNITS_PER_LONG);
+      addr = plus_constant (Pmode, base,
+                           offset + (start - first) * UNITS_PER_LONG);
       note = gen_store_multiple (gen_rtx_MEM (Pmode, addr),
                                 gen_rtx_REG (Pmode, start),
                                 GEN_INT (last - start + 1));
@@ -7659,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 ());
 
@@ -7688,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_");
@@ -7698,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
     {
@@ -7709,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 ();
@@ -7746,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
@@ -7768,7 +8440,7 @@ s390_emit_prologue (void)
      See below for why TPF must use the register 1.  */
 
   if (!has_hard_reg_initial_val (Pmode, RETURN_REGNUM)
-      && !current_function_is_leaf
+      && !crtl->is_leaf
       && !TARGET_TPF_PROFILING)
     temp_reg = gen_rtx_REG (Pmode, RETURN_REGNUM);
   else
@@ -7794,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)
@@ -7807,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)
@@ -7830,20 +8502,23 @@ 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.  */
 
@@ -7869,10 +8544,9 @@ s390_emit_prologue (void)
 
          if (cfun_frame_layout.frame_size >= s390_stack_size)
            {
-             warning (0, "frame size of function %qs is "
-                      HOST_WIDE_INT_PRINT_DEC
+             warning (0, "frame size of function %qs is %wd"
                       " bytes exceeding user provided stack limit of "
-                      HOST_WIDE_INT_PRINT_DEC " bytes.  "
+                      "%d bytes.  "
                       "An unconditional trap is added.",
                       current_function_name(), cfun_frame_layout.frame_size,
                       s390_stack_size);
@@ -7885,8 +8559,7 @@ s390_emit_prologue (void)
                 not match the test under mask pattern.  */
              if (stack_guard >= s390_stack_size)
                {
-                 warning (0, "frame size of function %qs is "
-                          HOST_WIDE_INT_PRINT_DEC
+                 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.",
@@ -7914,7 +8587,7 @@ s390_emit_prologue (void)
 
       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)
@@ -7955,7 +8628,7 @@ s390_emit_prologue (void)
        {
          if (cfun_frame_layout.backchain_offset)
            addr = gen_rtx_MEM (Pmode,
-                               plus_constant (stack_pointer_rtx,
+                               plus_constant (Pmode, stack_pointer_rtx,
                                  cfun_frame_layout.backchain_offset));
          else
            addr = gen_rtx_MEM (Pmode, stack_pointer_rtx);
@@ -7987,10 +8660,10 @@ s390_emit_prologue (void)
 
       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);
@@ -8117,9 +8790,9 @@ 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);
@@ -8135,9 +8808,10 @@ s390_emit_epilogue (bool sibcall)
   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);
@@ -8172,7 +8846,7 @@ s390_emit_epilogue (bool sibcall)
        {
          if (global_not_special_regno_p (i))
            {
-             addr = plus_constant (frame_pointer,
+             addr = plus_constant (Pmode, frame_pointer,
                                    offset + cfun_frame_layout.gprs_offset
                                    + (i - cfun_frame_layout.first_save_gpr_slot)
                                    * UNITS_PER_LONG);
@@ -8200,7 +8874,7 @@ 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
                                       - cfun_frame_layout.first_save_gpr_slot)
@@ -8221,7 +8895,8 @@ s390_emit_epilogue (bool sibcall)
       insn = emit_insn (insn);
       REG_NOTES (insn) = cfa_restores;
       add_reg_note (insn, REG_CFA_DEF_CFA,
-                   plus_constant (stack_pointer_rtx, STACK_POINTER_OFFSET));
+                   plus_constant (Pmode, stack_pointer_rtx,
+                                  STACK_POINTER_OFFSET));
       RTX_FRAME_RELATED_P (insn) = 1;
     }
 
@@ -8232,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));
     }
@@ -8261,7 +8936,7 @@ s390_function_arg_size (enum machine_mode mode, const_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)
@@ -8281,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;
@@ -8306,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)
@@ -8320,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;
@@ -8341,7 +9017,7 @@ s390_function_arg_integer (enum machine_mode mode, tree type)
    reference.  */
 
 static bool
-s390_pass_by_reference (CUMULATIVE_ARGS *ca ATTRIBUTE_UNUSED,
+s390_pass_by_reference (cumulative_args_t ca ATTRIBUTE_UNUSED,
                        enum machine_mode mode, const_tree type,
                        bool named ATTRIBUTE_UNUSED)
 {
@@ -8368,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;
@@ -8404,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)
@@ -8488,7 +9168,7 @@ s390_promote_function_mode (const_tree type, enum machine_mode mode,
   if (INTEGRAL_MODE_P (mode)
       && GET_MODE_SIZE (mode) < UNITS_PER_LONG)
     {
-      if (POINTER_TYPE_P (type))
+      if (type != NULL_TREE && POINTER_TYPE_P (type))
        *punsignedp = POINTERS_EXTEND_UNSIGNED;
       return Pmode;
     }
@@ -8496,17 +9176,23 @@ s390_promote_function_mode (const_tree type, enum machine_mode mode,
   return mode;
 }
 
-/* Define where to return a (scalar) value of type TYPE.
-   If TYPE is null, define where to return a (scalar)
+/* 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 (const_tree type, const_tree fn, 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_function_mode (type, TYPE_MODE (type), &unsignedp, fn, 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));
@@ -8519,6 +9205,10 @@ s390_function_value (const_tree type, const_tree fn, enum machine_mode mode)
     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)
@@ -8532,6 +9222,26 @@ s390_function_value (const_tree type, const_tree fn, enum machine_mode mode)
   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);
+}
+
 
 /* Create and return the va_list datatype.
 
@@ -8586,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);
 
@@ -8621,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);
@@ -8664,7 +9374,7 @@ s390_va_start (tree valist, rtx nextarg ATTRIBUTE_UNUSED)
        fprintf (stderr, "va_start: n_gpr = %d, n_fpr = %d off %d\n",
                 (int)n_gpr, (int)n_fpr, off);
 
-      t = build2 (POINTER_PLUS_EXPR, TREE_TYPE (ovf), t, size_int (off));
+      t = fold_build_pointer_plus_hwi (t, off);
 
       t = build2 (MODIFY_EXPR, TREE_TYPE (ovf), ovf, t);
       TREE_SIDE_EFFECTS (t) = 1;
@@ -8676,8 +9386,7 @@ 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 (POINTER_PLUS_EXPR, TREE_TYPE (sav), t,
-                 size_int (-RETURN_REGNUM * UNITS_PER_LONG));
+      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;
@@ -8719,9 +9428,9 @@ s390_gimplify_va_arg (tree valist, tree type, gimple_seq *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);
@@ -8809,11 +9518,10 @@ s390_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   t = build3 (COND_EXPR, void_type_node, t, u, NULL_TREE);
   gimplify_and_add (t, pre_p);
 
-  t = build2 (POINTER_PLUS_EXPR, ptr_type_node, sav,
-             size_int (sav_ofs));
+  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 (POINTER_PLUS_EXPR, ptr_type_node, t, fold_convert (sizetype, u));
+  t = fold_build_pointer_plus (t, u);
 
   gimplify_assign (addr, t, pre_p);
 
@@ -8826,15 +9534,13 @@ s390_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
 
   t = ovf;
   if (size < UNITS_PER_LONG)
-    t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t,
-               size_int (UNITS_PER_LONG - size));
+    t = fold_build_pointer_plus_hwi (t, UNITS_PER_LONG - size);
 
   gimplify_expr (&t, pre_p, NULL, is_gimple_val, fb_rvalue);
 
   gimplify_assign (addr, t, pre_p);
 
-  t = build2 (POINTER_PLUS_EXPR, ptr_type_node, t,
-             size_int (size));
+  t = fold_build_pointer_plus_hwi (t, size);
   gimplify_assign (ovf, t, pre_p);
 
   gimple_seq_add_stmt (pre_p, gimple_build_label (lab_over));
@@ -8862,41 +9568,179 @@ s390_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   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 enum insn_code const code_for_builtin_64[S390_BUILTIN_max] = {
-  CODE_FOR_get_tp_64,
-  CODE_FOR_set_tp_64
-};
-
-static enum insn_code 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);
-  add_builtin_function ("__builtin_thread_pointer", ftype,
-                       S390_BUILTIN_THREAD_POINTER, BUILT_IN_MD,
+  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);
-
-  ftype = build_function_type_list (void_type_node, ptr_type_node, NULL_TREE);
-  add_builtin_function ("__builtin_set_thread_pointer", ftype,
-                       S390_BUILTIN_SET_THREAD_POINTER, BUILT_IN_MD,
+  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,
+                                   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,
@@ -8912,9 +9756,6 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
 {
 #define MAX_ARGS 2
 
-  enum insn_code const *code_for_builtin =
-    TARGET_64BIT ? code_for_builtin_64 : code_for_builtin_31;
-
   tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
   unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
   enum insn_code icode;
@@ -8930,6 +9771,17 @@ 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;
 
   arity = 0;
@@ -8939,7 +9791,7 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
 
       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];
@@ -8947,7 +9799,20 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
       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++;
     }
 
@@ -8972,7 +9837,10 @@ 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]);
+      if (nonvoid)
+       pat = GEN_FCN (icode) (target, op[0], op[1]);
+      else
+       pat = GEN_FCN (icode) (op[0], op[1]);
       break;
     default:
       gcc_unreachable ();
@@ -9003,16 +9871,16 @@ s390_asm_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));
     }
 }
@@ -9028,11 +9896,11 @@ s390_trampoline_init (rtx m_tramp, tree fndecl, rtx cxt)
   rtx mem;
 
   emit_block_move (m_tramp, assemble_trampoline_template (),
-                  GEN_INT (2*UNITS_PER_WORD), BLOCK_OP_NORMAL);
+                  GEN_INT (2 * UNITS_PER_LONG), BLOCK_OP_NORMAL);
 
-  mem = adjust_address (m_tramp, Pmode, 2*UNITS_PER_WORD);
+  mem = adjust_address (m_tramp, Pmode, 2 * UNITS_PER_LONG);
   emit_move_insn (mem, cxt);
-  mem = adjust_address (m_tramp, Pmode, 3*UNITS_PER_WORD);
+  mem = adjust_address (m_tramp, Pmode, 3 * UNITS_PER_LONG);
   emit_move_insn (mem, fnaddr);
 }
 
@@ -9051,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_LONG));
+  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);
@@ -9422,14 +10290,16 @@ s390_valid_pointer_mode (enum machine_mode mode)
 static bool
 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, i;
 
-  INIT_CUMULATIVE_ARGS (cum, NULL, NULL, 0, 0);
+  INIT_CUMULATIVE_ARGS (cum_v, NULL, NULL, 0, 0);
+  cum = pack_cumulative_args (&cum_v);
 
   for (i = 0; i < call_expr_nargs (call_expr); i++)
     {
@@ -9447,15 +10317,15 @@ s390_call_saved_register_used (tree call_expr)
       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)
         continue;
@@ -9622,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;
@@ -9649,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;
     }
 }
@@ -9717,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
@@ -9844,13 +10713,13 @@ s390_optimize_prologue (void)
     }
 }
 
-/* On z10 the dynamic branch prediction must see the backward jump in
-   a window of 384 bytes. 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.  */
+/* 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_z10_fix_long_loop_prediction (rtx insn)
+s390_fix_long_loop_prediction (rtx insn)
 {
   rtx set = single_set (insn);
   rtx code_label, label_ref, new_label;
@@ -9876,11 +10745,11 @@ s390_z10_fix_long_loop_prediction (rtx insn)
   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)) < Z10_PREDICT_DISTANCE))
+         - INSN_ADDRESSES (INSN_UID (code_label)) < PREDICT_DISTANCE))
     return false;
 
   for (distance = 0, cur_insn = PREV_INSN (insn);
-       distance < Z10_PREDICT_DISTANCE - 6;
+       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;
@@ -10180,8 +11049,10 @@ 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)
+  /* 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;
@@ -10196,10 +11067,11 @@ s390_reorg (void)
            continue;
 
          if (JUMP_P (insn))
-           insn_added_p |= s390_z10_fix_long_loop_prediction (insn);
+           insn_added_p |= s390_fix_long_loop_prediction (insn);
 
-         if (GET_CODE (PATTERN (insn)) == PARALLEL
-             || GET_CODE (PATTERN (insn)) == SET)
+         if ((GET_CODE (PATTERN (insn)) == PARALLEL
+              || GET_CODE (PATTERN (insn)) == SET)
+             && s390_tune == PROCESSOR_2097_Z10)
            insn_added_p |= s390_z10_optimize_cmp (insn);
        }
 
@@ -10290,32 +11162,241 @@ s390_z10_prevent_earlyload_conflicts (rtx *ready, int *nready_p)
   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 ATTRIBUTE_UNUSED, int verbose ATTRIBUTE_UNUSED,
+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 ATTRIBUTE_UNUSED,
-                           int verbose ATTRIBUTE_UNUSED,
-                         rtx insn, int more)
+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;
@@ -10329,6 +11410,7 @@ s390_sched_init (FILE *file 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
@@ -10345,8 +11427,9 @@ check_dpu (rtx *x, unsigned *mem_count)
 }
 
 /* This target hook implementation for TARGET_LOOP_UNROLL_ADJUST calculates
-   a new number struct loop *loop should be unrolled if tuned for the z10
-   cpu. The loop is analyzed for memory accesses by calling check_dpu for
+   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.  */
@@ -10358,8 +11441,9 @@ s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop)
   unsigned i;
   unsigned mem_count = 0;
 
-  /* Only z10 needs special handling.  */
-  if (s390_tune != PROCESSOR_2097_Z10)
+  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.  */
@@ -10402,11 +11486,8 @@ s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop)
 #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
@@ -10435,6 +11516,9 @@ s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop)
 #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
@@ -10460,6 +11544,10 @@ s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop)
 #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
@@ -10481,6 +11569,14 @@ s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop)
 
 #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
@@ -10504,6 +11600,9 @@ s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop)
 #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
 
@@ -10516,9 +11615,18 @@ s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop)
 #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
 
@@ -10530,6 +11638,9 @@ s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop)
 #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"