Turn SECONDARY_MEMORY_NEEDED into a hook
[platform/upstream/gcc.git] / gcc / config / s390 / s390.c
index a1d0930..f62d740 100644 (file)
@@ -1,5 +1,5 @@
 /* Subroutines used for code generation on IBM S/390 and zSeries
-   Copyright (C) 1999-2016 Free Software Foundation, Inc.
+   Copyright (C) 1999-2017 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).
@@ -32,8 +32,10 @@ along with GCC; see the file COPYING3.  If not see
 #include "cfghooks.h"
 #include "cfgloop.h"
 #include "df.h"
+#include "memmodel.h"
 #include "tm_p.h"
 #include "stringpool.h"
+#include "attribs.h"
 #include "expmed.h"
 #include "optabs.h"
 #include "regs.h"
@@ -77,10 +79,16 @@ along with GCC; see the file COPYING3.  If not see
 #include "rtl-iter.h"
 #include "intl.h"
 #include "tm-constrs.h"
+#include "tree-vrp.h"
+#include "symbol-summary.h"
+#include "ipa-prop.h"
+#include "ipa-fnsummary.h"
 
 /* This file should be included last.  */
 #include "target-def.h"
 
+static bool s390_hard_regno_mode_ok (unsigned int, machine_mode);
+
 /* Remember the last target of s390_set_current_function.  */
 static GTY(()) tree s390_previous_fndecl;
 
@@ -317,23 +325,27 @@ struct processor_costs zEC12_cost =
 
 static struct
 {
+  /* The preferred name to be used in user visible output.  */
   const char *const name;
+  /* CPU name as it should be passed to Binutils via .machine  */
+  const char *const binutils_name;
   const enum processor_type processor;
   const struct processor_costs *cost;
 }
 const processor_table[] =
 {
-  { "g5",     PROCESSOR_9672_G5,     &z900_cost },
-  { "g6",     PROCESSOR_9672_G6,     &z900_cost },
-  { "z900",   PROCESSOR_2064_Z900,   &z900_cost },
-  { "z990",   PROCESSOR_2084_Z990,   &z990_cost },
-  { "z9-109", PROCESSOR_2094_Z9_109, &z9_109_cost },
-  { "z9-ec",  PROCESSOR_2094_Z9_EC,  &z9_109_cost },
-  { "z10",    PROCESSOR_2097_Z10,    &z10_cost },
-  { "z196",   PROCESSOR_2817_Z196,   &z196_cost },
-  { "zEC12",  PROCESSOR_2827_ZEC12,  &zEC12_cost },
-  { "z13",    PROCESSOR_2964_Z13,    &zEC12_cost },
-  { "native", PROCESSOR_NATIVE,      NULL }
+  { "g5",     "g5",     PROCESSOR_9672_G5,     &z900_cost },
+  { "g6",     "g6",     PROCESSOR_9672_G6,     &z900_cost },
+  { "z900",   "z900",   PROCESSOR_2064_Z900,   &z900_cost },
+  { "z990",   "z990",   PROCESSOR_2084_Z990,   &z990_cost },
+  { "z9-109", "z9-109", PROCESSOR_2094_Z9_109, &z9_109_cost },
+  { "z9-ec",  "z9-ec",  PROCESSOR_2094_Z9_EC,  &z9_109_cost },
+  { "z10",    "z10",    PROCESSOR_2097_Z10,    &z10_cost },
+  { "z196",   "z196",   PROCESSOR_2817_Z196,   &z196_cost },
+  { "zEC12",  "zEC12",  PROCESSOR_2827_ZEC12,  &zEC12_cost },
+  { "z13",    "z13",    PROCESSOR_2964_Z13,    &zEC12_cost },
+  { "z14",    "arch12", PROCESSOR_3906_Z14,    &zEC12_cost },
+  { "native", "",       PROCESSOR_NATIVE,      NULL }
 };
 
 extern int reload_completed;
@@ -484,7 +496,7 @@ struct GTY(()) machine_function
         CONST_OK_FOR_CONSTRAINT_P((x), 'O', "On")
 
 #define REGNO_PAIR_OK(REGNO, MODE)                               \
-  (HARD_REGNO_NREGS ((REGNO), (MODE)) == 1 || !((REGNO) & 1))
+  (s390_hard_regno_nregs ((REGNO), (MODE)) == 1 || !((REGNO) & 1))
 
 /* That's the read ahead of the dynamic branch prediction unit in
    bytes on a z10 (or higher) CPU.  */
@@ -623,6 +635,19 @@ const unsigned int bflags_overloaded_builtin[S390_OVERLOADED_BUILTIN_MAX + 1] =
   };
 
 const unsigned int
+bflags_overloaded_builtin_var[S390_OVERLOADED_BUILTIN_VAR_MAX + 1] =
+  {
+#undef B_DEF
+#undef OB_DEF
+#undef OB_DEF_VAR
+#define B_DEF(...)
+#define OB_DEF(...)
+#define OB_DEF_VAR(NAME, PATTERN, FLAGS, OPFLAGS, FNTYPE) FLAGS,
+#include "s390-builtins.def"
+    0
+  };
+
+const unsigned int
 opflags_overloaded_builtin_var[S390_OVERLOADED_BUILTIN_VAR_MAX + 1] =
   {
 #undef B_DEF
@@ -630,7 +655,7 @@ opflags_overloaded_builtin_var[S390_OVERLOADED_BUILTIN_VAR_MAX + 1] =
 #undef OB_DEF_VAR
 #define B_DEF(...)
 #define OB_DEF(...)
-#define OB_DEF_VAR(NAME, PATTERN, FLAGS, FNTYPE) FLAGS,
+#define OB_DEF_VAR(NAME, PATTERN, FLAGS, OPFLAGS, FNTYPE) OPFLAGS,
 #include "s390-builtins.def"
     0
   };
@@ -672,37 +697,37 @@ s390_init_builtins (void)
     c_uint64_type_node = long_long_unsigned_type_node;
 
 #undef DEF_TYPE
-#define DEF_TYPE(INDEX, BFLAGS, NODE, CONST_P)         \
+#define DEF_TYPE(INDEX, NODE, CONST_P)                 \
   if (s390_builtin_types[INDEX] == NULL)               \
     s390_builtin_types[INDEX] = (!CONST_P) ?           \
       (NODE) : build_type_variant ((NODE), 1, 0);
 
 #undef DEF_POINTER_TYPE
-#define DEF_POINTER_TYPE(INDEX, BFLAGS, INDEX_BASE)                    \
+#define DEF_POINTER_TYPE(INDEX, INDEX_BASE)                            \
   if (s390_builtin_types[INDEX] == NULL)                               \
     s390_builtin_types[INDEX] =                                                \
       build_pointer_type (s390_builtin_types[INDEX_BASE]);
 
 #undef DEF_DISTINCT_TYPE
-#define DEF_DISTINCT_TYPE(INDEX, BFLAGS, INDEX_BASE)                   \
+#define DEF_DISTINCT_TYPE(INDEX, INDEX_BASE)                           \
   if (s390_builtin_types[INDEX] == NULL)                               \
     s390_builtin_types[INDEX] =                                                \
       build_distinct_type_copy (s390_builtin_types[INDEX_BASE]);
 
 #undef DEF_VECTOR_TYPE
-#define DEF_VECTOR_TYPE(INDEX, BFLAGS, INDEX_BASE, ELEMENTS)           \
+#define DEF_VECTOR_TYPE(INDEX, INDEX_BASE, ELEMENTS)                   \
   if (s390_builtin_types[INDEX] == NULL)                               \
     s390_builtin_types[INDEX] =                                                \
       build_vector_type (s390_builtin_types[INDEX_BASE], ELEMENTS);
 
 #undef DEF_OPAQUE_VECTOR_TYPE
-#define DEF_OPAQUE_VECTOR_TYPE(INDEX, BFLAGS, INDEX_BASE, ELEMENTS)    \
+#define DEF_OPAQUE_VECTOR_TYPE(INDEX, INDEX_BASE, ELEMENTS)            \
   if (s390_builtin_types[INDEX] == NULL)                               \
     s390_builtin_types[INDEX] =                                                \
       build_opaque_vector_type (s390_builtin_types[INDEX_BASE], ELEMENTS);
 
 #undef DEF_FN_TYPE
-#define DEF_FN_TYPE(INDEX, BFLAGS, args...)                    \
+#define DEF_FN_TYPE(INDEX, args...)                            \
   if (s390_builtin_fn_types[INDEX] == NULL)                    \
     s390_builtin_fn_types[INDEX] =                             \
       build_function_type_list (args, NULL_TREE);
@@ -749,12 +774,12 @@ s390_const_operand_ok (tree arg, int argnum, int op_flags, tree decl)
       int bitwidth = bitwidths[op_flags - O_U1];
 
       if (!tree_fits_uhwi_p (arg)
-         || tree_to_uhwi (arg) > ((unsigned HOST_WIDE_INT)1 << bitwidth) - 1)
+         || tree_to_uhwi (arg) > (HOST_WIDE_INT_1U << bitwidth) - 1)
        {
          error("constant argument %d for builtin %qF is out of range (0.."
                HOST_WIDE_INT_PRINT_UNSIGNED ")",
                argnum, decl,
-               ((unsigned HOST_WIDE_INT)1 << bitwidth) - 1);
+               (HOST_WIDE_INT_1U << bitwidth) - 1);
          return false;
        }
     }
@@ -765,15 +790,15 @@ s390_const_operand_ok (tree arg, int argnum, int op_flags, tree decl)
       int bitwidth = bitwidths[op_flags - O_S2];
 
       if (!tree_fits_shwi_p (arg)
-         || tree_to_shwi (arg) < -((HOST_WIDE_INT)1 << (bitwidth - 1))
-         || tree_to_shwi (arg) > (((HOST_WIDE_INT)1 << (bitwidth - 1)) - 1))
+         || tree_to_shwi (arg) < -(HOST_WIDE_INT_1 << (bitwidth - 1))
+         || tree_to_shwi (arg) > ((HOST_WIDE_INT_1 << (bitwidth - 1)) - 1))
        {
          error("constant argument %d for builtin %qF is out of range ("
                HOST_WIDE_INT_PRINT_DEC ".."
                HOST_WIDE_INT_PRINT_DEC ")",
                argnum, decl,
-               -((HOST_WIDE_INT)1 << (bitwidth - 1)),
-               ((HOST_WIDE_INT)1 << (bitwidth - 1)) - 1);
+               -(HOST_WIDE_INT_1 << (bitwidth - 1)),
+               (HOST_WIDE_INT_1 << (bitwidth - 1)) - 1);
          return false;
        }
     }
@@ -791,7 +816,7 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
                     machine_mode mode ATTRIBUTE_UNUSED,
                     int ignore ATTRIBUTE_UNUSED)
 {
-#define MAX_ARGS 5
+#define MAX_ARGS 6
 
   tree fndecl = TREE_OPERAND (CALL_EXPR_FN (exp), 0);
   unsigned int fcode = DECL_FUNCTION_CODE (fndecl);
@@ -819,16 +844,22 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
       bflags = bflags_for_builtin (fcode);
       if ((bflags & B_HTM) && !TARGET_HTM)
        {
-         error ("Builtin %qF is not supported without -mhtm "
+         error ("builtin %qF is not supported without -mhtm "
                 "(default with -march=zEC12 and higher).", fndecl);
          return const0_rtx;
        }
-      if ((bflags & B_VX) && !TARGET_VX)
+      if (((bflags & B_VX) || (bflags & B_VXE)) && !TARGET_VX)
        {
-         error ("Builtin %qF is not supported without -mvx "
+         error ("builtin %qF requires -mvx "
                 "(default with -march=z13 and higher).", fndecl);
          return const0_rtx;
        }
+
+      if ((bflags & B_VXE) && !TARGET_VXE)
+       {
+         error ("Builtin %qF requires z14 or higher.", fndecl);
+         return const0_rtx;
+       }
     }
   if (fcode >= S390_OVERLOADED_BUILTIN_VAR_OFFSET
       && fcode < S390_ALL_BUILTIN_MAX)
@@ -845,7 +876,7 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
     }
   else if (fcode < S390_OVERLOADED_BUILTIN_VAR_OFFSET)
     {
-      error ("Unresolved overloaded builtin");
+      error ("unresolved overloaded builtin");
       return const0_rtx;
     }
   else
@@ -875,6 +906,7 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
   arity = 0;
   FOR_EACH_CALL_EXPR_ARG (arg, iter, exp)
     {
+      rtx tmp_rtx;
       const struct insn_operand_data *insn_op;
       unsigned int op_flags = all_op_flags & ((1 << O_SHIFT) - 1);
 
@@ -950,6 +982,20 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
                                             copy_to_mode_reg (Pmode,
                                               XEXP (op[arity], 0)));
        }
+      /* Some of the builtins require different modes/types than the
+        pattern in order to implement a specific API.  Instead of
+        adding many expanders which do the mode change we do it here.
+        E.g. s390_vec_add_u128 required to have vector unsigned char
+        arguments is mapped to addti3.  */
+      else if (insn_op->mode != VOIDmode
+              && GET_MODE (op[arity]) != VOIDmode
+              && GET_MODE (op[arity]) != insn_op->mode
+              && ((tmp_rtx = simplify_gen_subreg (insn_op->mode, op[arity],
+                                                  GET_MODE (op[arity]), 0))
+                  != NULL_RTX))
+       {
+         op[arity] = tmp_rtx;
+       }
       else if (GET_MODE (op[arity]) == insn_op->mode
               || GET_MODE (op[arity]) == VOIDmode
               || (insn_op->predicate == address_operand
@@ -959,13 +1005,13 @@ s390_expand_builtin (tree exp, rtx target, rtx subtarget ATTRIBUTE_UNUSED,
             so we cannot use this.  */
          machine_mode target_mode =
            (insn_op->predicate == address_operand
-            ? Pmode : insn_op->mode);
+            ? (machine_mode) Pmode : insn_op->mode);
          op[arity] = copy_to_mode_reg (target_mode, op[arity]);
        }
 
       if (!insn_op->predicate (op[arity], insn_op->mode))
        {
-         error ("Invalid argument %d for builtin %qF", arity + 1, fndecl);
+         error ("invalid argument %d for builtin %qF", arity + 1, fndecl);
          return const0_rtx;
        }
       arity++;
@@ -1094,11 +1140,20 @@ s390_handle_vectorbool_attribute (tree *node, tree name ATTRIBUTE_UNUSED,
   mode = TYPE_MODE (type);
   switch (mode)
     {
-    case DImode: case V2DImode: result = s390_builtin_types[BT_BV2DI]; break;
-    case SImode: case V4SImode: result = s390_builtin_types[BT_BV4SI]; break;
-    case HImode: case V8HImode: result = s390_builtin_types[BT_BV8HI]; break;
-    case QImode: case V16QImode: result = s390_builtin_types[BT_BV16QI];
-    default: break;
+    case E_DImode: case E_V2DImode:
+      result = s390_builtin_types[BT_BV2DI];
+      break;
+    case E_SImode: case E_V4SImode:
+      result = s390_builtin_types[BT_BV4SI];
+      break;
+    case E_HImode: case E_V8HImode:
+      result = s390_builtin_types[BT_BV8HI];
+      break;
+    case E_QImode: case E_V16QImode:
+      result = s390_builtin_types[BT_BV16QI];
+      break;
+    default:
+      break;
     }
 
   *no_add_attrs = true;  /* No need to hang on to the attribute.  */
@@ -1119,7 +1174,7 @@ static const struct attribute_spec s390_attribute_table[] = {
 /* 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)
+s390_label_align (rtx_insn *label)
 {
   rtx_insn *prev_insn = prev_active_insn (label);
   rtx set, src;
@@ -1143,19 +1198,36 @@ s390_label_align (rtx label)
   return align_labels_log;
 }
 
-static machine_mode
+static GTY(()) rtx got_symbol;
+
+/* Return the GOT table symbol.  The symbol will be created when the
+   function is invoked for the first time.  */
+
+static rtx
+s390_got_symbol (void)
+{
+  if (!got_symbol)
+    {
+      got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
+      SYMBOL_REF_FLAGS (got_symbol) = SYMBOL_FLAG_LOCAL;
+    }
+
+  return got_symbol;
+}
+
+static scalar_int_mode
 s390_libgcc_cmp_return_mode (void)
 {
   return TARGET_64BIT ? DImode : SImode;
 }
 
-static machine_mode
+static scalar_int_mode
 s390_libgcc_shift_count_mode (void)
 {
   return TARGET_64BIT ? DImode : SImode;
 }
 
-static machine_mode
+static scalar_int_mode
 s390_unwind_word_mode (void)
 {
   return TARGET_64BIT ? DImode : SImode;
@@ -1163,7 +1235,7 @@ s390_unwind_word_mode (void)
 
 /* Return true if the back end supports mode MODE.  */
 static bool
-s390_scalar_mode_supported_p (machine_mode mode)
+s390_scalar_mode_supported_p (scalar_mode mode)
 {
   /* In contrast to the default implementation reject TImode constants on 31bit
      TARGET_ZARCH for ABI compliance.  */
@@ -1191,14 +1263,14 @@ s390_vector_mode_supported_p (machine_mode mode)
 
   switch (inner)
     {
-    case QImode:
-    case HImode:
-    case SImode:
-    case DImode:
-    case TImode:
-    case SFmode:
-    case DFmode:
-    case TFmode:
+    case E_QImode:
+    case E_HImode:
+    case E_SImode:
+    case E_DImode:
+    case E_TImode:
+    case E_SFmode:
+    case E_DFmode:
+    case E_TFmode:
       return true;
     default:
       return false;
@@ -1225,18 +1297,18 @@ s390_cc_modes_compatible (machine_mode m1, machine_mode m2)
 
   switch (m1)
     {
-    case CCZmode:
+    case E_CCZmode:
       if (m2 == CCUmode || m2 == CCTmode || m2 == CCZ1mode
          || m2 == CCSmode || m2 == CCSRmode || m2 == CCURmode)
         return m2;
       return VOIDmode;
 
-    case CCSmode:
-    case CCUmode:
-    case CCTmode:
-    case CCSRmode:
-    case CCURmode:
-    case CCZ1mode:
+    case E_CCSmode:
+    case E_CCUmode:
+    case E_CCTmode:
+    case E_CCSRmode:
+    case E_CCURmode:
+    case E_CCZ1mode:
       if (m2 == CCZmode)
        return m1;
 
@@ -1259,40 +1331,47 @@ s390_match_ccmode_set (rtx set, machine_mode req_mode)
 
   gcc_assert (GET_CODE (set) == SET);
 
+  /* These modes are supposed to be used only in CC consumer
+     patterns.  */
+  gcc_assert (req_mode != CCVIALLmode && req_mode != CCVIANYmode
+             && req_mode != CCVFALLmode && req_mode != CCVFANYmode);
+
   if (GET_CODE (SET_DEST (set)) != REG || !CC_REGNO_P (REGNO (SET_DEST (set))))
     return 1;
 
   set_mode = GET_MODE (SET_DEST (set));
   switch (set_mode)
     {
-    case CCSmode:
-    case CCSRmode:
-    case CCUmode:
-    case CCURmode:
-    case CCLmode:
-    case CCL1mode:
-    case CCL2mode:
-    case CCL3mode:
-    case CCT1mode:
-    case CCT2mode:
-    case CCT3mode:
-    case CCVEQmode:
-    case CCVHmode:
-    case CCVHUmode:
-    case CCVFHmode:
-    case CCVFHEmode:
+    case E_CCZ1mode:
+    case E_CCSmode:
+    case E_CCSRmode:
+    case E_CCUmode:
+    case E_CCURmode:
+    case E_CCLmode:
+    case E_CCL1mode:
+    case E_CCL2mode:
+    case E_CCL3mode:
+    case E_CCT1mode:
+    case E_CCT2mode:
+    case E_CCT3mode:
+    case E_CCVEQmode:
+    case E_CCVIHmode:
+    case E_CCVIHUmode:
+    case E_CCVFHmode:
+    case E_CCVFHEmode:
       if (req_mode != set_mode)
         return 0;
       break;
 
-    case CCZmode:
+    case E_CCZmode:
       if (req_mode != CCSmode && req_mode != CCUmode && req_mode != CCTmode
-         && req_mode != CCSRmode && req_mode != CCURmode)
+         && req_mode != CCSRmode && req_mode != CCURmode
+         && req_mode != CCZ1mode)
         return 0;
       break;
 
-    case CCAPmode:
-    case CCANmode:
+    case E_CCAPmode:
+    case E_CCANmode:
       if (req_mode != CCAmode)
         return 0;
       break;
@@ -1381,29 +1460,6 @@ s390_tm_ccmode (rtx op1, rtx op2, bool mixed)
 machine_mode
 s390_select_ccmode (enum rtx_code code, rtx op0, rtx op1)
 {
-  if (TARGET_VX
-      && register_operand (op0, DFmode)
-      && register_operand (op1, DFmode))
-    {
-      /* LT, LE, UNGT, UNGE require swapping OP0 and OP1.  Either
-        s390_emit_compare or s390_canonicalize_comparison will take
-        care of it.  */
-      switch (code)
-       {
-       case EQ:
-       case NE:
-         return CCVEQmode;
-       case GT:
-       case UNLE:
-         return CCVFHmode;
-       case GE:
-       case UNLT:
-         return CCVFHEmode;
-       default:
-         ;
-       }
-    }
-
   switch (code)
     {
       case EQ:
@@ -1540,7 +1596,7 @@ s390_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
          && modesize <= HOST_BITS_PER_WIDE_INT)
        {
          unsigned HOST_WIDE_INT block;
-         block = ((unsigned HOST_WIDE_INT) 1 << len) - 1;
+         block = (HOST_WIDE_INT_1U << len) - 1;
          block <<= modesize - pos - len;
 
          *op0 = gen_rtx_AND (GET_MODE (inner), inner,
@@ -1589,7 +1645,7 @@ s390_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
       && INTVAL (*op1) == 0xffff
       && SCALAR_INT_MODE_P (GET_MODE (*op0))
       && (nonzero_bits (*op0, GET_MODE (*op0))
-         & ~(unsigned HOST_WIDE_INT) 0xffff) == 0)
+         & ~HOST_WIDE_INT_UC (0xffff)) == 0)
     {
       *op0 = gen_lowpart (HImode, *op0);
       *op1 = constm1_rtx;
@@ -1634,8 +1690,8 @@ s390_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
       enum rtx_code new_code = UNKNOWN;
       switch (GET_MODE (XVECEXP (*op0, 0, 0)))
        {
-       case CCZmode:
-       case CCRAWmode:
+       case E_CCZmode:
+       case E_CCRAWmode:
          switch (*code)
            {
            case EQ: new_code = EQ;  break;
@@ -1682,69 +1738,30 @@ s390_canonicalize_comparison (int *code, rtx *op0, rtx *op1,
       *code = (int)swap_condition ((enum rtx_code)*code);
     }
 
-  /* Using the scalar variants of vector instructions for 64 bit FP
-     comparisons might require swapping the operands.  */
-  if (TARGET_VX
-      && register_operand (*op0, DFmode)
-      && register_operand (*op1, DFmode)
-      && (*code == LT || *code == LE || *code == UNGT || *code == UNGE))
+  /* A comparison result is compared against zero.  Replace it with
+     the (perhaps inverted) original comparison.
+     This probably should be done by simplify_relational_operation.  */
+  if ((*code == EQ || *code == NE)
+      && *op1 == const0_rtx
+      && COMPARISON_P (*op0)
+      && CC_REG_P (XEXP (*op0, 0)))
     {
-      rtx tmp;
+      enum rtx_code new_code;
 
-      switch (*code)
+      if (*code == EQ)
+       new_code = reversed_comparison_code_parts (GET_CODE (*op0),
+                                                  XEXP (*op0, 0),
+                                                  XEXP (*op1, 0), NULL);
+      else
+       new_code = GET_CODE (*op0);
+
+      if (new_code != UNKNOWN)
        {
-       case LT:   *code = GT; break;
-       case LE:   *code = GE; break;
-       case UNGT: *code = UNLE; break;
-       case UNGE: *code = UNLT; break;
-       default: ;
+         *code = new_code;
+         *op1 = XEXP (*op0, 1);
+         *op0 = XEXP (*op0, 0);
        }
-      tmp = *op0; *op0 = *op1; *op1 = tmp;
-    }
-}
-
-/* Helper function for s390_emit_compare.  If possible emit a 64 bit
-   FP compare using the single element variant of vector instructions.
-   Replace CODE with the comparison code to be used in the CC reg
-   compare and return the condition code register RTX in CC.  */
-
-static bool
-s390_expand_vec_compare_scalar (enum rtx_code *code, rtx cmp1, rtx cmp2,
-                               rtx *cc)
-{
-  machine_mode cmp_mode;
-  bool swap_p = false;
-
-  switch (*code)
-    {
-    case EQ:   cmp_mode = CCVEQmode;  break;
-    case NE:   cmp_mode = CCVEQmode;  break;
-    case GT:   cmp_mode = CCVFHmode;  break;
-    case GE:   cmp_mode = CCVFHEmode; break;
-    case UNLE: cmp_mode = CCVFHmode;  break;
-    case UNLT: cmp_mode = CCVFHEmode; break;
-    case LT:   cmp_mode = CCVFHmode;  *code = GT;   swap_p = true; break;
-    case LE:   cmp_mode = CCVFHEmode; *code = GE;   swap_p = true; break;
-    case UNGE: cmp_mode = CCVFHmode;  *code = UNLE; swap_p = true; break;
-    case UNGT: cmp_mode = CCVFHEmode; *code = UNLT; swap_p = true; break;
-    default: return false;
-    }
-
-  if (swap_p)
-    {
-      rtx tmp = cmp2;
-      cmp2 = cmp1;
-      cmp1 = tmp;
     }
-  *cc = gen_rtx_REG (cmp_mode, CC_REGNUM);
-  emit_insn (gen_rtx_PARALLEL (VOIDmode,
-              gen_rtvec (2,
-                         gen_rtx_SET (*cc,
-                                      gen_rtx_COMPARE (cmp_mode, cmp1,
-                                                       cmp2)),
-                         gen_rtx_CLOBBER (VOIDmode,
-                                          gen_rtx_SCRATCH (V2DImode)))));
-  return true;
 }
 
 
@@ -1758,14 +1775,7 @@ s390_emit_compare (enum rtx_code code, rtx op0, rtx op1)
   machine_mode mode = s390_select_ccmode (code, op0, op1);
   rtx cc;
 
-  if (TARGET_VX
-      && register_operand (op0, DFmode)
-      && register_operand (op1, DFmode)
-      && s390_expand_vec_compare_scalar (&code, op0, op1, &cc))
-    {
-      /* Work has been done by s390_expand_vec_compare_scalar already.  */
-    }
-  else if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC)
+  if (GET_MODE_CLASS (GET_MODE (op0)) == MODE_CC)
     {
       /* Do not output a redundant compare instruction if a
         compare_and_swap pattern already computed the result and the
@@ -1790,11 +1800,31 @@ s390_emit_compare (enum rtx_code code, rtx op0, rtx op1)
 
 static rtx
 s390_emit_compare_and_swap (enum rtx_code code, rtx old, rtx mem,
-                           rtx cmp, rtx new_rtx)
+                           rtx cmp, rtx new_rtx, machine_mode ccmode)
 {
-  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);
+  rtx cc;
+
+  cc = gen_rtx_REG (ccmode, CC_REGNUM);
+  switch (GET_MODE (mem))
+    {
+    case E_SImode:
+      emit_insn (gen_atomic_compare_and_swapsi_internal (old, mem, cmp,
+                                                        new_rtx, cc));
+      break;
+    case E_DImode:
+      emit_insn (gen_atomic_compare_and_swapdi_internal (old, mem, cmp,
+                                                        new_rtx, cc));
+      break;
+    case E_TImode:
+       emit_insn (gen_atomic_compare_and_swapti_internal (old, mem, cmp,
+                                                          new_rtx, cc));
+      break;
+    case E_QImode:
+    case E_HImode:
+    default:
+      gcc_unreachable ();
+    }
+  return s390_emit_compare (code, cc, const0_rtx);
 }
 
 /* Emit a jump instruction to TARGET and return it.  If COND is
@@ -1834,8 +1864,8 @@ s390_branch_condition_mask (rtx code)
 
   switch (GET_MODE (XEXP (code, 0)))
     {
-    case CCZmode:
-    case CCZ1mode:
+    case E_CCZmode:
+    case E_CCZ1mode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC0;
@@ -1844,7 +1874,7 @@ s390_branch_condition_mask (rtx code)
         }
       break;
 
-    case CCT1mode:
+    case E_CCT1mode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC1;
@@ -1853,7 +1883,7 @@ s390_branch_condition_mask (rtx code)
         }
       break;
 
-    case CCT2mode:
+    case E_CCT2mode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC2;
@@ -1862,7 +1892,7 @@ s390_branch_condition_mask (rtx code)
         }
       break;
 
-    case CCT3mode:
+    case E_CCT3mode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC3;
@@ -1871,7 +1901,7 @@ s390_branch_condition_mask (rtx code)
         }
       break;
 
-    case CCLmode:
+    case E_CCLmode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC0 | CC2;
@@ -1880,7 +1910,7 @@ s390_branch_condition_mask (rtx code)
         }
       break;
 
-    case CCL1mode:
+    case E_CCL1mode:
       switch (GET_CODE (code))
         {
        case LTU:       return CC2 | CC3;  /* carry */
@@ -1889,7 +1919,7 @@ s390_branch_condition_mask (rtx code)
         }
       break;
 
-    case CCL2mode:
+    case E_CCL2mode:
       switch (GET_CODE (code))
         {
        case GTU:       return CC0 | CC1;  /* borrow */
@@ -1898,7 +1928,7 @@ s390_branch_condition_mask (rtx code)
         }
       break;
 
-    case CCL3mode:
+    case E_CCL3mode:
       switch (GET_CODE (code))
        {
        case EQ:        return CC0 | CC2;
@@ -1910,7 +1940,7 @@ s390_branch_condition_mask (rtx code)
        default:        return -1;
        }
 
-    case CCUmode:
+    case E_CCUmode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC0;
@@ -1923,7 +1953,7 @@ s390_branch_condition_mask (rtx code)
         }
       break;
 
-    case CCURmode:
+    case E_CCURmode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC0;
@@ -1936,7 +1966,7 @@ s390_branch_condition_mask (rtx code)
         }
       break;
 
-    case CCAPmode:
+    case E_CCAPmode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC0;
@@ -1949,7 +1979,7 @@ s390_branch_condition_mask (rtx code)
         }
       break;
 
-    case CCANmode:
+    case E_CCANmode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC0;
@@ -1962,7 +1992,7 @@ s390_branch_condition_mask (rtx code)
         }
       break;
 
-    case CCSmode:
+    case E_CCSmode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC0;
@@ -1983,7 +2013,7 @@ s390_branch_condition_mask (rtx code)
         }
       break;
 
-    case CCSRmode:
+    case E_CCSRmode:
       switch (GET_CODE (code))
         {
         case EQ:       return CC0;
@@ -2005,93 +2035,64 @@ s390_branch_condition_mask (rtx code)
       break;
 
       /* Vector comparison modes.  */
-
-    case CCVEQmode:
-      switch (GET_CODE (code))
-       {
-       case EQ:        return CC0;
-       case NE:        return CC3;
-       default:        return -1;
-       }
-
-    case CCVEQANYmode:
-      switch (GET_CODE (code))
-       {
-       case EQ:        return CC0 | CC1;
-       case NE:        return CC3 | CC1;
-       default:        return -1;
-       }
-
-      /* Integer vector compare modes.  */
-
-    case CCVHmode:
-      switch (GET_CODE (code))
-       {
-       case GT:        return CC0;
-       case LE:        return CC3;
-       default:        return -1;
-       }
-
-    case CCVHANYmode:
-      switch (GET_CODE (code))
-       {
-       case GT:        return CC0 | CC1;
-       case LE:        return CC3 | CC1;
-       default:        return -1;
-       }
-
-    case CCVHUmode:
-      switch (GET_CODE (code))
-       {
-       case GTU:       return CC0;
-       case LEU:       return CC3;
-       default:        return -1;
-       }
-
-    case CCVHUANYmode:
+      /* CC2 will never be set.  It however is part of the negated
+        masks.  */
+    case E_CCVIALLmode:
       switch (GET_CODE (code))
        {
-       case GTU:       return CC0 | CC1;
-       case LEU:       return CC3 | CC1;
-       default:        return -1;
-       }
-
-      /* FP vector compare modes.  */
-
-    case CCVFHmode:
-      switch (GET_CODE (code))
-       {
-       case GT:        return CC0;
-       case UNLE:      return CC3;
+       case EQ:
+       case GTU:
+       case GT:
+       case GE:        return CC0;
+         /* The inverted modes are in fact *any* modes.  */
+       case NE:
+       case LEU:
+       case LE:
+       case LT:        return CC3 | CC1 | CC2;
        default:        return -1;
        }
 
-    case CCVFHANYmode:
+    case E_CCVIANYmode:
       switch (GET_CODE (code))
        {
-       case GT:        return CC0 | CC1;
-       case UNLE:      return CC3 | CC1;
+       case EQ:
+       case GTU:
+       case GT:
+       case GE:        return CC0 | CC1;
+         /* The inverted modes are in fact *all* modes.  */
+       case NE:
+       case LEU:
+       case LE:
+       case LT:        return CC3 | CC2;
        default:        return -1;
        }
-
-    case CCVFHEmode:
+    case E_CCVFALLmode:
       switch (GET_CODE (code))
        {
+       case EQ:
+       case GT:
        case GE:        return CC0;
-       case UNLT:      return CC3;
+         /* The inverted modes are in fact *any* modes.  */
+       case NE:
+       case UNLE:
+       case UNLT:      return CC3 | CC1 | CC2;
        default:        return -1;
        }
 
-    case CCVFHEANYmode:
+    case E_CCVFANYmode:
       switch (GET_CODE (code))
        {
+       case EQ:
+       case GT:
        case GE:        return CC0 | CC1;
-       case UNLT:      return CC3 | CC1;
+         /* The inverted modes are in fact *all* modes.  */
+       case NE:
+       case UNLE:
+       case UNLT:      return CC3 | CC2;
        default:        return -1;
        }
 
-
-    case CCRAWmode:
+    case E_CCRAWmode:
       switch (GET_CODE (code))
        {
        case EQ:
@@ -2189,14 +2190,13 @@ s390_extract_part (rtx op, machine_mode mode, int def)
   unsigned HOST_WIDE_INT value = 0;
   int max_parts = HOST_BITS_PER_WIDE_INT / GET_MODE_BITSIZE (mode);
   int part_bits = GET_MODE_BITSIZE (mode);
-  unsigned HOST_WIDE_INT part_mask
-    = ((unsigned HOST_WIDE_INT)1 << part_bits) - 1;
+  unsigned HOST_WIDE_INT part_mask = (HOST_WIDE_INT_1U << part_bits) - 1;
   int i;
 
   for (i = 0; i < max_parts; i++)
     {
       if (i == 0)
-       value = (unsigned HOST_WIDE_INT) INTVAL (op);
+       value = UINTVAL (op);
       else
        value >>= part_bits;
 
@@ -2220,7 +2220,7 @@ s390_single_part (rtx op,
   unsigned HOST_WIDE_INT value = 0;
   int n_parts = GET_MODE_SIZE (mode) / GET_MODE_SIZE (part_mode);
   unsigned HOST_WIDE_INT part_mask
-    = ((unsigned HOST_WIDE_INT)1 << GET_MODE_BITSIZE (part_mode)) - 1;
+    = (HOST_WIDE_INT_1U << GET_MODE_BITSIZE (part_mode)) - 1;
   int i, part = -1;
 
   if (GET_CODE (op) != CONST_INT)
@@ -2229,7 +2229,7 @@ s390_single_part (rtx op,
   for (i = 0; i < n_parts; i++)
     {
       if (i == 0)
-       value = (unsigned HOST_WIDE_INT) INTVAL (op);
+       value = UINTVAL (op);
       else
        value >>= GET_MODE_BITSIZE (part_mode);
 
@@ -2245,67 +2245,108 @@ s390_single_part (rtx op,
 }
 
 /* Return true if IN contains a contiguous bitfield in the lower SIZE
-   bits and no other bits are set in IN.  POS and LENGTH can be used
-   to obtain the start position and the length of the bitfield.
+   bits and no other bits are set in (the lower SIZE bits of) IN.
 
-   POS gives the position of the first bit of the bitfield counting
-   from the lowest order bit starting with zero.  In order to use this
-   value for S/390 instructions this has to be converted to "bits big
-   endian" style.  */
+   PSTART and PEND can be used to obtain the start and end
+   position (inclusive) of the bitfield relative to 64
+   bits. *PSTART / *PEND gives the position of the first/last bit
+   of the bitfield counting from the highest order bit starting
+   with zero.  */
 
 bool
-s390_contiguous_bitmask_p (unsigned HOST_WIDE_INT in, int size,
-                          int *pos, int *length)
-{
-  int tmp_pos = 0;
-  int tmp_length = 0;
-  int i;
-  unsigned HOST_WIDE_INT mask = 1ULL;
-  bool contiguous = false;
+s390_contiguous_bitmask_nowrap_p (unsigned HOST_WIDE_INT in, int size,
+                                 int *pstart, int *pend)
+{
+  int start;
+  int end = -1;
+  int lowbit = HOST_BITS_PER_WIDE_INT - 1;
+  int highbit = HOST_BITS_PER_WIDE_INT - size;
+  unsigned HOST_WIDE_INT bitmask = HOST_WIDE_INT_1U;
+
+  gcc_assert (!!pstart == !!pend);
+  for (start = lowbit; start >= highbit; bitmask <<= 1, start--)
+    if (end == -1)
+      {
+       /* Look for the rightmost bit of a contiguous range of ones.  */
+       if (bitmask & in)
+         /* Found it.  */
+         end = start;
+      }
+    else
+      {
+       /* Look for the firt zero bit after the range of ones.  */
+       if (! (bitmask & in))
+         /* Found it.  */
+         break;
+      }
+  /* We're one past the last one-bit.  */
+  start++;
 
-  for (i = 0; i < size; mask <<= 1, i++)
+  if (end == -1)
+    /* No one bits found.  */
+    return false;
+
+  if (start > highbit)
     {
-      if (contiguous)
-       {
-         if (mask & in)
-           tmp_length++;
-         else
-           break;
-       }
-      else
-       {
-         if (mask & in)
-           {
-             contiguous = true;
-             tmp_length++;
-           }
-         else
-           tmp_pos++;
-       }
+      unsigned HOST_WIDE_INT mask;
+
+      /* Calculate a mask for all bits beyond the contiguous bits.  */
+      mask = ((~HOST_WIDE_INT_0U >> highbit)
+             & (~HOST_WIDE_INT_0U << (lowbit - start + 1)));
+      if (mask & in)
+       /* There are more bits set beyond the first range of one bits.  */
+       return false;
     }
 
-  if (!tmp_length)
-    return false;
+  if (pstart)
+    {
+      *pstart = start;
+      *pend = end;
+    }
 
-  /* Calculate a mask for all bits beyond the contiguous bits.  */
-  mask = (-1LL & ~(((1ULL << (tmp_length + tmp_pos - 1)) << 1) - 1));
+  return true;
+}
 
-  if ((unsigned)size < sizeof (HOST_WIDE_INT) * BITS_PER_UNIT)
-    mask &= (HOST_WIDE_INT_1U << size) - 1;
+/* Same as s390_contiguous_bitmask_nowrap_p but also returns true
+   if ~IN contains a contiguous bitfield.  In that case, *END is <
+   *START.
 
-  if (mask & in)
-    return false;
+   If WRAP_P is true, a bitmask that wraps around is also tested.
+   When a wraparoud occurs *START is greater than *END (in
+   non-null pointers), and the uppermost (64 - SIZE) bits are thus
+   part of the range.  If WRAP_P is false, no wraparound is
+   tested.  */
 
-  if (tmp_length + tmp_pos - 1 > size)
-    return false;
+bool
+s390_contiguous_bitmask_p (unsigned HOST_WIDE_INT in, bool wrap_p,
+                          int size, int *start, int *end)
+{
+  int bs = HOST_BITS_PER_WIDE_INT;
+  bool b;
 
-  if (length)
-    *length = tmp_length;
+  gcc_assert (!!start == !!end);
+  if ((in & ((~HOST_WIDE_INT_0U) >> (bs - size))) == 0)
+    /* This cannot be expressed as a contiguous bitmask.  Exit early because
+       the second call of s390_contiguous_bitmask_nowrap_p would accept this as
+       a valid bitmask.  */
+    return false;
+  b = s390_contiguous_bitmask_nowrap_p (in, size, start, end);
+  if (b)
+    return true;
+  if (! wrap_p)
+    return false;
+  b = s390_contiguous_bitmask_nowrap_p (~in, size, start, end);
+  if (b && start)
+    {
+      int s = *start;
+      int e = *end;
 
-  if (pos)
-    *pos = tmp_pos;
+      gcc_assert (s >= 1);
+      *start = ((e + 1) & (bs - 1));
+      *end = ((s - 1 + bs) & (bs - 1));
+    }
 
-  return true;
+  return b;
 }
 
 /* Return true if OP contains the same contiguous bitfield in *all*
@@ -2321,9 +2362,11 @@ bool
 s390_contiguous_bitmask_vector_p (rtx op, int *start, int *end)
 {
   unsigned HOST_WIDE_INT mask;
-  int length, size;
+  int size;
   rtx elt;
+  bool b;
 
+  gcc_assert (!!start == !!end);
   if (!const_vec_duplicate_p (op, &elt)
       || !CONST_INT_P (elt))
     return false;
@@ -2335,25 +2378,19 @@ s390_contiguous_bitmask_vector_p (rtx op, int *start, int *end)
     return false;
 
   mask = UINTVAL (elt);
-  if (s390_contiguous_bitmask_p (mask, size, start,
-                                end != NULL ? &length : NULL))
-    {
-      if (end != NULL)
-       *end = *start + length - 1;
-      return true;
-    }
-  /* 0xff00000f style immediates can be covered by swapping start and
-     end indices in vgm.  */
-  if (s390_contiguous_bitmask_p (~mask, size, start,
-                                end != NULL ? &length : NULL))
+
+  b = s390_contiguous_bitmask_p (mask, true, size, start, end);
+  if (b)
     {
-      if (end != NULL)
-       *end = *start - 1;
-      if (start != NULL)
-       *start = *start + length;
+      if (start)
+       {
+         *start -= (HOST_BITS_PER_WIDE_INT - size);
+         *end -= (HOST_BITS_PER_WIDE_INT - size);
+       }
       return true;
     }
-  return false;
+  else
+    return false;
 }
 
 /* Return true if C consists only of byte chunks being either 0 or
@@ -2407,14 +2444,21 @@ s390_bytemask_vector_p (rtx op, unsigned *mask)
 bool
 s390_extzv_shift_ok (int bitsize, int rotl, unsigned HOST_WIDE_INT contig)
 {
-  int pos, len;
+  int start, end;
   bool ok;
 
-  ok = s390_contiguous_bitmask_p (contig, bitsize, &pos, &len);
+  ok = s390_contiguous_bitmask_nowrap_p (contig, bitsize, &start, &end);
   gcc_assert (ok);
 
-  return ((rotl >= 0 && rotl <= pos)
-         || (rotl < 0 && -rotl <= bitsize - len - pos));
+  if (rotl >= 0)
+    return (64 - end >= rotl);
+  else
+    {
+      /* Translate "- rotate right" in BITSIZE mode to "rotate left" in
+        DIMode.  */
+      rotl = -rotl + (64 - bitsize);
+      return (start >= rotl);
+    }
 }
 
 /* Check whether we can (and want to) split a double-word
@@ -2428,10 +2472,6 @@ s390_split_ok_p (rtx dst, rtx src, machine_mode mode, int first_subword)
   if (FP_REG_P (src) || FP_REG_P (dst) || VECTOR_REG_P (src) || VECTOR_REG_P (dst))
     return false;
 
-  /* We don't need to split if operands are directly accessible.  */
-  if (s_operand (src, mode) || s_operand (dst, mode))
-    return false;
-
   /* Non-offsettable memory references cannot be split.  */
   if ((GET_CODE (src) == MEM && !offsettable_memref_p (src))
       || (GET_CODE (dst) == MEM && !offsettable_memref_p (dst)))
@@ -2776,13 +2816,10 @@ s390_decompose_address (rtx addr, struct s390_address *out)
      displacements by basing them off the base register.  */
   if (disp && GET_CODE (disp) == SYMBOL_REF && CONSTANT_POOL_ADDRESS_P (disp))
     {
-      /* Either base or index must be free to hold the base register.  */
-      if (!base)
-        base = fake_pool_base, literal_pool = true;
-      else if (!indx)
-        indx = fake_pool_base, literal_pool = true;
-      else
-        return false;
+      if (base || indx)
+       return false;
+
+      base = fake_pool_base, literal_pool = true;
 
       /* Mark up the displacement.  */
       disp = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, disp),
@@ -3101,6 +3138,9 @@ s390_check_qrst_address (char c, rtx op, bool lit_pool_ok)
   struct s390_address addr;
   bool decomposed = false;
 
+  if (!address_operand (op, GET_MODE (op)))
+    return 0;
+
   /* This check makes sure that no symbolic address (except literal
      pool references) are accepted by the R or T constraints.  */
   if (s390_loadrelative_operand_p (op, NULL, NULL))
@@ -3116,6 +3156,19 @@ s390_check_qrst_address (char c, rtx op, bool lit_pool_ok)
       decomposed = true;
     }
 
+  /* With reload, we sometimes get intermediate address forms that are
+     actually invalid as-is, but we need to accept them in the most
+     generic cases below ('R' or 'T'), since reload will in fact fix
+     them up.  LRA behaves differently here; we never see such forms,
+     but on the other hand, we need to strictly reject every invalid
+     address form.  Perform this check right up front.  */
+  if (lra_in_progress)
+    {
+      if (!decomposed && !s390_decompose_address (op, &addr))
+       return 0;
+      decomposed = true;
+    }
+
   switch (c)
     {
     case 'Q': /* no index short displacement */
@@ -3140,25 +3193,17 @@ s390_check_qrst_address (char c, rtx op, bool lit_pool_ok)
       break;
 
     case 'S': /* no index long displacement */
-      if (!TARGET_LONG_DISPLACEMENT)
-       return 0;
       if (!decomposed && !s390_decompose_address (op, &addr))
        return 0;
       if (addr.indx)
        return 0;
-      if (s390_short_displacement (addr.disp))
-       return 0;
       break;
 
     case 'T': /* with index long displacement */
-      if (!TARGET_LONG_DISPLACEMENT)
-       return 0;
       /* Any invalid address here will be fixed up by reload,
         so accept it for the most generic constraint.  */
-      if ((decomposed || s390_decompose_address (op, &addr))
-         && s390_short_displacement (addr.disp))
-       return 0;
       break;
+
     default:
       return 0;
     }
@@ -3167,7 +3212,7 @@ s390_check_qrst_address (char c, rtx op, bool lit_pool_ok)
 
 
 /* Evaluates constraint strings described by the regular expression
-   ([A|B|Z](Q|R|S|T))|U|W|Y and returns 1 if OP is a valid operand for
+   ([A|B|Z](Q|R|S|T))|Y and returns 1 if OP is a valid operand for
    the constraint given in STR, or 0 else.  */
 
 int
@@ -3197,12 +3242,6 @@ s390_mem_constraint (const char *str, rtx op)
       if (GET_CODE (op) != MEM)
        return 0;
       return s390_check_qrst_address (c, XEXP (op, 0), true);
-    case 'U':
-      return (s390_check_qrst_address ('Q', op, true)
-             || s390_check_qrst_address ('R', op, true));
-    case 'W':
-      return (s390_check_qrst_address ('S', op, true)
-             || s390_check_qrst_address ('T', op, true));
     case 'Y':
       /* Simply check for the basic form of a shift count.  Reload will
         take care of making sure we have a proper base register.  */
@@ -3371,8 +3410,10 @@ s390_memory_move_cost (machine_mode mode ATTRIBUTE_UNUSED,
 
 /* Compute a (partial) cost for rtx X.  Return true if the complete
    cost has been computed, and false if subexpressions should be
-   scanned.  In either case, *TOTAL contains the cost result.
-   OUTER_CODE contains the code of the superexpression of x.  */
+   scanned.  In either case, *TOTAL contains the cost result.  The
+   initial value of *TOTAL is the default value computed by
+   rtx_cost.  It may be left unmodified.  OUTER_CODE contains the
+   code of the superexpression of x.  */
 
 static bool
 s390_rtx_costs (rtx x, machine_mode mode, int outer_code,
@@ -3392,6 +3433,48 @@ s390_rtx_costs (rtx x, machine_mode mode, int outer_code,
       *total = 0;
       return true;
 
+    case SET:
+      {
+       /* Without this a conditional move instruction would be
+          accounted as 3 * COSTS_N_INSNS (set, if_then_else,
+          comparison operator).  That's a bit pessimistic.  */
+
+       if (!TARGET_Z196 || GET_CODE (SET_SRC (x)) != IF_THEN_ELSE)
+         return false;
+
+       rtx cond = XEXP (SET_SRC (x), 0);
+
+       if (!CC_REG_P (XEXP (cond, 0)) || !CONST_INT_P (XEXP (cond, 1)))
+         return false;
+
+       /* It is going to be a load/store on condition.  Make it
+          slightly more expensive than a normal load.  */
+       *total = COSTS_N_INSNS (1) + 1;
+
+       rtx dst = SET_DEST (x);
+       rtx then = XEXP (SET_SRC (x), 1);
+       rtx els = XEXP (SET_SRC (x), 2);
+
+       /* It is a real IF-THEN-ELSE.  An additional move will be
+          needed to implement that.  */
+       if (reload_completed
+           && !rtx_equal_p (dst, then)
+           && !rtx_equal_p (dst, els))
+         *total += COSTS_N_INSNS (1) / 2;
+
+       /* A minor penalty for constants we cannot directly handle.  */
+       if ((CONST_INT_P (then) || CONST_INT_P (els))
+           && (!TARGET_Z13 || MEM_P (dst)
+               || (CONST_INT_P (then) && !satisfies_constraint_K (then))
+               || (CONST_INT_P (els) && !satisfies_constraint_K (els))))
+         *total += COSTS_N_INSNS (1) / 2;
+
+       /* A store on condition can only handle register src operands.  */
+       if (MEM_P (dst) && (!REG_P (then) || !REG_P (els)))
+         *total += COSTS_N_INSNS (1) / 2;
+
+       return true;
+      }
     case IOR:
       /* risbg */
       if (GET_CODE (XEXP (x, 0)) == AND
@@ -3401,11 +3484,27 @@ s390_rtx_costs (rtx x, machine_mode mode, int outer_code,
          && CONST_INT_P (XEXP (XEXP (x, 0), 1))
          && CONST_INT_P (XEXP (XEXP (x, 1), 1))
          && (UINTVAL (XEXP (XEXP (x, 0), 1)) ==
-             (1UL << UINTVAL (XEXP (XEXP (x, 1), 1))) - 1))
+             (HOST_WIDE_INT_1U << UINTVAL (XEXP (XEXP (x, 1), 1))) - 1))
        {
          *total = COSTS_N_INSNS (2);
          return true;
        }
+
+      /* ~AND on a 128 bit mode.  This can be done using a vector
+        instruction.  */
+      if (TARGET_VXE
+         && GET_CODE (XEXP (x, 0)) == NOT
+         && GET_CODE (XEXP (x, 1)) == NOT
+         && REG_P (XEXP (XEXP (x, 0), 0))
+         && REG_P (XEXP (XEXP (x, 1), 0))
+         && GET_MODE_SIZE (GET_MODE (XEXP (XEXP (x, 0), 0))) == 16
+         && s390_hard_regno_mode_ok (VR0_REGNUM,
+                                     GET_MODE (XEXP (XEXP (x, 0), 0))))
+       {
+         *total = COSTS_N_INSNS (1);
+         return true;
+       }
+      /* fallthrough */
     case ASHIFT:
     case ASHIFTRT:
     case LSHIFTRT:
@@ -3426,7 +3525,7 @@ s390_rtx_costs (rtx x, machine_mode mode, int outer_code,
     case MULT:
       switch (mode)
        {
-       case SImode:
+       case E_SImode:
          {
            rtx left = XEXP (x, 0);
            rtx right = XEXP (x, 1);
@@ -3439,7 +3538,7 @@ s390_rtx_costs (rtx x, machine_mode mode, int outer_code,
              *total = s390_cost->ms;  /* msr, ms, msy */
            break;
          }
-       case DImode:
+       case E_DImode:
          {
            rtx left = XEXP (x, 0);
            rtx right = XEXP (x, 1);
@@ -3470,11 +3569,11 @@ s390_rtx_costs (rtx x, machine_mode mode, int outer_code,
              }
            break;
          }
-       case SFmode:
-       case DFmode:
+       case E_SFmode:
+       case E_DFmode:
          *total = s390_cost->mult_df;
          break;
-       case TFmode:
+       case E_TFmode:
          *total = s390_cost->mxbr;
          break;
        default:
@@ -3485,10 +3584,10 @@ s390_rtx_costs (rtx x, machine_mode mode, int outer_code,
     case FMA:
       switch (mode)
        {
-       case DFmode:
+       case E_DFmode:
          *total = s390_cost->madbr;
          break;
-       case SFmode:
+       case E_SFmode:
          *total = s390_cost->maebr;
          break;
        default:
@@ -3604,6 +3703,40 @@ s390_address_cost (rtx addr, machine_mode mode ATTRIBUTE_UNUSED,
   return ad.indx? COSTS_N_INSNS (1) + 1 : COSTS_N_INSNS (1);
 }
 
+/* Implement targetm.vectorize.builtin_vectorization_cost.  */
+static int
+s390_builtin_vectorization_cost (enum vect_cost_for_stmt type_of_cost,
+                                tree vectype,
+                                int misalign ATTRIBUTE_UNUSED)
+{
+  switch (type_of_cost)
+    {
+      case scalar_stmt:
+      case scalar_load:
+      case scalar_store:
+      case vector_stmt:
+      case vector_load:
+      case vector_store:
+      case vec_to_scalar:
+      case scalar_to_vec:
+      case cond_branch_not_taken:
+      case vec_perm:
+      case vec_promote_demote:
+      case unaligned_load:
+      case unaligned_store:
+       return 1;
+
+      case cond_branch_taken:
+       return 3;
+
+      case vec_construct:
+       return TYPE_VECTOR_SUBPARTS (vectype) - 1;
+
+      default:
+       gcc_unreachable ();
+    }
+}
+
 /* If OP is a SYMBOL_REF of a thread-local symbol, return its TLS mode,
    otherwise return 0.  */
 
@@ -4174,7 +4307,7 @@ s390_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
                                               GET_MODE_SIZE (mode))))
        {
 #define __SECONDARY_RELOAD_CASE(M,m)                                   \
-         case M##mode:                                                 \
+         case E_##M##mode:                                             \
            if (TARGET_64BIT)                                           \
              sri->icode = in_p ? CODE_FOR_reload##m##di_toreg_z10 :    \
                                   CODE_FOR_reload##m##di_tomem_z10;    \
@@ -4276,6 +4409,48 @@ s390_secondary_reload (bool in_p, rtx x, reg_class_t rclass_i,
   return NO_REGS;
 }
 
+/* Implement TARGET_SECONDARY_MEMORY_NEEDED.
+
+   We need secondary memory to move data between GPRs and FPRs.
+
+   - With DFP the ldgr lgdr instructions are available.  Due to the
+     different alignment we cannot use them for SFmode.  For 31 bit a
+     64 bit value in GPR would be a register pair so here we still
+     need to go via memory.
+
+   - With z13 we can do the SF/SImode moves with vlgvf.  Due to the
+     overlapping of FPRs and VRs we still disallow TF/TD modes to be
+     in full VRs so as before also on z13 we do these moves via
+     memory.
+
+     FIXME: Should we try splitting it into two vlgvg's/vlvg's instead?  */
+
+static bool
+s390_secondary_memory_needed (machine_mode mode,
+                             reg_class_t class1, reg_class_t class2)
+{
+  return (((reg_classes_intersect_p (class1, VEC_REGS)
+           && reg_classes_intersect_p (class2, GENERAL_REGS))
+          || (reg_classes_intersect_p (class1, GENERAL_REGS)
+              && reg_classes_intersect_p (class2, VEC_REGS)))
+         && (!TARGET_DFP || !TARGET_64BIT || GET_MODE_SIZE (mode) != 8)
+         && (!TARGET_VX || (SCALAR_FLOAT_MODE_P (mode)
+                            && GET_MODE_SIZE (mode) > 8)));
+}
+
+/* Implement TARGET_SECONDARY_MEMORY_NEEDED_MODE.
+
+   get_secondary_mem widens its argument to BITS_PER_WORD which loses on 64bit
+   because the movsi and movsf patterns don't handle r/f moves.  */
+
+static machine_mode
+s390_secondary_memory_needed_mode (machine_mode mode)
+{
+  if (GET_MODE_BITSIZE (mode) < 32)
+    return mode_for_size (32, GET_MODE_CLASS (mode), 0).require ();
+  return mode;
+}
+
 /* Generate code to load SRC, which is PLUS that is not a
    legitimate operand for the LA instruction, into TARGET.
    SCRATCH may be used as scratch register.  */
@@ -4441,6 +4616,26 @@ s390_load_address (rtx dst, rtx src)
     emit_insn (gen_force_la_31 (dst, src));
 }
 
+/* Return true if it ok to use SYMBOL_REF in a relative address.  */
+
+bool
+s390_rel_address_ok_p (rtx symbol_ref)
+{
+  tree decl;
+
+  if (symbol_ref == s390_got_symbol () || CONSTANT_POOL_ADDRESS_P (symbol_ref))
+    return true;
+
+  decl = SYMBOL_REF_DECL (symbol_ref);
+
+  if (!flag_pic || SYMBOL_REF_LOCAL_P (symbol_ref))
+    return (s390_pic_data_is_text_relative
+           || (decl
+               && TREE_CODE (decl) == FUNCTION_DECL));
+
+  return false;
+}
+
 /* Return a legitimate reference for ORIG (an address) using the
    register REG.  If REG is 0, a new pseudo is generated.
 
@@ -4478,7 +4673,7 @@ legitimize_pic_address (rtx orig, rtx reg)
     }
 
   if ((GET_CODE (addr) == LABEL_REF
-       || (GET_CODE (addr) == SYMBOL_REF && SYMBOL_REF_LOCAL_P (addr))
+       || (SYMBOL_REF_P (addr) && s390_rel_address_ok_p (addr))
        || (GET_CODE (addr) == UNSPEC &&
           (XINT (addr, 1) == UNSPEC_GOTENT
            || (TARGET_CPU_ZARCH && XINT (addr, 1) == UNSPEC_PLT))))
@@ -4492,8 +4687,8 @@ legitimize_pic_address (rtx orig, rtx reg)
 
       if (TARGET_CPU_ZARCH
          && larl_operand (const_addr, VOIDmode)
-         && INTVAL (addend) < (HOST_WIDE_INT)1 << 31
-         && INTVAL (addend) >= -((HOST_WIDE_INT)1 << 31))
+         && INTVAL (addend) < HOST_WIDE_INT_1 << 31
+         && INTVAL (addend) >= -(HOST_WIDE_INT_1 << 31))
        {
          if (INTVAL (addend) & 1)
            {
@@ -4777,7 +4972,8 @@ s390_emit_tls_call_insn (rtx result_reg, rtx tls_call)
 static rtx
 legitimize_tls_address (rtx addr, rtx reg)
 {
-  rtx new_rtx, tls_call, temp, base, r2, insn;
+  rtx new_rtx, tls_call, temp, base, r2;
+  rtx_insn *insn;
 
   if (GET_CODE (addr) == SYMBOL_REF)
     switch (tls_symbolic_operand (addr))
@@ -5141,10 +5337,25 @@ s390_expand_movmem (rtx dst, rtx src, rtx len)
       && (GET_CODE (len) != CONST_INT || INTVAL (len) > (1<<16)))
     return false;
 
-  if (GET_CODE (len) == CONST_INT && INTVAL (len) >= 0 && INTVAL (len) <= 256)
+  /* Expand memcpy for constant length operands without a loop if it
+     is shorter that way.
+
+     With a constant length argument a
+     memcpy loop (without pfd) is 36 bytes -> 6 * mvc  */
+  if (GET_CODE (len) == CONST_INT
+      && INTVAL (len) >= 0
+      && INTVAL (len) <= 256 * 6
+      && (!TARGET_MVCLE || INTVAL (len) <= 256))
     {
-      if (INTVAL (len) > 0)
-        emit_insn (gen_movmem_short (dst, src, GEN_INT (INTVAL (len) - 1)));
+      HOST_WIDE_INT o, l;
+
+      for (l = INTVAL (len), o = 0; l > 0; l -= 256, o += 256)
+       {
+         rtx newdst = adjust_address (dst, BLKmode, o);
+         rtx newsrc = adjust_address (src, BLKmode, o);
+         emit_insn (gen_movmem_short (newdst, newsrc,
+                                      GEN_INT (l > 256 ? 255 : l - 1)));
+       }
     }
 
   else if (TARGET_MVCLE)
@@ -5241,34 +5452,46 @@ s390_expand_movmem (rtx dst, rtx src, rtx len)
 void
 s390_expand_setmem (rtx dst, rtx len, rtx val)
 {
-  if (GET_CODE (len) == CONST_INT && INTVAL (len) == 0)
+  if (GET_CODE (len) == CONST_INT && INTVAL (len) <= 0)
     return;
 
   gcc_assert (GET_CODE (val) == CONST_INT || GET_MODE (val) == QImode);
 
-  if (GET_CODE (len) == CONST_INT && INTVAL (len) > 0 && INTVAL (len) <= 257)
+  /* Expand setmem/clrmem for a constant length operand without a
+     loop if it will be shorter that way.
+     With a constant length and without pfd argument a
+     clrmem loop is 32 bytes -> 5.3 * xc
+     setmem loop is 36 bytes -> 3.6 * (mvi/stc + mvc) */
+  if (GET_CODE (len) == CONST_INT
+      && ((INTVAL (len) <= 256 * 5 && val == const0_rtx)
+         || INTVAL (len) <= 257 * 3)
+      && (!TARGET_MVCLE || INTVAL (len) <= 256))
     {
-      if (val == const0_rtx && INTVAL (len) <= 256)
-        emit_insn (gen_clrmem_short (dst, GEN_INT (INTVAL (len) - 1)));
-      else
-       {
-         /* Initialize memory by storing the first byte.  */
-         emit_move_insn (adjust_address (dst, QImode, 0), val);
+      HOST_WIDE_INT o, l;
 
-         if (INTVAL (len) > 1)
-           {
-             /* Initiate 1 byte overlap move.
-                The first byte of DST is propagated through DSTP1.
-                Prepare a movmem for:  DST+1 = DST (length = LEN - 1).
-                DST is set to size 1 so the rest of the memory location
-                does not count as source operand.  */
-             rtx dstp1 = adjust_address (dst, VOIDmode, 1);
-             set_mem_size (dst, 1);
-
-             emit_insn (gen_movmem_short (dstp1, dst,
-                                          GEN_INT (INTVAL (len) - 2)));
-           }
-       }
+      if (val == const0_rtx)
+       /* clrmem: emit 256 byte blockwise XCs.  */
+       for (l = INTVAL (len), o = 0; l > 0; l -= 256, o += 256)
+         {
+           rtx newdst = adjust_address (dst, BLKmode, o);
+           emit_insn (gen_clrmem_short (newdst,
+                                        GEN_INT (l > 256 ? 255 : l - 1)));
+         }
+      else
+       /* setmem: emit 1(mvi) + 256(mvc) byte blockwise memsets by
+          setting first byte to val and using a 256 byte mvc with one
+          byte overlap to propagate the byte.  */
+       for (l = INTVAL (len), o = 0; l > 0; l -= 257, o += 257)
+         {
+           rtx newdst = adjust_address (dst, BLKmode, o);
+           emit_move_insn (adjust_address (dst, QImode, o), val);
+           if (l > 1)
+             {
+               rtx newdstp1 = adjust_address (dst, BLKmode, o + 1);
+               emit_insn (gen_movmem_short (newdstp1, newdst,
+                                            GEN_INT (l > 257 ? 255 : l - 2)));
+             }
+         }
     }
 
   else if (TARGET_MVCLE)
@@ -5286,13 +5509,14 @@ s390_expand_setmem (rtx dst, rtx len, rtx val)
     {
       rtx dst_addr, count, blocks, temp, dstp1 = NULL_RTX;
       rtx_code_label *loop_start_label = gen_label_rtx ();
-      rtx_code_label *loop_end_label = gen_label_rtx ();
-      rtx_code_label *end_label = gen_label_rtx ();
+      rtx_code_label *onebyte_end_label = gen_label_rtx ();
+      rtx_code_label *zerobyte_end_label = gen_label_rtx ();
+      rtx_code_label *restbyte_end_label = gen_label_rtx ();
       machine_mode mode;
 
       mode = GET_MODE (len);
       if (mode == VOIDmode)
-        mode = Pmode;
+       mode = Pmode;
 
       dst_addr = gen_reg_rtx (Pmode);
       count = gen_reg_rtx (mode);
@@ -5300,39 +5524,57 @@ s390_expand_setmem (rtx dst, rtx len, rtx val)
 
       convert_move (count, len, 1);
       emit_cmp_and_jump_insns (count, const0_rtx,
-                              EQ, NULL_RTX, mode, 1, end_label);
+                              EQ, NULL_RTX, mode, 1, zerobyte_end_label,
+                              profile_probability::very_unlikely ());
 
+      /* We need to make a copy of the target address since memset is
+        supposed to return it unmodified.  We have to make it here
+        already since the new reg is used at onebyte_end_label.  */
       emit_move_insn (dst_addr, force_operand (XEXP (dst, 0), NULL_RTX));
       dst = change_address (dst, VOIDmode, dst_addr);
 
-      if (val == const0_rtx)
-        temp = expand_binop (mode, add_optab, count, constm1_rtx, count, 1,
-                            OPTAB_DIRECT);
-      else
+      if (val != const0_rtx)
        {
-         dstp1 = adjust_address (dst, VOIDmode, 1);
+         /* When using the overlapping mvc the original target
+            address is only accessed as single byte entity (even by
+            the mvc reading this value).  */
          set_mem_size (dst, 1);
-
-         /* Initialize memory by storing the first byte.  */
-         emit_move_insn (adjust_address (dst, QImode, 0), val);
-
-         /* If count is 1 we are done.  */
-         emit_cmp_and_jump_insns (count, const1_rtx,
-                                  EQ, NULL_RTX, mode, 1, end_label);
-
-         temp = expand_binop (mode, add_optab, count, GEN_INT (-2), count, 1,
-                              OPTAB_DIRECT);
-       }
+         dstp1 = adjust_address (dst, VOIDmode, 1);
+         emit_cmp_and_jump_insns (count,
+                                  const1_rtx, EQ, NULL_RTX, mode, 1,
+                                  onebyte_end_label,
+                                  profile_probability::very_unlikely ());
+       }
+
+      /* There is one unconditional (mvi+mvc)/xc after the loop
+        dealing with the rest of the bytes, subtracting two (mvi+mvc)
+        or one (xc) here leaves this number of bytes to be handled by
+        it.  */
+      temp = expand_binop (mode, add_optab, count,
+                          val == const0_rtx ? constm1_rtx : GEN_INT (-2),
+                          count, 1, OPTAB_DIRECT);
       if (temp != count)
-        emit_move_insn (count, temp);
+       emit_move_insn (count, temp);
 
       temp = expand_binop (mode, lshr_optab, count, GEN_INT (8), blocks, 1,
                           OPTAB_DIRECT);
       if (temp != blocks)
-        emit_move_insn (blocks, temp);
+       emit_move_insn (blocks, temp);
 
       emit_cmp_and_jump_insns (blocks, const0_rtx,
-                              EQ, NULL_RTX, mode, 1, loop_end_label);
+                              EQ, NULL_RTX, mode, 1, restbyte_end_label);
+
+      emit_jump (loop_start_label);
+
+      if (val != const0_rtx)
+       {
+         /* The 1 byte != 0 special case.  Not handled efficiently
+            since we require two jumps for that.  However, this
+            should be very rare.  */
+         emit_label (onebyte_end_label);
+         emit_move_insn (adjust_address (dst, QImode, 0), val);
+         emit_jump (zerobyte_end_label);
+       }
 
       emit_label (loop_start_label);
 
@@ -5350,26 +5592,39 @@ s390_expand_setmem (rtx dst, rtx len, rtx val)
       if (val == const0_rtx)
        emit_insn (gen_clrmem_short (dst, GEN_INT (255)));
       else
-       emit_insn (gen_movmem_short (dstp1, dst, GEN_INT (255)));
+       {
+         /* Set the first byte in the block to the value and use an
+            overlapping mvc for the block.  */
+         emit_move_insn (adjust_address (dst, QImode, 0), val);
+         emit_insn (gen_movmem_short (dstp1, dst, GEN_INT (254)));
+       }
       s390_load_address (dst_addr,
                         gen_rtx_PLUS (Pmode, dst_addr, GEN_INT (256)));
 
       temp = expand_binop (mode, add_optab, blocks, constm1_rtx, blocks, 1,
                           OPTAB_DIRECT);
       if (temp != blocks)
-        emit_move_insn (blocks, temp);
+       emit_move_insn (blocks, temp);
 
       emit_cmp_and_jump_insns (blocks, const0_rtx,
-                              EQ, NULL_RTX, mode, 1, loop_end_label);
+                              NE, NULL_RTX, mode, 1, loop_start_label);
 
-      emit_jump (loop_start_label);
-      emit_label (loop_end_label);
+      emit_label (restbyte_end_label);
 
       if (val == const0_rtx)
-        emit_insn (gen_clrmem_short (dst, convert_to_mode (Pmode, count, 1)));
+       emit_insn (gen_clrmem_short (dst, convert_to_mode (Pmode, count, 1)));
       else
-        emit_insn (gen_movmem_short (dstp1, dst, convert_to_mode (Pmode, count, 1)));
-      emit_label (end_label);
+       {
+         /* Set the first byte in the block to the value and use an
+            overlapping mvc for the block.  */
+         emit_move_insn (adjust_address (dst, QImode, 0), val);
+         /* execute only uses the lowest 8 bits of count that's
+            exactly what we need here.  */
+         emit_insn (gen_movmem_short (dstp1, dst,
+                                      convert_to_mode (Pmode, count, 1)));
+       }
+
+      emit_label (zerobyte_end_label);
     }
 }
 
@@ -5502,7 +5757,7 @@ s390_expand_cmpmem (rtx target, rtx op0, rtx op1, rtx len)
 /* Emit a conditional jump to LABEL for condition code mask MASK using
    comparsion operator COMPARISON.  Return the emitted jump insn.  */
 
-static rtx
+static rtx_insn *
 s390_emit_ccraw_jump (HOST_WIDE_INT mask, enum rtx_code comparison, rtx label)
 {
   rtx temp;
@@ -5526,8 +5781,6 @@ s390_emit_ccraw_jump (HOST_WIDE_INT mask, enum rtx_code comparison, rtx label)
 void
 s390_expand_vec_strlen (rtx target, rtx string, rtx alignment)
 {
-  int very_unlikely = REG_BR_PROB_BASE / 100 - 1;
-  int very_likely = REG_BR_PROB_BASE - 1;
   rtx highest_index_to_load_reg = gen_reg_rtx (Pmode);
   rtx str_reg = gen_reg_rtx (V16QImode);
   rtx str_addr_base_reg = gen_reg_rtx (Pmode);
@@ -5598,8 +5851,9 @@ s390_expand_vec_strlen (rtx target, rtx string, rtx alignment)
                                  GEN_INT (VSTRING_FLAG_ZS | VSTRING_FLAG_CS)));
 
   add_int_reg_note (s390_emit_ccraw_jump (8, NE, loop_start_label),
-                   REG_BR_PROB, very_likely);
-  emit_insn (gen_vec_extractv16qi (len, result_reg, GEN_INT (7)));
+                   REG_BR_PROB,
+                   profile_probability::very_likely ().to_reg_br_prob_note ());
+  emit_insn (gen_vec_extractv16qiqi (len, result_reg, GEN_INT (7)));
 
   /* If the string pointer wasn't aligned we have loaded less then 16
      bytes and the remaining bytes got filled with zeros (by vll).
@@ -5618,8 +5872,8 @@ s390_expand_vec_strlen (rtx target, rtx string, rtx alignment)
     emit_insn (gen_movsicc (str_idx_reg, cond,
                            highest_index_to_load_reg, str_idx_reg));
 
-  add_int_reg_note (s390_emit_jump (is_aligned_label, cond), REG_BR_PROB,
-                   very_unlikely);
+  add_reg_br_prob_note (s390_emit_jump (is_aligned_label, cond),
+                       profile_probability::very_unlikely ());
 
   expand_binop (Pmode, add_optab, str_idx_reg,
                GEN_INT (-16), str_idx_reg, 1, OPTAB_DIRECT);
@@ -5635,7 +5889,6 @@ s390_expand_vec_strlen (rtx target, rtx string, rtx alignment)
 void
 s390_expand_vec_movstr (rtx result, rtx dst, rtx src)
 {
-  int very_unlikely = REG_BR_PROB_BASE / 100 - 1;
   rtx temp = gen_reg_rtx (Pmode);
   rtx src_addr = XEXP (src, 0);
   rtx dst_addr = XEXP (dst, 0);
@@ -5658,7 +5911,7 @@ s390_expand_vec_movstr (rtx result, rtx dst, rtx src)
   emit_insn (gen_vlbb (vsrc, src, GEN_INT (6)));
   emit_insn (gen_lcbb (loadlen, src_addr, GEN_INT (6)));
   emit_insn (gen_vfenezv16qi (vpos, vsrc, vsrc));
-  emit_insn (gen_vec_extractv16qi (gpos_qi, vpos, GEN_INT (7)));
+  emit_insn (gen_vec_extractv16qiqi (gpos_qi, vpos, GEN_INT (7)));
   emit_move_insn (gpos, gen_rtx_SUBREG (SImode, gpos_qi, 0));
   /* gpos is the byte index if a zero was found and 16 otherwise.
      So if it is lower than the loaded bytes we have a hit.  */
@@ -5712,7 +5965,8 @@ s390_expand_vec_movstr (rtx result, rtx dst, rtx src)
   emit_insn (gen_vec_vfenesv16qi (vpos, vsrc, vsrc,
                                  GEN_INT (VSTRING_FLAG_ZS | VSTRING_FLAG_CS)));
   add_int_reg_note (s390_emit_ccraw_jump (8, EQ, done_label),
-                   REG_BR_PROB, very_unlikely);
+                   REG_BR_PROB, profile_probability::very_unlikely ()
+                                 .to_reg_br_prob_note ());
 
   emit_move_insn (gen_rtx_MEM (V16QImode,
                               gen_rtx_PLUS (Pmode, dst_addr_reg, offset)),
@@ -5735,7 +5989,7 @@ s390_expand_vec_movstr (rtx result, rtx dst, rtx src)
   force_expand_binop (Pmode, add_optab, dst_addr_reg, offset, dst_addr_reg,
                      1, OPTAB_DIRECT);
 
-  emit_insn (gen_vec_extractv16qi (gpos_qi, vpos, GEN_INT (7)));
+  emit_insn (gen_vec_extractv16qiqi (gpos_qi, vpos, GEN_INT (7)));
   emit_move_insn (gpos, gen_rtx_SUBREG (SImode, gpos_qi, 0));
 
   emit_insn (gen_vstlv16qi (vsrc, gpos, gen_rtx_MEM (BLKmode, dst_addr_reg)));
@@ -5979,7 +6233,7 @@ s390_expand_insv (rtx dest, rtx op1, rtx op2, rtx src)
       return true;
     }
 
-  smode = smallest_mode_for_size (bitsize, MODE_INT);
+  smode = smallest_int_mode_for_size (bitsize);
   smode_bsize = GET_MODE_BITSIZE (smode);
   mode_bsize = GET_MODE_BITSIZE (mode);
 
@@ -6123,7 +6377,7 @@ s390_expand_vec_compare (rtx target, enum rtx_code cond,
   bool neg_p = false, swap_p = false;
   rtx tmp;
 
-  if (GET_MODE (cmp_op1) == V2DFmode)
+  if (GET_MODE_CLASS (GET_MODE (cmp_op1)) == MODE_VECTOR_FLOAT)
     {
       switch (cond)
        {
@@ -6192,13 +6446,15 @@ s390_expand_vec_compare (rtx target, enum rtx_code cond,
 
 /* Expand the comparison CODE of CMP1 and CMP2 and copy 1 or 0 into
    TARGET if either all (ALL_P is true) or any (ALL_P is false) of the
-   elements in CMP1 and CMP2 fulfill the comparison.  */
+   elements in CMP1 and CMP2 fulfill the comparison.
+   This function is only used to emit patterns for the vx builtins and
+   therefore only handles comparison codes required by the
+   builtins.  */
 void
 s390_expand_vec_compare_cc (rtx target, enum rtx_code code,
                            rtx cmp1, rtx cmp2, bool all_p)
 {
-  enum rtx_code new_code = code;
-  machine_mode cmp_mode, full_cmp_mode, scratch_mode;
+  machine_mode cc_producer_mode, cc_consumer_mode, scratch_mode;
   rtx tmp_reg = gen_reg_rtx (SImode);
   bool swap_p = false;
 
@@ -6206,53 +6462,68 @@ s390_expand_vec_compare_cc (rtx target, enum rtx_code code,
     {
       switch (code)
        {
-       case EQ:  cmp_mode = CCVEQmode; break;
-       case NE:  cmp_mode = CCVEQmode; break;
-       case GT:  cmp_mode = CCVHmode;  break;
-       case GE:  cmp_mode = CCVHmode;  new_code = LE; swap_p = true; break;
-       case LT:  cmp_mode = CCVHmode;  new_code = GT; swap_p = true; break;
-       case LE:  cmp_mode = CCVHmode;  new_code = LE; break;
-       case GTU: cmp_mode = CCVHUmode; break;
-       case GEU: cmp_mode = CCVHUmode; new_code = LEU; swap_p = true; break;
-       case LTU: cmp_mode = CCVHUmode; new_code = GTU; swap_p = true; break;
-       case LEU: cmp_mode = CCVHUmode; new_code = LEU; break;
-       default: gcc_unreachable ();
+       case EQ:
+       case NE:
+         cc_producer_mode = CCVEQmode;
+         break;
+       case GE:
+       case LT:
+         code = swap_condition (code);
+         swap_p = true;
+         /* fallthrough */
+       case GT:
+       case LE:
+         cc_producer_mode = CCVIHmode;
+         break;
+       case GEU:
+       case LTU:
+         code = swap_condition (code);
+         swap_p = true;
+         /* fallthrough */
+       case GTU:
+       case LEU:
+         cc_producer_mode = CCVIHUmode;
+         break;
+       default:
+         gcc_unreachable ();
        }
+
       scratch_mode = GET_MODE (cmp1);
+      /* These codes represent inverted CC interpretations.  Inverting
+        an ALL CC mode results in an ANY CC mode and the other way
+        around.  Invert the all_p flag here to compensate for
+        that.  */
+      if (code == NE || code == LE || code == LEU)
+       all_p = !all_p;
+
+      cc_consumer_mode = all_p ? CCVIALLmode : CCVIANYmode;
     }
-  else if (GET_MODE (cmp1) == V2DFmode)
+  else if (GET_MODE_CLASS (GET_MODE (cmp1)) == MODE_VECTOR_FLOAT)
     {
+      bool inv_p = false;
+
       switch (code)
        {
-       case EQ:   cmp_mode = CCVEQmode;  break;
-       case NE:   cmp_mode = CCVEQmode;  break;
-       case GT:   cmp_mode = CCVFHmode;  break;
-       case GE:   cmp_mode = CCVFHEmode; break;
-       case UNLE: cmp_mode = CCVFHmode;  break;
-       case UNLT: cmp_mode = CCVFHEmode; break;
-       case LT:   cmp_mode = CCVFHmode;  new_code = GT; swap_p = true; break;
-       case LE:   cmp_mode = CCVFHEmode; new_code = GE; swap_p = true; break;
+       case EQ:   cc_producer_mode = CCVEQmode;  break;
+       case NE:   cc_producer_mode = CCVEQmode;  inv_p = true; break;
+       case GT:   cc_producer_mode = CCVFHmode;  break;
+       case GE:   cc_producer_mode = CCVFHEmode; break;
+       case UNLE: cc_producer_mode = CCVFHmode;  inv_p = true; break;
+       case UNLT: cc_producer_mode = CCVFHEmode; inv_p = true; break;
+       case LT:   cc_producer_mode = CCVFHmode;  code = GT; swap_p = true; break;
+       case LE:   cc_producer_mode = CCVFHEmode; code = GE; swap_p = true; break;
        default: gcc_unreachable ();
        }
-      scratch_mode = V2DImode;
+      scratch_mode = mode_for_int_vector (GET_MODE (cmp1)).require ();
+
+      if (inv_p)
+       all_p = !all_p;
+
+      cc_consumer_mode = all_p ? CCVFALLmode : CCVFANYmode;
     }
   else
     gcc_unreachable ();
 
-  if (!all_p)
-    switch (cmp_mode)
-      {
-      case CCVEQmode:  full_cmp_mode = CCVEQANYmode;  break;
-      case CCVHmode:   full_cmp_mode = CCVHANYmode;   break;
-      case CCVHUmode:  full_cmp_mode = CCVHUANYmode;  break;
-      case CCVFHmode:  full_cmp_mode = CCVFHANYmode;  break;
-      case CCVFHEmode: full_cmp_mode = CCVFHEANYmode; break;
-      default: gcc_unreachable ();
-      }
-  else
-    /* The modes without ANY match the ALL modes.  */
-    full_cmp_mode = cmp_mode;
-
   if (swap_p)
     {
       rtx tmp = cmp2;
@@ -6262,8 +6533,8 @@ s390_expand_vec_compare_cc (rtx target, enum rtx_code code,
 
   emit_insn (gen_rtx_PARALLEL (VOIDmode,
               gen_rtvec (2, gen_rtx_SET (
-                              gen_rtx_REG (cmp_mode, CC_REGNUM),
-                              gen_rtx_COMPARE (cmp_mode, cmp1, cmp2)),
+                              gen_rtx_REG (cc_producer_mode, CC_REGNUM),
+                              gen_rtx_COMPARE (cc_producer_mode, cmp1, cmp2)),
                          gen_rtx_CLOBBER (VOIDmode,
                                           gen_rtx_SCRATCH (scratch_mode)))));
   emit_move_insn (target, const0_rtx);
@@ -6271,10 +6542,27 @@ s390_expand_vec_compare_cc (rtx target, enum rtx_code code,
 
   emit_move_insn (target,
                  gen_rtx_IF_THEN_ELSE (SImode,
-                   gen_rtx_fmt_ee (new_code, VOIDmode,
-                                   gen_rtx_REG (full_cmp_mode, CC_REGNUM),
+                   gen_rtx_fmt_ee (code, VOIDmode,
+                                   gen_rtx_REG (cc_consumer_mode, CC_REGNUM),
                                    const0_rtx),
-                     target, tmp_reg));
+                                       tmp_reg, target));
+}
+
+/* Invert the comparison CODE applied to a CC mode.  This is only safe
+   if we know whether there result was created by a floating point
+   compare or not.  For the CCV modes this is encoded as part of the
+   mode.  */
+enum rtx_code
+s390_reverse_condition (machine_mode mode, enum rtx_code code)
+{
+  /* Reversal of FP compares takes care -- an ordered compare
+     becomes an unordered compare and vice versa.  */
+  if (mode == CCVFALLmode || mode == CCVFANYmode)
+    return reverse_condition_maybe_unordered (code);
+  else if (mode == CCVIALLmode || mode == CCVIANYmode)
+    return reverse_condition (code);
+  else
+    gcc_unreachable ();
 }
 
 /* Generate a vector comparison expression loading either elements of
@@ -6332,7 +6620,7 @@ s390_expand_vcond (rtx target, rtx then, rtx els,
 
   /* We always use an integral type vector to hold the comparison
      result.  */
-  result_mode = cmp_mode == V2DFmode ? V2DImode : cmp_mode;
+  result_mode = mode_for_int_vector (cmp_mode).require ();
   result_target = gen_reg_rtx (result_mode);
 
   /* We allow vector immediates as comparison operands that
@@ -6424,7 +6712,10 @@ s390_expand_vec_init (rtx target, rtx vals)
       return;
     }
 
-  if (all_regs && REG_P (target) && n_elts == 2 && inner_mode == DImode)
+  if (all_regs
+      && REG_P (target)
+      && n_elts == 2
+      && GET_MODE_SIZE (inner_mode) == 8)
     {
       /* Use vector load pair.  */
       emit_insn (gen_rtx_SET (target,
@@ -6434,6 +6725,34 @@ s390_expand_vec_init (rtx target, rtx vals)
       return;
     }
 
+  /* Use vector load logical element and zero.  */
+  if (TARGET_VXE && (mode == V4SImode || mode == V4SFmode))
+    {
+      bool found = true;
+
+      x = XVECEXP (vals, 0, 0);
+      if (memory_operand (x, inner_mode))
+       {
+         for (i = 1; i < n_elts; ++i)
+           found = found && XVECEXP (vals, 0, i) == const0_rtx;
+
+         if (found)
+           {
+             machine_mode half_mode = (inner_mode == SFmode
+                                       ? V2SFmode : V2SImode);
+             emit_insn (gen_rtx_SET (target,
+                             gen_rtx_VEC_CONCAT (mode,
+                                                 gen_rtx_VEC_CONCAT (half_mode,
+                                                                     x,
+                                                                     const0_rtx),
+                                                 gen_rtx_VEC_CONCAT (half_mode,
+                                                                     const0_rtx,
+                                                                     const0_rtx))));
+             return;
+           }
+       }
+    }
+
   /* We are about to set the vector elements one by one.  Zero out the
      full register first in order to help the data flow framework to
      detect it as full VR set.  */
@@ -6442,11 +6761,17 @@ s390_expand_vec_init (rtx target, rtx vals)
   /* Unfortunately the vec_init expander is not allowed to fail.  So
      we have to implement the fallback ourselves.  */
   for (i = 0; i < n_elts; i++)
-    emit_insn (gen_rtx_SET (target,
-                           gen_rtx_UNSPEC (mode,
-                                           gen_rtvec (3, XVECEXP (vals, 0, i),
-                                                      GEN_INT (i), target),
-                                           UNSPEC_VEC_SET)));
+    {
+      rtx elem = XVECEXP (vals, 0, i);
+      if (!general_operand (elem, GET_MODE (elem)))
+       elem = force_reg (inner_mode, elem);
+
+      emit_insn (gen_rtx_SET (target,
+                             gen_rtx_UNSPEC (mode,
+                                             gen_rtvec (3, elem,
+                                                        GEN_INT (i), target),
+                                             UNSPEC_VEC_SET)));
+    }
 }
 
 /* Structure to hold the initial parameters for a compare_and_swap operation
@@ -6557,7 +6882,7 @@ s390_two_part_insv (struct alignment_context *ac, rtx *seq1, rtx *seq2,
    the memory location, CMP the old value to compare MEM with and NEW_RTX the
    value to set if CMP == MEM.  */
 
-void
+static void
 s390_expand_cs_hqi (machine_mode mode, rtx btarget, rtx vtarget, rtx mem,
                    rtx cmp, rtx new_rtx, bool is_weak)
 {
@@ -6604,7 +6929,7 @@ s390_expand_cs_hqi (machine_mode mode, rtx btarget, rtx vtarget, rtx mem,
   emit_insn (seq2);
   emit_insn (seq3);
 
-  cc = s390_emit_compare_and_swap (EQ, res, ac.memsi, cmpv, newv);
+  cc = s390_emit_compare_and_swap (EQ, res, ac.memsi, cmpv, newv, CCZ1mode);
   if (is_weak)
     emit_insn (gen_cstorecc4 (btarget, cc, XEXP (cc, 0), XEXP (cc, 1)));
   else
@@ -6633,6 +6958,151 @@ s390_expand_cs_hqi (machine_mode mode, rtx btarget, rtx vtarget, rtx mem,
                                              NULL_RTX, 1, OPTAB_DIRECT), 1);
 }
 
+/* Variant of s390_expand_cs for SI, DI and TI modes.  */
+static void
+s390_expand_cs_tdsi (machine_mode mode, rtx btarget, rtx vtarget, rtx mem,
+                    rtx cmp, rtx new_rtx, bool is_weak)
+{
+  rtx output = vtarget;
+  rtx_code_label *skip_cs_label = NULL;
+  bool do_const_opt = false;
+
+  if (!register_operand (output, mode))
+    output = gen_reg_rtx (mode);
+
+  /* If IS_WEAK is true and the INPUT value is a constant, compare the memory
+     with the constant first and skip the compare_and_swap because its very
+     expensive and likely to fail anyway.
+     Note 1: This is done only for IS_WEAK.  C11 allows optimizations that may
+     cause spurious in that case.
+     Note 2: It may be useful to do this also for non-constant INPUT.
+     Note 3: Currently only targets with "load on condition" are supported
+     (z196 and newer).  */
+
+  if (TARGET_Z196
+      && (mode == SImode || mode == DImode))
+    do_const_opt = (is_weak && CONST_INT_P (cmp));
+
+  if (do_const_opt)
+    {
+      rtx cc = gen_rtx_REG (CCZmode, CC_REGNUM);
+
+      skip_cs_label = gen_label_rtx ();
+      emit_move_insn (btarget, const0_rtx);
+      if (CONST_INT_P (cmp) && INTVAL (cmp) == 0)
+       {
+         rtvec lt = rtvec_alloc (2);
+
+         /* Load-and-test + conditional jump.  */
+         RTVEC_ELT (lt, 0)
+           = gen_rtx_SET (cc, gen_rtx_COMPARE (CCZmode, mem, cmp));
+         RTVEC_ELT (lt, 1) = gen_rtx_SET (output, mem);
+         emit_insn (gen_rtx_PARALLEL (VOIDmode, lt));
+       }
+      else
+       {
+         emit_move_insn (output, mem);
+         emit_insn (gen_rtx_SET (cc, gen_rtx_COMPARE (CCZmode, output, cmp)));
+       }
+      s390_emit_jump (skip_cs_label, gen_rtx_NE (VOIDmode, cc, const0_rtx));
+      add_reg_br_prob_note (get_last_insn (), 
+                           profile_probability::very_unlikely ());
+      /* If the jump is not taken, OUTPUT is the expected value.  */
+      cmp = output;
+      /* Reload newval to a register manually, *after* the compare and jump
+        above.  Otherwise Reload might place it before the jump.  */
+    }
+  else
+    cmp = force_reg (mode, cmp);
+  new_rtx = force_reg (mode, new_rtx);
+  s390_emit_compare_and_swap (EQ, output, mem, cmp, new_rtx,
+                             (do_const_opt) ? CCZmode : CCZ1mode);
+  if (skip_cs_label != NULL)
+    emit_label (skip_cs_label);
+
+  /* We deliberately accept non-register operands in the predicate
+     to ensure the write back to the output operand happens *before*
+     the store-flags code below.  This makes it easier for combine
+     to merge the store-flags code with a potential test-and-branch
+     pattern following (immediately!) afterwards.  */
+  if (output != vtarget)
+    emit_move_insn (vtarget, output);
+
+  if (do_const_opt)
+    {
+      rtx cc, cond, ite;
+
+      /* Do not use gen_cstorecc4 here because it writes either 1 or 0, but
+        btarget has already been initialized with 0 above.  */
+      cc = gen_rtx_REG (CCZmode, CC_REGNUM);
+      cond = gen_rtx_EQ (VOIDmode, cc, const0_rtx);
+      ite = gen_rtx_IF_THEN_ELSE (SImode, cond, const1_rtx, btarget);
+      emit_insn (gen_rtx_SET (btarget, ite));
+    }
+  else
+    {
+      rtx cc, cond;
+
+      cc = gen_rtx_REG (CCZ1mode, CC_REGNUM);
+      cond = gen_rtx_EQ (SImode, cc, const0_rtx);
+      emit_insn (gen_cstorecc4 (btarget, cond, cc, const0_rtx));
+    }
+}
+
+/* Expand an atomic compare and swap operation.  MEM is 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 (machine_mode mode, rtx btarget, rtx vtarget, rtx mem,
+               rtx cmp, rtx new_rtx, bool is_weak)
+{
+  switch (mode)
+    {
+    case E_TImode:
+    case E_DImode:
+    case E_SImode:
+      s390_expand_cs_tdsi (mode, btarget, vtarget, mem, cmp, new_rtx, is_weak);
+      break;
+    case E_HImode:
+    case E_QImode:
+      s390_expand_cs_hqi (mode, btarget, vtarget, mem, cmp, new_rtx, is_weak);
+      break;
+    default:
+      gcc_unreachable ();
+    }
+}
+
+/* Expand an atomic_exchange operation simulated with a compare-and-swap loop.
+   The memory location MEM is set to INPUT.  OUTPUT is set to the previous value
+   of MEM.  */
+
+void
+s390_expand_atomic_exchange_tdsi (rtx output, rtx mem, rtx input)
+{
+  machine_mode mode = GET_MODE (mem);
+  rtx_code_label *csloop;
+
+  if (TARGET_Z196
+      && (mode == DImode || mode == SImode)
+      && CONST_INT_P (input) && INTVAL (input) == 0)
+    {
+      emit_move_insn (output, const0_rtx);
+      if (mode == DImode)
+       emit_insn (gen_atomic_fetch_anddi (output, mem, const0_rtx, input));
+      else
+       emit_insn (gen_atomic_fetch_andsi (output, mem, const0_rtx, input));
+      return;
+    }
+
+  input = force_reg (mode, input);
+  emit_move_insn (output, mem);
+  csloop = gen_label_rtx ();
+  emit_label (csloop);
+  s390_emit_jump (csloop, s390_emit_compare_and_swap (NE, output, mem, output,
+                                                     input, CCZ1mode));
+}
+
 /* Expand an atomic operation CODE of mode MODE.  MEM is the memory location
    and VAL the value to play with.  If AFTER is true then store the value
    MEM holds after the operation, if AFTER is false then store the value MEM
@@ -6712,7 +7182,8 @@ s390_expand_atomic (machine_mode mode, enum rtx_code code,
     }
 
   s390_emit_jump (csloop, s390_emit_compare_and_swap (NE, cmp,
-                                                     ac.memsi, cmp, new_rtx));
+                                                     ac.memsi, cmp, new_rtx,
+                                                     CCZ1mode));
 
   /* Return the correct part of the bitfield.  */
   if (target)
@@ -6945,7 +7416,8 @@ s390_asm_output_machine_for_arch (FILE *asm_out_file)
 {
   fprintf (asm_out_file, "\t.machinemode %s\n",
           (TARGET_ZARCH) ? "zarch" : "esa");
-  fprintf (asm_out_file, "\t.machine \"%s", processor_table[s390_arch].name);
+  fprintf (asm_out_file, "\t.machine \"%s",
+          processor_table[s390_arch].binutils_name);
   if (S390_USE_ARCHITECTURE_MODIFIERS)
     {
       int cpu_flags;
@@ -7012,11 +7484,11 @@ s390_asm_output_function_label (FILE *asm_out_file, const char *fname,
       /* Add a trampoline code area before the function label and initialize it
         with two-byte nop instructions.  This area can be overwritten with code
         that jumps to a patched version of the function.  */
-      asm_fprintf (asm_out_file, "\tnopr\t%%r7"
+      asm_fprintf (asm_out_file, "\tnopr\t%%r0"
                   "\t# pre-label NOPs for hotpatch (%d halfwords)\n",
                   hw_before);
       for (i = 1; i < hw_before; i++)
-       fputs ("\tnopr\t%r7\n", asm_out_file);
+       fputs ("\tnopr\t%r0\n", asm_out_file);
 
       /* Note:  The function label must be aligned so that (a) the bytes of the
         following nop do not cross a cacheline boundary, and (b) a jump address
@@ -7139,6 +7611,7 @@ void
 print_operand_address (FILE *file, rtx addr)
 {
   struct s390_address ad;
+  memset (&ad, 0, sizeof (s390_address));
 
   if (s390_loadrelative_operand_p (addr, NULL, NULL))
     {
@@ -7419,16 +7892,17 @@ print_operand (FILE *file, rtx x, int code)
        case 'e': case 'f':
        case 's': case 't':
          {
-           int pos, len;
+           int start, end;
+           int len;
            bool ok;
 
            len = (code == 's' || code == 'e' ? 64 : 32);
-           ok = s390_contiguous_bitmask_p (ival, len, &pos, &len);
+           ok = s390_contiguous_bitmask_p (ival, true, len, &start, &end);
            gcc_assert (ok);
            if (code == 's' || code == 't')
-             ival = 64 - pos - len;
+             ival = start;
            else
-             ival = 64 - 1 - pos;
+             ival = end;
          }
          break;
        default:
@@ -7468,16 +7942,12 @@ print_operand (FILE *file, rtx x, int code)
        case 'e':
        case 's':
          {
-           int start, stop, inner_len;
+           int start, end;
            bool ok;
 
-           inner_len = GET_MODE_UNIT_BITSIZE (GET_MODE (x));
-           ok = s390_contiguous_bitmask_vector_p (x, &start, &stop);
+           ok = s390_contiguous_bitmask_vector_p (x, &start, &end);
            gcc_assert (ok);
-           if (code == 's' || code == 't')
-             ival = inner_len - stop - 1;
-           else
-             ival = inner_len - start - 1;
+           ival = (code == 's') ? start : end;
            fprintf (file, HOST_WIDE_INT_PRINT_DEC, ival);
          }
          break;
@@ -7681,6 +8151,7 @@ s390_issue_rate (void)
         instruction gets issued per cycle.  */
     case PROCESSOR_2827_ZEC12:
     case PROCESSOR_2964_Z13:
+    case PROCESSOR_3906_Z14:
     default:
       return 1;
     }
@@ -9071,7 +9542,8 @@ s390_output_pool_entry (rtx exp, machine_mode mode, unsigned int align)
     case MODE_DECIMAL_FLOAT:
       gcc_assert (GET_CODE (exp) == CONST_DOUBLE);
 
-      assemble_real (*CONST_DOUBLE_REAL_VALUE (exp), mode, align);
+      assemble_real (*CONST_DOUBLE_REAL_VALUE (exp),
+                    as_a <scalar_float_mode> (mode), align);
       break;
 
     case MODE_INT:
@@ -9200,7 +9672,7 @@ s390_reg_clobbered_rtx (rtx setreg, const_rtx set_insn ATTRIBUTE_UNUSED, void *d
     return;
 
   for (i = regno;
-       i < regno + HARD_REGNO_NREGS (regno, mode);
+       i < end_hard_regno (mode, regno);
        i++)
     regs_ever_clobbered[i] = 1;
 }
@@ -9347,6 +9819,12 @@ s390_register_info_gprtofpr ()
   if (!TARGET_Z10 || !TARGET_HARD_FLOAT || !crtl->is_leaf)
     return;
 
+  /* builtin_eh_return needs to be able to modify the return address
+     on the stack.  It could also adjust the FPR save slot instead but
+     is it worth the trouble?!  */
+  if (crtl->calls_eh_return)
+    return;
+
   for (i = 15; i >= 6; i--)
     {
       if (cfun_gpr_save_slot (i) == SAVE_SLOT_NONE)
@@ -9947,9 +10425,34 @@ s390_optimize_nonescaping_tx (void)
   return;
 }
 
-/* Return true if it is legal to put a value with MODE into REGNO.  */
+/* Implement TARGET_HARD_REGNO_NREGS.  Because all registers in a class
+   have the same size, this is equivalent to CLASS_MAX_NREGS.  */
 
-bool
+static unsigned int
+s390_hard_regno_nregs (unsigned int regno, machine_mode mode)
+{
+  return s390_class_max_nregs (REGNO_REG_CLASS (regno), mode);
+}
+
+/* Implement TARGET_HARD_REGNO_MODE_OK.
+
+   Integer modes <= word size fit into any GPR.
+   Integer modes > word size fit into successive GPRs, starting with
+   an even-numbered register.
+   SImode and DImode fit into FPRs as well.
+
+   Floating point modes <= word size fit into any FPR or GPR.
+   Floating point modes > word size (i.e. DFmode on 32-bit) fit
+   into any FPR, or an even-odd GPR pair.
+   TFmode fits only into an even-odd FPR pair.
+
+   Complex floating point modes fit either into two FPRs, or into
+   successive GPRs (again starting with an even number).
+   TCmode fits only into two successive even-odd FPR pairs.
+
+   Condition code modes fit only into the CC register.  */
+
+static bool
 s390_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
 {
   if (!TARGET_VX && VECTOR_NOFP_REGNO_P (regno))
@@ -9961,6 +10464,7 @@ s390_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
       return ((GET_MODE_CLASS (mode) == MODE_INT
               && s390_class_max_nregs (VEC_REGS, mode) == 1)
              || mode == DFmode
+             || (TARGET_VXE && mode == SFmode)
              || s390_vector_mode_supported_p (mode));
       break;
     case FP_REGS:
@@ -10011,6 +10515,15 @@ s390_hard_regno_mode_ok (unsigned int regno, machine_mode mode)
   return false;
 }
 
+/* Implement TARGET_MODES_TIEABLE_P.  */
+
+static bool
+s390_modes_tieable_p (machine_mode mode1, machine_mode mode2)
+{
+  return ((mode1 == SFmode || mode1 == DFmode)
+         == (mode2 == SFmode || mode2 == DFmode));
+}
+
 /* Return nonzero if register OLD_REG can be renamed to register NEW_REG.  */
 
 bool
@@ -10051,6 +10564,29 @@ s390_hard_regno_scratch_ok (unsigned int regno)
   return true;
 }
 
+/* Implement TARGET_HARD_REGNO_CALL_PART_CLOBBERED.  When generating
+   code that runs in z/Architecture mode, but conforms to the 31-bit
+   ABI, GPRs can hold 8 bytes; the ABI guarantees only that the lower 4
+   bytes are saved across calls, however.  */
+
+static bool
+s390_hard_regno_call_part_clobbered (unsigned int regno, machine_mode mode)
+{
+  if (!TARGET_64BIT
+      && TARGET_ZARCH
+      && GET_MODE_SIZE (mode) > 4
+      && ((regno >= 6 && regno <= 15) || regno == 32))
+    return true;
+
+  if (TARGET_VX
+      && GET_MODE_SIZE (mode) > 8
+      && (((TARGET_64BIT && regno >= 24 && regno <= 31))
+         || (!TARGET_64BIT && (regno == 18 || regno == 19))))
+    return true;
+
+  return false;
+}
+
 /* Maximum number of registers to represent a value of mode MODE
    in a register of class RCLASS.  */
 
@@ -10105,6 +10641,13 @@ s390_cannot_change_mode_class (machine_mode from_mode,
   machine_mode small_mode;
   machine_mode big_mode;
 
+  /* V1TF and TF have different representations in vector
+     registers.  */
+  if (reg_classes_intersect_p (VEC_REGS, rclass)
+      && ((from_mode == V1TFmode && to_mode == TFmode)
+         || (from_mode == TFmode && to_mode == V1TFmode)))
+    return 1;
+
   if (GET_MODE_SIZE (from_mode) == GET_MODE_SIZE (to_mode))
     return 0;
 
@@ -10441,7 +10984,6 @@ restore_gprs (rtx base, int offset, int first, int last)
 
 /* Return insn sequence to load the GOT register.  */
 
-static GTY(()) rtx got_symbol;
 rtx_insn *
 s390_load_got (void)
 {
@@ -10453,23 +10995,17 @@ s390_load_got (void)
      aren't usable.  */
   rtx got_rtx = gen_rtx_REG (Pmode, 12);
 
-  if (!got_symbol)
-    {
-      got_symbol = gen_rtx_SYMBOL_REF (Pmode, "_GLOBAL_OFFSET_TABLE_");
-      SYMBOL_REF_FLAGS (got_symbol) = SYMBOL_FLAG_LOCAL;
-    }
-
   start_sequence ();
 
   if (TARGET_CPU_ZARCH)
     {
-      emit_move_insn (got_rtx, got_symbol);
+      emit_move_insn (got_rtx, s390_got_symbol ());
     }
   else
     {
       rtx offset;
 
-      offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, got_symbol),
+      offset = gen_rtx_UNSPEC (Pmode, gen_rtvec (1, s390_got_symbol ()),
                               UNSPEC_LTREL_OFFSET);
       offset = gen_rtx_CONST (Pmode, offset);
       offset = force_const_mem (Pmode, offset);
@@ -10521,7 +11057,7 @@ s390_save_gprs_to_fprs (void)
          /* This prevents dwarf2cfi from interpreting the set.  Doing
             so it might emit def_cfa_register infos setting an FPR as
             new CFA.  */
-         add_reg_note (insn, REG_CFA_REGISTER, PATTERN (insn));
+         add_reg_note (insn, REG_CFA_REGISTER, copy_rtx (PATTERN (insn)));
        }
     }
 }
@@ -11060,38 +11596,39 @@ s390_emit_epilogue (bool sibcall)
                                gen_rtx_REG (Pmode, i), cfa_restores);
        }
 
-      if (! sibcall)
-       {
-         /* Fetch return address from stack before load multiple,
-            this will do good for scheduling.
+      /* Fetch return address from stack before load multiple,
+        this will do good for scheduling.
 
-            Only do this if we already decided that r14 needs to be
-            saved to a stack slot. (And not just because r14 happens to
-            be in between two GPRs which need saving.)  Otherwise it
-            would be difficult to take that decision back in
-            s390_optimize_prologue.  */
-         if (cfun_gpr_save_slot (RETURN_REGNUM) == SAVE_SLOT_STACK)
-           {
-             int return_regnum = find_unused_clobbered_reg();
-             if (!return_regnum)
-               return_regnum = 4;
-             return_reg = gen_rtx_REG (Pmode, return_regnum);
+        Only do this if we already decided that r14 needs to be
+        saved to a stack slot. (And not just because r14 happens to
+        be in between two GPRs which need saving.)  Otherwise it
+        would be difficult to take that decision back in
+        s390_optimize_prologue.
 
-             addr = plus_constant (Pmode, frame_pointer,
-                                   offset + cfun_frame_layout.gprs_offset
-                                   + (RETURN_REGNUM
-                                      - cfun_frame_layout.first_save_gpr_slot)
-                                   * UNITS_PER_LONG);
-             addr = gen_rtx_MEM (Pmode, addr);
-             set_mem_alias_set (addr, get_frame_alias_set ());
-             emit_move_insn (return_reg, addr);
+        This optimization is only helpful on in-order machines.  */
+      if (! sibcall
+         && cfun_gpr_save_slot (RETURN_REGNUM) == SAVE_SLOT_STACK
+         && s390_tune <= PROCESSOR_2097_Z10)
+       {
+         int return_regnum = find_unused_clobbered_reg();
+         if (!return_regnum)
+           return_regnum = 4;
+         return_reg = gen_rtx_REG (Pmode, return_regnum);
 
-             /* Once we did that optimization we have to make sure
-                s390_optimize_prologue does not try to remove the
-                store of r14 since we will not be able to find the
-                load issued here.  */
-             cfun_frame_layout.save_return_addr_p = true;
-           }
+         addr = plus_constant (Pmode, frame_pointer,
+                               offset + cfun_frame_layout.gprs_offset
+                               + (RETURN_REGNUM
+                                  - cfun_frame_layout.first_save_gpr_slot)
+                               * UNITS_PER_LONG);
+         addr = gen_rtx_MEM (Pmode, addr);
+         set_mem_alias_set (addr, get_frame_alias_set ());
+         emit_move_insn (return_reg, addr);
+
+         /* Once we did that optimization we have to make sure
+            s390_optimize_prologue does not try to remove the store
+            of r14 since we will not be able to find the load issued
+            here.  */
+         cfun_frame_layout.save_return_addr_p = true;
        }
 
       insn = restore_gprs (frame_pointer,
@@ -11256,7 +11793,8 @@ s390_expand_split_stack_prologue (void)
       LABEL_NUSES (call_done)++;
 
       /* Mark the jump as very unlikely to be taken.  */
-      add_int_reg_note (insn, REG_BR_PROB, REG_BR_PROB_BASE / 100);
+      add_reg_br_prob_note (insn, 
+                           profile_probability::very_unlikely ());
 
       if (cfun->machine->split_stack_varargs_pointer != NULL_RTX)
        {
@@ -11353,7 +11891,7 @@ s390_invalid_arg_for_unprototyped_fn (const_tree typelist, const_tree funcdecl,
           && (funcdecl == NULL_TREE
               || (TREE_CODE (funcdecl) == FUNCTION_DECL
                   && DECL_BUILT_IN_CLASS (funcdecl) != BUILT_IN_MD)))
-         ? N_("Vector argument passed to unprototyped function")
+         ? N_("vector argument passed to unprototyped function")
          : NULL);
 }
 
@@ -11648,6 +12186,18 @@ s390_function_arg (cumulative_args_t cum_v, machine_mode mode,
   gcc_unreachable ();
 }
 
+/* Implement TARGET_FUNCTION_ARG_BOUNDARY.  Vector arguments are
+   left-justified when placed on the stack during parameter passing.  */
+
+static pad_direction
+s390_function_arg_padding (machine_mode mode, const_tree type)
+{
+  if (s390_function_arg_vector (mode, type))
+    return PAD_UPWARD;
+
+  return default_function_arg_padding (mode, type);
+}
+
 /* Return true if return values of type TYPE should be returned
    in a memory buffer whose address is passed by the caller as
    hidden first argument.  */
@@ -11990,7 +12540,7 @@ s390_gimplify_va_arg (tree valist, tree type, gimple_seq *pre_p,
   tree f_gpr, f_fpr, f_ovf, f_sav;
   tree gpr, fpr, ovf, sav, reg, t, u;
   int indirect_p, size, n_reg, sav_ofs, sav_scale, max_reg;
-  tree lab_false, lab_over;
+  tree lab_false, lab_over = NULL_TREE;
   tree addr = create_tmp_var (ptr_type_node, "addr");
   bool left_align_p; /* How a value < UNITS_PER_LONG is aligned within
                        a stack slot.  */
@@ -12405,17 +12955,13 @@ s390_encode_section_info (tree decl, rtx rtl, int first)
     {
       /* Store the alignment to be able to check if we can use
         a larl/load-relative instruction.  We only handle the cases
-        that can go wrong (i.e. no FUNC_DECLs).  If a symref does
-        not have any flag we assume it to be correctly aligned.  */
-
-      if (DECL_ALIGN (decl) % 64)
-       SYMBOL_FLAG_SET_NOTALIGN8 (XEXP (rtl, 0));
-
-      if (DECL_ALIGN (decl) % 32)
-       SYMBOL_FLAG_SET_NOTALIGN4 (XEXP (rtl, 0));
-
+        that can go wrong (i.e. no FUNC_DECLs).  */
       if (DECL_ALIGN (decl) == 0 || DECL_ALIGN (decl) % 16)
        SYMBOL_FLAG_SET_NOTALIGN2 (XEXP (rtl, 0));
+      else if (DECL_ALIGN (decl) % 32)
+       SYMBOL_FLAG_SET_NOTALIGN4 (XEXP (rtl, 0));
+      else if (DECL_ALIGN (decl) % 64)
+       SYMBOL_FLAG_SET_NOTALIGN8 (XEXP (rtl, 0));
     }
 
   /* Literal pool references don't have a decl so they are handled
@@ -12423,18 +12969,14 @@ s390_encode_section_info (tree decl, rtx rtl, int first)
      entry to decide upon the alignment.  */
   if (MEM_P (rtl)
       && GET_CODE (XEXP (rtl, 0)) == SYMBOL_REF
-      && TREE_CONSTANT_POOL_ADDRESS_P (XEXP (rtl, 0))
-      && MEM_ALIGN (rtl) != 0
-      && GET_MODE_BITSIZE (GET_MODE (rtl)) != 0)
+      && TREE_CONSTANT_POOL_ADDRESS_P (XEXP (rtl, 0)))
     {
-      if (MEM_ALIGN (rtl) % 64)
-       SYMBOL_FLAG_SET_NOTALIGN8 (XEXP (rtl, 0));
-
-      if (MEM_ALIGN (rtl) % 32)
-       SYMBOL_FLAG_SET_NOTALIGN4 (XEXP (rtl, 0));
-
       if (MEM_ALIGN (rtl) == 0 || MEM_ALIGN (rtl) % 16)
        SYMBOL_FLAG_SET_NOTALIGN2 (XEXP (rtl, 0));
+      else if (MEM_ALIGN (rtl) % 32)
+       SYMBOL_FLAG_SET_NOTALIGN4 (XEXP (rtl, 0));
+      else if (MEM_ALIGN (rtl) % 64)
+       SYMBOL_FLAG_SET_NOTALIGN8 (XEXP (rtl, 0));
     }
 }
 
@@ -12708,7 +13250,7 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
 }
 
 static bool
-s390_valid_pointer_mode (machine_mode mode)
+s390_valid_pointer_mode (scalar_int_mode mode)
 {
   return (mode == SImode || (TARGET_64BIT && mode == DImode));
 }
@@ -12766,9 +13308,7 @@ s390_call_saved_register_used (tree call_expr)
 
        if (REG_P (parm_rtx))
         {
-          for (reg = 0;
-               reg < HARD_REGNO_NREGS (REGNO (parm_rtx), GET_MODE (parm_rtx));
-               reg++)
+          for (reg = 0; reg < REG_NREGS (parm_rtx); reg++)
             if (!call_used_regs[reg + REGNO (parm_rtx)])
               return true;
         }
@@ -12783,9 +13323,7 @@ s390_call_saved_register_used (tree call_expr)
 
               gcc_assert (REG_P (r));
 
-              for (reg = 0;
-                   reg < HARD_REGNO_NREGS (REGNO (r), GET_MODE (r));
-                   reg++)
+              for (reg = 0; reg < REG_NREGS (r); reg++)
                 if (!call_used_regs[reg + REGNO (r)])
                   return true;
             }
@@ -13222,7 +13760,7 @@ static bool
 s390_fix_long_loop_prediction (rtx_insn *insn)
 {
   rtx set = single_set (insn);
-  rtx code_label, label_ref, new_label;
+  rtx code_label, label_ref;
   rtx_insn *uncond_jump;
   rtx_insn *cur_insn;
   rtx tmp;
@@ -13259,7 +13797,7 @@ s390_fix_long_loop_prediction (rtx_insn *insn)
     if (!cur_insn || JUMP_P (cur_insn) || LABEL_P (cur_insn))
       return false;
 
-  new_label = gen_label_rtx ();
+  rtx_code_label *new_label = gen_label_rtx ();
   uncond_jump = emit_jump_insn_after (
                  gen_rtx_SET (pc_rtx,
                               gen_rtx_LABEL_REF (VOIDmode, code_label)),
@@ -13444,6 +13982,155 @@ s390_z10_optimize_cmp (rtx_insn *insn)
   return insn_added_p;
 }
 
+/* Number of INSNs to be scanned backward in the last BB of the loop
+   and forward in the first BB of the loop.  This usually should be a
+   bit more than the number of INSNs which could go into one
+   group.  */
+#define S390_OSC_SCAN_INSN_NUM 5
+
+/* Scan LOOP for static OSC collisions and return true if a osc_break
+   should be issued for this loop.  */
+static bool
+s390_adjust_loop_scan_osc (struct loop* loop)
+
+{
+  HARD_REG_SET modregs, newregs;
+  rtx_insn *insn, *store_insn = NULL;
+  rtx set;
+  struct s390_address addr_store, addr_load;
+  subrtx_iterator::array_type array;
+  int insn_count;
+
+  CLEAR_HARD_REG_SET (modregs);
+
+  insn_count = 0;
+  FOR_BB_INSNS_REVERSE (loop->latch, insn)
+    {
+      if (!INSN_P (insn) || INSN_CODE (insn) <= 0)
+       continue;
+
+      insn_count++;
+      if (insn_count > S390_OSC_SCAN_INSN_NUM)
+       return false;
+
+      find_all_hard_reg_sets (insn, &newregs, true);
+      IOR_HARD_REG_SET (modregs, newregs);
+
+      set = single_set (insn);
+      if (!set)
+       continue;
+
+      if (MEM_P (SET_DEST (set))
+         && s390_decompose_address (XEXP (SET_DEST (set), 0), &addr_store))
+       {
+         store_insn = insn;
+         break;
+       }
+    }
+
+  if (store_insn == NULL_RTX)
+    return false;
+
+  insn_count = 0;
+  FOR_BB_INSNS (loop->header, insn)
+    {
+      if (!INSN_P (insn) || INSN_CODE (insn) <= 0)
+       continue;
+
+      if (insn == store_insn)
+       return false;
+
+      insn_count++;
+      if (insn_count > S390_OSC_SCAN_INSN_NUM)
+       return false;
+
+      find_all_hard_reg_sets (insn, &newregs, true);
+      IOR_HARD_REG_SET (modregs, newregs);
+
+      set = single_set (insn);
+      if (!set)
+       continue;
+
+      /* An intermediate store disrupts static OSC checking
+        anyway.  */
+      if (MEM_P (SET_DEST (set))
+         && s390_decompose_address (XEXP (SET_DEST (set), 0), NULL))
+       return false;
+
+      FOR_EACH_SUBRTX (iter, array, SET_SRC (set), NONCONST)
+       if (MEM_P (*iter)
+           && s390_decompose_address (XEXP (*iter, 0), &addr_load)
+           && rtx_equal_p (addr_load.base, addr_store.base)
+           && rtx_equal_p (addr_load.indx, addr_store.indx)
+           && rtx_equal_p (addr_load.disp, addr_store.disp))
+         {
+           if ((addr_load.base != NULL_RTX
+                && TEST_HARD_REG_BIT (modregs, REGNO (addr_load.base)))
+               || (addr_load.indx != NULL_RTX
+                   && TEST_HARD_REG_BIT (modregs, REGNO (addr_load.indx))))
+             return true;
+         }
+    }
+  return false;
+}
+
+/* Look for adjustments which can be done on simple innermost
+   loops.  */
+static void
+s390_adjust_loops ()
+{
+  struct loop *loop = NULL;
+
+  df_analyze ();
+  compute_bb_for_insn ();
+
+  /* Find the loops.  */
+  loop_optimizer_init (AVOID_CFG_MODIFICATIONS);
+
+  FOR_EACH_LOOP (loop, LI_ONLY_INNERMOST)
+    {
+      if (dump_file)
+       {
+         flow_loop_dump (loop, dump_file, NULL, 0);
+         fprintf (dump_file, ";;  OSC loop scan Loop: ");
+       }
+      if (loop->latch == NULL
+         || pc_set (BB_END (loop->latch)) == NULL_RTX
+         || !s390_adjust_loop_scan_osc (loop))
+       {
+         if (dump_file)
+           {
+             if (loop->latch == NULL)
+               fprintf (dump_file, " muliple backward jumps\n");
+             else
+               {
+                 fprintf (dump_file, " header insn: %d latch insn: %d ",
+                          INSN_UID (BB_HEAD (loop->header)),
+                          INSN_UID (BB_END (loop->latch)));
+                 if (pc_set (BB_END (loop->latch)) == NULL_RTX)
+                   fprintf (dump_file, " loop does not end with jump\n");
+                 else
+                   fprintf (dump_file, " not instrumented\n");
+               }
+           }
+       }
+      else
+       {
+         rtx_insn *new_insn;
+
+         if (dump_file)
+           fprintf (dump_file, " adding OSC break insn: ");
+         new_insn = emit_insn_before (gen_osc_break (),
+                                      BB_END (loop->latch));
+         INSN_ADDRESSES_NEW (new_insn, -1);
+       }
+    }
+
+  loop_optimizer_finalize ();
+
+  df_finish_pass (false);
+}
+
 /* Perform machine-dependent processing.  */
 
 static void
@@ -13452,6 +14139,9 @@ s390_reorg (void)
   bool pool_overflow = false;
   int hw_before, hw_after;
 
+  if (s390_tune == PROCESSOR_2964_Z13)
+    s390_adjust_loops ();
+
   /* Make sure all splits have been performed; splits after
      machine_dependent_reorg might confuse insn length counts.  */
   split_all_insns_noflow ();
@@ -13651,7 +14341,7 @@ s390_fpload_toreg (rtx_insn *insn, unsigned int regno)
 }
 
 /* This value describes the distance to be avoided between an
-   aritmetic fp instruction and an fp load writing the same register.
+   arithmetic fp instruction and an fp load writing the same register.
    Z10_EARLYLOAD_DISTANCE - 1 as well as Z10_EARLYLOAD_DISTANCE + 1 is
    fine but the exact value has to be avoided. Otherwise the FP
    pipeline will throw an exception causing a major penalty.  */
@@ -13743,6 +14433,7 @@ s390_get_sched_attrmask (rtx_insn *insn)
        mask |= S390_SCHED_ATTR_MASK_GROUPALONE;
       break;
     case PROCESSOR_2964_Z13:
+    case PROCESSOR_3906_Z14:
       if (get_attr_z13_cracked (insn))
        mask |= S390_SCHED_ATTR_MASK_CRACKED;
       if (get_attr_z13_expanded (insn))
@@ -13766,6 +14457,7 @@ s390_get_unit_mask (rtx_insn *insn, int *units)
   switch (s390_tune)
     {
     case PROCESSOR_2964_Z13:
+    case PROCESSOR_3906_Z14:
       *units = 3;
       if (get_attr_z13_unit_lsu (insn))
        mask |= 1 << 0;
@@ -13799,6 +14491,7 @@ s390_sched_score (rtx_insn *insn)
        score += 5;
       if ((mask & S390_SCHED_ATTR_MASK_GROUPALONE) != 0)
        score += 10;
+      /* fallthrough */
     case 1:
       /* Prefer not cracked insns while trying to put together a
         group.  */
@@ -13837,7 +14530,7 @@ s390_sched_score (rtx_insn *insn)
       break;
     }
 
-  if (s390_tune == PROCESSOR_2964_Z13)
+  if (s390_tune >= PROCESSOR_2964_Z13)
     {
       int units, i;
       unsigned unit_mask, m = 1;
@@ -13942,7 +14635,7 @@ s390_sched_reorder (FILE *file, int verbose,
              PRINT_SCHED_ATTR (S390_SCHED_ATTR_MASK_ENDGROUP, endgroup);
              PRINT_SCHED_ATTR (S390_SCHED_ATTR_MASK_GROUPALONE, groupalone);
 #undef PRINT_SCHED_ATTR
-             if (s390_tune == PROCESSOR_2964_Z13)
+             if (s390_tune >= PROCESSOR_2964_Z13)
                {
                  unsigned int unit_mask, m = 1;
                  int units, j;
@@ -14005,7 +14698,7 @@ s390_sched_variable_issue (FILE *file, int verbose, rtx_insn *insn, int more)
            }
        }
 
-      if (s390_tune == PROCESSOR_2964_Z13)
+      if (s390_tune >= PROCESSOR_2964_Z13)
        {
          int units, i;
          unsigned unit_mask, m = 1;
@@ -14034,7 +14727,7 @@ s390_sched_variable_issue (FILE *file, int verbose, rtx_insn *insn, int more)
          PRINT_SCHED_ATTR (S390_SCHED_ATTR_MASK_GROUPALONE, groupalone);
 #undef PRINT_SCHED_ATTR
 
-         if (s390_tune == PROCESSOR_2964_Z13)
+         if (s390_tune >= PROCESSOR_2964_Z13)
            {
              unsigned int unit_mask, m = 1;
              int units, j;
@@ -14048,7 +14741,7 @@ s390_sched_variable_issue (FILE *file, int verbose, rtx_insn *insn, int more)
            }
          fprintf (file, " sched state: %d\n", s390_sched_state);
 
-         if (s390_tune == PROCESSOR_2964_Z13)
+         if (s390_tune >= PROCESSOR_2964_Z13)
            {
              int units, j;
 
@@ -14330,6 +15023,10 @@ s390_option_override_internal (bool main_args_p,
                          opts->x_param_values,
                          opts_set->x_param_values);
 
+  maybe_set_param_value (PARAM_MIN_VECT_LOOP_BOUND, 2,
+                        opts->x_param_values,
+                        opts_set->x_param_values);
+
   /* Call target specific restore function to do post-init work.  At the moment,
      this just sets opts->x_s390_cost_pointer.  */
   s390_function_specific_restore (opts, NULL);
@@ -14409,6 +15106,9 @@ s390_option_override (void)
   if (flag_prefetch_loop_arrays < 0 && HAVE_prefetch && optimize >= 3)
     flag_prefetch_loop_arrays = 1;
 
+  if (!s390_pic_data_is_text_relative && !flag_pic)
+    error ("-mno-pic-data-is-text-relative cannot be used without -fpic/-fPIC");
+
   if (TARGET_TPF)
     {
       /* Don't emit DWARF3/4 unless specifically selected.  The TPF
@@ -14588,7 +15288,7 @@ s390_valid_target_attribute_inner_p (tree args,
       else if (attrs[i].only_as_pragma && !force_pragma)
        {
          /* Value is not allowed for the target attribute.  */
-         error ("Value %qs is not supported by attribute %<target%>",
+         error ("value %qs is not supported by attribute %<target%>",
                 attrs[i].string);
          return false;
        }
@@ -14762,6 +15462,52 @@ s390_valid_target_attribute_p (tree fndecl,
   return ret;
 }
 
+/* Hook to determine if one function can safely inline another.  */
+
+static bool
+s390_can_inline_p (tree caller, tree callee)
+{
+  tree caller_tree = DECL_FUNCTION_SPECIFIC_TARGET (caller);
+  tree callee_tree = DECL_FUNCTION_SPECIFIC_TARGET (callee);
+
+  if (!callee_tree)
+    callee_tree = target_option_default_node;
+  if (!caller_tree)
+    caller_tree = target_option_default_node;
+  if (callee_tree == caller_tree)
+    return true;
+
+  struct cl_target_option *caller_opts = TREE_TARGET_OPTION (caller_tree);
+  struct cl_target_option *callee_opts = TREE_TARGET_OPTION (callee_tree);
+  bool ret = true;
+
+  if ((caller_opts->x_target_flags & ~(MASK_SOFT_FLOAT | MASK_HARD_DFP))
+      != (callee_opts->x_target_flags & ~(MASK_SOFT_FLOAT | MASK_HARD_DFP)))
+    ret = false;
+
+  /* Don't inline functions to be compiled for a more recent arch into a
+     function for an older arch.  */
+  else if (caller_opts->x_s390_arch < callee_opts->x_s390_arch)
+    ret = false;
+
+  /* Inlining a hard float function into a soft float function is only
+     allowed if the hard float function doesn't actually make use of
+     floating point.
+
+     We are called from FEs for multi-versioning call optimization, so
+     beware of ipa_fn_summaries not available.  */
+  else if (((TARGET_SOFT_FLOAT_P (caller_opts->x_target_flags)
+            && !TARGET_SOFT_FLOAT_P (callee_opts->x_target_flags))
+           || (!TARGET_HARD_DFP_P (caller_opts->x_target_flags)
+               && TARGET_HARD_DFP_P (callee_opts->x_target_flags)))
+          && (! ipa_fn_summaries
+              || ipa_fn_summaries->get
+              (cgraph_node::get (callee))->fp_expressions))
+    ret = false;
+
+  return ret;
+}
+
 /* Restore targets globals from NEW_TREE and invalidate s390_previous_fndecl
    cache.  */
 
@@ -14904,20 +15650,20 @@ s390_atomic_assign_expand_fenv (tree *hold, tree *clear, tree *update)
 /* Return the vector mode to be used for inner mode MODE when doing
    vectorization.  */
 static machine_mode
-s390_preferred_simd_mode (machine_mode mode)
+s390_preferred_simd_mode (scalar_mode mode)
 {
   if (TARGET_VX)
     switch (mode)
       {
-      case DFmode:
+      case E_DFmode:
        return V2DFmode;
-      case DImode:
+      case E_DImode:
        return V2DImode;
-      case SImode:
+      case E_SImode:
        return V4SImode;
-      case HImode:
+      case E_HImode:
        return V8HImode;
-      case QImode:
+      case E_QImode:
        return V16QImode;
       default:;
       }
@@ -14958,6 +15704,7 @@ s390_vector_alignment (const_tree type)
 static void
 s390_asm_file_start (void)
 {
+  default_file_start ();
   s390_asm_output_machine_for_arch (asm_out_file);
 }
 #endif
@@ -15020,7 +15767,7 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty
      operators.  */
   if (!bool1_p && !bool2_p
       && TYPE_UNSIGNED (type1) != TYPE_UNSIGNED (type2))
-    return N_("types differ in signess");
+    return N_("types differ in signedness");
 
   plusminus_p = (op == PLUS_EXPR || op == MINUS_EXPR);
   muldiv_p = (op == MULT_EXPR || op == RDIV_EXPR || op == TRUNC_DIV_EXPR
@@ -15047,6 +15794,51 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty
   return NULL;
 }
 
+/* Implement TARGET_C_EXCESS_PRECISION.
+
+   FIXME: For historical reasons, float_t and double_t are typedef'ed to
+   double on s390, causing operations on float_t to operate in a higher
+   precision than is necessary.  However, it is not the case that SFmode
+   operations have implicit excess precision, and we generate more optimal
+   code if we let the compiler know no implicit extra precision is added.
+
+   That means when we are compiling with -fexcess-precision=fast, the value
+   we set for FLT_EVAL_METHOD will be out of line with the actual precision of
+   float_t (though they would be correct for -fexcess-precision=standard).
+
+   A complete fix would modify glibc to remove the unnecessary typedef
+   of float_t to double.  */
+
+static enum flt_eval_method
+s390_excess_precision (enum excess_precision_type type)
+{
+  switch (type)
+    {
+      case EXCESS_PRECISION_TYPE_IMPLICIT:
+      case EXCESS_PRECISION_TYPE_FAST:
+       /* The fastest type to promote to will always be the native type,
+          whether that occurs with implicit excess precision or
+          otherwise.  */
+       return FLT_EVAL_METHOD_PROMOTE_TO_FLOAT;
+      case EXCESS_PRECISION_TYPE_STANDARD:
+       /* Otherwise, when we are in a standards compliant mode, to
+          ensure consistency with the implementation in glibc, report that
+          float is evaluated to the range and precision of double.  */
+       return FLT_EVAL_METHOD_PROMOTE_TO_DOUBLE;
+      default:
+       gcc_unreachable ();
+    }
+  return FLT_EVAL_METHOD_UNPREDICTABLE;
+}
+
+/* Implement the TARGET_ASAN_SHADOW_OFFSET hook.  */
+
+static unsigned HOST_WIDE_INT
+s390_asan_shadow_offset (void)
+{
+  return TARGET_64BIT ? HOST_WIDE_INT_1U << 52 : HOST_WIDE_INT_UC (0x20000000);
+}
+
 /* Initialize GCC target structure.  */
 
 #undef  TARGET_ASM_ALIGNED_HI_OP
@@ -15065,6 +15857,11 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty
 #undef TARGET_OPTION_OVERRIDE
 #define TARGET_OPTION_OVERRIDE s390_option_override
 
+#ifdef TARGET_THREAD_SSP_OFFSET
+#undef TARGET_STACK_PROTECT_GUARD
+#define TARGET_STACK_PROTECT_GUARD hook_tree_void_null
+#endif
+
 #undef TARGET_ENCODE_SECTION_INFO
 #define TARGET_ENCODE_SECTION_INFO s390_encode_section_info
 
@@ -15102,6 +15899,9 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty
 #undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
 #define TARGET_ASM_CAN_OUTPUT_MI_THUNK hook_bool_const_tree_hwi_hwi_const_tree_true
 
+#undef TARGET_C_EXCESS_PRECISION
+#define TARGET_C_EXCESS_PRECISION s390_excess_precision
+
 #undef  TARGET_SCHED_ADJUST_PRIORITY
 #define TARGET_SCHED_ADJUST_PRIORITY s390_adjust_priority
 #undef TARGET_SCHED_ISSUE_RATE
@@ -15126,6 +15926,9 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty
 #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_VECTORIZE_BUILTIN_VECTORIZATION_COST
+#define TARGET_VECTORIZE_BUILTIN_VECTORIZATION_COST \
+  s390_builtin_vectorization_cost
 
 #undef TARGET_MACHINE_DEPENDENT_REORG
 #define TARGET_MACHINE_DEPENDENT_REORG s390_reorg
@@ -15137,6 +15940,8 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty
 #define TARGET_BUILD_BUILTIN_VA_LIST s390_build_builtin_va_list
 #undef TARGET_EXPAND_BUILTIN_VA_START
 #define TARGET_EXPAND_BUILTIN_VA_START s390_va_start
+#undef TARGET_ASAN_SHADOW_OFFSET
+#define TARGET_ASAN_SHADOW_OFFSET s390_asan_shadow_offset
 #undef TARGET_GIMPLIFY_VA_ARG_EXPR
 #define TARGET_GIMPLIFY_VA_ARG_EXPR s390_gimplify_va_arg
 
@@ -15151,6 +15956,8 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty
 #define TARGET_FUNCTION_ARG s390_function_arg
 #undef TARGET_FUNCTION_ARG_ADVANCE
 #define TARGET_FUNCTION_ARG_ADVANCE s390_function_arg_advance
+#undef TARGET_FUNCTION_ARG_PADDING
+#define TARGET_FUNCTION_ARG_PADDING s390_function_arg_padding
 #undef TARGET_FUNCTION_VALUE
 #define TARGET_FUNCTION_VALUE s390_function_value
 #undef TARGET_LIBCALL_VALUE
@@ -15194,6 +16001,10 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty
 
 #undef TARGET_SECONDARY_RELOAD
 #define TARGET_SECONDARY_RELOAD s390_secondary_reload
+#undef TARGET_SECONDARY_MEMORY_NEEDED
+#define TARGET_SECONDARY_MEMORY_NEEDED s390_secondary_memory_needed
+#undef TARGET_SECONDARY_MEMORY_NEEDED_MODE
+#define TARGET_SECONDARY_MEMORY_NEEDED_MODE s390_secondary_memory_needed_mode
 
 #undef TARGET_LIBGCC_CMP_RETURN_MODE
 #define TARGET_LIBGCC_CMP_RETURN_MODE s390_libgcc_cmp_return_mode
@@ -15224,6 +16035,10 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty
 #undef TARGET_TRAMPOLINE_INIT
 #define TARGET_TRAMPOLINE_INIT s390_trampoline_init
 
+/* PR 79421 */
+#undef TARGET_CUSTOM_FUNCTION_DESCRIPTORS
+#define TARGET_CUSTOM_FUNCTION_DESCRIPTORS 1
+
 #undef TARGET_UNWIND_WORD_MODE
 #define TARGET_UNWIND_WORD_MODE s390_unwind_word_mode
 
@@ -15233,6 +16048,17 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty
 #undef TARGET_HARD_REGNO_SCRATCH_OK
 #define TARGET_HARD_REGNO_SCRATCH_OK s390_hard_regno_scratch_ok
 
+#undef TARGET_HARD_REGNO_NREGS
+#define TARGET_HARD_REGNO_NREGS s390_hard_regno_nregs
+#undef TARGET_HARD_REGNO_MODE_OK
+#define TARGET_HARD_REGNO_MODE_OK s390_hard_regno_mode_ok
+#undef TARGET_MODES_TIEABLE_P
+#define TARGET_MODES_TIEABLE_P s390_modes_tieable_p
+
+#undef TARGET_HARD_REGNO_CALL_PART_CLOBBERED
+#define TARGET_HARD_REGNO_CALL_PART_CLOBBERED \
+  s390_hard_regno_call_part_clobbered
+
 #undef TARGET_ATTRIBUTE_TABLE
 #define TARGET_ATTRIBUTE_TABLE s390_attribute_table
 
@@ -15281,6 +16107,9 @@ s390_invalid_binary_op (int op ATTRIBUTE_UNUSED, const_tree type1, const_tree ty
 
 #undef TARGET_OPTION_VALID_ATTRIBUTE_P
 #define TARGET_OPTION_VALID_ATTRIBUTE_P s390_valid_target_attribute_p
+
+#undef TARGET_CAN_INLINE_P
+#define TARGET_CAN_INLINE_P s390_can_inline_p
 #endif
 
 #undef TARGET_OPTION_RESTORE