Blame SOURCES/gcc48-rh1552021.patch

d950bf
diff -Nrup gcc/config/s390/s390.c gcc/config/s390/s390.c
d950bf
--- gcc/config/s390/s390.c	2018-03-27 09:33:20.158140823 -0600
d950bf
+++ gcc/config/s390/s390.c	2018-03-27 09:33:58.826861609 -0600
d950bf
@@ -958,6 +958,35 @@ s390_expand_builtin (tree exp, rtx targe
d950bf
 }
d950bf
 
d950bf
 
d950bf
+/* Masks per jump target register indicating which thunk need to be
d950bf
+   generated.  */
d950bf
+static GTY(()) int indirect_branch_prez10thunk_mask = 0;
d950bf
+static GTY(()) int indirect_branch_z10thunk_mask = 0;
d950bf
+
d950bf
+#define INDIRECT_BRANCH_NUM_OPTIONS 4
d950bf
+
d950bf
+enum s390_indirect_branch_option
d950bf
+  {
d950bf
+    s390_opt_indirect_branch_jump = 0,
d950bf
+    s390_opt_indirect_branch_call,
d950bf
+    s390_opt_function_return_reg,
d950bf
+    s390_opt_function_return_mem
d950bf
+  };
d950bf
+
d950bf
+static GTY(()) int indirect_branch_table_label_no[INDIRECT_BRANCH_NUM_OPTIONS] = { 0 };
d950bf
+const char *indirect_branch_table_label[INDIRECT_BRANCH_NUM_OPTIONS] = \
d950bf
+  { "LJUMP", "LCALL", "LRETREG", "LRETMEM" };
d950bf
+const char *indirect_branch_table_name[INDIRECT_BRANCH_NUM_OPTIONS] =	\
d950bf
+  { ".s390_indirect_jump", ".s390_indirect_call",
d950bf
+    ".s390_return_reg", ".s390_return_mem" };
d950bf
+
d950bf
+bool
d950bf
+s390_return_addr_from_memory ()
d950bf
+{
d950bf
+  return (cfun_frame_layout.first_save_gpr <= RETURN_REGNUM
d950bf
+	  && cfun_frame_layout.last_save_gpr >= RETURN_REGNUM);
d950bf
+}
d950bf
+
d950bf
 static const int s390_hotpatch_hw_max = 1000000;
d950bf
 static int s390_hotpatch_hw_before_label = 0;
d950bf
 static int s390_hotpatch_hw_after_label = 0;
d950bf
@@ -2669,6 +2698,34 @@ s390_option_override (void)
d950bf
   if (TARGET_64BIT && !TARGET_ZARCH)
d950bf
     error ("64-bit ABI not supported in ESA/390 mode");
d950bf
 
d950bf
+  if (s390_indirect_branch != indirect_branch_keep)
d950bf
+    {
d950bf
+      if (!global_options_set.x_s390_indirect_branch_call)
d950bf
+	s390_indirect_branch_call = s390_indirect_branch;
d950bf
+
d950bf
+      if (!global_options_set.x_s390_indirect_branch_jump)
d950bf
+	s390_indirect_branch_jump = s390_indirect_branch;
d950bf
+    }
d950bf
+
d950bf
+  if (s390_function_return != indirect_branch_keep)
d950bf
+    {
d950bf
+      if (!global_options_set.x_s390_function_return_reg)
d950bf
+	s390_function_return_reg = s390_function_return;
d950bf
+
d950bf
+      if (!global_options_set.x_s390_function_return_mem)
d950bf
+	s390_function_return_mem = s390_function_return;
d950bf
+    }
d950bf
+
d950bf
+  if (!TARGET_CPU_ZARCH)
d950bf
+    {
d950bf
+      if (s390_indirect_branch_call != indirect_branch_keep
d950bf
+	  || s390_indirect_branch_jump != indirect_branch_keep)
d950bf
+	error ("-mindirect-branch* options require -march=z900 or higher");
d950bf
+      if (s390_function_return_reg != indirect_branch_keep
d950bf
+	  || s390_function_return_mem != indirect_branch_keep)
d950bf
+	error ("-mfunction-return* options require -march=z900 or higher");
d950bf
+    }
d950bf
+
d950bf
   /* Use hardware DFP if available and not explicitly disabled by
d950bf
      user. E.g. with -m31 -march=z10 -mzarch   */
d950bf
   if (!(target_flags_explicit & MASK_HARD_DFP) && TARGET_DFP)
d950bf
@@ -10873,7 +10930,6 @@ s390_emit_epilogue (bool sibcall)
d950bf
   rtx frame_pointer, return_reg, cfa_restores = NULL_RTX;
d950bf
   int area_bottom, area_top, offset = 0;
d950bf
   int next_offset;
d950bf
-  rtvec p;
d950bf
   int i;
d950bf
 
d950bf
   if (TARGET_TPF_PROFILING)
d950bf
@@ -11023,8 +11079,14 @@ s390_emit_epilogue (bool sibcall)
d950bf
 		  && cfun_frame_layout.last_restore_gpr > RETURN_REGNUM))
d950bf
 	    {
d950bf
 	      int return_regnum = find_unused_clobbered_reg();
d950bf
-	      if (!return_regnum)
d950bf
-		return_regnum = 4;
d950bf
+	      if (!return_regnum
d950bf
+		  || (TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION
d950bf
+		      && !TARGET_CPU_Z10
d950bf
+		      && return_regnum == INDIRECT_BRANCH_THUNK_REGNUM))
d950bf
+		{
d950bf
+		  gcc_assert (INDIRECT_BRANCH_THUNK_REGNUM != 4);
d950bf
+		  return_regnum = 4;
d950bf
+		}
d950bf
 	      return_reg = gen_rtx_REG (Pmode, return_regnum);
d950bf
 
d950bf
 	      addr = plus_constant (Pmode, frame_pointer,
d950bf
@@ -11054,16 +11116,7 @@ s390_emit_epilogue (bool sibcall)
d950bf
     }
d950bf
 
d950bf
   if (! sibcall)
d950bf
-    {
d950bf
-
d950bf
-      /* Return to caller.  */
d950bf
-
d950bf
-      p = rtvec_alloc (2);
d950bf
-
d950bf
-      RTVEC_ELT (p, 0) = ret_rtx;
d950bf
-      RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg);
d950bf
-      emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
d950bf
-    }
d950bf
+    emit_jump_insn (gen_return_use (return_reg));
d950bf
 }
d950bf
 
d950bf
 
d950bf
@@ -12371,6 +12424,84 @@ s390_output_mi_thunk (FILE *file, tree t
d950bf
   final_end_function ();
d950bf
 }
d950bf
 
d950bf
+/* Output either an indirect jump or a an indirect call
d950bf
+   (RETURN_ADDR_REGNO != INVALID_REGNUM) with target register REGNO
d950bf
+   using a branch trampoline disabling branch target prediction.  */
d950bf
+
d950bf
+void
d950bf
+s390_indirect_branch_via_thunk (unsigned int regno,
d950bf
+				unsigned int return_addr_regno,
d950bf
+				rtx comparison_operator,
d950bf
+				enum s390_indirect_branch_type type)
d950bf
+{
d950bf
+  enum s390_indirect_branch_option option;
d950bf
+
d950bf
+  if (type == s390_indirect_branch_type_return)
d950bf
+    {
d950bf
+      if (s390_function_return_reg != indirect_branch_keep
d950bf
+	  && !s390_return_addr_from_memory ())
d950bf
+	option = s390_opt_function_return_reg;
d950bf
+
d950bf
+      if (s390_function_return_mem != indirect_branch_keep
d950bf
+	  && s390_return_addr_from_memory ())
d950bf
+	option = s390_opt_function_return_mem;
d950bf
+    }
d950bf
+  else if (type == s390_indirect_branch_type_jump)
d950bf
+    option = s390_opt_indirect_branch_jump;
d950bf
+  else if (type == s390_indirect_branch_type_call)
d950bf
+    option = s390_opt_indirect_branch_call;
d950bf
+  else
d950bf
+    gcc_unreachable ();
d950bf
+
d950bf
+  if (TARGET_INDIRECT_BRANCH_TABLE)
d950bf
+    {
d950bf
+      char label[32];
d950bf
+
d950bf
+      ASM_GENERATE_INTERNAL_LABEL (label,
d950bf
+				   indirect_branch_table_label[option],
d950bf
+				   indirect_branch_table_label_no[option]++);
d950bf
+      ASM_OUTPUT_LABEL (asm_out_file, label);
d950bf
+    }
d950bf
+
d950bf
+  if (return_addr_regno != INVALID_REGNUM)
d950bf
+    {
d950bf
+      gcc_assert (comparison_operator == NULL_RTX);
d950bf
+      fprintf (asm_out_file, " \tbrasl\t%%r%d,", return_addr_regno);
d950bf
+    }
d950bf
+  else
d950bf
+    {
d950bf
+      fputs (" \tjg", asm_out_file);
d950bf
+      if (comparison_operator != NULL_RTX)
d950bf
+	print_operand (asm_out_file, comparison_operator, 'C');
d950bf
+
d950bf
+      fputs ("\t", asm_out_file);
d950bf
+    }
d950bf
+
d950bf
+  if (TARGET_CPU_Z10)
d950bf
+    fprintf (asm_out_file,
d950bf
+	     TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL "\n",
d950bf
+	     regno);
d950bf
+  else
d950bf
+    fprintf (asm_out_file,
d950bf
+	     TARGET_INDIRECT_BRANCH_THUNK_NAME_EX "\n",
d950bf
+	     INDIRECT_BRANCH_THUNK_REGNUM, regno);
d950bf
+
d950bf
+  if ((option == s390_opt_indirect_branch_jump
d950bf
+       && s390_indirect_branch_jump == indirect_branch_thunk)
d950bf
+      || (option == s390_opt_indirect_branch_call
d950bf
+	  && s390_indirect_branch_call == indirect_branch_thunk)
d950bf
+      || (option == s390_opt_function_return_reg
d950bf
+	  && s390_function_return_reg == indirect_branch_thunk)
d950bf
+      || (option == s390_opt_function_return_mem
d950bf
+	  && s390_function_return_mem == indirect_branch_thunk))
d950bf
+    {
d950bf
+      if (TARGET_CPU_Z10)
d950bf
+	indirect_branch_z10thunk_mask |= (1 << regno);
d950bf
+      else
d950bf
+	indirect_branch_prez10thunk_mask |= (1 << regno);
d950bf
+    }
d950bf
+}
d950bf
+
d950bf
 static bool
d950bf
 s390_valid_pointer_mode (enum machine_mode mode)
d950bf
 {
d950bf
@@ -12476,6 +12607,14 @@ s390_function_ok_for_sibcall (tree decl,
d950bf
   if (!TARGET_64BIT && flag_pic && decl && !targetm.binds_local_p (decl))
d950bf
     return false;
d950bf
 
d950bf
+  /* The thunks for indirect branches require r1 if no exrl is
d950bf
+     available.  r1 might not be available when doing a sibling
d950bf
+     call.  */
d950bf
+  if (TARGET_INDIRECT_BRANCH_NOBP_CALL
d950bf
+      && !TARGET_CPU_Z10
d950bf
+      && !decl)
d950bf
+    return false;
d950bf
+
d950bf
   /* Register 6 on s390 is available as an argument register but unfortunately
d950bf
      "caller saved". This makes functions needing this register for arguments
d950bf
      not suitable for sibcalls.  */
d950bf
@@ -12509,9 +12648,13 @@ s390_emit_call (rtx addr_location, rtx t
d950bf
 {
d950bf
   bool plt_call = false;
d950bf
   rtx insn;
d950bf
-  rtx call;
d950bf
-  rtx clobber;
d950bf
-  rtvec vec;
d950bf
+  rtx vec[4] = { NULL_RTX };
d950bf
+  int elts = 0;
d950bf
+  rtx *call = &vec[0];
d950bf
+  rtx *clobber_ret_reg = &vec[1];
d950bf
+  rtx *use = &vec[2];
d950bf
+  rtx *clobber_thunk_reg = &vec[3];
d950bf
+  int i;
d950bf
 
d950bf
   /* Direct function calls need special treatment.  */
d950bf
   if (GET_CODE (addr_location) == SYMBOL_REF)
d950bf
@@ -12520,7 +12663,7 @@ s390_emit_call (rtx addr_location, rtx t
d950bf
          replace the symbol itself with the PLT stub.  */
d950bf
       if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location))
d950bf
         {
d950bf
-	  if (retaddr_reg != NULL_RTX)
d950bf
+	  if (TARGET_64BIT || retaddr_reg != NULL_RTX)
d950bf
 	    {
d950bf
 	      addr_location = gen_rtx_UNSPEC (Pmode,
d950bf
 					      gen_rtvec (1, addr_location),
d950bf
@@ -12563,26 +12706,57 @@ s390_emit_call (rtx addr_location, rtx t
d950bf
       addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM);
d950bf
     }
d950bf
 
d950bf
+  if (TARGET_INDIRECT_BRANCH_NOBP_CALL
d950bf
+      && GET_CODE (addr_location) != SYMBOL_REF
d950bf
+      && !plt_call)
d950bf
+    {
d950bf
+      /* Indirect branch thunks require the target to be a single GPR.  */
d950bf
+      addr_location = force_reg (Pmode, addr_location);
d950bf
+
d950bf
+      /* Without exrl the indirect branch thunks need an additional
d950bf
+	 register for larl;ex */
d950bf
+      if (!TARGET_CPU_Z10)
d950bf
+	{
d950bf
+	  *clobber_thunk_reg = gen_rtx_REG (Pmode, INDIRECT_BRANCH_THUNK_REGNUM);
d950bf
+	  *clobber_thunk_reg = gen_rtx_CLOBBER (VOIDmode, *clobber_thunk_reg);
d950bf
+	}
d950bf
+    }
d950bf
+
d950bf
   addr_location = gen_rtx_MEM (QImode, addr_location);
d950bf
-  call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);
d950bf
+  *call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);
d950bf
 
d950bf
   if (result_reg != NULL_RTX)
d950bf
-    call = gen_rtx_SET (VOIDmode, result_reg, call);
d950bf
+    *call = gen_rtx_SET (VOIDmode, result_reg, *call);
d950bf
 
d950bf
   if (retaddr_reg != NULL_RTX)
d950bf
     {
d950bf
-      clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg);
d950bf
+      *clobber_ret_reg = gen_rtx_CLOBBER (VOIDmode, retaddr_reg);
d950bf
 
d950bf
       if (tls_call != NULL_RTX)
d950bf
-	vec = gen_rtvec (3, call, clobber,
d950bf
-			 gen_rtx_USE (VOIDmode, tls_call));
d950bf
-      else
d950bf
-	vec = gen_rtvec (2, call, clobber);
d950bf
+	*use = gen_rtx_USE (VOIDmode, tls_call);
d950bf
+    }
d950bf
+
d950bf
+  for (i = 0; i < 4; i++)
d950bf
+    if (vec[i] != NULL_RTX)
d950bf
+      elts++;
d950bf
 
d950bf
-      call = gen_rtx_PARALLEL (VOIDmode, vec);
d950bf
+  if (elts > 1)
d950bf
+    {
d950bf
+      rtvec v;
d950bf
+      int e = 0;
d950bf
+
d950bf
+      v = rtvec_alloc (elts);
d950bf
+      for (i = 0; i < 4; i++)
d950bf
+	if (vec[i] != NULL_RTX)
d950bf
+	  {
d950bf
+	    RTVEC_ELT (v, e) = vec[i];
d950bf
+	    e++;
d950bf
+	  }
d950bf
+
d950bf
+      *call = gen_rtx_PARALLEL (VOIDmode, v);
d950bf
     }
d950bf
 
d950bf
-  insn = emit_call_insn (call);
d950bf
+  insn = emit_call_insn (*call);
d950bf
 
d950bf
   /* 31-bit PLT stubs and tls calls use the GOT register implicitly.  */
d950bf
   if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX)
d950bf
@@ -13819,6 +13993,190 @@ s390_asm_file_end (void)
d950bf
   file_end_indicate_exec_stack ();
d950bf
 }
d950bf
 
d950bf
+#ifdef HAVE_GAS_HIDDEN
d950bf
+# define USE_HIDDEN_LINKONCE 1
d950bf
+#else
d950bf
+# define USE_HIDDEN_LINKONCE 0
d950bf
+#endif
d950bf
+
d950bf
+/* Output an indirect branch trampoline for target register REGNO.  */
d950bf
+
d950bf
+static void
d950bf
+s390_output_indirect_thunk_function (unsigned int regno, bool z10_p)
d950bf
+{
d950bf
+  tree decl;
d950bf
+  char thunk_label[32];
d950bf
+
d950bf
+  int i;
d950bf
+
d950bf
+  if (z10_p)
d950bf
+    sprintf (thunk_label, TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL, regno);
d950bf
+  else
d950bf
+    sprintf (thunk_label, TARGET_INDIRECT_BRANCH_THUNK_NAME_EX,
d950bf
+	     INDIRECT_BRANCH_THUNK_REGNUM, regno);
d950bf
+
d950bf
+  decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
d950bf
+		     get_identifier (thunk_label),
d950bf
+		     build_function_type_list (void_type_node, NULL_TREE));
d950bf
+  DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL,
d950bf
+				   NULL_TREE, void_type_node);
d950bf
+  TREE_PUBLIC (decl) = 1;
d950bf
+  TREE_STATIC (decl) = 1;
d950bf
+  DECL_IGNORED_P (decl) = 1;
d950bf
+
d950bf
+  if (USE_HIDDEN_LINKONCE)
d950bf
+    {
d950bf
+      DECL_COMDAT_GROUP (decl) = DECL_ASSEMBLER_NAME (decl);
d950bf
+
d950bf
+      targetm.asm_out.unique_section (decl, 0);
d950bf
+      switch_to_section (get_named_section (decl, NULL, 0));
d950bf
+
d950bf
+      targetm.asm_out.globalize_label (asm_out_file, thunk_label);
d950bf
+      fputs ("\t.hidden\t", asm_out_file);
d950bf
+      assemble_name (asm_out_file, thunk_label);
d950bf
+      putc ('\n', asm_out_file);
d950bf
+      ASM_DECLARE_FUNCTION_NAME (asm_out_file, thunk_label, decl);
d950bf
+    }
d950bf
+  else
d950bf
+    {
d950bf
+      switch_to_section (text_section);
d950bf
+      ASM_OUTPUT_LABEL (asm_out_file, thunk_label);
d950bf
+    }
d950bf
+
d950bf
+  DECL_INITIAL (decl) = make_node (BLOCK);
d950bf
+  current_function_decl = decl;
d950bf
+  allocate_struct_function (decl, false);
d950bf
+  init_function_start (decl);
d950bf
+  cfun->is_thunk = true;
d950bf
+  first_function_block_is_cold = false;
d950bf
+  final_start_function (emit_barrier (), asm_out_file, 1);
d950bf
+
d950bf
+  /* This makes CFI at least usable for indirect jumps.
d950bf
+
d950bf
+     jumps: stopping in the thunk: backtrace will point to the thunk
d950bf
+     target is if it was interrupted by a signal
d950bf
+
d950bf
+     calls: Instead of caller->thunk the backtrace will be
d950bf
+     caller->callee->thunk   */
d950bf
+  if (flag_asynchronous_unwind_tables)
d950bf
+    {
d950bf
+      fputs ("\t.cfi_signal_frame\n", asm_out_file);
d950bf
+      fprintf (asm_out_file, "\t.cfi_return_column %d\n", regno);
d950bf
+      for (i = 0; i < FPR15_REGNUM; i++)
d950bf
+	fprintf (asm_out_file, "\t.cfi_same_value %s\n", reg_names[i]);
d950bf
+    }
d950bf
+
d950bf
+  if (z10_p)
d950bf
+    {
d950bf
+      /* exrl  0,1f  */
d950bf
+
d950bf
+      /* We generate a thunk for z10 compiled code although z10 is
d950bf
+	 currently not enabled.  Tell the assembler to accept the
d950bf
+	 instruction.  */
d950bf
+      if (!TARGET_CPU_Z10)
d950bf
+	{
d950bf
+	  fputs ("\t.machine push\n", asm_out_file);
d950bf
+	  fputs ("\t.machine z10\n", asm_out_file);
d950bf
+	}
d950bf
+      /* We use exrl even if -mzarch hasn't been specified on the
d950bf
+	 command line so we have to tell the assembler to accept
d950bf
+	 it.  */
d950bf
+      if (!TARGET_ZARCH)
d950bf
+	fputs ("\t.machinemode zarch\n", asm_out_file);
d950bf
+
d950bf
+      fputs ("\texrl\t0,1f\n", asm_out_file);
d950bf
+
d950bf
+      if (!TARGET_ZARCH)
d950bf
+	fputs ("\t.machinemode esa\n", asm_out_file);
d950bf
+
d950bf
+      if (!TARGET_CPU_Z10)
d950bf
+	fputs ("\t.machine pop\n", asm_out_file);
d950bf
+    }
d950bf
+  else if (TARGET_CPU_ZARCH)
d950bf
+    {
d950bf
+      /* larl %r1,1f  */
d950bf
+      fprintf (asm_out_file, "\tlarl\t%%r%d,1f\n",
d950bf
+	       INDIRECT_BRANCH_THUNK_REGNUM);
d950bf
+
d950bf
+      /* ex 0,0(%r1)  */
d950bf
+      fprintf (asm_out_file, "\tex\t0,0(%%r%d)\n",
d950bf
+	       INDIRECT_BRANCH_THUNK_REGNUM);
d950bf
+    }
d950bf
+  else
d950bf
+    gcc_unreachable ();
d950bf
+
d950bf
+  /* 0:    j 0b  */
d950bf
+  fputs ("0:\tj\t0b\n", asm_out_file);
d950bf
+
d950bf
+  /* 1:    br <regno>  */
d950bf
+  fprintf (asm_out_file, "1:\tbr\t%%r%d\n", regno);
d950bf
+
d950bf
+  final_end_function ();
d950bf
+  init_insn_lengths ();
d950bf
+  free_after_compilation (cfun);
d950bf
+  set_cfun (NULL);
d950bf
+  current_function_decl = NULL;
d950bf
+}
d950bf
+
d950bf
+/* Implement the asm.code_end target hook.  */
d950bf
+
d950bf
+static void
d950bf
+s390_code_end (void)
d950bf
+{
d950bf
+  int i;
d950bf
+
d950bf
+  for (i = 1; i < 16; i++)
d950bf
+    {
d950bf
+      if (indirect_branch_z10thunk_mask & (1 << i))
d950bf
+	s390_output_indirect_thunk_function (i, true);
d950bf
+
d950bf
+      if (indirect_branch_prez10thunk_mask & (1 << i))
d950bf
+	s390_output_indirect_thunk_function (i, false);
d950bf
+    }
d950bf
+
d950bf
+  if (TARGET_INDIRECT_BRANCH_TABLE)
d950bf
+    {
d950bf
+      int o;
d950bf
+      int i;
d950bf
+
d950bf
+      for (o = 0; o < INDIRECT_BRANCH_NUM_OPTIONS; o++)
d950bf
+	{
d950bf
+	  if (indirect_branch_table_label_no[o] == 0)
d950bf
+	    continue;
d950bf
+
d950bf
+	  switch_to_section (get_section (indirect_branch_table_name[o],
d950bf
+					  0,
d950bf
+					  NULL_TREE));
d950bf
+	  for (i = 0; i < indirect_branch_table_label_no[o]; i++)
d950bf
+	    {
d950bf
+	      char label_start[32];
d950bf
+
d950bf
+	      ASM_GENERATE_INTERNAL_LABEL (label_start,
d950bf
+					   indirect_branch_table_label[o], i);
d950bf
+
d950bf
+	      fputs ("\t.long\t", asm_out_file);
d950bf
+	      assemble_name_raw (asm_out_file, label_start);
d950bf
+	      fputs ("-.\n", asm_out_file);
d950bf
+	    }
d950bf
+	  switch_to_section (current_function_section ());
d950bf
+	}
d950bf
+    }
d950bf
+}
d950bf
+
d950bf
+/* Implement the TARGET_CASE_VALUES_THRESHOLD target hook.  */
d950bf
+
d950bf
+unsigned int
d950bf
+s390_case_values_threshold (void)
d950bf
+{
d950bf
+  /* Disabling branch prediction for indirect jumps makes jump table
d950bf
+     much more expensive.  */
d950bf
+  if (TARGET_INDIRECT_BRANCH_NOBP_JUMP)
d950bf
+    return 20;
d950bf
+
d950bf
+  return default_case_values_threshold ();
d950bf
+}
d950bf
+
d950bf
+
d950bf
 /* Initialize GCC target structure.  */
d950bf
 
d950bf
 #undef  TARGET_ASM_ALIGNED_HI_OP
d950bf
@@ -14015,6 +14373,12 @@ s390_asm_file_end (void)
d950bf
 #undef TARGET_ASM_FILE_END
d950bf
 #define TARGET_ASM_FILE_END s390_asm_file_end
d950bf
 
d950bf
+#undef TARGET_ASM_CODE_END
d950bf
+#define TARGET_ASM_CODE_END s390_code_end
d950bf
+
d950bf
+#undef TARGET_CASE_VALUES_THRESHOLD
d950bf
+#define TARGET_CASE_VALUES_THRESHOLD s390_case_values_threshold
d950bf
+
d950bf
 struct gcc_target targetm = TARGET_INITIALIZER;
d950bf
 
d950bf
 #include "gt-s390.h"
d950bf
diff -Nrup gcc/config/s390/s390.h gcc/config/s390/s390.h
d950bf
--- gcc/config/s390/s390.h	2018-03-27 09:33:19.762143683 -0600
d950bf
+++ gcc/config/s390/s390.h	2018-03-27 09:33:58.827861602 -0600
d950bf
@@ -1006,4 +1006,37 @@ extern const int processor_flags_table[]
d950bf
     s390_register_target_pragmas ();		\
d950bf
   } while (0)
d950bf
 
d950bf
+
d950bf
+#define TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION			\
d950bf
+  (s390_function_return_reg != indirect_branch_keep		\
d950bf
+   || s390_function_return_mem != indirect_branch_keep)
d950bf
+
d950bf
+#define TARGET_INDIRECT_BRANCH_NOBP_RET					\
d950bf
+  ((s390_function_return_reg != indirect_branch_keep			\
d950bf
+    && !s390_return_addr_from_memory ())				\
d950bf
+   || (s390_function_return_mem != indirect_branch_keep			\
d950bf
+       && s390_return_addr_from_memory ()))
d950bf
+
d950bf
+#define TARGET_INDIRECT_BRANCH_NOBP_JUMP		\
d950bf
+  (s390_indirect_branch_jump != indirect_branch_keep)
d950bf
+
d950bf
+#define TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK				\
d950bf
+  (s390_indirect_branch_jump == indirect_branch_thunk			\
d950bf
+   || s390_indirect_branch_jump == indirect_branch_thunk_extern)
d950bf
+
d950bf
+#define TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK		\
d950bf
+  (s390_indirect_branch_jump == indirect_branch_thunk_inline)
d950bf
+
d950bf
+#define TARGET_INDIRECT_BRANCH_NOBP_CALL		\
d950bf
+  (s390_indirect_branch_call != indirect_branch_keep)
d950bf
+
d950bf
+#ifndef TARGET_DEFAULT_INDIRECT_BRANCH_TABLE
d950bf
+#define TARGET_DEFAULT_INDIRECT_BRANCH_TABLE 0
d950bf
+#endif
d950bf
+
d950bf
+#define TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL "__s390_indirect_jump_r%d"
d950bf
+#define TARGET_INDIRECT_BRANCH_THUNK_NAME_EX   "__s390_indirect_jump_r%duse_r%d"
d950bf
+
d950bf
+#define TARGET_INDIRECT_BRANCH_TABLE s390_indirect_branch_table
d950bf
+
d950bf
 #endif /* S390_H */
d950bf
diff -Nrup gcc/config/s390/s390.md gcc/config/s390/s390.md
d950bf
--- gcc/config/s390/s390.md	2018-03-27 09:33:19.763143675 -0600
d950bf
+++ gcc/config/s390/s390.md	2018-03-27 09:33:58.831861573 -0600
d950bf
@@ -285,6 +285,8 @@
d950bf
   [
d950bf
    ; Sibling call register.
d950bf
    (SIBCALL_REGNUM		 1)
d950bf
+   ; A call-clobbered reg which can be used in indirect branch thunks
d950bf
+   (INDIRECT_BRANCH_THUNK_REGNUM 1)
d950bf
    ; Literal pool base register.
d950bf
    (BASE_REGNUM			13)
d950bf
    ; Return address register.
d950bf
@@ -304,6 +306,7 @@
d950bf
    ; Floating point registers.
d950bf
    (FPR0_REGNUM                 16)
d950bf
    (FPR2_REGNUM                 18)
d950bf
+   (FPR15_REGNUM                31)
d950bf
    (VR0_REGNUM                  16)
d950bf
    (VR16_REGNUM                 38)
d950bf
    (VR23_REGNUM                 45)
d950bf
@@ -402,7 +405,10 @@
d950bf
                          z196_cracked"
d950bf
              (const_string "none"))
d950bf
 
d950bf
-(define_attr "mnemonic" "bcr_flush,unknown" (const_string "unknown"))
d950bf
+; mnemonics which only get defined through if_then_else currently
d950bf
+; don't get added to the list values automatically and hence need to
d950bf
+; be listed here.
d950bf
+(define_attr "mnemonic" "b,br,bas,bc,bcr,bcr_flush,unknown" (const_string "unknown"))
d950bf
 
d950bf
 ;; Length in bytes.
d950bf
 
d950bf
@@ -8436,7 +8442,7 @@
d950bf
           (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)])
d950bf
           (match_operand 0 "address_operand" "ZQZR")
d950bf
           (pc)))]
d950bf
-  ""
d950bf
+  "!TARGET_INDIRECT_BRANCH_NOBP_JUMP"
d950bf
 {
d950bf
   if (get_attr_op_type (insn) == OP_TYPE_RR)
d950bf
     return "b%C1r\t%0";
d950bf
@@ -8446,6 +8452,9 @@
d950bf
   [(set (attr "op_type")
d950bf
         (if_then_else (match_operand 0 "register_operand" "")
d950bf
                       (const_string "RR") (const_string "RX")))
d950bf
+   (set (attr "mnemonic")
d950bf
+        (if_then_else (match_operand 0 "register_operand" "")
d950bf
+                      (const_string "bcr") (const_string "bc")))
d950bf
    (set_attr "type"  "branch")
d950bf
    (set_attr "atype" "agen")])
d950bf
 
d950bf
@@ -8499,7 +8508,7 @@
d950bf
           (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)])
d950bf
           (pc)
d950bf
           (match_operand 0 "address_operand" "ZQZR")))]
d950bf
-  ""
d950bf
+  "!TARGET_INDIRECT_BRANCH_NOBP_JUMP"
d950bf
 {
d950bf
   if (get_attr_op_type (insn) == OP_TYPE_RR)
d950bf
     return "b%D1r\t%0";
d950bf
@@ -8509,6 +8518,9 @@
d950bf
   [(set (attr "op_type")
d950bf
         (if_then_else (match_operand 0 "register_operand" "")
d950bf
                       (const_string "RR") (const_string "RX")))
d950bf
+   (set (attr "mnemonic")
d950bf
+        (if_then_else (match_operand 0 "register_operand" "")
d950bf
+                      (const_string "bcr") (const_string "bc")))
d950bf
    (set_attr "type"  "branch")
d950bf
    (set_attr "atype" "agen")])
d950bf
 
d950bf
@@ -9005,29 +9017,125 @@
d950bf
 ; indirect-jump instruction pattern(s).
d950bf
 ;
d950bf
 
d950bf
-(define_insn "indirect_jump"
d950bf
- [(set (pc) (match_operand 0 "address_operand" "ZQZR"))]
d950bf
-  ""
d950bf
+(define_expand "indirect_jump"
d950bf
+  [(set (pc) (match_operand 0 "address_operand" "ZQZR"))]
d950bf
+   ""
d950bf
+{
d950bf
+  if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK)
d950bf
+    {
d950bf
+      operands[0] = force_reg (Pmode, operands[0]);
d950bf
+      if (TARGET_CPU_Z10)
d950bf
+	{
d950bf
+	  if (TARGET_64BIT)
d950bf
+	    emit_jump_insn (gen_indirect_jump_via_thunkdi_z10 (operands[0]));
d950bf
+	  else
d950bf
+	    emit_jump_insn (gen_indirect_jump_via_thunksi_z10 (operands[0]));
d950bf
+	}
d950bf
+      else
d950bf
+	{
d950bf
+	  if (TARGET_64BIT)
d950bf
+	    emit_jump_insn (gen_indirect_jump_via_thunkdi (operands[0]));
d950bf
+	  else
d950bf
+	    emit_jump_insn (gen_indirect_jump_via_thunksi (operands[0]));
d950bf
+	}
d950bf
+      DONE;
d950bf
+    }
d950bf
+})
d950bf
+
d950bf
+(define_insn "*indirect_jump"
d950bf
+  [(set (pc)
d950bf
+	(match_operand 0 "address_operand" "ZR"))]
d950bf
+ "!TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK"
d950bf
 {
d950bf
   if (get_attr_op_type (insn) == OP_TYPE_RR)
d950bf
     return "br\t%0";
d950bf
   else
d950bf
     return "b\t%a0";
d950bf
 }
d950bf
-  [(set (attr "op_type")
d950bf
-        (if_then_else (match_operand 0 "register_operand" "")
d950bf
-                      (const_string "RR") (const_string "RX")))
d950bf
-   (set_attr "type"  "branch")
d950bf
-   (set_attr "atype" "agen")])
d950bf
+ [(set (attr "op_type")
d950bf
+       (if_then_else (match_operand 0 "register_operand" "")
d950bf
+		     (const_string "RR") (const_string "RX")))
d950bf
+  (set (attr "mnemonic")
d950bf
+       (if_then_else (match_operand 0 "register_operand" "")
d950bf
+		     (const_string "br") (const_string "b")))
d950bf
+  (set_attr "type"  "branch")
d950bf
+  (set_attr "atype" "agen")])
d950bf
+
d950bf
+(define_insn "indirect_jump_via_thunk<mode>_z10"
d950bf
+  [(set (pc)
d950bf
+	(match_operand:P 0 "register_operand" "a"))]
d950bf
+ "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK
d950bf
+  && TARGET_CPU_Z10"
d950bf
+{
d950bf
+  s390_indirect_branch_via_thunk (REGNO (operands[0]),
d950bf
+				  INVALID_REGNUM,
d950bf
+				  NULL_RTX,
d950bf
+				  s390_indirect_branch_type_jump);
d950bf
+  return "";
d950bf
+}
d950bf
+ [(set_attr "op_type"  "RIL")
d950bf
+  (set_attr "mnemonic" "jg")
d950bf
+  (set_attr "type"  "branch")
d950bf
+  (set_attr "atype" "agen")])
d950bf
+
d950bf
+(define_insn "indirect_jump_via_thunk<mode>"
d950bf
+  [(set (pc)
d950bf
+	(match_operand:P 0 "register_operand" " a"))
d950bf
+   (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]
d950bf
+ "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK
d950bf
+  && !TARGET_CPU_Z10"
d950bf
+{
d950bf
+  s390_indirect_branch_via_thunk (REGNO (operands[0]),
d950bf
+				  INVALID_REGNUM,
d950bf
+				  NULL_RTX,
d950bf
+				  s390_indirect_branch_type_jump);
d950bf
+  return "";
d950bf
+}
d950bf
+ [(set_attr "op_type"  "RIL")
d950bf
+  (set_attr "mnemonic" "jg")
d950bf
+  (set_attr "type"  "branch")
d950bf
+  (set_attr "atype" "agen")])
d950bf
 
d950bf
 ;
d950bf
 ; casesi instruction pattern(s).
d950bf
 ;
d950bf
 
d950bf
-(define_insn "casesi_jump"
d950bf
- [(set (pc) (match_operand 0 "address_operand" "ZQZR"))
d950bf
-   (use (label_ref (match_operand 1 "" "")))]
d950bf
-  ""
d950bf
+(define_expand "casesi_jump"
d950bf
+  [(parallel
d950bf
+    [(set (pc) (match_operand 0 "address_operand"))
d950bf
+     (use (label_ref (match_operand 1 "")))])]
d950bf
+   ""
d950bf
+{
d950bf
+  if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK)
d950bf
+    {
d950bf
+      operands[0] = force_reg (GET_MODE (operands[0]), operands[0]);
d950bf
+
d950bf
+      if (TARGET_CPU_Z10)
d950bf
+	{
d950bf
+	  if (TARGET_64BIT)
d950bf
+	    emit_jump_insn (gen_casesi_jump_via_thunkdi_z10 (operands[0],
d950bf
+							     operands[1]));
d950bf
+	  else
d950bf
+	    emit_jump_insn (gen_casesi_jump_via_thunksi_z10 (operands[0],
d950bf
+							     operands[1]));
d950bf
+	}
d950bf
+      else
d950bf
+	{
d950bf
+	  if (TARGET_64BIT)
d950bf
+	    emit_jump_insn (gen_casesi_jump_via_thunkdi (operands[0],
d950bf
+							 operands[1]));
d950bf
+	  else
d950bf
+	    emit_jump_insn (gen_casesi_jump_via_thunksi (operands[0],
d950bf
+							 operands[1]));
d950bf
+	}
d950bf
+      DONE;
d950bf
+    }
d950bf
+})
d950bf
+
d950bf
+(define_insn "*casesi_jump"
d950bf
+ [(set (pc) (match_operand 0 "address_operand" "ZR"))
d950bf
+  (use (label_ref (match_operand 1 "" "")))]
d950bf
+ "!TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK"
d950bf
 {
d950bf
   if (get_attr_op_type (insn) == OP_TYPE_RR)
d950bf
     return "br\t%0";
d950bf
@@ -9035,11 +9143,50 @@
d950bf
     return "b\t%a0";
d950bf
 }
d950bf
   [(set (attr "op_type")
d950bf
+	(if_then_else (match_operand 0 "register_operand" "")
d950bf
+		      (const_string "RR") (const_string "RX")))
d950bf
+   (set (attr "mnemonic")
d950bf
         (if_then_else (match_operand 0 "register_operand" "")
d950bf
-                      (const_string "RR") (const_string "RX")))
d950bf
+                      (const_string "br") (const_string "b")))
d950bf
+   (set_attr "type"  "branch")
d950bf
+   (set_attr "atype" "agen")])
d950bf
+
d950bf
+(define_insn "casesi_jump_via_thunk<mode>_z10"
d950bf
+ [(set (pc) (match_operand:P 0 "register_operand" "a"))
d950bf
+  (use (label_ref (match_operand 1 "" "")))]
d950bf
+ "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK
d950bf
+  && TARGET_CPU_Z10"
d950bf
+{
d950bf
+  s390_indirect_branch_via_thunk (REGNO (operands[0]),
d950bf
+				  INVALID_REGNUM,
d950bf
+				  NULL_RTX,
d950bf
+				  s390_indirect_branch_type_jump);
d950bf
+  return "";
d950bf
+}
d950bf
+  [(set_attr "op_type" "RIL")
d950bf
+   (set_attr "mnemonic" "jg")
d950bf
    (set_attr "type"  "branch")
d950bf
    (set_attr "atype" "agen")])
d950bf
 
d950bf
+(define_insn "casesi_jump_via_thunk<mode>"
d950bf
+ [(set (pc) (match_operand:P 0 "register_operand" "a"))
d950bf
+  (use (label_ref (match_operand 1 "" "")))
d950bf
+  (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]
d950bf
+ "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK
d950bf
+  && !TARGET_CPU_Z10"
d950bf
+{
d950bf
+  s390_indirect_branch_via_thunk (REGNO (operands[0]),
d950bf
+				  INVALID_REGNUM,
d950bf
+				  NULL_RTX,
d950bf
+				  s390_indirect_branch_type_jump);
d950bf
+  return "";
d950bf
+}
d950bf
+ [(set_attr "op_type" "RIL")
d950bf
+  (set_attr "mnemonic" "jg")
d950bf
+  (set_attr "type"  "branch")
d950bf
+  (set_attr "atype" "agen")])
d950bf
+
d950bf
+
d950bf
 (define_expand "casesi"
d950bf
   [(match_operand:SI 0 "general_operand" "")
d950bf
    (match_operand:SI 1 "general_operand" "")
d950bf
@@ -9141,11 +9288,30 @@
d950bf
 
d950bf
 (define_insn "*sibcall_br"
d950bf
   [(call (mem:QI (reg SIBCALL_REGNUM))
d950bf
-         (match_operand 0 "const_int_operand" "n"))]
d950bf
+	 (match_operand 0 "const_int_operand" "n"))]
d950bf
   "SIBLING_CALL_P (insn)
d950bf
-   && GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode"
d950bf
-  "br\t%%r1"
d950bf
-  [(set_attr "op_type" "RR")
d950bf
+    && GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode"
d950bf
+{
d950bf
+  if (TARGET_INDIRECT_BRANCH_NOBP_CALL)
d950bf
+    {
d950bf
+      gcc_assert (TARGET_CPU_Z10);
d950bf
+      s390_indirect_branch_via_thunk (SIBCALL_REGNUM,
d950bf
+				      INVALID_REGNUM,
d950bf
+				      NULL_RTX,
d950bf
+				      s390_indirect_branch_type_call);
d950bf
+      return "";
d950bf
+    }
d950bf
+  else
d950bf
+    return "br\t%%r1";
d950bf
+}
d950bf
+  [(set (attr "op_type")
d950bf
+	(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")
d950bf
+		      (const_string "RIL")
d950bf
+		      (const_string "RR")))
d950bf
+   (set (attr "mnemonic")
d950bf
+	(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")
d950bf
+		      (const_string "jg")
d950bf
+		      (const_string "br")))
d950bf
    (set_attr "type"  "branch")
d950bf
    (set_attr "atype" "agen")])
d950bf
 
d950bf
@@ -9185,8 +9351,27 @@
d950bf
 	      (match_operand 1 "const_int_operand" "n")))]
d950bf
   "SIBLING_CALL_P (insn)
d950bf
    && GET_MODE (XEXP (XEXP (XEXP (PATTERN (insn), 1), 0), 0)) == Pmode"
d950bf
-  "br\t%%r1"
d950bf
-  [(set_attr "op_type" "RR")
d950bf
+{
d950bf
+  if (TARGET_INDIRECT_BRANCH_NOBP_CALL)
d950bf
+    {
d950bf
+      gcc_assert (TARGET_CPU_Z10);
d950bf
+      s390_indirect_branch_via_thunk (SIBCALL_REGNUM,
d950bf
+				      INVALID_REGNUM,
d950bf
+				      NULL_RTX,
d950bf
+				      s390_indirect_branch_type_call);
d950bf
+      return "";
d950bf
+    }
d950bf
+  else
d950bf
+    return "br\t%%r1";
d950bf
+}
d950bf
+  [(set (attr "op_type")
d950bf
+	(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")
d950bf
+		      (const_string "RIL")
d950bf
+		      (const_string "RR")))
d950bf
+   (set (attr "mnemonic")
d950bf
+	(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")
d950bf
+		      (const_string "jg")
d950bf
+		      (const_string "br")))
d950bf
    (set_attr "type"  "branch")
d950bf
    (set_attr "atype" "agen")])
d950bf
 
d950bf
@@ -9252,7 +9437,9 @@
d950bf
   [(call (mem:QI (match_operand 0 "address_operand" "ZQZR"))
d950bf
          (match_operand 1 "const_int_operand" "n"))
d950bf
    (clobber (match_operand 2 "register_operand" "=r"))]
d950bf
-  "!SIBLING_CALL_P (insn) && GET_MODE (operands[2]) == Pmode"
d950bf
+  "!TARGET_INDIRECT_BRANCH_NOBP_CALL
d950bf
+   && !SIBLING_CALL_P (insn)
d950bf
+   && GET_MODE (operands[2]) == Pmode"
d950bf
 {
d950bf
   if (get_attr_op_type (insn) == OP_TYPE_RR)
d950bf
     return "basr\t%2,%0";
d950bf
@@ -9262,6 +9449,50 @@
d950bf
   [(set (attr "op_type")
d950bf
         (if_then_else (match_operand 0 "register_operand" "")
d950bf
                       (const_string "RR") (const_string "RX")))
d950bf
+   (set (attr "mnemonic")
d950bf
+        (if_then_else (match_operand 0 "register_operand" "")
d950bf
+		      (const_string "basr") (const_string "bas")))
d950bf
+   (set_attr "type"  "jsr")
d950bf
+   (set_attr "atype" "agen")
d950bf
+   (set_attr "z196prop" "z196_cracked")])
d950bf
+
d950bf
+(define_insn "*basr_via_thunk<mode>_z10"
d950bf
+  [(call (mem:QI (match_operand:P 0 "register_operand" "a"))
d950bf
+         (match_operand 1 "const_int_operand"          "n"))
d950bf
+   (clobber (match_operand:P 2 "register_operand"    "=&r"))]
d950bf
+  "TARGET_INDIRECT_BRANCH_NOBP_CALL
d950bf
+   && TARGET_CPU_Z10
d950bf
+   && !SIBLING_CALL_P (insn)"
d950bf
+{
d950bf
+  s390_indirect_branch_via_thunk (REGNO (operands[0]),
d950bf
+				  REGNO (operands[2]),
d950bf
+				  NULL_RTX,
d950bf
+				  s390_indirect_branch_type_call);
d950bf
+  return "";
d950bf
+}
d950bf
+  [(set_attr "op_type" "RIL")
d950bf
+   (set_attr "mnemonic" "brasl")
d950bf
+   (set_attr "type"  "jsr")
d950bf
+   (set_attr "atype" "agen")
d950bf
+   (set_attr "z196prop" "z196_cracked")])
d950bf
+
d950bf
+(define_insn "*basr_via_thunk<mode>"
d950bf
+  [(call (mem:QI (match_operand:P 0 "register_operand" "a"))
d950bf
+         (match_operand 1 "const_int_operand"          "n"))
d950bf
+   (clobber (match_operand:P 2 "register_operand"    "=&r"))
d950bf
+   (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]
d950bf
+  "TARGET_INDIRECT_BRANCH_NOBP_CALL
d950bf
+   && !TARGET_CPU_Z10
d950bf
+   && !SIBLING_CALL_P (insn)"
d950bf
+{
d950bf
+  s390_indirect_branch_via_thunk (REGNO (operands[0]),
d950bf
+				  REGNO (operands[2]),
d950bf
+				  NULL_RTX,
d950bf
+				  s390_indirect_branch_type_call);
d950bf
+  return "";
d950bf
+}
d950bf
+  [(set_attr "op_type" "RIL")
d950bf
+   (set_attr "mnemonic" "brasl")
d950bf
    (set_attr "type"  "jsr")
d950bf
    (set_attr "atype" "agen")
d950bf
    (set_attr "z196prop" "z196_cracked")])
d950bf
@@ -9313,7 +9544,10 @@
d950bf
         (call (mem:QI (match_operand 1 "address_operand" "ZQZR"))
d950bf
               (match_operand 2 "const_int_operand" "n")))
d950bf
    (clobber (match_operand 3 "register_operand" "=r"))]
d950bf
-  "!SIBLING_CALL_P (insn) && GET_MODE (operands[3]) == Pmode"
d950bf
+  "!TARGET_INDIRECT_BRANCH_NOBP_CALL
d950bf
+   && !SIBLING_CALL_P (insn)
d950bf
+   && GET_MODE (operands[3]) == Pmode"
d950bf
+
d950bf
 {
d950bf
   if (get_attr_op_type (insn) == OP_TYPE_RR)
d950bf
     return "basr\t%3,%1";
d950bf
@@ -9323,6 +9557,54 @@
d950bf
   [(set (attr "op_type")
d950bf
         (if_then_else (match_operand 1 "register_operand" "")
d950bf
                       (const_string "RR") (const_string "RX")))
d950bf
+   (set (attr "mnemonic")
d950bf
+        (if_then_else (match_operand 1 "register_operand" "")
d950bf
+                      (const_string "basr") (const_string "bas")))
d950bf
+   (set_attr "type"  "jsr")
d950bf
+   (set_attr "atype" "agen")
d950bf
+   (set_attr "z196prop" "z196_cracked")])
d950bf
+
d950bf
+(define_insn "*basr_r_via_thunk_z10"
d950bf
+  [(set (match_operand 0 "" "")
d950bf
+        (call (mem:QI (match_operand 1 "register_operand" "a"))
d950bf
+              (match_operand 2 "const_int_operand"        "n")))
d950bf
+   (clobber (match_operand 3 "register_operand"         "=&r"))]
d950bf
+  "TARGET_INDIRECT_BRANCH_NOBP_CALL
d950bf
+   && TARGET_CPU_Z10
d950bf
+   && !SIBLING_CALL_P (insn)
d950bf
+   && GET_MODE (operands[3]) == Pmode"
d950bf
+{
d950bf
+  s390_indirect_branch_via_thunk (REGNO (operands[1]),
d950bf
+				  REGNO (operands[3]),
d950bf
+				  NULL_RTX,
d950bf
+				  s390_indirect_branch_type_call);
d950bf
+  return "";
d950bf
+}
d950bf
+  [(set_attr "op_type" "RIL")
d950bf
+   (set_attr "mnemonic" "brasl")
d950bf
+   (set_attr "type"  "jsr")
d950bf
+   (set_attr "atype" "agen")
d950bf
+   (set_attr "z196prop" "z196_cracked")])
d950bf
+
d950bf
+(define_insn "*basr_r_via_thunk"
d950bf
+  [(set (match_operand 0 "" "")
d950bf
+        (call (mem:QI (match_operand 1 "register_operand" "a"))
d950bf
+              (match_operand 2 "const_int_operand"        "n")))
d950bf
+   (clobber (match_operand 3 "register_operand"         "=&r"))
d950bf
+   (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]
d950bf
+  "TARGET_INDIRECT_BRANCH_NOBP_CALL
d950bf
+   && !TARGET_CPU_Z10
d950bf
+   && !SIBLING_CALL_P (insn)
d950bf
+   && GET_MODE (operands[3]) == Pmode"
d950bf
+{
d950bf
+  s390_indirect_branch_via_thunk (REGNO (operands[1]),
d950bf
+				  REGNO (operands[3]),
d950bf
+				  NULL_RTX,
d950bf
+				  s390_indirect_branch_type_call);
d950bf
+  return "";
d950bf
+}
d950bf
+  [(set_attr "op_type" "RIL")
d950bf
+   (set_attr "mnemonic"  "brasl")
d950bf
    (set_attr "type"  "jsr")
d950bf
    (set_attr "atype" "agen")
d950bf
    (set_attr "z196prop" "z196_cracked")])
d950bf
@@ -10056,15 +10338,78 @@
d950bf
   ""
d950bf
   "s390_emit_epilogue (true); DONE;")
d950bf
 
d950bf
-(define_insn "*return"
d950bf
+(define_expand "return_use"
d950bf
+  [(parallel
d950bf
+    [(return)
d950bf
+     (use (match_operand 0 "register_operand" "a"))])]
d950bf
+  ""
d950bf
+{
d950bf
+  if (!TARGET_CPU_Z10
d950bf
+      && TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION)
d950bf
+    {
d950bf
+      if (TARGET_64BIT)
d950bf
+        emit_jump_insn (gen_returndi_prez10 (operands[0]));
d950bf
+      else
d950bf
+        emit_jump_insn (gen_returnsi_prez10 (operands[0]));
d950bf
+      DONE;
d950bf
+    }
d950bf
+})
d950bf
+
d950bf
+(define_insn "*return<mode>"
d950bf
   [(return)
d950bf
-   (use (match_operand 0 "register_operand" "a"))]
d950bf
-  "GET_MODE (operands[0]) == Pmode"
d950bf
-  "br\t%0"
d950bf
-  [(set_attr "op_type" "RR")
d950bf
+   (use (match_operand:P 0 "register_operand" "a"))]
d950bf
+  "TARGET_CPU_Z10 || !TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION"
d950bf
+{
d950bf
+  if (TARGET_INDIRECT_BRANCH_NOBP_RET)
d950bf
+    {
d950bf
+      s390_indirect_branch_via_thunk (REGNO (operands[0]),
d950bf
+                                      INVALID_REGNUM,
d950bf
+                                      NULL_RTX,
d950bf
+                                      s390_indirect_branch_type_return);
d950bf
+      return "";
d950bf
+    }
d950bf
+  else
d950bf
+    return "br\t%0";
d950bf
+}
d950bf
+  [(set (attr "op_type")
d950bf
+       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
d950bf
+                     (const_string "RIL")
d950bf
+                     (const_string "RR")))
d950bf
+   (set (attr "mnemonic")
d950bf
+       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
d950bf
+                     (const_string "jg")
d950bf
+                     (const_string "br")))
d950bf
    (set_attr "type"    "jsr")
d950bf
    (set_attr "atype"   "agen")])
d950bf
 
d950bf
+(define_insn "return<mode>_prez10"
d950bf
+  [(return)
d950bf
+   (use (match_operand:P 0 "register_operand" "a"))
d950bf
+   (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]
d950bf
+  "!TARGET_CPU_Z10 && TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION"
d950bf
+{
d950bf
+  if (TARGET_INDIRECT_BRANCH_NOBP_RET)
d950bf
+    {
d950bf
+      s390_indirect_branch_via_thunk (REGNO (operands[0]),
d950bf
+                                      INVALID_REGNUM,
d950bf
+                                      NULL_RTX,
d950bf
+                                      s390_indirect_branch_type_return);
d950bf
+      return "";
d950bf
+    }
d950bf
+  else
d950bf
+    return "br\t%0";
d950bf
+}
d950bf
+  [(set (attr "op_type")
d950bf
+       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
d950bf
+                     (const_string "RIL")
d950bf
+                     (const_string "RR")))
d950bf
+   (set (attr "mnemonic")
d950bf
+       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
d950bf
+                     (const_string "jg")
d950bf
+                     (const_string "br")))
d950bf
+    (set_attr "type"    "jsr")
d950bf
+    (set_attr "atype"   "agen")])
d950bf
+
d950bf
 
d950bf
 ;; Instruction definition to extend a 31-bit pointer into a 64-bit
d950bf
 ;; pointer. This is used for compatibility.
d950bf
diff -Nrup gcc/config/s390/s390.opt gcc/config/s390/s390.opt
d950bf
--- gcc/config/s390/s390.opt	2018-03-27 09:33:19.763143675 -0600
d950bf
+++ gcc/config/s390/s390.opt	2018-03-27 09:33:58.832861566 -0600
d950bf
@@ -175,3 +175,59 @@ Target Report Joined RejectNegative UInt
d950bf
 Set the branch costs for conditional branch instructions.  Reasonable
d950bf
 values are small, non-negative integers.  The default branch cost is
d950bf
 1.
d950bf
+
d950bf
+mindirect-branch=
d950bf
+Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch) Init(indirect_branch_keep)
d950bf
+Wrap all indirect branches into execute in order to disable branch
d950bf
+prediction.
d950bf
+
d950bf
+mindirect-branch-jump=
d950bf
+Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch_jump) Init(indirect_branch_keep)
d950bf
+Wrap indirect table jumps and computed gotos into execute in order to
d950bf
+disable branch prediction.  Using thunk or thunk-extern with this
d950bf
+option requires the thunks to be considered signal handlers to order to
d950bf
+generate correct CFI.  For environments where unwinding (e.g. for
d950bf
+exceptions) is required please use thunk-inline instead.
d950bf
+
d950bf
+mindirect-branch-call=
d950bf
+Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch_call) Init(indirect_branch_keep)
d950bf
+Wrap all indirect calls into execute in order to disable branch prediction.
d950bf
+
d950bf
+mfunction-return=
d950bf
+Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return) Init(indirect_branch_keep)
d950bf
+Wrap all indirect return branches into execute in order to disable branch
d950bf
+prediction.
d950bf
+
d950bf
+mfunction-return-mem=
d950bf
+Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return_mem) Init(indirect_branch_keep)
d950bf
+Wrap indirect return branches into execute in order to disable branch
d950bf
+prediction. This affects only branches where the return address is
d950bf
+going to be restored from memory.
d950bf
+
d950bf
+mfunction-return-reg=
d950bf
+Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return_reg) Init(indirect_branch_keep)
d950bf
+Wrap indirect return branches into execute in order to disable branch
d950bf
+prediction. This affects only branches where the return address
d950bf
+doesn't need to be restored from memory.
d950bf
+
d950bf
+Enum
d950bf
+Name(indirect_branch) Type(enum indirect_branch)
d950bf
+Known indirect branch choices (for use with the -mindirect-branch=/-mfunction-return= options):
d950bf
+
d950bf
+EnumValue
d950bf
+Enum(indirect_branch) String(keep) Value(indirect_branch_keep)
d950bf
+
d950bf
+EnumValue
d950bf
+Enum(indirect_branch) String(thunk) Value(indirect_branch_thunk)
d950bf
+
d950bf
+EnumValue
d950bf
+Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern)
d950bf
+
d950bf
+mindirect-branch-table
d950bf
+Target Report Var(s390_indirect_branch_table) Init(TARGET_DEFAULT_INDIRECT_BRANCH_TABLE)
d950bf
+Generate sections .s390_indirect_jump, .s390_indirect_call,
d950bf
+.s390_return_reg, and .s390_return_mem to contain the indirect branch
d950bf
+locations which have been patched as part of using one of the
d950bf
+-mindirect-branch* or -mfunction-return* options.  The sections
d950bf
+consist of an array of 32 bit elements. Each entry holds the offset
d950bf
+from the entry to the patched location.
d950bf
diff -Nrup gcc/config/s390/s390-opts.h gcc/config/s390/s390-opts.h
d950bf
--- gcc/config/s390/s390-opts.h	2018-03-27 09:33:19.764143668 -0600
d950bf
+++ gcc/config/s390/s390-opts.h	2018-03-27 09:33:58.821861645 -0600
d950bf
@@ -39,4 +39,12 @@ enum processor_type
d950bf
   PROCESSOR_max
d950bf
 };
d950bf
 
d950bf
+/* Values for -mindirect-branch and -mfunction-return options.  */
d950bf
+enum indirect_branch {
d950bf
+  indirect_branch_unset = 0,
d950bf
+  indirect_branch_keep,
d950bf
+  indirect_branch_thunk,
d950bf
+  indirect_branch_thunk_extern
d950bf
+};
d950bf
+
d950bf
 #endif
d950bf
diff -Nrup gcc/config/s390/s390-protos.h gcc/config/s390/s390-protos.h
d950bf
--- gcc/config/s390/s390-protos.h	2018-03-27 09:33:19.764143668 -0600
d950bf
+++ gcc/config/s390/s390-protos.h	2018-03-27 09:33:58.821861645 -0600
d950bf
@@ -41,6 +41,7 @@ extern void s390_set_has_landing_pad_p (
d950bf
 extern bool s390_hard_regno_mode_ok (unsigned int, enum machine_mode);
d950bf
 extern bool s390_hard_regno_rename_ok (unsigned int, unsigned int);
d950bf
 extern int s390_class_max_nregs (enum reg_class, enum machine_mode);
d950bf
+extern bool s390_return_addr_from_memory(void);
d950bf
 extern int s390_cannot_change_mode_class (enum machine_mode, enum machine_mode,
d950bf
 					  enum reg_class);
d950bf
 extern bool s390_function_arg_vector (enum machine_mode, const_tree);
d950bf
@@ -124,6 +125,18 @@ extern int s390_compare_and_branch_condi
d950bf
 extern bool s390_extzv_shift_ok (int, int, unsigned HOST_WIDE_INT);
d950bf
 extern void s390_asm_output_function_label (FILE *, const char *, tree);
d950bf
 
d950bf
+enum s390_indirect_branch_type
d950bf
+  {
d950bf
+    s390_indirect_branch_type_jump = 0,
d950bf
+    s390_indirect_branch_type_call,
d950bf
+    s390_indirect_branch_type_return
d950bf
+  };
d950bf
+extern void s390_indirect_branch_via_thunk (unsigned int regno,
d950bf
+					    unsigned int return_addr_regno,
d950bf
+					    rtx comparison_operator,
d950bf
+					    enum s390_indirect_branch_type type);
d950bf
+extern void s390_indirect_branch_via_inline_thunk (rtx execute_target);
d950bf
+
d950bf
 #endif /* RTX_CODE */
d950bf
 
d950bf
 /* s390-c.c routines */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c	2018-03-27 09:33:58.832861566 -0600
d950bf
@@ -0,0 +1,59 @@
d950bf
+/* { dg-do compile } */
d950bf
+/* { dg-options "-O3  -march=z10 --save-temps -mindirect-branch-call=thunk-extern -mindirect-branch-table" } */
d950bf
+
d950bf
+int gl;
d950bf
+
d950bf
+void __attribute__((noinline,noclone))
d950bf
+foo (int a)
d950bf
+{
d950bf
+  gl = a + 40;
d950bf
+}
d950bf
+
d950bf
+int __attribute__((noinline,noclone))
d950bf
+foo_value (int a)
d950bf
+{
d950bf
+  return a + 40;
d950bf
+}
d950bf
+
d950bf
+void*  __attribute__((noinline,noclone))
d950bf
+get_fptr (int a)
d950bf
+{
d950bf
+  switch (a)
d950bf
+    {
d950bf
+    case 0: return &foo; break;
d950bf
+    case 1: return &foo_value; break;
d950bf
+    default: __builtin_abort ();
d950bf
+    }
d950bf
+}
d950bf
+
d950bf
+void (*f) (int);
d950bf
+int (*g) (int);
d950bf
+
d950bf
+int
d950bf
+main ()
d950bf
+{
d950bf
+  int res;
d950bf
+
d950bf
+  f = get_fptr(0);
d950bf
+  f (2);
d950bf
+  if (gl != 42)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  g = get_fptr(1);
d950bf
+  if (g (2) != 42)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 2 x main
d950bf
+/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */
d950bf
+
d950bf
+/* No thunks due to thunk-extern.  */
d950bf
+/* { dg-final { scan-assembler-not "exrl" } } */
d950bf
+/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c	2018-03-27 09:33:58.833861558 -0600
d950bf
@@ -0,0 +1,56 @@
d950bf
+/* { dg-do run } */
d950bf
+/* { dg-options "-O3  -march=z10 --save-temps -mindirect-branch-call=thunk -mindirect-branch-table" } */
d950bf
+
d950bf
+int gl;
d950bf
+
d950bf
+void __attribute__((noinline,noclone))
d950bf
+foo (int a)
d950bf
+{
d950bf
+  gl = a + 40;
d950bf
+}
d950bf
+
d950bf
+int __attribute__((noinline,noclone))
d950bf
+foo_value (int a)
d950bf
+{
d950bf
+  return a + 40;
d950bf
+}
d950bf
+
d950bf
+void*  __attribute__((noinline,noclone))
d950bf
+get_fptr (int a)
d950bf
+{
d950bf
+  switch (a)
d950bf
+    {
d950bf
+    case 0: return &foo; break;
d950bf
+    case 1: return &foo_value; break;
d950bf
+    default: __builtin_abort ();
d950bf
+    }
d950bf
+}
d950bf
+
d950bf
+void (*f) (int);
d950bf
+int (*g) (int);
d950bf
+
d950bf
+int
d950bf
+main ()
d950bf
+{
d950bf
+  int res;
d950bf
+
d950bf
+  f = get_fptr(0);
d950bf
+  f (2);
d950bf
+  if (gl != 42)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  g = get_fptr(1);
d950bf
+  if (g (2) != 42)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 2 x main
d950bf
+/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */
d950bf
+/* { dg-final { scan-assembler "exrl" } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c	2018-03-27 09:33:58.833861558 -0600
d950bf
@@ -0,0 +1,56 @@
d950bf
+/* { dg-do run } */
d950bf
+/* { dg-options "-O3  -march=z900 --save-temps -mindirect-branch-call=thunk -mindirect-branch-table" } */
d950bf
+
d950bf
+int gl;
d950bf
+
d950bf
+void __attribute__((noinline,noclone))
d950bf
+foo (int a)
d950bf
+{
d950bf
+  gl = a + 40;
d950bf
+}
d950bf
+
d950bf
+int __attribute__((noinline,noclone))
d950bf
+foo_value (int a)
d950bf
+{
d950bf
+  return a + 40;
d950bf
+}
d950bf
+
d950bf
+void*  __attribute__((noinline,noclone))
d950bf
+get_fptr (int a)
d950bf
+{
d950bf
+  switch (a)
d950bf
+    {
d950bf
+    case 0: return &foo; break;
d950bf
+    case 1: return &foo_value; break;
d950bf
+    default: __builtin_abort ();
d950bf
+    }
d950bf
+}
d950bf
+
d950bf
+void (*f) (int);
d950bf
+int (*g) (int);
d950bf
+
d950bf
+int
d950bf
+main ()
d950bf
+{
d950bf
+  int res;
d950bf
+
d950bf
+  f = get_fptr(0);
d950bf
+  f (2);
d950bf
+  if (gl != 42)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  g = get_fptr(1);
d950bf
+  if (g (2) != 42)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 2 x main
d950bf
+/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */
d950bf
+/* { dg-final { scan-assembler "ex\t" } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c	2018-03-27 09:33:58.833861558 -0600
d950bf
@@ -0,0 +1,45 @@
d950bf
+/* { dg-do compile } */
d950bf
+/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk-extern -mindirect-branch-table" } */
d950bf
+
d950bf
+/* This is a copy of the gcc.c-torture/execute/20040302-1.c
d950bf
+   testcase.  */
d950bf
+
d950bf
+int code[]={0,0,0,0,1};
d950bf
+
d950bf
+void
d950bf
+foo(int x) {
d950bf
+  volatile int b;
d950bf
+  b = 0xffffffff;
d950bf
+}
d950bf
+
d950bf
+void
d950bf
+bar(int *pc) {
d950bf
+  static const void *l[] = {&&lab0, &&end;;
d950bf
+
d950bf
+  foo(0);
d950bf
+  goto *l[*pc];
d950bf
+ lab0:
d950bf
+  foo(0);
d950bf
+  pc++;
d950bf
+  goto *l[*pc];
d950bf
+ end:
d950bf
+  return;
d950bf
+}
d950bf
+
d950bf
+int
d950bf
+main() {
d950bf
+  bar(code);
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 2 x bar
d950bf
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
d950bf
+
d950bf
+/* No thunks due to thunk-extern.  */
d950bf
+/* { dg-final { scan-assembler-not "exrl" } } */
d950bf
+/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c	2018-03-27 09:33:58.834861551 -0600
d950bf
@@ -0,0 +1,42 @@
d950bf
+/* { dg-do run } */
d950bf
+/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
d950bf
+
d950bf
+/* This is a copy of the gcc.c-torture/execute/20040302-1.c
d950bf
+   testcase.  */
d950bf
+
d950bf
+int code[]={0,0,0,0,1};
d950bf
+
d950bf
+void
d950bf
+foo(int x) {
d950bf
+  volatile int b;
d950bf
+  b = 0xffffffff;
d950bf
+}
d950bf
+
d950bf
+void
d950bf
+bar(int *pc) {
d950bf
+  static const void *l[] = {&&lab0, &&end;;
d950bf
+
d950bf
+  foo(0);
d950bf
+  goto *l[*pc];
d950bf
+ lab0:
d950bf
+  foo(0);
d950bf
+  pc++;
d950bf
+  goto *l[*pc];
d950bf
+ end:
d950bf
+  return;
d950bf
+}
d950bf
+
d950bf
+int
d950bf
+main() {
d950bf
+  bar(code);
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 2x bar */
d950bf
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
d950bf
+/* { dg-final { scan-assembler "exrl" } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c	2018-03-27 09:33:58.834861551 -0600
d950bf
@@ -0,0 +1,42 @@
d950bf
+/* { dg-do run } */
d950bf
+/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
d950bf
+
d950bf
+/* This is a copy of the gcc.c-torture/execute/20040302-1.c
d950bf
+   testcase.  */
d950bf
+
d950bf
+int code[]={0,0,0,0,1};
d950bf
+
d950bf
+void
d950bf
+foo(int x) {
d950bf
+  volatile int b;
d950bf
+  b = 0xffffffff;
d950bf
+}
d950bf
+
d950bf
+void
d950bf
+bar(int *pc) {
d950bf
+  static const void *l[] = {&&lab0, &&end;;
d950bf
+
d950bf
+  foo(0);
d950bf
+  goto *l[*pc];
d950bf
+ lab0:
d950bf
+  foo(0);
d950bf
+  pc++;
d950bf
+  goto *l[*pc];
d950bf
+ end:
d950bf
+  return;
d950bf
+}
d950bf
+
d950bf
+int
d950bf
+main() {
d950bf
+  bar(code);
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 2 x bar
d950bf
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
d950bf
+/* { dg-final { scan-assembler "ex\t" } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c	2018-03-27 09:33:58.834861551 -0600
d950bf
@@ -0,0 +1,44 @@
d950bf
+/* { dg-do compile } */
d950bf
+/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mfunction-return-mem=thunk-extern -mindirect-branch-table" } */
d950bf
+
d950bf
+int gl = 0;
d950bf
+
d950bf
+int __attribute__((noinline,noclone))
d950bf
+bar (int a)
d950bf
+{
d950bf
+  return a + 2;
d950bf
+}
d950bf
+
d950bf
+void __attribute__((noinline,noclone))
d950bf
+foo (int a)
d950bf
+{
d950bf
+  int i;
d950bf
+
d950bf
+  if (a == 42)
d950bf
+    return;
d950bf
+
d950bf
+  for (i = 0; i < a; i++)
d950bf
+    gl += bar (i);
d950bf
+}
d950bf
+
d950bf
+int
d950bf
+main ()
d950bf
+{
d950bf
+  foo (3);
d950bf
+  if (gl != 9)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 1 x foo, 1 x main
d950bf
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
d950bf
+
d950bf
+/* No thunks due to thunk-extern.  */
d950bf
+/* { dg-final { scan-assembler-not "exrl" } } */
d950bf
+/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_return_mem" } } */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c	2018-03-27 09:33:58.835861544 -0600
d950bf
@@ -0,0 +1,41 @@
d950bf
+/* { dg-do run } */
d950bf
+/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mfunction-return-mem=thunk -mindirect-branch-table" } */
d950bf
+
d950bf
+int gl = 0;
d950bf
+
d950bf
+int __attribute__((noinline,noclone))
d950bf
+bar (int a)
d950bf
+{
d950bf
+  return a + 2;
d950bf
+}
d950bf
+
d950bf
+void __attribute__((noinline,noclone))
d950bf
+foo (int a)
d950bf
+{
d950bf
+  int i;
d950bf
+
d950bf
+  if (a == 42)
d950bf
+    return;
d950bf
+
d950bf
+  for (i = 0; i < a; i++)
d950bf
+    gl += bar (i);
d950bf
+}
d950bf
+
d950bf
+int
d950bf
+main ()
d950bf
+{
d950bf
+  foo (3);
d950bf
+  if (gl != 9)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 1 x foo, 1 x main
d950bf
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
d950bf
+/* { dg-final { scan-assembler "exrl" } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_return_mem" } } */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c	2018-03-27 09:33:58.835861544 -0600
d950bf
@@ -0,0 +1,42 @@
d950bf
+/* { dg-do run } */
d950bf
+/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-mem=thunk -mindirect-branch-table" } */
d950bf
+
d950bf
+int gl = 0;
d950bf
+
d950bf
+int __attribute__((noinline,noclone))
d950bf
+bar (int a)
d950bf
+{
d950bf
+  return a + 2;
d950bf
+}
d950bf
+
d950bf
+void __attribute__((noinline,noclone))
d950bf
+foo (int a)
d950bf
+{
d950bf
+  int i;
d950bf
+
d950bf
+  if (a == 42)
d950bf
+    return;
d950bf
+
d950bf
+  for (i = 0; i < a; i++)
d950bf
+    gl += bar (i);
d950bf
+}
d950bf
+
d950bf
+int
d950bf
+main ()
d950bf
+{
d950bf
+  foo (3);
d950bf
+  if (gl != 9)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 1 x foo, 1 x main
d950bf
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler "ex\t" } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_return_mem" } } */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c	2018-03-27 09:33:58.835861544 -0600
d950bf
@@ -0,0 +1,44 @@
d950bf
+/* { dg-do compile } */
d950bf
+/* { dg-options "-O3 -march=z10 --save-temps -mfunction-return-reg=thunk-extern -mindirect-branch-table" } */
d950bf
+
d950bf
+int gl = 0;
d950bf
+
d950bf
+int __attribute__((noinline,noclone))
d950bf
+bar (int a)
d950bf
+{
d950bf
+  return a + 2;
d950bf
+}
d950bf
+
d950bf
+void __attribute__((noinline,noclone))
d950bf
+foo (int a)
d950bf
+{
d950bf
+  int i;
d950bf
+
d950bf
+  if (a == 42)
d950bf
+    return;
d950bf
+
d950bf
+  for (i = 0; i < a; i++)
d950bf
+    gl += bar (i);
d950bf
+}
d950bf
+
d950bf
+int
d950bf
+main ()
d950bf
+{
d950bf
+  foo (3);
d950bf
+  if (gl != 9)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 1 x bar
d950bf
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */
d950bf
+
d950bf
+/* No thunks due to thunk-extern.  */
d950bf
+/* { dg-final { scan-assembler-not "exrl" } } */
d950bf
+/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_return_reg" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c	2018-03-27 09:33:58.836861537 -0600
d950bf
@@ -0,0 +1,41 @@
d950bf
+/* { dg-do run } */
d950bf
+/* { dg-options "-O3 -march=z10 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */
d950bf
+
d950bf
+int gl = 0;
d950bf
+
d950bf
+int __attribute__((noinline,noclone))
d950bf
+bar (int a)
d950bf
+{
d950bf
+  return a + 2;
d950bf
+}
d950bf
+
d950bf
+void __attribute__((noinline,noclone))
d950bf
+foo (int a)
d950bf
+{
d950bf
+  int i;
d950bf
+
d950bf
+  if (a == 42)
d950bf
+    return;
d950bf
+
d950bf
+  for (i = 0; i < a; i++)
d950bf
+    gl += bar (i);
d950bf
+}
d950bf
+
d950bf
+int
d950bf
+main ()
d950bf
+{
d950bf
+  foo (3);
d950bf
+  if (gl != 9)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 1 x bar
d950bf
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */
d950bf
+/* { dg-final { scan-assembler "exrl" } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_return_reg" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c	2018-03-27 09:33:58.836861537 -0600
d950bf
@@ -0,0 +1,41 @@
d950bf
+/* { dg-do run } */
d950bf
+/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */
d950bf
+
d950bf
+int gl = 0;
d950bf
+
d950bf
+int __attribute__((noinline,noclone))
d950bf
+bar (int a)
d950bf
+{
d950bf
+  return a + 2;
d950bf
+}
d950bf
+
d950bf
+void __attribute__((noinline,noclone))
d950bf
+foo (int a)
d950bf
+{
d950bf
+  int i;
d950bf
+
d950bf
+  if (a == 42)
d950bf
+    return;
d950bf
+
d950bf
+  for (i = 0; i < a; i++)
d950bf
+    gl += bar (i);
d950bf
+}
d950bf
+
d950bf
+int
d950bf
+main ()
d950bf
+{
d950bf
+  foo (3);
d950bf
+  if (gl != 9)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 1 x bar
d950bf
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */
d950bf
+/* { dg-final { scan-assembler "ex\t" } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_return_reg" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c	2018-03-27 09:33:58.836861537 -0600
d950bf
@@ -0,0 +1,77 @@
d950bf
+/* { dg-do run } */
d950bf
+/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
d950bf
+/* case-values-threshold will be set to 20 by the back-end when jump
d950bf
+   thunk are requested.  */
d950bf
+
d950bf
+int __attribute__((noinline,noclone)) foo1 (void) { return 1; }
d950bf
+int __attribute__((noinline,noclone)) foo2 (void) { return 2; }
d950bf
+int __attribute__((noinline,noclone)) foo3 (void) { return 3; }
d950bf
+int __attribute__((noinline,noclone)) foo4 (void) { return 4; }
d950bf
+int __attribute__((noinline,noclone)) foo5 (void) { return 5; }
d950bf
+int __attribute__((noinline,noclone)) foo6 (void) { return 6; }
d950bf
+int __attribute__((noinline,noclone)) foo7 (void) { return 7; }
d950bf
+int __attribute__((noinline,noclone)) foo8 (void) { return 8; }
d950bf
+int __attribute__((noinline,noclone)) foo9 (void) { return 9; }
d950bf
+int __attribute__((noinline,noclone)) foo10 (void) { return 10; }
d950bf
+int __attribute__((noinline,noclone)) foo11 (void) { return 11; }
d950bf
+int __attribute__((noinline,noclone)) foo12 (void) { return 12; }
d950bf
+int __attribute__((noinline,noclone)) foo13 (void) { return 13; }
d950bf
+int __attribute__((noinline,noclone)) foo14 (void) { return 14; }
d950bf
+int __attribute__((noinline,noclone)) foo15 (void) { return 15; }
d950bf
+int __attribute__((noinline,noclone)) foo16 (void) { return 16; }
d950bf
+int __attribute__((noinline,noclone)) foo17 (void) { return 17; }
d950bf
+int __attribute__((noinline,noclone)) foo18 (void) { return 18; }
d950bf
+int __attribute__((noinline,noclone)) foo19 (void) { return 19; }
d950bf
+int __attribute__((noinline,noclone)) foo20 (void) { return 20; }
d950bf
+
d950bf
+
d950bf
+int __attribute__((noinline,noclone))
d950bf
+bar (int a)
d950bf
+{
d950bf
+  int ret = 0;
d950bf
+
d950bf
+  switch (a)
d950bf
+    {
d950bf
+    case 1: ret = foo1 (); break;
d950bf
+    case 2: ret = foo2 (); break;
d950bf
+    case 3: ret = foo3 (); break;
d950bf
+    case 4: ret = foo4 (); break;
d950bf
+    case 5: ret = foo5 (); break;
d950bf
+    case 6: ret = foo6 (); break;
d950bf
+    case 7: ret = foo7 (); break;
d950bf
+    case 8: ret = foo8 (); break;
d950bf
+    case 9: ret = foo9 (); break;
d950bf
+    case 10: ret = foo10 (); break;
d950bf
+    case 11: ret = foo11 (); break;
d950bf
+    case 12: ret = foo12 (); break;
d950bf
+    case 13: ret = foo13 (); break;
d950bf
+    case 14: ret = foo14 (); break;
d950bf
+    case 15: ret = foo15 (); break;
d950bf
+    case 16: ret = foo16 (); break;
d950bf
+    case 17: ret = foo17 (); break;
d950bf
+    case 18: ret = foo18 (); break;
d950bf
+    case 19: ret = foo19 (); break;
d950bf
+    case 20: ret = foo20 (); break;
d950bf
+    default:
d950bf
+      __builtin_abort ();
d950bf
+    }
d950bf
+
d950bf
+  return ret;
d950bf
+}
d950bf
+
d950bf
+int
d950bf
+main ()
d950bf
+{
d950bf
+  if (bar (3) != 3)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 1 x bar
d950bf
+/* { dg-final { scan-assembler-times "exrl" 1 } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */
d950bf
diff -Nrup gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c
d950bf
--- gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c	1969-12-31 17:00:00.000000000 -0700
d950bf
+++ gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c	2018-03-27 09:33:58.837861529 -0600
d950bf
@@ -0,0 +1,78 @@
d950bf
+/* { dg-do run } */
d950bf
+/* { dg-options "-O3 -march=z900 -mzarch --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
d950bf
+
d950bf
+/* case-values-threshold will be set to 20 by the back-end when jump
d950bf
+   thunk are requested.  */
d950bf
+
d950bf
+int __attribute__((noinline,noclone)) foo1 (void) { return 1; }
d950bf
+int __attribute__((noinline,noclone)) foo2 (void) { return 2; }
d950bf
+int __attribute__((noinline,noclone)) foo3 (void) { return 3; }
d950bf
+int __attribute__((noinline,noclone)) foo4 (void) { return 4; }
d950bf
+int __attribute__((noinline,noclone)) foo5 (void) { return 5; }
d950bf
+int __attribute__((noinline,noclone)) foo6 (void) { return 6; }
d950bf
+int __attribute__((noinline,noclone)) foo7 (void) { return 7; }
d950bf
+int __attribute__((noinline,noclone)) foo8 (void) { return 8; }
d950bf
+int __attribute__((noinline,noclone)) foo9 (void) { return 9; }
d950bf
+int __attribute__((noinline,noclone)) foo10 (void) { return 10; }
d950bf
+int __attribute__((noinline,noclone)) foo11 (void) { return 11; }
d950bf
+int __attribute__((noinline,noclone)) foo12 (void) { return 12; }
d950bf
+int __attribute__((noinline,noclone)) foo13 (void) { return 13; }
d950bf
+int __attribute__((noinline,noclone)) foo14 (void) { return 14; }
d950bf
+int __attribute__((noinline,noclone)) foo15 (void) { return 15; }
d950bf
+int __attribute__((noinline,noclone)) foo16 (void) { return 16; }
d950bf
+int __attribute__((noinline,noclone)) foo17 (void) { return 17; }
d950bf
+int __attribute__((noinline,noclone)) foo18 (void) { return 18; }
d950bf
+int __attribute__((noinline,noclone)) foo19 (void) { return 19; }
d950bf
+int __attribute__((noinline,noclone)) foo20 (void) { return 20; }
d950bf
+
d950bf
+
d950bf
+int __attribute__((noinline,noclone))
d950bf
+bar (int a)
d950bf
+{
d950bf
+  int ret = 0;
d950bf
+
d950bf
+  switch (a)
d950bf
+    {
d950bf
+    case 1: ret = foo1 (); break;
d950bf
+    case 2: ret = foo2 (); break;
d950bf
+    case 3: ret = foo3 (); break;
d950bf
+    case 4: ret = foo4 (); break;
d950bf
+    case 5: ret = foo5 (); break;
d950bf
+    case 6: ret = foo6 (); break;
d950bf
+    case 7: ret = foo7 (); break;
d950bf
+    case 8: ret = foo8 (); break;
d950bf
+    case 9: ret = foo9 (); break;
d950bf
+    case 10: ret = foo10 (); break;
d950bf
+    case 11: ret = foo11 (); break;
d950bf
+    case 12: ret = foo12 (); break;
d950bf
+    case 13: ret = foo13 (); break;
d950bf
+    case 14: ret = foo14 (); break;
d950bf
+    case 15: ret = foo15 (); break;
d950bf
+    case 16: ret = foo16 (); break;
d950bf
+    case 17: ret = foo17 (); break;
d950bf
+    case 18: ret = foo18 (); break;
d950bf
+    case 19: ret = foo19 (); break;
d950bf
+    case 20: ret = foo20 (); break;
d950bf
+    default:
d950bf
+      __builtin_abort ();
d950bf
+    }
d950bf
+
d950bf
+  return ret;
d950bf
+}
d950bf
+
d950bf
+int
d950bf
+main ()
d950bf
+{
d950bf
+  if (bar (3) != 3)
d950bf
+    __builtin_abort ();
d950bf
+
d950bf
+  return 0;
d950bf
+}
d950bf
+
d950bf
+/* 1 x bar
d950bf
+/* { dg-final { scan-assembler-times "ex\t" 1 } } */
d950bf
+
d950bf
+/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */
d950bf
+/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */