diff --git a/gcc/config/s390/s390-opts.h b/gcc/config/s390/s390-opts.h
index 6d506e2..6ec891e 100644
--- a/gcc/config/s390/s390-opts.h
+++ b/gcc/config/s390/s390-opts.h
@@ -43,4 +43,13 @@ enum processor_type
   PROCESSOR_max
 };
 
+
+/* Values for -mindirect-branch and -mfunction-return options.  */
+enum indirect_branch {
+  indirect_branch_unset = 0,
+  indirect_branch_keep,
+  indirect_branch_thunk,
+  indirect_branch_thunk_inline,
+  indirect_branch_thunk_extern
+};
 #endif
diff --git a/gcc/config/s390/s390-protos.h b/gcc/config/s390/s390-protos.h
index 3fdb320..3fb3268 100644
--- a/gcc/config/s390/s390-protos.h
+++ b/gcc/config/s390/s390-protos.h
@@ -53,6 +53,7 @@ extern int s390_class_max_nregs (enum reg_class, machine_mode);
 extern int s390_cannot_change_mode_class (machine_mode, machine_mode,
 					  enum reg_class);
 extern bool s390_function_arg_vector (machine_mode, const_tree);
+extern bool s390_return_addr_from_memory(void);
 #if S390_USE_TARGET_ATTRIBUTE
 extern tree s390_valid_target_attribute_tree (tree args,
 					      struct gcc_options *opts,
@@ -147,6 +148,17 @@ extern int s390_compare_and_branch_condition_mask (rtx);
 extern bool s390_extzv_shift_ok (int, int, unsigned HOST_WIDE_INT);
 extern void s390_asm_output_function_label (FILE *, const char *, tree);
 
+enum s390_indirect_branch_type
+  {
+    s390_indirect_branch_type_jump = 0,
+    s390_indirect_branch_type_call,
+    s390_indirect_branch_type_return
+  };
+extern void s390_indirect_branch_via_thunk (unsigned int regno,
+					    unsigned int return_addr_regno,
+					    rtx comparison_operator,
+					    enum s390_indirect_branch_type type);
+extern void s390_indirect_branch_via_inline_thunk (rtx execute_target);
 #endif /* RTX_CODE */
 
 /* s390-c.c routines */
diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c
index c408d59..af755aa 100644
--- a/gcc/config/s390/s390.c
+++ b/gcc/config/s390/s390.c
@@ -377,84 +377,6 @@ struct s390_address
   bool literal_pool;
 };
 
-/* The following structure is embedded in the machine
-   specific part of struct function.  */
-
-struct GTY (()) s390_frame_layout
-{
-  /* Offset within stack frame.  */
-  HOST_WIDE_INT gprs_offset;
-  HOST_WIDE_INT f0_offset;
-  HOST_WIDE_INT f4_offset;
-  HOST_WIDE_INT f8_offset;
-  HOST_WIDE_INT backchain_offset;
-
-  /* Number of first and last gpr where slots in the register
-     save area are reserved for.  */
-  int first_save_gpr_slot;
-  int last_save_gpr_slot;
-
-  /* Location (FP register number) where GPRs (r0-r15) should
-     be saved to.
-      0 - does not need to be saved at all
-     -1 - stack slot  */
-#define SAVE_SLOT_NONE   0
-#define SAVE_SLOT_STACK -1
-  signed char gpr_save_slots[16];
-
-  /* Number of first and last gpr to be saved, restored.  */
-  int first_save_gpr;
-  int first_restore_gpr;
-  int last_save_gpr;
-  int last_restore_gpr;
-
-  /* Bits standing for floating point registers. Set, if the
-     respective register has to be saved. Starting with reg 16 (f0)
-     at the rightmost bit.
-     Bit 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
-     fpr 15 13 11  9 14 12 10  8  7  5  3  1  6  4  2  0
-     reg 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16  */
-  unsigned int fpr_bitmap;
-
-  /* Number of floating point registers f8-f15 which must be saved.  */
-  int high_fprs;
-
-  /* Set if return address needs to be saved.
-     This flag is set by s390_return_addr_rtx if it could not use
-     the initial value of r14 and therefore depends on r14 saved
-     to the stack.  */
-  bool save_return_addr_p;
-
-  /* Size of stack frame.  */
-  HOST_WIDE_INT frame_size;
-};
-
-/* Define the structure for the machine field in struct function.  */
-
-struct GTY(()) machine_function
-{
-  struct s390_frame_layout frame_layout;
-
-  /* Literal pool base register.  */
-  rtx base_reg;
-
-  /* True if we may need to perform branch splitting.  */
-  bool split_branches_pending_p;
-
-  bool has_landing_pad_p;
-
-  /* True if the current function may contain a tbegin clobbering
-     FPRs.  */
-  bool tbegin_p;
-
-  /* For -fsplit-stack support: A stack local which holds a pointer to
-     the stack arguments for a function with a variable number of
-     arguments.  This is set at the start of the function and is used
-     to initialize the overflow_arg_area field of the va_list
-     structure.  */
-  rtx split_stack_varargs_pointer;
-};
-
 /* Few accessor macros for struct cfun->machine->s390_frame_layout.  */
 
 #define cfun_frame_layout (cfun->machine->frame_layout)
@@ -495,6 +417,33 @@ struct GTY(()) machine_function
    bytes on a z10 (or higher) CPU.  */
 #define PREDICT_DISTANCE (TARGET_Z10 ? 384 : 2048)
 
+/* Masks per jump target register indicating which thunk need to be
+   generated.  */
+static GTY(()) int indirect_branch_prez10thunk_mask = 0;
+static GTY(()) int indirect_branch_z10thunk_mask = 0;
+
+#define INDIRECT_BRANCH_NUM_OPTIONS 4
+
+enum s390_indirect_branch_option
+  {
+    s390_opt_indirect_branch_jump = 0,
+    s390_opt_indirect_branch_call,
+    s390_opt_function_return_reg,
+    s390_opt_function_return_mem
+  };
+
+static GTY(()) int indirect_branch_table_label_no[INDIRECT_BRANCH_NUM_OPTIONS] = { 0 };
+const char *indirect_branch_table_label[INDIRECT_BRANCH_NUM_OPTIONS] = \
+  { "LJUMP", "LCALL", "LRETREG", "LRETMEM" };
+const char *indirect_branch_table_name[INDIRECT_BRANCH_NUM_OPTIONS] =	\
+  { ".s390_indirect_jump", ".s390_indirect_call",
+    ".s390_return_reg", ".s390_return_mem" };
+
+bool
+s390_return_addr_from_memory ()
+{
+  return cfun_gpr_save_slot(RETURN_REGNUM) == SAVE_SLOT_STACK;
+}
 
 /* Indicate which ABI has been used for passing vector args.
    0 - no vector type arguments have been passed where the ABI is relevant
@@ -1148,9 +1097,83 @@ s390_handle_vectorbool_attribute (tree *node, tree name ATTRIBUTE_UNUSED,
   return NULL_TREE;
 }
 
+/* Check syntax of function decl attributes having a string type value.  */
+
+static tree
+s390_handle_string_attribute (tree *node, tree name ATTRIBUTE_UNUSED,
+			      tree args ATTRIBUTE_UNUSED,
+			      int flags ATTRIBUTE_UNUSED,
+			      bool *no_add_attrs)
+{
+  tree cst;
+
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+	       name);
+      *no_add_attrs = true;
+    }
+
+  cst = TREE_VALUE (args);
+
+  if (TREE_CODE (cst) != STRING_CST)
+    {
+      warning (OPT_Wattributes,
+	       "%qE attribute requires a string constant argument",
+	       name);
+      *no_add_attrs = true;
+    }
+
+  if (is_attribute_p ("indirect_branch", name)
+      || is_attribute_p ("indirect_branch_call", name)
+      || is_attribute_p ("function_return", name)
+      || is_attribute_p ("function_return_reg", name)
+      || is_attribute_p ("function_return_mem", name))
+    {
+      if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0
+	  && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0
+	  && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0)
+      {
+	warning (OPT_Wattributes,
+		 "argument to %qE attribute is not "
+		 "(keep|thunk|thunk-extern)", name);
+	*no_add_attrs = true;
+      }
+    }
+
+  if (is_attribute_p ("indirect_branch_jump", name)
+      && strcmp (TREE_STRING_POINTER (cst), "keep") != 0
+      && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0
+      && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0
+      && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0)
+    {
+      warning (OPT_Wattributes,
+	       "argument to %qE attribute is not "
+	       "(keep|thunk|thunk-inline|thunk-extern)", name);
+      *no_add_attrs = true;
+    }
+
+  return NULL_TREE;
+}
+
 static const struct attribute_spec s390_attribute_table[] = {
-  { "hotpatch", 2, 2, true, false, false, s390_handle_hotpatch_attribute, false },
-  { "s390_vector_bool", 0, 0, false, true, false, s390_handle_vectorbool_attribute, true },
+  { "hotpatch", 2, 2, true, false, false,
+    s390_handle_hotpatch_attribute, false },
+  { "s390_vector_bool", 0, 0, false, true, false,
+    s390_handle_vectorbool_attribute, true },
+  { "indirect_branch", 1, 1, true, false, false,
+    s390_handle_string_attribute, false },
+  { "indirect_branch_jump", 1, 1, true, false, false,
+    s390_handle_string_attribute, false },
+  { "indirect_branch_call", 1, 1, true, false, false,
+    s390_handle_string_attribute, false },
+  { "function_return", 1, 1, true, false, false,
+    s390_handle_string_attribute, false },
+  { "function_return_reg", 1, 1, true, false, false,
+    s390_handle_string_attribute, false },
+  { "function_return_mem", 1, 1, true, false, false,
+    s390_handle_string_attribute, false },
+
   /* End element.  */
   { NULL,        0, 0, false, false, false, NULL, false }
 };
@@ -8583,11 +8606,25 @@ s390_find_constant (struct constant_pool *pool, rtx val,
 static rtx
 s390_execute_label (rtx insn)
 {
-  if (NONJUMP_INSN_P (insn)
+  if (INSN_P (insn)
       && GET_CODE (PATTERN (insn)) == PARALLEL
       && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC
-      && XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE)
-    return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2);
+      && (XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE
+	  || XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE_JUMP))
+    {
+      if (XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE)
+	return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2);
+      else
+	{
+	  gcc_assert (JUMP_P (insn));
+	  /* For jump insns as execute target:
+	     - There is one operand less in the parallel (the
+	       modification register of the execute is always 0).
+	     - The execute target label is wrapped into an
+	       if_then_else in order to hide it from jump analysis.  */
+	  return XEXP (XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 0), 0);
+	}
+    }
 
   return NULL_RTX;
 }
@@ -11273,7 +11310,6 @@ s390_emit_epilogue (bool sibcall)
   rtx frame_pointer, return_reg, cfa_restores = NULL_RTX;
   int area_bottom, area_top, offset = 0;
   int next_offset;
-  rtvec p;
   int i;
 
   if (TARGET_TPF_PROFILING)
@@ -11427,10 +11463,15 @@ s390_emit_epilogue (bool sibcall)
 	  if (cfun_gpr_save_slot (RETURN_REGNUM) == SAVE_SLOT_STACK)
 	    {
 	      int return_regnum = find_unused_clobbered_reg();
-	      if (!return_regnum)
-		return_regnum = 4;
+	      if (!return_regnum
+		  || (TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION
+		      && !TARGET_CPU_Z10
+		      && return_regnum == INDIRECT_BRANCH_THUNK_REGNUM))
+		{
+		  gcc_assert (INDIRECT_BRANCH_THUNK_REGNUM != 4);
+		  return_regnum = 4;
+		}
 	      return_reg = gen_rtx_REG (Pmode, return_regnum);
-
 	      addr = plus_constant (Pmode, frame_pointer,
 				    offset + cfun_frame_layout.gprs_offset
 				    + (RETURN_REGNUM
@@ -11466,16 +11507,7 @@ s390_emit_epilogue (bool sibcall)
   s390_restore_gprs_from_fprs ();
 
   if (! sibcall)
-    {
-
-      /* Return to caller.  */
-
-      p = rtvec_alloc (2);
-
-      RTVEC_ELT (p, 0) = ret_rtx;
-      RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg);
-      emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
-    }
+    emit_jump_insn (gen_return_use (return_reg));
 }
 
 /* Implement TARGET_SET_UP_BY_PROLOGUE.  */
@@ -13053,6 +13085,112 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
   final_end_function ();
 }
 
+/* Output either an indirect jump or a an indirect call
+   (RETURN_ADDR_REGNO != INVALID_REGNUM) with target register REGNO
+   using a branch trampoline disabling branch target prediction.  */
+
+void
+s390_indirect_branch_via_thunk (unsigned int regno,
+				unsigned int return_addr_regno,
+				rtx comparison_operator,
+				enum s390_indirect_branch_type type)
+{
+  enum s390_indirect_branch_option option;
+
+  if (type == s390_indirect_branch_type_return)
+    {
+      if (s390_return_addr_from_memory ())
+	option = s390_opt_function_return_mem;
+      else
+	option = s390_opt_function_return_reg;
+    }
+  else if (type == s390_indirect_branch_type_jump)
+    option = s390_opt_indirect_branch_jump;
+  else if (type == s390_indirect_branch_type_call)
+    option = s390_opt_indirect_branch_call;
+  else
+    gcc_unreachable ();
+
+  if (TARGET_INDIRECT_BRANCH_TABLE)
+    {
+      char label[32];
+
+      ASM_GENERATE_INTERNAL_LABEL (label,
+				   indirect_branch_table_label[option],
+				   indirect_branch_table_label_no[option]++);
+      ASM_OUTPUT_LABEL (asm_out_file, label);
+    }
+
+  if (return_addr_regno != INVALID_REGNUM)
+    {
+      gcc_assert (comparison_operator == NULL_RTX);
+      fprintf (asm_out_file, " \tbrasl\t%%r%d,", return_addr_regno);
+    }
+  else
+    {
+      fputs (" \tjg", asm_out_file);
+      if (comparison_operator != NULL_RTX)
+	print_operand (asm_out_file, comparison_operator, 'C');
+
+      fputs ("\t", asm_out_file);
+    }
+
+  if (TARGET_CPU_Z10)
+    fprintf (asm_out_file,
+	     TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL "\n",
+	     regno);
+  else
+    fprintf (asm_out_file,
+	     TARGET_INDIRECT_BRANCH_THUNK_NAME_EX "\n",
+	     INDIRECT_BRANCH_THUNK_REGNUM, regno);
+
+  if ((option == s390_opt_indirect_branch_jump
+       && cfun->machine->indirect_branch_jump == indirect_branch_thunk)
+      || (option == s390_opt_indirect_branch_call
+	  && cfun->machine->indirect_branch_call == indirect_branch_thunk)
+      || (option == s390_opt_function_return_reg
+	  && cfun->machine->function_return_reg == indirect_branch_thunk)
+      || (option == s390_opt_function_return_mem
+	  && cfun->machine->function_return_mem == indirect_branch_thunk))
+    {
+      if (TARGET_CPU_Z10)
+	indirect_branch_z10thunk_mask |= (1 << regno);
+      else
+	indirect_branch_prez10thunk_mask |= (1 << regno);
+    }
+}
+
+/* Output an inline thunk for indirect jumps.  EXECUTE_TARGET can
+   either be an address register or a label pointing to the location
+   of the jump instruction.  */
+
+void
+s390_indirect_branch_via_inline_thunk (rtx execute_target)
+{
+  if (TARGET_INDIRECT_BRANCH_TABLE)
+    {
+      char label[32];
+
+      ASM_GENERATE_INTERNAL_LABEL (label,
+				   indirect_branch_table_label[s390_opt_indirect_branch_jump],
+				   indirect_branch_table_label_no[s390_opt_indirect_branch_jump]++);
+      ASM_OUTPUT_LABEL (asm_out_file, label);
+    }
+
+  if (!TARGET_ZARCH)
+    fputs ("\t.machinemode zarch\n", asm_out_file);
+
+  if (REG_P (execute_target))
+    fprintf (asm_out_file, "\tex\t%%r0,0(%%r%d)\n", REGNO (execute_target));
+  else
+    output_asm_insn ("exrl\t%%r0,%0", &execute_target);
+
+  if (!TARGET_ZARCH)
+    fputs ("\t.machinemode esa\n", asm_out_file);
+
+  fputs ("0:\tj\t0b\n", asm_out_file);
+}
+
 static bool
 s390_valid_pointer_mode (machine_mode mode)
 {
@@ -13158,6 +13296,14 @@ s390_function_ok_for_sibcall (tree decl, tree exp)
   if (!TARGET_64BIT && flag_pic && decl && !targetm.binds_local_p (decl))
     return false;
 
+  /* The thunks for indirect branches require r1 if no exrl is
+     available.  r1 might not be available when doing a sibling
+     call.  */
+  if (TARGET_INDIRECT_BRANCH_NOBP_CALL
+      && !TARGET_CPU_Z10
+      && !decl)
+    return false;
+
   /* Register 6 on s390 is available as an argument register but unfortunately
      "caller saved". This makes functions needing this register for arguments
      not suitable for sibcalls.  */
@@ -13191,9 +13337,13 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
 {
   bool plt_call = false;
   rtx_insn *insn;
-  rtx call;
-  rtx clobber;
-  rtvec vec;
+  rtx vec[4] = { NULL_RTX };
+  int elts = 0;
+  rtx *call = &vec[0];
+  rtx *clobber_ret_reg = &vec[1];
+  rtx *use = &vec[2];
+  rtx *clobber_thunk_reg = &vec[3];
+  int i;
 
   /* Direct function calls need special treatment.  */
   if (GET_CODE (addr_location) == SYMBOL_REF)
@@ -13245,26 +13395,58 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
       addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM);
     }
 
+  if (TARGET_INDIRECT_BRANCH_NOBP_CALL
+      && GET_CODE (addr_location) != SYMBOL_REF
+      && !plt_call)
+    {
+      /* Indirect branch thunks require the target to be a single GPR.  */
+      addr_location = force_reg (Pmode, addr_location);
+
+      /* Without exrl the indirect branch thunks need an additional
+	 register for larl;ex */
+      if (!TARGET_CPU_Z10)
+	{
+	  *clobber_thunk_reg = gen_rtx_REG (Pmode, INDIRECT_BRANCH_THUNK_REGNUM);
+	  *clobber_thunk_reg = gen_rtx_CLOBBER (VOIDmode, *clobber_thunk_reg);
+	}
+    }
+
   addr_location = gen_rtx_MEM (QImode, addr_location);
-  call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);
+  *call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);
 
   if (result_reg != NULL_RTX)
-    call = gen_rtx_SET (result_reg, call);
+    *call = gen_rtx_SET (result_reg, *call);
 
   if (retaddr_reg != NULL_RTX)
     {
-      clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg);
+      *clobber_ret_reg = gen_rtx_CLOBBER (VOIDmode, retaddr_reg);
 
       if (tls_call != NULL_RTX)
-	vec = gen_rtvec (3, call, clobber,
-			 gen_rtx_USE (VOIDmode, tls_call));
-      else
-	vec = gen_rtvec (2, call, clobber);
+	*use = gen_rtx_USE (VOIDmode, tls_call);
+    }
+
+
+  for (i = 0; i < 4; i++)
+    if (vec[i] != NULL_RTX)
+      elts++;
+
+  if (elts > 1)
+    {
+      rtvec v;
+      int e = 0;
+
+      v = rtvec_alloc (elts);
+      for (i = 0; i < 4; i++)
+	if (vec[i] != NULL_RTX)
+	  {
+	    RTVEC_ELT (v, e) = vec[i];
+	    e++;
+	  }
 
-      call = gen_rtx_PARALLEL (VOIDmode, vec);
+      *call = gen_rtx_PARALLEL (VOIDmode, v);
     }
 
-  insn = emit_call_insn (call);
+  insn = emit_call_insn (*call);
 
   /* 31-bit PLT stubs and tls calls use the GOT register implicitly.  */
   if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX)
@@ -14046,7 +14228,16 @@ s390_reorg (void)
 	  target = emit_label (XEXP (label, 0));
 	  INSN_ADDRESSES_NEW (target, -1);
 
-	  target = emit_insn (s390_execute_target (insn));
+	  if (JUMP_P (insn))
+	    {
+	      target = emit_jump_insn (s390_execute_target (insn));
+	      /* This is important in order to keep a table jump
+		 pointing at the jump table label.  Only this makes it
+		 being recognized as table jump.  */
+	      JUMP_LABEL (target) = JUMP_LABEL (insn);
+	    }
+	  else
+	    target = emit_insn (s390_execute_target (insn));
 	  INSN_ADDRESSES_NEW (target, -1);
 	}
     }
@@ -14699,6 +14890,42 @@ s390_option_override_internal (bool main_args_p,
   if (TARGET_64BIT && !TARGET_ZARCH_P (opts->x_target_flags))
     error ("64-bit ABI not supported in ESA/390 mode");
 
+  if (opts->x_s390_indirect_branch == indirect_branch_thunk_inline
+      || opts->x_s390_indirect_branch_call == indirect_branch_thunk_inline
+      || opts->x_s390_function_return == indirect_branch_thunk_inline
+      || opts->x_s390_function_return_reg == indirect_branch_thunk_inline
+      || opts->x_s390_function_return_mem == indirect_branch_thunk_inline)
+    error ("thunk-inline is only supported with -mindirect-branch-jump");
+
+  if (opts->x_s390_indirect_branch != indirect_branch_keep)
+    {
+      if (!opts_set->x_s390_indirect_branch_call)
+	opts->x_s390_indirect_branch_call = opts->x_s390_indirect_branch;
+
+      if (!opts_set->x_s390_indirect_branch_jump)
+	opts->x_s390_indirect_branch_jump = opts->x_s390_indirect_branch;
+    }
+
+  if (opts->x_s390_function_return != indirect_branch_keep)
+    {
+      if (!opts_set->x_s390_function_return_reg)
+	opts->x_s390_function_return_reg = opts->x_s390_function_return;
+
+      if (!opts_set->x_s390_function_return_mem)
+	opts->x_s390_function_return_mem = opts->x_s390_function_return;
+    }
+
+  if (!TARGET_CPU_ZARCH)
+    {
+      if (opts->x_s390_indirect_branch_call != indirect_branch_keep
+	  || opts->x_s390_indirect_branch_jump != indirect_branch_keep)
+	error ("-mindirect-branch* options require -march=z900 or higher");
+      if (opts->x_s390_function_return_reg != indirect_branch_keep
+	  || opts->x_s390_function_return_mem != indirect_branch_keep)
+	error ("-mfunction-return* options require -march=z900 or higher");
+    }
+
+
   /* Enable hardware transactions if available and not explicitly
      disabled by user.  E.g. with -m31 -march=zEC12 -mzarch */
   if (!TARGET_OPT_HTM_P (opts_set->x_target_flags))
@@ -15267,6 +15494,79 @@ s390_valid_target_attribute_p (tree fndecl,
   return ret;
 }
 
+/* Set VAL to correct enum value according to the indirect-branch or
+   function-return attribute in ATTR.  */
+
+static inline void
+s390_indirect_branch_attrvalue (tree attr, enum indirect_branch *val)
+{
+  const char *str = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)));
+  if (strcmp (str, "keep") == 0)
+    *val = indirect_branch_keep;
+  else if (strcmp (str, "thunk") == 0)
+    *val = indirect_branch_thunk;
+  else if (strcmp (str, "thunk-inline") == 0)
+    *val = indirect_branch_thunk_inline;
+  else if (strcmp (str, "thunk-extern") == 0)
+    *val = indirect_branch_thunk_extern;
+}
+
+/* Memorize the setting for -mindirect-branch* and -mfunction-return*
+   from either the cmdline or the function attributes in
+   cfun->machine.  */
+
+static void
+s390_indirect_branch_settings (tree fndecl)
+{
+  tree attr;
+
+  if (!fndecl)
+    return;
+
+  /* Initialize with the cmdline options and let the attributes
+     override it.  */
+  cfun->machine->indirect_branch_jump = s390_indirect_branch_jump;
+  cfun->machine->indirect_branch_call = s390_indirect_branch_call;
+
+  cfun->machine->function_return_reg = s390_function_return_reg;
+  cfun->machine->function_return_mem = s390_function_return_mem;
+
+  if ((attr = lookup_attribute ("indirect_branch",
+				DECL_ATTRIBUTES (fndecl))))
+    {
+      s390_indirect_branch_attrvalue (attr,
+				      &cfun->machine->indirect_branch_jump);
+      s390_indirect_branch_attrvalue (attr,
+				      &cfun->machine->indirect_branch_call);
+    }
+
+  if ((attr = lookup_attribute ("indirect_branch_jump",
+				DECL_ATTRIBUTES (fndecl))))
+    s390_indirect_branch_attrvalue (attr, &cfun->machine->indirect_branch_jump);
+
+  if ((attr = lookup_attribute ("indirect_branch_call",
+				DECL_ATTRIBUTES (fndecl))))
+    s390_indirect_branch_attrvalue (attr, &cfun->machine->indirect_branch_call);
+
+  if ((attr = lookup_attribute ("function_return",
+				DECL_ATTRIBUTES (fndecl))))
+    {
+      s390_indirect_branch_attrvalue (attr,
+				      &cfun->machine->function_return_reg);
+      s390_indirect_branch_attrvalue (attr,
+				      &cfun->machine->function_return_mem);
+    }
+
+  if ((attr = lookup_attribute ("function_return_reg",
+				DECL_ATTRIBUTES (fndecl))))
+    s390_indirect_branch_attrvalue (attr, &cfun->machine->function_return_reg);
+
+  if ((attr = lookup_attribute ("function_return_mem",
+				DECL_ATTRIBUTES (fndecl))))
+    s390_indirect_branch_attrvalue (attr, &cfun->machine->function_return_mem);
+}
+
+
 /* Restore targets globals from NEW_TREE and invalidate s390_previous_fndecl
    cache.  */
 
@@ -15317,6 +15617,8 @@ s390_set_current_function (tree fndecl)
   if (old_tree != new_tree)
     s390_activate_target_options (new_tree);
   s390_previous_fndecl = fndecl;
+
+  s390_indirect_branch_settings (fndecl);
 }
 #endif
 
@@ -15598,6 +15900,186 @@ s390_asan_shadow_offset (void)
   return TARGET_64BIT ? HOST_WIDE_INT_1U << 52 : HOST_WIDE_INT_UC (0x20000000);
 }
 
+#ifdef HAVE_GAS_HIDDEN
+# define USE_HIDDEN_LINKONCE 1
+#else
+# define USE_HIDDEN_LINKONCE 0
+#endif
+
+/* Output an indirect branch trampoline for target register REGNO.  */
+
+static void
+s390_output_indirect_thunk_function (unsigned int regno, bool z10_p)
+{
+  tree decl;
+  char thunk_label[32];
+  int i;
+
+  if (z10_p)
+    sprintf (thunk_label, TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL, regno);
+  else
+    sprintf (thunk_label, TARGET_INDIRECT_BRANCH_THUNK_NAME_EX,
+	     INDIRECT_BRANCH_THUNK_REGNUM, regno);
+
+  decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
+		     get_identifier (thunk_label),
+		     build_function_type_list (void_type_node, NULL_TREE));
+  DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL,
+				   NULL_TREE, void_type_node);
+  TREE_PUBLIC (decl) = 1;
+  TREE_STATIC (decl) = 1;
+  DECL_IGNORED_P (decl) = 1;
+
+  if (USE_HIDDEN_LINKONCE)
+    {
+      cgraph_node::create (decl)->set_comdat_group (DECL_ASSEMBLER_NAME (decl));
+
+      targetm.asm_out.unique_section (decl, 0);
+      switch_to_section (get_named_section (decl, NULL, 0));
+
+      targetm.asm_out.globalize_label (asm_out_file, thunk_label);
+      fputs ("\t.hidden\t", asm_out_file);
+      assemble_name (asm_out_file, thunk_label);
+      putc ('\n', asm_out_file);
+      ASM_DECLARE_FUNCTION_NAME (asm_out_file, thunk_label, decl);
+    }
+  else
+    {
+      switch_to_section (text_section);
+      ASM_OUTPUT_LABEL (asm_out_file, thunk_label);
+    }
+
+  DECL_INITIAL (decl) = make_node (BLOCK);
+  current_function_decl = decl;
+  allocate_struct_function (decl, false);
+  init_function_start (decl);
+  cfun->is_thunk = true;
+  first_function_block_is_cold = false;
+  final_start_function (emit_barrier (), asm_out_file, 1);
+
+  /* This makes CFI at least usable for indirect jumps.
+
+     Stopping in the thunk: backtrace will point to the thunk target
+     is if it was interrupted by a signal.  For a call this means that
+     the call chain will be: caller->callee->thunk   */
+  if (flag_asynchronous_unwind_tables)
+    {
+      fputs ("\t.cfi_signal_frame\n", asm_out_file);
+      fprintf (asm_out_file, "\t.cfi_return_column %d\n", regno);
+      for (i = 0; i < FPR15_REGNUM; i++)
+	fprintf (asm_out_file, "\t.cfi_same_value %s\n", reg_names[i]);
+    }
+
+  if (z10_p)
+    {
+      /* exrl  0,1f  */
+
+      /* We generate a thunk for z10 compiled code although z10 is
+	 currently not enabled.  Tell the assembler to accept the
+	 instruction.  */
+      if (!TARGET_CPU_Z10)
+	{
+	  fputs ("\t.machine push\n", asm_out_file);
+	  fputs ("\t.machine z10\n", asm_out_file);
+	}
+      /* We use exrl even if -mzarch hasn't been specified on the
+	 command line so we have to tell the assembler to accept
+	 it.  */
+      if (!TARGET_ZARCH)
+	fputs ("\t.machinemode zarch\n", asm_out_file);
+
+      fputs ("\texrl\t0,1f\n", asm_out_file);
+
+      if (!TARGET_ZARCH)
+	fputs ("\t.machinemode esa\n", asm_out_file);
+
+      if (!TARGET_CPU_Z10)
+	fputs ("\t.machine pop\n", asm_out_file);
+    }
+  else if (TARGET_CPU_ZARCH)
+    {
+      /* larl %r1,1f  */
+      fprintf (asm_out_file, "\tlarl\t%%r%d,1f\n",
+	       INDIRECT_BRANCH_THUNK_REGNUM);
+
+      /* ex 0,0(%r1)  */
+      fprintf (asm_out_file, "\tex\t0,0(%%r%d)\n",
+	       INDIRECT_BRANCH_THUNK_REGNUM);
+    }
+  else
+    gcc_unreachable ();
+
+  /* 0:    j 0b  */
+  fputs ("0:\tj\t0b\n", asm_out_file);
+
+  /* 1:    br <regno>  */
+  fprintf (asm_out_file, "1:\tbr\t%%r%d\n", regno);
+
+  final_end_function ();
+  init_insn_lengths ();
+  free_after_compilation (cfun);
+  set_cfun (NULL);
+  current_function_decl = NULL;
+}
+
+/* Implement the asm.code_end target hook.  */
+
+static void
+s390_code_end (void)
+{
+  int i;
+
+  for (i = 1; i < 16; i++)
+    {
+      if (indirect_branch_z10thunk_mask & (1 << i))
+	s390_output_indirect_thunk_function (i, true);
+
+      if (indirect_branch_prez10thunk_mask & (1 << i))
+	s390_output_indirect_thunk_function (i, false);
+    }
+
+  if (TARGET_INDIRECT_BRANCH_TABLE)
+    {
+      int o;
+      int i;
+
+      for (o = 0; o < INDIRECT_BRANCH_NUM_OPTIONS; o++)
+	{
+	  if (indirect_branch_table_label_no[o] == 0)
+	    continue;
+
+	  switch_to_section (get_section (indirect_branch_table_name[o],
+					  0,
+					  NULL_TREE));
+	  for (i = 0; i < indirect_branch_table_label_no[o]; i++)
+	    {
+	      char label_start[32];
+
+	      ASM_GENERATE_INTERNAL_LABEL (label_start,
+					   indirect_branch_table_label[o], i);
+
+	      fputs ("\t.long\t", asm_out_file);
+	      assemble_name_raw (asm_out_file, label_start);
+	      fputs ("-.\n", asm_out_file);
+	    }
+	  switch_to_section (current_function_section ());
+	}
+    }
+}
+
+/* Implement the TARGET_CASE_VALUES_THRESHOLD target hook.  */
+
+unsigned int
+s390_case_values_threshold (void)
+{
+  /* Disabling branch prediction for indirect jumps makes jump tables
+     much more expensive.  */
+  if (TARGET_INDIRECT_BRANCH_NOBP_JUMP)
+    return 20;
+
+  return default_case_values_threshold ();
+}
+
 /* Initialize GCC target structure.  */
 
 #undef  TARGET_ASM_ALIGNED_HI_OP
@@ -15854,6 +16336,12 @@ s390_asan_shadow_offset (void)
 #undef TARGET_OPTION_RESTORE
 #define TARGET_OPTION_RESTORE s390_function_specific_restore
 
+#undef TARGET_ASM_CODE_END
+#define TARGET_ASM_CODE_END s390_code_end
+
+#undef TARGET_CASE_VALUES_THRESHOLD
+#define TARGET_CASE_VALUES_THRESHOLD s390_case_values_threshold
+
 struct gcc_target targetm = TARGET_INITIALIZER;
 
 #include "gt-s390.h"
diff --git a/gcc/config/s390/s390.h b/gcc/config/s390/s390.h
index a372981..68e3876 100644
--- a/gcc/config/s390/s390.h
+++ b/gcc/config/s390/s390.h
@@ -1120,4 +1120,124 @@ extern const int processor_flags_table[];
     s390_register_target_pragmas ();		\
   } while (0)
 
+#ifndef USED_FOR_TARGET
+/* The following structure is embedded in the machine
+   specific part of struct function.  */
+
+struct GTY (()) s390_frame_layout
+{
+  /* Offset within stack frame.  */
+  HOST_WIDE_INT gprs_offset;
+  HOST_WIDE_INT f0_offset;
+  HOST_WIDE_INT f4_offset;
+  HOST_WIDE_INT f8_offset;
+  HOST_WIDE_INT backchain_offset;
+
+  /* Number of first and last gpr where slots in the register
+     save area are reserved for.  */
+  int first_save_gpr_slot;
+  int last_save_gpr_slot;
+
+  /* Location (FP register number) where GPRs (r0-r15) should
+     be saved to.
+      0 - does not need to be saved at all
+     -1 - stack slot  */
+#define SAVE_SLOT_NONE   0
+#define SAVE_SLOT_STACK -1
+  signed char gpr_save_slots[16];
+
+  /* Number of first and last gpr to be saved, restored.  */
+  int first_save_gpr;
+  int first_restore_gpr;
+  int last_save_gpr;
+  int last_restore_gpr;
+
+  /* Bits standing for floating point registers. Set, if the
+     respective register has to be saved. Starting with reg 16 (f0)
+     at the rightmost bit.
+     Bit 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0
+     fpr 15 13 11  9 14 12 10  8  7  5  3  1  6  4  2  0
+     reg 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16  */
+  unsigned int fpr_bitmap;
+
+  /* Number of floating point registers f8-f15 which must be saved.  */
+  int high_fprs;
+
+  /* Set if return address needs to be saved.
+     This flag is set by s390_return_addr_rtx if it could not use
+     the initial value of r14 and therefore depends on r14 saved
+     to the stack.  */
+  bool save_return_addr_p;
+
+  /* Size of stack frame.  */
+  HOST_WIDE_INT frame_size;
+};
+
+
+/* Define the structure for the machine field in struct function.  */
+
+struct GTY(()) machine_function
+{
+  struct s390_frame_layout frame_layout;
+
+  /* Literal pool base register.  */
+  rtx base_reg;
+
+  /* True if we may need to perform branch splitting.  */
+  bool split_branches_pending_p;
+
+  bool has_landing_pad_p;
+
+  /* True if the current function may contain a tbegin clobbering
+     FPRs.  */
+  bool tbegin_p;
+
+  /* For -fsplit-stack support: A stack local which holds a pointer to
+     the stack arguments for a function with a variable number of
+     arguments.  This is set at the start of the function and is used
+     to initialize the overflow_arg_area field of the va_list
+     structure.  */
+  rtx split_stack_varargs_pointer;
+
+  enum indirect_branch indirect_branch_jump;
+  enum indirect_branch indirect_branch_call;
+
+  enum indirect_branch function_return_mem;
+  enum indirect_branch function_return_reg;
+};
+#endif
+
+#define TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION				\
+  (cfun->machine->function_return_reg != indirect_branch_keep		\
+   || cfun->machine->function_return_mem != indirect_branch_keep)
+
+#define TARGET_INDIRECT_BRANCH_NOBP_RET					\
+  ((cfun->machine->function_return_reg != indirect_branch_keep		\
+    && !s390_return_addr_from_memory ())				\
+   || (cfun->machine->function_return_mem != indirect_branch_keep	\
+       && s390_return_addr_from_memory ()))
+
+#define TARGET_INDIRECT_BRANCH_NOBP_JUMP				\
+  (cfun->machine->indirect_branch_jump != indirect_branch_keep)
+
+#define TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK				\
+  (cfun->machine->indirect_branch_jump == indirect_branch_thunk		\
+   || cfun->machine->indirect_branch_jump == indirect_branch_thunk_extern)
+
+#define TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK			\
+  (cfun->machine->indirect_branch_jump == indirect_branch_thunk_inline)
+
+#define TARGET_INDIRECT_BRANCH_NOBP_CALL			\
+  (cfun->machine->indirect_branch_call != indirect_branch_keep)
+
+#ifndef TARGET_DEFAULT_INDIRECT_BRANCH_TABLE
+#define TARGET_DEFAULT_INDIRECT_BRANCH_TABLE 0
+#endif
+
+#define TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL "__s390_indirect_jump_r%d"
+#define TARGET_INDIRECT_BRANCH_THUNK_NAME_EX   "__s390_indirect_jump_r%duse_r%d"
+
+#define TARGET_INDIRECT_BRANCH_TABLE s390_indirect_branch_table
+
+
 #endif /* S390_H */
diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md
index 7b6d09b..1778d60 100644
--- a/gcc/config/s390/s390.md
+++ b/gcc/config/s390/s390.md
@@ -89,6 +89,7 @@
    UNSPEC_LTREF
    UNSPEC_INSN
    UNSPEC_EXECUTE
+   UNSPEC_EXECUTE_JUMP
 
    ; Atomic Support
    UNSPEC_MB
@@ -302,6 +303,8 @@
   [
    ; Sibling call register.
    (SIBCALL_REGNUM		 1)
+   ; A call-clobbered reg which can be used in indirect branch thunks
+   (INDIRECT_BRANCH_THUNK_REGNUM 1)
    ; Literal pool base register.
    (BASE_REGNUM			13)
    ; Return address register.
@@ -471,7 +474,10 @@
                          z196_cracked"
              (const_string "none"))
 
-(define_attr "mnemonic" "bcr_flush,unknown" (const_string "unknown"))
+; mnemonics which only get defined through if_then_else currently
+; don't get added to the list values automatically and hence need to
+; be listed here.
+(define_attr "mnemonic" "b,bas,bc,bcr_flush,unknown" (const_string "unknown"))
 
 ;; Length in bytes.
 
@@ -9069,7 +9075,7 @@
           (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)])
           (match_operand 0 "address_operand" "ZQZR")
           (pc)))]
-  ""
+  "!TARGET_INDIRECT_BRANCH_NOBP_JUMP"
 {
   if (get_attr_op_type (insn) == OP_TYPE_RR)
     return "b%C1r\t%0";
@@ -9079,6 +9085,9 @@
   [(set (attr "op_type")
         (if_then_else (match_operand 0 "register_operand" "")
                       (const_string "RR") (const_string "RX")))
+   (set (attr "mnemonic")
+        (if_then_else (match_operand 0 "register_operand" "")
+                      (const_string "bcr") (const_string "bc")))
    (set_attr "type"  "branch")
    (set_attr "atype" "agen")])
 
@@ -9090,8 +9099,26 @@
           (ANY_RETURN)
           (pc)))]
   "s390_can_use_<code>_insn ()"
-  "b%C0r\t%%r14"
-  [(set_attr "op_type" "RR")
+{
+  if (TARGET_INDIRECT_BRANCH_NOBP_RET)
+    {
+      s390_indirect_branch_via_thunk (RETURN_REGNUM,
+				      INVALID_REGNUM,
+				      operands[0],
+				      s390_indirect_branch_type_return);
+      return "";
+    }
+  else
+    return "b%C0r\t%%r14";
+}
+  [(set (attr "op_type")
+	(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
+		      (const_string "RIL")
+		      (const_string "RR")))
+   (set (attr "mnemonic")
+	(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
+		      (const_string "brcl")
+		      (const_string "bcr")))
    (set_attr "type"  "jsr")
    (set_attr "atype" "agen")])
 
@@ -9144,7 +9171,7 @@
           (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)])
           (pc)
           (match_operand 0 "address_operand" "ZQZR")))]
-  ""
+  "!TARGET_INDIRECT_BRANCH_NOBP_JUMP"
 {
   if (get_attr_op_type (insn) == OP_TYPE_RR)
     return "b%D1r\t%0";
@@ -9154,6 +9181,9 @@
   [(set (attr "op_type")
         (if_then_else (match_operand 0 "register_operand" "")
                       (const_string "RR") (const_string "RX")))
+   (set (attr "mnemonic")
+        (if_then_else (match_operand 0 "register_operand" "")
+                      (const_string "bcr") (const_string "bc")))
    (set_attr "type"  "branch")
    (set_attr "atype" "agen")])
 
@@ -9658,21 +9688,144 @@
     ;
   else
     operands[0] = force_reg (Pmode, operands[0]);
+
+  if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK)
+    {
+      operands[0] = force_reg (Pmode, operands[0]);
+      if (TARGET_CPU_Z10)
+	{
+	  if (TARGET_64BIT)
+	    emit_jump_insn (gen_indirect_jump_via_thunkdi_z10 (operands[0]));
+	  else
+	    emit_jump_insn (gen_indirect_jump_via_thunksi_z10 (operands[0]));
+	}
+      else
+	{
+	  if (TARGET_64BIT)
+	    emit_jump_insn (gen_indirect_jump_via_thunkdi (operands[0]));
+	  else
+	    emit_jump_insn (gen_indirect_jump_via_thunksi (operands[0]));
+	}
+      DONE;
+    }
+
+  if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK)
+    {
+      operands[0] = force_reg (Pmode, operands[0]);
+      rtx label_ref = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
+      if (TARGET_CPU_Z10)
+	{
+	  if (TARGET_64BIT)
+	    emit_jump_insn (gen_indirect_jump_via_inlinethunkdi_z10 (operands[0],
+								     label_ref));
+	  else
+	    emit_jump_insn (gen_indirect_jump_via_inlinethunksi_z10 (operands[0],
+								     label_ref));
+	}
+      else
+	{
+	  if (TARGET_64BIT)
+	    emit_jump_insn (gen_indirect_jump_via_inlinethunkdi (operands[0],
+								 label_ref,
+								 force_reg (Pmode, label_ref)));
+	  else
+	    emit_jump_insn (gen_indirect_jump_via_inlinethunksi (operands[0],
+								 label_ref,
+								 force_reg (Pmode, label_ref)));
+	}
+      DONE;
+    }
 })
 
-; The first constraint must be an "extra address constraint" in order
-; to trigger address reloading in LRA/reload
 (define_insn "*indirect_jump"
   [(set (pc)
-	(match_operand 0 "address_operand" "ZR,a"))]
- ""
- "@
-  b\t%a0
-  br\t%0"
- [(set_attr "op_type" "RX,RR")
+	(match_operand 0 "address_operand" "ZR"))]
+ "!TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK"
+{
+  if (get_attr_op_type (insn) == OP_TYPE_RR)
+    return "br\t%0";
+  else
+    return "b\t%a0";
+}
+ [(set (attr "op_type")
+       (if_then_else (match_operand 0 "register_operand" "")
+		     (const_string "RR") (const_string "RX")))
+  (set (attr "mnemonic")
+       (if_then_else (match_operand 0 "register_operand" "")
+		     (const_string "br") (const_string "b")))
   (set_attr "type"  "branch")
-  (set_attr "atype" "agen")
-  (set_attr "cpu_facility" "*")])
+  (set_attr "atype" "agen")])
+
+(define_insn "indirect_jump_via_thunk<mode>_z10"
+  [(set (pc)
+	(match_operand:P 0 "register_operand" "a"))]
+ "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK
+  && TARGET_CPU_Z10"
+{
+  s390_indirect_branch_via_thunk (REGNO (operands[0]),
+				  INVALID_REGNUM,
+				  NULL_RTX,
+				  s390_indirect_branch_type_jump);
+  return "";
+}
+ [(set_attr "op_type"  "RIL")
+  (set_attr "mnemonic" "jg")
+  (set_attr "type"  "branch")
+  (set_attr "atype" "agen")])
+
+(define_insn "indirect_jump_via_thunk<mode>"
+  [(set (pc)
+	(match_operand:P 0 "register_operand" " a"))
+   (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]
+ "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK
+  && !TARGET_CPU_Z10"
+{
+  s390_indirect_branch_via_thunk (REGNO (operands[0]),
+				  INVALID_REGNUM,
+				  NULL_RTX,
+				  s390_indirect_branch_type_jump);
+  return "";
+}
+ [(set_attr "op_type"  "RIL")
+  (set_attr "mnemonic" "jg")
+  (set_attr "type"  "branch")
+  (set_attr "atype" "agen")])
+
+
+; The label_ref is wrapped into an if_then_else in order to hide it
+; from mark_jump_label.  Without this the label_ref would become the
+; ONLY jump target of that jump breaking the control flow graph.
+(define_insn "indirect_jump_via_inlinethunk<mode>_z10"
+  [(unspec [(if_then_else (match_operand:P 1 "larl_operand" "X")
+			  (const_int 0)
+			  (const_int 0))
+	    (const_int 0)] UNSPEC_EXECUTE_JUMP)
+   (set (pc) (match_operand:P 0 "register_operand" "a"))]
+  "TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK
+   && TARGET_CPU_Z10"
+{
+  s390_indirect_branch_via_inline_thunk (operands[1]);
+  return "";
+}
+  [(set_attr "op_type" "RIL")
+   (set_attr "type"    "branch")
+   (set_attr "length"  "10")])
+
+(define_insn "indirect_jump_via_inlinethunk<mode>"
+  [(unspec [(if_then_else (match_operand:P 1 "larl_operand" "X")
+			  (const_int 0)
+			  (const_int 0))
+	    (match_operand:P 2 "register_operand" "a")] UNSPEC_EXECUTE_JUMP)
+   (set (pc) (match_operand:P 0 "register_operand" "a"))]
+  "TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK
+   && !TARGET_CPU_Z10"
+{
+  s390_indirect_branch_via_inline_thunk (operands[2]);
+  return "";
+}
+  [(set_attr "op_type" "RX")
+   (set_attr "type"    "branch")
+   (set_attr "length"  "8")])
 
 ; FIXME: LRA does not appear to be able to deal with MEMs being
 ; checked against address constraints like ZR above.  So make this a
@@ -9680,7 +9833,7 @@
 (define_insn "*indirect2_jump"
   [(set (pc)
 	(match_operand 0 "nonimmediate_operand" "a,T"))]
- ""
+ "!TARGET_INDIRECT_BRANCH_NOBP_JUMP"
  "@
   br\t%0
   bi\t%0"
@@ -9693,11 +9846,74 @@
 ; casesi instruction pattern(s).
 ;
 
-(define_insn "casesi_jump"
- [(set (pc) (match_operand 0 "address_operand" "ZR"))
-   (use (label_ref (match_operand 1 "" "")))]
+(define_expand "casesi_jump"
+  [(parallel
+    [(set (pc) (match_operand 0 "address_operand"))
+     (use (label_ref (match_operand 1 "")))])]
   ""
 {
+  if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK)
+    {
+      operands[0] = force_reg (GET_MODE (operands[0]), operands[0]);
+
+      if (TARGET_CPU_Z10)
+	{
+	  if (TARGET_64BIT)
+	    emit_jump_insn (gen_casesi_jump_via_thunkdi_z10 (operands[0],
+							     operands[1]));
+	  else
+	    emit_jump_insn (gen_casesi_jump_via_thunksi_z10 (operands[0],
+							     operands[1]));
+	}
+      else
+	{
+	  if (TARGET_64BIT)
+	    emit_jump_insn (gen_casesi_jump_via_thunkdi (operands[0],
+							 operands[1]));
+	  else
+	    emit_jump_insn (gen_casesi_jump_via_thunksi (operands[0],
+							 operands[1]));
+	}
+      DONE;
+    }
+
+    if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK)
+    {
+      operands[0] = force_reg (Pmode, operands[0]);
+      rtx label_ref = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
+      if (TARGET_CPU_Z10)
+	{
+	  if (TARGET_64BIT)
+	    emit_jump_insn (gen_casesi_jump_via_inlinethunkdi_z10 (operands[0],
+								   operands[1],
+								   label_ref));
+	  else
+	    emit_jump_insn (gen_casesi_jump_via_inlinethunksi_z10 (operands[0],
+								   operands[1],
+								   label_ref));
+	}
+      else
+	{
+	  if (TARGET_64BIT)
+	    emit_jump_insn (gen_casesi_jump_via_inlinethunkdi (operands[0],
+							       operands[1],
+							       label_ref,
+							       force_reg (Pmode, label_ref)));
+	  else
+	    emit_jump_insn (gen_casesi_jump_via_inlinethunksi (operands[0],
+							       operands[1],
+							       label_ref,
+							       force_reg (Pmode, label_ref)));
+	}
+      DONE;
+    }
+})
+
+(define_insn "*casesi_jump"
+ [(set (pc) (match_operand 0 "address_operand" "ZR"))
+  (use (label_ref (match_operand 1 "" "")))]
+ "!TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK"
+{
   if (get_attr_op_type (insn) == OP_TYPE_RR)
     return "br\t%0";
   else
@@ -9706,9 +9922,85 @@
   [(set (attr "op_type")
         (if_then_else (match_operand 0 "register_operand" "")
                       (const_string "RR") (const_string "RX")))
+   (set (attr "mnemonic")
+        (if_then_else (match_operand 0 "register_operand" "")
+                      (const_string "br") (const_string "b")))
+   (set_attr "type"  "branch")
+   (set_attr "atype" "agen")])
+
+(define_insn "casesi_jump_via_thunk<mode>_z10"
+ [(set (pc) (match_operand:P 0 "register_operand" "a"))
+  (use (label_ref (match_operand 1 "" "")))]
+ "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK
+  && TARGET_CPU_Z10"
+{
+  s390_indirect_branch_via_thunk (REGNO (operands[0]),
+				  INVALID_REGNUM,
+				  NULL_RTX,
+				  s390_indirect_branch_type_jump);
+  return "";
+}
+  [(set_attr "op_type" "RIL")
+   (set_attr "mnemonic" "jg")
+   (set_attr "type"  "branch")
+   (set_attr "atype" "agen")])
+
+(define_insn "casesi_jump_via_thunk<mode>"
+ [(set (pc) (match_operand:P 0 "register_operand" "a"))
+  (use (label_ref (match_operand 1 "" "")))
+  (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]
+ "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK
+  && !TARGET_CPU_Z10"
+{
+  s390_indirect_branch_via_thunk (REGNO (operands[0]),
+				  INVALID_REGNUM,
+				  NULL_RTX,
+				  s390_indirect_branch_type_jump);
+  return "";
+}
+  [(set_attr "op_type" "RIL")
+   (set_attr "mnemonic" "jg")
    (set_attr "type"  "branch")
    (set_attr "atype" "agen")])
 
+
+; The label_ref is wrapped into an if_then_else in order to hide it
+; from mark_jump_label.  Without this the label_ref would become the
+; ONLY jump target of that jump breaking the control flow graph.
+(define_insn "casesi_jump_via_inlinethunk<mode>_z10"
+  [(unspec [(if_then_else (match_operand:P 2 "larl_operand" "X")
+			  (const_int 0)
+			  (const_int 0))
+	    (const_int 0)] UNSPEC_EXECUTE_JUMP)
+   (set (pc) (match_operand:P 0 "register_operand" "a"))
+   (use (label_ref (match_operand 1 "" "")))]
+  "TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK
+   && TARGET_CPU_Z10"
+{
+  s390_indirect_branch_via_inline_thunk (operands[2]);
+  return "";
+}
+  [(set_attr "op_type" "RIL")
+   (set_attr "type"    "cs")
+   (set_attr "length"  "10")])
+
+(define_insn "casesi_jump_via_inlinethunk<mode>"
+  [(unspec [(if_then_else (match_operand:P 2 "larl_operand" "X")
+			  (const_int 0)
+			  (const_int 0))
+	    (match_operand:P 3 "register_operand" "a")] UNSPEC_EXECUTE_JUMP)
+   (set (pc) (match_operand:P 0 "register_operand" "a"))
+   (use (label_ref (match_operand 1 "" "")))]
+  "TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK
+   && !TARGET_CPU_Z10"
+{
+  s390_indirect_branch_via_inline_thunk (operands[3]);
+  return "";
+}
+  [(set_attr "op_type" "RX")
+   (set_attr "type"    "cs")
+   (set_attr "length"  "8")])
+
 (define_expand "casesi"
   [(match_operand:SI 0 "general_operand" "")
    (match_operand:SI 1 "general_operand" "")
@@ -9813,8 +10105,27 @@
          (match_operand 0 "const_int_operand" "n"))]
   "SIBLING_CALL_P (insn)
    && GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode"
-  "br\t%%r1"
-  [(set_attr "op_type" "RR")
+{
+  if (TARGET_INDIRECT_BRANCH_NOBP_CALL)
+    {
+      gcc_assert (TARGET_CPU_Z10);
+      s390_indirect_branch_via_thunk (SIBCALL_REGNUM,
+				      INVALID_REGNUM,
+				      NULL_RTX,
+				      s390_indirect_branch_type_call);
+      return "";
+    }
+  else
+    return "br\t%%r1";
+}
+ [(set (attr "op_type")
+       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")
+		     (const_string "RIL")
+		     (const_string "RR")))
+  (set (attr "mnemonic")
+       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")
+		     (const_string "jg")
+		     (const_string "br")))
    (set_attr "type"  "branch")
    (set_attr "atype" "agen")])
 
@@ -9854,8 +10165,27 @@
 	      (match_operand 1 "const_int_operand" "n")))]
   "SIBLING_CALL_P (insn)
    && GET_MODE (XEXP (XEXP (XEXP (PATTERN (insn), 1), 0), 0)) == Pmode"
-  "br\t%%r1"
-  [(set_attr "op_type" "RR")
+{
+  if (TARGET_INDIRECT_BRANCH_NOBP_CALL)
+    {
+      gcc_assert (TARGET_CPU_Z10);
+      s390_indirect_branch_via_thunk (SIBCALL_REGNUM,
+				      INVALID_REGNUM,
+				      NULL_RTX,
+				      s390_indirect_branch_type_call);
+      return "";
+    }
+  else
+    return "br\t%%r1";
+}
+  [(set (attr "op_type")
+       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")
+		     (const_string "RIL")
+		     (const_string "RR")))
+   (set (attr "mnemonic")
+       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")
+		     (const_string "jg")
+		     (const_string "br")))
    (set_attr "type"  "branch")
    (set_attr "atype" "agen")])
 
@@ -9921,7 +10251,9 @@
   [(call (mem:QI (match_operand 0 "address_operand" "ZR"))
          (match_operand 1 "const_int_operand" "n"))
    (clobber (match_operand 2 "register_operand" "=r"))]
-  "!SIBLING_CALL_P (insn) && GET_MODE (operands[2]) == Pmode"
+  "!TARGET_INDIRECT_BRANCH_NOBP_CALL
+   && !SIBLING_CALL_P (insn)
+   && GET_MODE (operands[2]) == Pmode"
 {
   if (get_attr_op_type (insn) == OP_TYPE_RR)
     return "basr\t%2,%0";
@@ -9931,6 +10263,50 @@
   [(set (attr "op_type")
         (if_then_else (match_operand 0 "register_operand" "")
                       (const_string "RR") (const_string "RX")))
+   (set (attr "mnemonic")
+        (if_then_else (match_operand 0 "register_operand" "")
+                      (const_string "basr") (const_string "bas")))
+   (set_attr "type"  "jsr")
+   (set_attr "atype" "agen")
+   (set_attr "z196prop" "z196_cracked")])
+
+(define_insn "*basr_via_thunk<mode>_z10"
+  [(call (mem:QI (match_operand:P 0 "register_operand" "a"))
+         (match_operand 1 "const_int_operand"          "n"))
+   (clobber (match_operand:P 2 "register_operand"    "=&r"))]
+  "TARGET_INDIRECT_BRANCH_NOBP_CALL
+   && TARGET_CPU_Z10
+   && !SIBLING_CALL_P (insn)"
+{
+  s390_indirect_branch_via_thunk (REGNO (operands[0]),
+				  REGNO (operands[2]),
+				  NULL_RTX,
+				  s390_indirect_branch_type_call);
+  return "";
+}
+  [(set_attr "op_type" "RIL")
+   (set_attr "mnemonic" "brasl")
+   (set_attr "type"  "jsr")
+   (set_attr "atype" "agen")
+   (set_attr "z196prop" "z196_cracked")])
+
+(define_insn "*basr_via_thunk<mode>"
+  [(call (mem:QI (match_operand:P 0 "register_operand" "a"))
+         (match_operand 1 "const_int_operand"          "n"))
+   (clobber (match_operand:P 2 "register_operand"    "=&r"))
+   (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]
+  "TARGET_INDIRECT_BRANCH_NOBP_CALL
+   && !TARGET_CPU_Z10
+   && !SIBLING_CALL_P (insn)"
+{
+  s390_indirect_branch_via_thunk (REGNO (operands[0]),
+				  REGNO (operands[2]),
+				  NULL_RTX,
+				  s390_indirect_branch_type_call);
+  return "";
+}
+  [(set_attr "op_type" "RIL")
+   (set_attr "mnemonic" "brasl")
    (set_attr "type"  "jsr")
    (set_attr "atype" "agen")
    (set_attr "z196prop" "z196_cracked")])
@@ -9982,7 +10358,9 @@
         (call (mem:QI (match_operand 1 "address_operand" "ZR"))
               (match_operand 2 "const_int_operand" "n")))
    (clobber (match_operand 3 "register_operand" "=r"))]
-  "!SIBLING_CALL_P (insn) && GET_MODE (operands[3]) == Pmode"
+  "!TARGET_INDIRECT_BRANCH_NOBP_CALL
+   && !SIBLING_CALL_P (insn)
+   && GET_MODE (operands[3]) == Pmode"
 {
   if (get_attr_op_type (insn) == OP_TYPE_RR)
     return "basr\t%3,%1";
@@ -9992,6 +10370,54 @@
   [(set (attr "op_type")
         (if_then_else (match_operand 1 "register_operand" "")
                       (const_string "RR") (const_string "RX")))
+   (set (attr "mnemonic")
+        (if_then_else (match_operand 1 "register_operand" "")
+                      (const_string "basr") (const_string "bas")))
+   (set_attr "type"  "jsr")
+   (set_attr "atype" "agen")
+   (set_attr "z196prop" "z196_cracked")])
+
+(define_insn "*basr_r_via_thunk_z10"
+  [(set (match_operand 0 "" "")
+        (call (mem:QI (match_operand 1 "register_operand" "a"))
+              (match_operand 2 "const_int_operand"        "n")))
+   (clobber (match_operand 3 "register_operand"         "=&r"))]
+  "TARGET_INDIRECT_BRANCH_NOBP_CALL
+   && TARGET_CPU_Z10
+   && !SIBLING_CALL_P (insn)
+   && GET_MODE (operands[3]) == Pmode"
+{
+  s390_indirect_branch_via_thunk (REGNO (operands[1]),
+				  REGNO (operands[3]),
+				  NULL_RTX,
+				  s390_indirect_branch_type_call);
+  return "";
+}
+  [(set_attr "op_type" "RIL")
+   (set_attr "mnemonic" "brasl")
+   (set_attr "type"  "jsr")
+   (set_attr "atype" "agen")
+   (set_attr "z196prop" "z196_cracked")])
+
+(define_insn "*basr_r_via_thunk"
+  [(set (match_operand 0 "" "")
+        (call (mem:QI (match_operand 1 "register_operand" "a"))
+              (match_operand 2 "const_int_operand"        "n")))
+   (clobber (match_operand 3 "register_operand"         "=&r"))
+   (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]
+  "TARGET_INDIRECT_BRANCH_NOBP_CALL
+   && !TARGET_CPU_Z10
+   && !SIBLING_CALL_P (insn)
+   && GET_MODE (operands[3]) == Pmode"
+{
+  s390_indirect_branch_via_thunk (REGNO (operands[1]),
+				  REGNO (operands[3]),
+				  NULL_RTX,
+				  s390_indirect_branch_type_call);
+  return "";
+}
+  [(set_attr "op_type" "RIL")
+   (set_attr "mnemonic"  "brasl")
    (set_attr "type"  "jsr")
    (set_attr "atype" "agen")
    (set_attr "z196prop" "z196_cracked")])
@@ -10737,17 +11163,101 @@
 (define_insn "<code>"
   [(ANY_RETURN)]
   "s390_can_use_<code>_insn ()"
-  "br\t%%r14"
-  [(set_attr "op_type" "RR")
+{
+  if (TARGET_INDIRECT_BRANCH_NOBP_RET)
+    {
+      /* The target is always r14 so there is no clobber
+	 of r1 needed for pre z10 targets.  */
+      s390_indirect_branch_via_thunk (RETURN_REGNUM,
+				      INVALID_REGNUM,
+				      NULL_RTX,
+				      s390_indirect_branch_type_return);
+      return "";
+    }
+  else
+    return "br\t%%r14";
+}
+  [(set (attr "op_type")
+	(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
+		      (const_string "RIL")
+		      (const_string "RR")))
+   (set (attr "mnemonic")
+	(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
+		      (const_string "jg")
+		      (const_string "br")))
    (set_attr "type"    "jsr")
    (set_attr "atype"   "agen")])
 
-(define_insn "*return"
+
+(define_expand "return_use"
+  [(parallel
+    [(return)
+     (use (match_operand 0 "register_operand" "a"))])]
+  ""
+{
+  if (!TARGET_CPU_Z10
+      && TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION)
+    {
+      if (TARGET_64BIT)
+        emit_jump_insn (gen_returndi_prez10 (operands[0]));
+      else
+        emit_jump_insn (gen_returnsi_prez10 (operands[0]));
+      DONE;
+    }
+})
+
+(define_insn "*return<mode>"
   [(return)
-   (use (match_operand 0 "register_operand" "a"))]
-  "GET_MODE (operands[0]) == Pmode"
-  "br\t%0"
-  [(set_attr "op_type" "RR")
+   (use (match_operand:P 0 "register_operand" "a"))]
+  "TARGET_CPU_Z10 || !TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION"
+{
+  if (TARGET_INDIRECT_BRANCH_NOBP_RET)
+    {
+      s390_indirect_branch_via_thunk (REGNO (operands[0]),
+                                      INVALID_REGNUM,
+                                      NULL_RTX,
+                                      s390_indirect_branch_type_return);
+      return "";
+    }
+  else
+    return "br\t%0";
+}
+  [(set (attr "op_type")
+       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
+                     (const_string "RIL")
+                     (const_string "RR")))
+   (set (attr "mnemonic")
+       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
+                     (const_string "jg")
+                     (const_string "br")))
+   (set_attr "type"    "jsr")
+   (set_attr "atype"   "agen")])
+
+(define_insn "return<mode>_prez10"
+  [(return)
+   (use (match_operand:P 0 "register_operand" "a"))
+   (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]
+  "!TARGET_CPU_Z10 && TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION"
+{
+  if (TARGET_INDIRECT_BRANCH_NOBP_RET)
+    {
+      s390_indirect_branch_via_thunk (REGNO (operands[0]),
+                                      INVALID_REGNUM,
+                                      NULL_RTX,
+                                      s390_indirect_branch_type_return);
+      return "";
+    }
+  else
+    return "br\t%0";
+}
+  [(set (attr "op_type")
+       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
+                     (const_string "RIL")
+                     (const_string "RR")))
+   (set (attr "mnemonic")
+       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
+                     (const_string "jg")
+                     (const_string "br")))
    (set_attr "type"    "jsr")
    (set_attr "atype"   "agen")])
 
diff --git a/gcc/config/s390/s390.opt b/gcc/config/s390/s390.opt
index 28ba631..85d0985 100644
--- a/gcc/config/s390/s390.opt
+++ b/gcc/config/s390/s390.opt
@@ -229,3 +229,62 @@ values are small, non-negative integers.  The default branch cost is
 mlra
 Target Report Var(s390_lra_flag) Init(1) Save
 Use LRA instead of reload.
+
+mindirect-branch=
+Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch) Init(indirect_branch_keep)
+Wrap all indirect branches into execute in order to disable branch
+prediction.
+
+mindirect-branch-jump=
+Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch_jump) Init(indirect_branch_keep)
+Wrap indirect table jumps and computed gotos into execute in order to
+disable branch prediction.  Using thunk or thunk-extern with this
+option requires the thunks to be considered signal handlers to order to
+generate correct CFI.  For environments where unwinding (e.g. for
+exceptions) is required please use thunk-inline instead.
+
+mindirect-branch-call=
+Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch_call) Init(indirect_branch_keep)
+Wrap all indirect calls into execute in order to disable branch prediction.
+
+mfunction-return=
+Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return) Init(indirect_branch_keep)
+Wrap all indirect return branches into execute in order to disable branch
+prediction.
+
+mfunction-return-mem=
+Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return_mem) Init(indirect_branch_keep)
+Wrap indirect return branches into execute in order to disable branch
+prediction. This affects only branches where the return address is
+going to be restored from memory.
+
+mfunction-return-reg=
+Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return_reg) Init(indirect_branch_keep)
+Wrap indirect return branches into execute in order to disable branch
+prediction. This affects only branches where the return address
+doesn't need to be restored from memory.
+
+Enum
+Name(indirect_branch) Type(enum indirect_branch)
+Known indirect branch choices (for use with the -mindirect-branch=/-mfunction-return= options):
+
+EnumValue
+Enum(indirect_branch) String(keep) Value(indirect_branch_keep)
+
+EnumValue
+Enum(indirect_branch) String(thunk) Value(indirect_branch_thunk)
+
+EnumValue
+Enum(indirect_branch) String(thunk-inline) Value(indirect_branch_thunk_inline)
+
+EnumValue
+Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern)
+
+mindirect-branch-table
+Target Report Var(s390_indirect_branch_table) Init(TARGET_DEFAULT_INDIRECT_BRANCH_TABLE)
+Generate sections .s390_indirect_jump, .s390_indirect_call,
+.s390_return_reg, and .s390_return_mem to contain the indirect branch
+locations which have been patched as part of using one of the
+-mindirect-branch* or -mfunction-return* options.  The sections
+consist of an array of 32 bit elements. Each entry holds the offset
+from the entry to the patched location.
diff --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c
new file mode 100644
index 0000000..db9336d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c
@@ -0,0 +1,56 @@
+/* { dg-do run } */
+/* { dg-options "-O3  -march=z10 --save-temps -mindirect-branch-table" } */
+
+int gl;
+
+void __attribute__((noinline,noclone))
+foo (int a)
+{
+  gl = a + 40;
+}
+
+int __attribute__((noinline,noclone))
+foo_value (int a)
+{
+  return a + 40;
+}
+
+void* __attribute__((noinline,noclone))
+get_fptr (int a)
+{
+  switch (a)
+    {
+    case 0: return &foo; break;
+    case 1: return &foo_value; break;
+    default: __builtin_abort ();
+    }
+}
+
+void (*f) (int);
+int (*g) (int);
+
+int __attribute__((indirect_branch_call("thunk")))
+main ()
+{
+  int res;
+
+  f = get_fptr(0);
+  f (2);
+  if (gl != 42)
+    __builtin_abort ();
+
+  g = get_fptr(1);
+  if (g (2) != 42)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 2 x main
+/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */
+/* { dg-final { scan-assembler "exrl" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler     "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c
new file mode 100644
index 0000000..c02b45a
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c
@@ -0,0 +1,59 @@
+/* { dg-do compile } */
+/* { dg-options "-O3  -march=z10 --save-temps -mindirect-branch-call=thunk-extern -mindirect-branch-table" } */
+
+int gl;
+
+void __attribute__((noinline,noclone))
+foo (int a)
+{
+  gl = a + 40;
+}
+
+int __attribute__((noinline,noclone))
+foo_value (int a)
+{
+  return a + 40;
+}
+
+void*  __attribute__((noinline,noclone))
+get_fptr (int a)
+{
+  switch (a)
+    {
+    case 0: return &foo; break;
+    case 1: return &foo_value; break;
+    default: __builtin_abort ();
+    }
+}
+
+void (*f) (int);
+int (*g) (int);
+
+int
+main ()
+{
+  int res;
+
+  f = get_fptr(0);
+  f (2);
+  if (gl != 42)
+    __builtin_abort ();
+
+  g = get_fptr(1);
+  if (g (2) != 42)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 2 x main
+/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */
+
+/* No thunks due to thunk-extern.  */
+/* { dg-final { scan-assembler-not "exrl" } } */
+/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler     "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c
new file mode 100644
index 0000000..b5f13eb
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c
@@ -0,0 +1,56 @@
+/* { dg-do run } */
+/* { dg-options "-O3  -march=z10 --save-temps -mindirect-branch-call=thunk -mindirect-branch-table" } */
+
+int gl;
+
+void __attribute__((noinline,noclone))
+foo (int a)
+{
+  gl = a + 40;
+}
+
+int __attribute__((noinline,noclone))
+foo_value (int a)
+{
+  return a + 40;
+}
+
+void*  __attribute__((noinline,noclone))
+get_fptr (int a)
+{
+  switch (a)
+    {
+    case 0: return &foo; break;
+    case 1: return &foo_value; break;
+    default: __builtin_abort ();
+    }
+}
+
+void (*f) (int);
+int (*g) (int);
+
+int
+main ()
+{
+  int res;
+
+  f = get_fptr(0);
+  f (2);
+  if (gl != 42)
+    __builtin_abort ();
+
+  g = get_fptr(1);
+  if (g (2) != 42)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 2 x main
+/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */
+/* { dg-final { scan-assembler "exrl" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler     "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c
new file mode 100644
index 0000000..486495b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c
@@ -0,0 +1,56 @@
+/* { dg-do run } */
+/* { dg-options "-O3  -march=z900 --save-temps -mindirect-branch-call=thunk -mindirect-branch-table" } */
+
+int gl;
+
+void __attribute__((noinline,noclone))
+foo (int a)
+{
+  gl = a + 40;
+}
+
+int __attribute__((noinline,noclone))
+foo_value (int a)
+{
+  return a + 40;
+}
+
+void*  __attribute__((noinline,noclone))
+get_fptr (int a)
+{
+  switch (a)
+    {
+    case 0: return &foo; break;
+    case 1: return &foo_value; break;
+    default: __builtin_abort ();
+    }
+}
+
+void (*f) (int);
+int (*g) (int);
+
+int
+main ()
+{
+  int res;
+
+  f = get_fptr(0);
+  f (2);
+  if (gl != 42)
+    __builtin_abort ();
+
+  g = get_fptr(1);
+  if (g (2) != 42)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 2 x main
+/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */
+/* { dg-final { scan-assembler "ex\t" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler     "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c
new file mode 100644
index 0000000..c62ddf5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c
@@ -0,0 +1,42 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-table" } */
+/* { dg-require-effective-target label_values } */
+
+/* This is a copy of the gcc.c-torture/execute/20040302-1.c
+   testcase.  */
+
+int code[]={0,0,0,0,1};
+
+void
+foo(int x) {
+  volatile int b;
+  b = 0xffffffff;
+}
+
+void __attribute__((indirect_branch_jump("thunk")))
+bar(int *pc) {
+  static const void *l[] = {&&lab0, &&end};
+
+  foo(0);
+  goto *l[*pc];
+ lab0:
+  foo(0);
+  pc++;
+  goto *l[*pc];
+ end:
+  return;
+}
+
+int main() {
+  bar(code);
+  return 0;
+}
+
+/* 2x bar */
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
+/* { dg-final { scan-assembler "exrl" } } */
+
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c
new file mode 100644
index 0000000..63d64c1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c
@@ -0,0 +1,42 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-table" } */
+/* { dg-require-effective-target label_values } */
+
+/* This is a copy of the gcc.c-torture/execute/20040302-1.c
+   testcase.  */
+
+int code[]={0,0,0,0,1};
+
+void foo(int x) {
+  volatile int b;
+  b = 0xffffffff;
+}
+
+void __attribute__((indirect_branch_jump("thunk-inline")))
+bar(int *pc) {
+  static const void *l[] = {&&lab0, &&end};
+
+  foo(0);
+  goto *l[*pc];
+ lab0:
+  foo(0);
+  pc++;
+  goto *l[*pc];
+ end:
+  return;
+}
+
+int
+main() {
+  bar(code);
+  return 0;
+}
+
+/* The two gotos in bar get merged.  */
+/* { dg-final { scan-assembler-times "exrl" 1 } } */
+
+/* { dg-final { scan-assembler-not "jg\t__s390_indirect_jump" } } */
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c
new file mode 100644
index 0000000..28d7837
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c
@@ -0,0 +1,43 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */
+/* { dg-require-effective-target label_values } */
+
+/* This is a copy of the gcc.c-torture/execute/20040302-1.c
+   testcase.  */
+
+int code[]={0,0,0,0,1};
+
+void
+foo(int x) {
+  volatile int b;
+  b = 0xffffffff;
+}
+
+void
+bar(int *pc) {
+  static const void *l[] = {&&lab0, &&end};
+
+  foo(0);
+  goto *l[*pc];
+ lab0:
+  foo(0);
+  pc++;
+  goto *l[*pc];
+ end:
+  return;
+}
+
+int
+main() {
+  bar(code);
+  return 0;
+}
+
+/* The two gotos in bar get merged.  */
+/* { dg-final { scan-assembler-times "exrl" 1 } } */
+
+/* { dg-final { scan-assembler-not "jg\t__s390_indirect_jump" } } */
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c
new file mode 100644
index 0000000..3c0c007
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c
@@ -0,0 +1,43 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */
+/* { dg-require-effective-target label_values } */
+
+/* This is a copy of the gcc.c-torture/execute/20040302-1.c
+   testcase.  */
+
+int code[]={0,0,0,0,1};
+
+void
+foo(int x) {
+  volatile int b;
+  b = 0xffffffff;
+}
+
+void
+bar(int *pc) {
+  static const void *l[] = {&&lab0, &&end};
+
+  foo(0);
+  goto *l[*pc];
+ lab0:
+  foo(0);
+  pc++;
+  goto *l[*pc];
+ end:
+  return;
+}
+
+int
+main() {
+  bar(code);
+  return 0;
+}
+
+/* The two gotos in bar get merged.  */
+/* { dg-final { scan-assembler-times "\tex\t" 1 } } */
+
+/* { dg-final { scan-assembler-not "jg\t__s390_indirect_jump" } } */
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c
new file mode 100644
index 0000000..05c8bb8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c
@@ -0,0 +1,46 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk-extern -mindirect-branch-table" } */
+/* { dg-require-effective-target label_values } */
+
+/* This is a copy of the gcc.c-torture/execute/20040302-1.c
+   testcase.  */
+
+int code[]={0,0,0,0,1};
+
+void
+foo(int x) {
+  volatile int b;
+  b = 0xffffffff;
+}
+
+void
+bar(int *pc) {
+  static const void *l[] = {&&lab0, &&end};
+
+  foo(0);
+  goto *l[*pc];
+ lab0:
+  foo(0);
+  pc++;
+  goto *l[*pc];
+ end:
+  return;
+}
+
+int
+main() {
+  bar(code);
+  return 0;
+}
+
+/* 2 x bar
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
+
+/* No thunks due to thunk-extern.  */
+/* { dg-final { scan-assembler-not "exrl" } } */
+/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */
+
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c
new file mode 100644
index 0000000..71c86fd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c
@@ -0,0 +1,43 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
+/* { dg-require-effective-target label_values } */
+
+/* This is a copy of the gcc.c-torture/execute/20040302-1.c
+   testcase.  */
+
+int code[]={0,0,0,0,1};
+
+void
+foo(int x) {
+  volatile int b;
+  b = 0xffffffff;
+}
+
+void
+bar(int *pc) {
+  static const void *l[] = {&&lab0, &&end};
+
+  foo(0);
+  goto *l[*pc];
+ lab0:
+  foo(0);
+  pc++;
+  goto *l[*pc];
+ end:
+  return;
+}
+
+int
+main() {
+  bar(code);
+  return 0;
+}
+
+/* 2x bar */
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
+/* { dg-final { scan-assembler "exrl" } } */
+
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c
new file mode 100644
index 0000000..89ad799
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c
@@ -0,0 +1,43 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
+/* { dg-require-effective-target label_values } */
+
+/* This is a copy of the gcc.c-torture/execute/20040302-1.c
+   testcase.  */
+
+int code[]={0,0,0,0,1};
+
+void
+foo(int x) {
+  volatile int b;
+  b = 0xffffffff;
+}
+
+void
+bar(int *pc) {
+  static const void *l[] = {&&lab0, &&end};
+
+  foo(0);
+  goto *l[*pc];
+ lab0:
+  foo(0);
+  pc++;
+  goto *l[*pc];
+ end:
+  return;
+}
+
+int
+main() {
+  bar(code);
+  return 0;
+}
+
+/* 2 x bar
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
+/* { dg-final { scan-assembler "ex\t" } } */
+
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c b/gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c
new file mode 100644
index 0000000..4bf88cf
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c
@@ -0,0 +1,46 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-table" } */
+
+int gl = 0;
+
+int __attribute__((noinline,noclone))
+bar (int a)
+{
+  return a + 2;
+}
+
+void __attribute__((function_return("thunk"),noinline,noclone))
+foo (int a)
+{
+  int i;
+
+  if (a == 42)
+    return;
+
+  for (i = 0; i < a; i++)
+    gl += bar (i);
+}
+
+int
+main ()
+{
+  foo (3);
+  if (gl != 9)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* With -march=z10 -mzarch the shrink wrapped returns use compare and
+   swap relative to jump to the exit block instead of making use of
+   the conditional return pattern.
+   FIXME: Use compare and branch register for that!!!! */
+
+/* 2 x foo
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
+/* { dg-final { scan-assembler "exrl" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler     "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c b/gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c
new file mode 100644
index 0000000..8b32bfe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c
@@ -0,0 +1,40 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mfunction-return-mem=thunk -mindirect-branch-table" } */
+
+int gl = 0;
+
+int __attribute__((noinline,noclone))
+bar (int a)
+{
+  return a + 2;
+}
+
+void __attribute__((function_return("keep"),noinline,noclone))
+foo (int a)
+{
+  int i;
+
+  if (a == 42)
+    return;
+
+  for (i = 0; i < a; i++)
+    gl += bar (i);
+}
+
+int __attribute__((function_return("keep")))
+main ()
+{
+  foo (3);
+  if (gl != 9)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* { dg-final { scan-assembler-not "jg\t__s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "exrl" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c b/gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c
new file mode 100644
index 0000000..39cab8b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c
@@ -0,0 +1,46 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-table" } */
+
+int gl = 0;
+
+int __attribute__((noinline,noclone))
+bar (int a)
+{
+  return a + 2;
+}
+
+void __attribute__((function_return_mem("thunk"),noinline,noclone))
+foo (int a)
+{
+  int i;
+
+  if (a == 42)
+    return;
+
+  for (i = 0; i < a; i++)
+    gl += bar (i);
+}
+
+int
+main ()
+{
+  foo (3);
+  if (gl != 9)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* With -march=z10 -mzarch the shrink wrapped returns use compare and
+   swap relative to jump to the exit block instead of making use of
+   the conditional return pattern.
+   FIXME: Use compare and branch register for that!!!! */
+
+/* 2 x foo
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
+/* { dg-final { scan-assembler "exrl" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler     "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c b/gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c
new file mode 100644
index 0000000..f99f152
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c
@@ -0,0 +1,49 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mfunction-return-mem=thunk-extern -mindirect-branch-table" } */
+
+int gl = 0;
+
+int __attribute__((noinline,noclone))
+bar (int a)
+{
+  return a + 2;
+}
+
+void __attribute__((noinline,noclone))
+foo (int a)
+{
+  int i;
+
+  if (a == 42)
+    return;
+
+  for (i = 0; i < a; i++)
+    gl += bar (i);
+}
+
+int
+main ()
+{
+  foo (3);
+  if (gl != 9)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* With -march=z10 -mzarch the shrink wrapped returns use compare and
+   swap relative to jump to the exit block instead of making use of
+   the conditional return pattern.
+   FIXME: Use compare and branch register for that!!!! */
+
+/* 2 x foo, 1 x main
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 3 } } */
+
+/* No thunks due to thunk-extern.  */
+/* { dg-final { scan-assembler-not "exrl" } } */
+/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler     "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c
new file mode 100644
index 0000000..177fc32
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c
@@ -0,0 +1,46 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mfunction-return-mem=thunk -mindirect-branch-table" } */
+
+int gl = 0;
+
+int __attribute__((noinline,noclone))
+bar (int a)
+{
+  return a + 2;
+}
+
+void __attribute__((noinline,noclone))
+foo (int a)
+{
+  int i;
+
+  if (a == 42)
+    return;
+
+  for (i = 0; i < a; i++)
+    gl += bar (i);
+}
+
+int
+main ()
+{
+  foo (3);
+  if (gl != 9)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* With -march=z10 -mzarch the shrink wrapped returns use compare and
+   swap relative to jump to the exit block instead of making use of
+   the conditional return pattern.
+   FIXME: Use compare and branch register for that!!!! */
+
+/* 2 x foo, 1 x main
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 3 } } */
+/* { dg-final { scan-assembler "exrl" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler     "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c
new file mode 100644
index 0000000..0b31811
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c
@@ -0,0 +1,48 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-mem=thunk -mindirect-branch-table" } */
+
+int gl = 0;
+
+int __attribute__((noinline,noclone))
+bar (int a)
+{
+  return a + 2;
+}
+
+void __attribute__((noinline,noclone))
+foo (int a)
+{
+  int i;
+
+  if (a == 42)
+    return;
+
+  for (i = 0; i < a; i++)
+    gl += bar (i);
+}
+
+int
+main ()
+{
+  foo (3);
+  if (gl != 9)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 1 x foo, 1 x main
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
+
+/* 1 x foo, conditional return, shrink wrapped
+/* { dg-final { scan-assembler "jge\t__s390_indirect_jump" } } */
+
+/* 1 x foo, conditional return, shrink wrapped
+/* { dg-final { scan-assembler "jgle\t__s390_indirect_jump" } } */
+
+/* { dg-final { scan-assembler "ex\t" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler     "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c
new file mode 100644
index 0000000..ebfc9ff
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c
@@ -0,0 +1,41 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-table" } */
+
+int gl = 0;
+
+int __attribute__((function_return_reg("thunk"),noinline,noclone))
+bar (int a)
+{
+  return a + 2;
+}
+
+void __attribute__((noinline,noclone))
+foo (int a)
+{
+  int i;
+
+  if (a == 42)
+    return;
+
+  for (i = 0; i < a; i++)
+    gl += bar (i);
+}
+
+int
+main ()
+{
+  foo (3);
+  if (gl != 9)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 1 x bar
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */
+/* { dg-final { scan-assembler "exrl" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler     "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c
new file mode 100644
index 0000000..82833f7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c
@@ -0,0 +1,44 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */
+
+/* We have to generate different thunks for indirect branches
+   depending on whether the code is compiled for pre z10 machines or
+   later.  This testcase makes sure this works within the same compile
+   unit.  */
+
+int __attribute__((noinline,noclone,target("arch=z10")))
+bar (int a)
+{
+  return a + 2;
+}
+
+int __attribute__((noinline,noclone,target("arch=z9-ec")))
+foo (int a)
+{
+  return a + 3;
+}
+
+int
+main ()
+{
+  if (bar (42) != 44)
+    __builtin_abort ();
+
+  if (foo (42) != 45)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 1 x bar, 1 x foo */
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
+/* 1 x foo */
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump_r1use" 1 } } */
+
+/* { dg-final { scan-assembler-times "ex\t" 1 } } */
+/* { dg-final { scan-assembler-times "exrl\t" 1 } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler     "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c
new file mode 100644
index 0000000..4ea14e3
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c
@@ -0,0 +1,44 @@
+/* { dg-do compile } */
+/* { dg-options "-O3 -march=z10 --save-temps -mfunction-return-reg=thunk-extern -mindirect-branch-table" } */
+
+int gl = 0;
+
+int __attribute__((noinline,noclone))
+bar (int a)
+{
+  return a + 2;
+}
+
+void __attribute__((noinline,noclone))
+foo (int a)
+{
+  int i;
+
+  if (a == 42)
+    return;
+
+  for (i = 0; i < a; i++)
+    gl += bar (i);
+}
+
+int
+main ()
+{
+  foo (3);
+  if (gl != 9)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 1 x bar
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */
+
+/* No thunks due to thunk-extern.  */
+/* { dg-final { scan-assembler-not "exrl" } } */
+/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler     "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c
new file mode 100644
index 0000000..42c3e74
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c
@@ -0,0 +1,41 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z10 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */
+
+int gl = 0;
+
+int __attribute__((noinline,noclone))
+bar (int a)
+{
+  return a + 2;
+}
+
+void __attribute__((noinline,noclone))
+foo (int a)
+{
+  int i;
+
+  if (a == 42)
+    return;
+
+  for (i = 0; i < a; i++)
+    gl += bar (i);
+}
+
+int
+main ()
+{
+  foo (3);
+  if (gl != 9)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 1 x bar
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */
+/* { dg-final { scan-assembler "exrl" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler     "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c
new file mode 100644
index 0000000..3f4efa5
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c
@@ -0,0 +1,41 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */
+
+int gl = 0;
+
+int __attribute__((noinline,noclone))
+bar (int a)
+{
+  return a + 2;
+}
+
+void __attribute__((noinline,noclone))
+foo (int a)
+{
+  int i;
+
+  if (a == 42)
+    return;
+
+  for (i = 0; i < a; i++)
+    gl += bar (i);
+}
+
+int
+main ()
+{
+  foo (3);
+  if (gl != 9)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 1 x bar
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */
+/* { dg-final { scan-assembler "ex\t" } } */
+
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler     "section\t.s390_return_reg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c
new file mode 100644
index 0000000..8dfd7e4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c
@@ -0,0 +1,78 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */
+
+/* case-values-threshold will be set to 20 by the back-end when jump
+   thunk are requested.  */
+
+int __attribute__((noinline,noclone)) foo1 (void) { return 1; }
+int __attribute__((noinline,noclone)) foo2 (void) { return 2; }
+int __attribute__((noinline,noclone)) foo3 (void) { return 3; }
+int __attribute__((noinline,noclone)) foo4 (void) { return 4; }
+int __attribute__((noinline,noclone)) foo5 (void) { return 5; }
+int __attribute__((noinline,noclone)) foo6 (void) { return 6; }
+int __attribute__((noinline,noclone)) foo7 (void) { return 7; }
+int __attribute__((noinline,noclone)) foo8 (void) { return 8; }
+int __attribute__((noinline,noclone)) foo9 (void) { return 9; }
+int __attribute__((noinline,noclone)) foo10 (void) { return 10; }
+int __attribute__((noinline,noclone)) foo11 (void) { return 11; }
+int __attribute__((noinline,noclone)) foo12 (void) { return 12; }
+int __attribute__((noinline,noclone)) foo13 (void) { return 13; }
+int __attribute__((noinline,noclone)) foo14 (void) { return 14; }
+int __attribute__((noinline,noclone)) foo15 (void) { return 15; }
+int __attribute__((noinline,noclone)) foo16 (void) { return 16; }
+int __attribute__((noinline,noclone)) foo17 (void) { return 17; }
+int __attribute__((noinline,noclone)) foo18 (void) { return 18; }
+int __attribute__((noinline,noclone)) foo19 (void) { return 19; }
+int __attribute__((noinline,noclone)) foo20 (void) { return 20; }
+
+
+int __attribute__((noinline,noclone))
+bar (int a)
+{
+  int ret = 0;
+
+  switch (a)
+    {
+    case 1: ret = foo1 (); break;
+    case 2: ret = foo2 (); break;
+    case 3: ret = foo3 (); break;
+    case 4: ret = foo4 (); break;
+    case 5: ret = foo5 (); break;
+    case 6: ret = foo6 (); break;
+    case 7: ret = foo7 (); break;
+    case 8: ret = foo8 (); break;
+    case 9: ret = foo9 (); break;
+    case 10: ret = foo10 (); break;
+    case 11: ret = foo11 (); break;
+    case 12: ret = foo12 (); break;
+    case 13: ret = foo13 (); break;
+    case 14: ret = foo14 (); break;
+    case 15: ret = foo15 (); break;
+    case 16: ret = foo16 (); break;
+    case 17: ret = foo17 (); break;
+    case 18: ret = foo18 (); break;
+    case 19: ret = foo19 (); break;
+    case 20: ret = foo20 (); break;
+    default:
+      __builtin_abort ();
+    }
+
+  return ret;
+}
+
+int
+main ()
+{
+  if (bar (3) != 3)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 1 x bar
+/* { dg-final { scan-assembler-times "exrl" 1 } } */
+
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c
new file mode 100644
index 0000000..46d2c54
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c
@@ -0,0 +1,78 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z900 -mzarch --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */
+
+/* case-values-threshold will be set to 20 by the back-end when jump
+   thunk are requested.  */
+
+int __attribute__((noinline,noclone)) foo1 (void) { return 1; }
+int __attribute__((noinline,noclone)) foo2 (void) { return 2; }
+int __attribute__((noinline,noclone)) foo3 (void) { return 3; }
+int __attribute__((noinline,noclone)) foo4 (void) { return 4; }
+int __attribute__((noinline,noclone)) foo5 (void) { return 5; }
+int __attribute__((noinline,noclone)) foo6 (void) { return 6; }
+int __attribute__((noinline,noclone)) foo7 (void) { return 7; }
+int __attribute__((noinline,noclone)) foo8 (void) { return 8; }
+int __attribute__((noinline,noclone)) foo9 (void) { return 9; }
+int __attribute__((noinline,noclone)) foo10 (void) { return 10; }
+int __attribute__((noinline,noclone)) foo11 (void) { return 11; }
+int __attribute__((noinline,noclone)) foo12 (void) { return 12; }
+int __attribute__((noinline,noclone)) foo13 (void) { return 13; }
+int __attribute__((noinline,noclone)) foo14 (void) { return 14; }
+int __attribute__((noinline,noclone)) foo15 (void) { return 15; }
+int __attribute__((noinline,noclone)) foo16 (void) { return 16; }
+int __attribute__((noinline,noclone)) foo17 (void) { return 17; }
+int __attribute__((noinline,noclone)) foo18 (void) { return 18; }
+int __attribute__((noinline,noclone)) foo19 (void) { return 19; }
+int __attribute__((noinline,noclone)) foo20 (void) { return 20; }
+
+
+int __attribute__((noinline,noclone))
+bar (int a)
+{
+  int ret = 0;
+
+  switch (a)
+    {
+    case 1: ret = foo1 (); break;
+    case 2: ret = foo2 (); break;
+    case 3: ret = foo3 (); break;
+    case 4: ret = foo4 (); break;
+    case 5: ret = foo5 (); break;
+    case 6: ret = foo6 (); break;
+    case 7: ret = foo7 (); break;
+    case 8: ret = foo8 (); break;
+    case 9: ret = foo9 (); break;
+    case 10: ret = foo10 (); break;
+    case 11: ret = foo11 (); break;
+    case 12: ret = foo12 (); break;
+    case 13: ret = foo13 (); break;
+    case 14: ret = foo14 (); break;
+    case 15: ret = foo15 (); break;
+    case 16: ret = foo16 (); break;
+    case 17: ret = foo17 (); break;
+    case 18: ret = foo18 (); break;
+    case 19: ret = foo19 (); break;
+    case 20: ret = foo20 (); break;
+    default:
+      __builtin_abort ();
+    }
+
+  return ret;
+}
+
+int
+main ()
+{
+  if (bar (3) != 3)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 1 x bar
+/* { dg-final { scan-assembler-times "\tex\t" 1 } } */
+
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c
new file mode 100644
index 0000000..9dfe391
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c
@@ -0,0 +1,77 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
+/* case-values-threshold will be set to 20 by the back-end when jump
+   thunk are requested.  */
+
+int __attribute__((noinline,noclone)) foo1 (void) { return 1; }
+int __attribute__((noinline,noclone)) foo2 (void) { return 2; }
+int __attribute__((noinline,noclone)) foo3 (void) { return 3; }
+int __attribute__((noinline,noclone)) foo4 (void) { return 4; }
+int __attribute__((noinline,noclone)) foo5 (void) { return 5; }
+int __attribute__((noinline,noclone)) foo6 (void) { return 6; }
+int __attribute__((noinline,noclone)) foo7 (void) { return 7; }
+int __attribute__((noinline,noclone)) foo8 (void) { return 8; }
+int __attribute__((noinline,noclone)) foo9 (void) { return 9; }
+int __attribute__((noinline,noclone)) foo10 (void) { return 10; }
+int __attribute__((noinline,noclone)) foo11 (void) { return 11; }
+int __attribute__((noinline,noclone)) foo12 (void) { return 12; }
+int __attribute__((noinline,noclone)) foo13 (void) { return 13; }
+int __attribute__((noinline,noclone)) foo14 (void) { return 14; }
+int __attribute__((noinline,noclone)) foo15 (void) { return 15; }
+int __attribute__((noinline,noclone)) foo16 (void) { return 16; }
+int __attribute__((noinline,noclone)) foo17 (void) { return 17; }
+int __attribute__((noinline,noclone)) foo18 (void) { return 18; }
+int __attribute__((noinline,noclone)) foo19 (void) { return 19; }
+int __attribute__((noinline,noclone)) foo20 (void) { return 20; }
+
+
+int __attribute__((noinline,noclone))
+bar (int a)
+{
+  int ret = 0;
+
+  switch (a)
+    {
+    case 1: ret = foo1 (); break;
+    case 2: ret = foo2 (); break;
+    case 3: ret = foo3 (); break;
+    case 4: ret = foo4 (); break;
+    case 5: ret = foo5 (); break;
+    case 6: ret = foo6 (); break;
+    case 7: ret = foo7 (); break;
+    case 8: ret = foo8 (); break;
+    case 9: ret = foo9 (); break;
+    case 10: ret = foo10 (); break;
+    case 11: ret = foo11 (); break;
+    case 12: ret = foo12 (); break;
+    case 13: ret = foo13 (); break;
+    case 14: ret = foo14 (); break;
+    case 15: ret = foo15 (); break;
+    case 16: ret = foo16 (); break;
+    case 17: ret = foo17 (); break;
+    case 18: ret = foo18 (); break;
+    case 19: ret = foo19 (); break;
+    case 20: ret = foo20 (); break;
+    default:
+      __builtin_abort ();
+    }
+
+  return ret;
+}
+
+int
+main ()
+{
+  if (bar (3) != 3)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 1 x bar
+/* { dg-final { scan-assembler-times "exrl" 1 } } */
+
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */
diff --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c
new file mode 100644
index 0000000..f1439a8
--- /dev/null
+++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c
@@ -0,0 +1,78 @@
+/* { dg-do run } */
+/* { dg-options "-O3 -march=z900 -mzarch --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
+
+/* case-values-threshold will be set to 20 by the back-end when jump
+   thunk are requested.  */
+
+int __attribute__((noinline,noclone)) foo1 (void) { return 1; }
+int __attribute__((noinline,noclone)) foo2 (void) { return 2; }
+int __attribute__((noinline,noclone)) foo3 (void) { return 3; }
+int __attribute__((noinline,noclone)) foo4 (void) { return 4; }
+int __attribute__((noinline,noclone)) foo5 (void) { return 5; }
+int __attribute__((noinline,noclone)) foo6 (void) { return 6; }
+int __attribute__((noinline,noclone)) foo7 (void) { return 7; }
+int __attribute__((noinline,noclone)) foo8 (void) { return 8; }
+int __attribute__((noinline,noclone)) foo9 (void) { return 9; }
+int __attribute__((noinline,noclone)) foo10 (void) { return 10; }
+int __attribute__((noinline,noclone)) foo11 (void) { return 11; }
+int __attribute__((noinline,noclone)) foo12 (void) { return 12; }
+int __attribute__((noinline,noclone)) foo13 (void) { return 13; }
+int __attribute__((noinline,noclone)) foo14 (void) { return 14; }
+int __attribute__((noinline,noclone)) foo15 (void) { return 15; }
+int __attribute__((noinline,noclone)) foo16 (void) { return 16; }
+int __attribute__((noinline,noclone)) foo17 (void) { return 17; }
+int __attribute__((noinline,noclone)) foo18 (void) { return 18; }
+int __attribute__((noinline,noclone)) foo19 (void) { return 19; }
+int __attribute__((noinline,noclone)) foo20 (void) { return 20; }
+
+
+int __attribute__((noinline,noclone))
+bar (int a)
+{
+  int ret = 0;
+
+  switch (a)
+    {
+    case 1: ret = foo1 (); break;
+    case 2: ret = foo2 (); break;
+    case 3: ret = foo3 (); break;
+    case 4: ret = foo4 (); break;
+    case 5: ret = foo5 (); break;
+    case 6: ret = foo6 (); break;
+    case 7: ret = foo7 (); break;
+    case 8: ret = foo8 (); break;
+    case 9: ret = foo9 (); break;
+    case 10: ret = foo10 (); break;
+    case 11: ret = foo11 (); break;
+    case 12: ret = foo12 (); break;
+    case 13: ret = foo13 (); break;
+    case 14: ret = foo14 (); break;
+    case 15: ret = foo15 (); break;
+    case 16: ret = foo16 (); break;
+    case 17: ret = foo17 (); break;
+    case 18: ret = foo18 (); break;
+    case 19: ret = foo19 (); break;
+    case 20: ret = foo20 (); break;
+    default:
+      __builtin_abort ();
+    }
+
+  return ret;
+}
+
+int
+main ()
+{
+  if (bar (3) != 3)
+    __builtin_abort ();
+
+  return 0;
+}
+
+/* 1 x bar
+/* { dg-final { scan-assembler-times "ex\t" 1 } } */
+
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */
+/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */
