summaryrefslogtreecommitdiff
path: root/gcc48-rh1552021.patch
diff options
context:
space:
mode:
Diffstat (limited to 'gcc48-rh1552021.patch')
-rw-r--r--gcc48-rh1552021.patch1966
1 files changed, 1966 insertions, 0 deletions
diff --git a/gcc48-rh1552021.patch b/gcc48-rh1552021.patch
new file mode 100644
index 0000000..c931115
--- /dev/null
+++ b/gcc48-rh1552021.patch
@@ -0,0 +1,1966 @@
+diff -Nrup gcc/config/s390/s390.c gcc/config/s390/s390.c
+--- gcc/config/s390/s390.c 2018-03-27 09:33:20.158140823 -0600
++++ gcc/config/s390/s390.c 2018-03-27 09:33:58.826861609 -0600
+@@ -958,6 +958,35 @@ s390_expand_builtin (tree exp, rtx targe
+ }
+
+
++/* 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_frame_layout.first_save_gpr <= RETURN_REGNUM
++ && cfun_frame_layout.last_save_gpr >= RETURN_REGNUM);
++}
++
+ static const int s390_hotpatch_hw_max = 1000000;
+ static int s390_hotpatch_hw_before_label = 0;
+ static int s390_hotpatch_hw_after_label = 0;
+@@ -2669,6 +2698,34 @@ s390_option_override (void)
+ if (TARGET_64BIT && !TARGET_ZARCH)
+ error ("64-bit ABI not supported in ESA/390 mode");
+
++ if (s390_indirect_branch != indirect_branch_keep)
++ {
++ if (!global_options_set.x_s390_indirect_branch_call)
++ s390_indirect_branch_call = s390_indirect_branch;
++
++ if (!global_options_set.x_s390_indirect_branch_jump)
++ s390_indirect_branch_jump = s390_indirect_branch;
++ }
++
++ if (s390_function_return != indirect_branch_keep)
++ {
++ if (!global_options_set.x_s390_function_return_reg)
++ s390_function_return_reg = s390_function_return;
++
++ if (!global_options_set.x_s390_function_return_mem)
++ s390_function_return_mem = s390_function_return;
++ }
++
++ if (!TARGET_CPU_ZARCH)
++ {
++ if (s390_indirect_branch_call != indirect_branch_keep
++ || s390_indirect_branch_jump != indirect_branch_keep)
++ error ("-mindirect-branch* options require -march=z900 or higher");
++ if (s390_function_return_reg != indirect_branch_keep
++ || s390_function_return_mem != indirect_branch_keep)
++ error ("-mfunction-return* options require -march=z900 or higher");
++ }
++
+ /* Use hardware DFP if available and not explicitly disabled by
+ user. E.g. with -m31 -march=z10 -mzarch */
+ if (!(target_flags_explicit & MASK_HARD_DFP) && TARGET_DFP)
+@@ -10873,7 +10930,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)
+@@ -11023,8 +11079,14 @@ s390_emit_epilogue (bool sibcall)
+ && cfun_frame_layout.last_restore_gpr > RETURN_REGNUM))
+ {
+ 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,
+@@ -11054,16 +11116,7 @@ s390_emit_epilogue (bool sibcall)
+ }
+
+ 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));
+ }
+
+
+@@ -12371,6 +12424,84 @@ s390_output_mi_thunk (FILE *file, tree t
+ 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_function_return_reg != indirect_branch_keep
++ && !s390_return_addr_from_memory ())
++ option = s390_opt_function_return_reg;
++
++ if (s390_function_return_mem != indirect_branch_keep
++ && s390_return_addr_from_memory ())
++ option = s390_opt_function_return_mem;
++ }
++ 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
++ && s390_indirect_branch_jump == indirect_branch_thunk)
++ || (option == s390_opt_indirect_branch_call
++ && s390_indirect_branch_call == indirect_branch_thunk)
++ || (option == s390_opt_function_return_reg
++ && s390_function_return_reg == indirect_branch_thunk)
++ || (option == s390_opt_function_return_mem
++ && s390_function_return_mem == indirect_branch_thunk))
++ {
++ if (TARGET_CPU_Z10)
++ indirect_branch_z10thunk_mask |= (1 << regno);
++ else
++ indirect_branch_prez10thunk_mask |= (1 << regno);
++ }
++}
++
+ static bool
+ s390_valid_pointer_mode (enum machine_mode mode)
+ {
+@@ -12476,6 +12607,14 @@ s390_function_ok_for_sibcall (tree decl,
+ 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. */
+@@ -12509,9 +12648,13 @@ s390_emit_call (rtx addr_location, rtx t
+ {
+ bool plt_call = false;
+ rtx 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)
+@@ -12520,7 +12663,7 @@ s390_emit_call (rtx addr_location, rtx t
+ replace the symbol itself with the PLT stub. */
+ if (flag_pic && !SYMBOL_REF_LOCAL_P (addr_location))
+ {
+- if (retaddr_reg != NULL_RTX)
++ if (TARGET_64BIT || retaddr_reg != NULL_RTX)
+ {
+ addr_location = gen_rtx_UNSPEC (Pmode,
+ gen_rtvec (1, addr_location),
+@@ -12563,26 +12706,57 @@ s390_emit_call (rtx addr_location, rtx t
+ 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 (VOIDmode, result_reg, call);
++ *call = gen_rtx_SET (VOIDmode, 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++;
+
+- call = gen_rtx_PARALLEL (VOIDmode, vec);
++ 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, 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)
+@@ -13819,6 +13993,190 @@ s390_asm_file_end (void)
+ file_end_indicate_exec_stack ();
+ }
+
++#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)
++ {
++ DECL_COMDAT_GROUP (decl) = 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.
++
++ jumps: stopping in the thunk: backtrace will point to the thunk
++ target is if it was interrupted by a signal
++
++ calls: Instead of caller->thunk the backtrace 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 table
++ 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
+@@ -14015,6 +14373,12 @@ s390_asm_file_end (void)
+ #undef TARGET_ASM_FILE_END
+ #define TARGET_ASM_FILE_END s390_asm_file_end
+
++#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 -Nrup gcc/config/s390/s390.h gcc/config/s390/s390.h
+--- gcc/config/s390/s390.h 2018-03-27 09:33:19.762143683 -0600
++++ gcc/config/s390/s390.h 2018-03-27 09:33:58.827861602 -0600
+@@ -1006,4 +1006,37 @@ extern const int processor_flags_table[]
+ s390_register_target_pragmas (); \
+ } while (0)
+
++
++#define TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION \
++ (s390_function_return_reg != indirect_branch_keep \
++ || s390_function_return_mem != indirect_branch_keep)
++
++#define TARGET_INDIRECT_BRANCH_NOBP_RET \
++ ((s390_function_return_reg != indirect_branch_keep \
++ && !s390_return_addr_from_memory ()) \
++ || (s390_function_return_mem != indirect_branch_keep \
++ && s390_return_addr_from_memory ()))
++
++#define TARGET_INDIRECT_BRANCH_NOBP_JUMP \
++ (s390_indirect_branch_jump != indirect_branch_keep)
++
++#define TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK \
++ (s390_indirect_branch_jump == indirect_branch_thunk \
++ || s390_indirect_branch_jump == indirect_branch_thunk_extern)
++
++#define TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK \
++ (s390_indirect_branch_jump == indirect_branch_thunk_inline)
++
++#define TARGET_INDIRECT_BRANCH_NOBP_CALL \
++ (s390_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 -Nrup gcc/config/s390/s390.md gcc/config/s390/s390.md
+--- gcc/config/s390/s390.md 2018-03-27 09:33:19.763143675 -0600
++++ gcc/config/s390/s390.md 2018-03-27 09:33:58.831861573 -0600
+@@ -285,6 +285,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.
+@@ -304,6 +306,7 @@
+ ; Floating point registers.
+ (FPR0_REGNUM 16)
+ (FPR2_REGNUM 18)
++ (FPR15_REGNUM 31)
+ (VR0_REGNUM 16)
+ (VR16_REGNUM 38)
+ (VR23_REGNUM 45)
+@@ -402,7 +405,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,br,bas,bc,bcr,bcr_flush,unknown" (const_string "unknown"))
+
+ ;; Length in bytes.
+
+@@ -8436,7 +8442,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";
+@@ -8446,6 +8452,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")])
+
+@@ -8499,7 +8508,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";
+@@ -8509,6 +8518,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")])
+
+@@ -9005,29 +9017,125 @@
+ ; indirect-jump instruction pattern(s).
+ ;
+
+-(define_insn "indirect_jump"
+- [(set (pc) (match_operand 0 "address_operand" "ZQZR"))]
+- ""
++(define_expand "indirect_jump"
++ [(set (pc) (match_operand 0 "address_operand" "ZQZR"))]
++ ""
++{
++ 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;
++ }
++})
++
++(define_insn "*indirect_jump"
++ [(set (pc)
++ (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 "type" "branch")
+- (set_attr "atype" "agen")])
++ [(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 "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")])
+
+ ;
+ ; casesi instruction pattern(s).
+ ;
+
+-(define_insn "casesi_jump"
+- [(set (pc) (match_operand 0 "address_operand" "ZQZR"))
+- (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;
++ }
++})
++
++(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";
+@@ -9035,11 +9143,50 @@
+ 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 "RR") (const_string "RX")))
++ (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")])
++
++
+ (define_expand "casesi"
+ [(match_operand:SI 0 "general_operand" "")
+ (match_operand:SI 1 "general_operand" "")
+@@ -9141,11 +9288,30 @@
+
+ (define_insn "*sibcall_br"
+ [(call (mem:QI (reg SIBCALL_REGNUM))
+- (match_operand 0 "const_int_operand" "n"))]
++ (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")
++ && GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode"
++{
++ 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")])
+
+@@ -9185,8 +9351,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")])
+
+@@ -9252,7 +9437,9 @@
+ [(call (mem:QI (match_operand 0 "address_operand" "ZQZR"))
+ (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";
+@@ -9262,6 +9449,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")])
+@@ -9313,7 +9544,10 @@
+ (call (mem:QI (match_operand 1 "address_operand" "ZQZR"))
+ (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";
+@@ -9323,6 +9557,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")])
+@@ -10056,15 +10338,78 @@
+ ""
+ "s390_emit_epilogue (true); DONE;")
+
+-(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")])
++
+
+ ;; Instruction definition to extend a 31-bit pointer into a 64-bit
+ ;; pointer. This is used for compatibility.
+diff -Nrup gcc/config/s390/s390.opt gcc/config/s390/s390.opt
+--- gcc/config/s390/s390.opt 2018-03-27 09:33:19.763143675 -0600
++++ gcc/config/s390/s390.opt 2018-03-27 09:33:58.832861566 -0600
+@@ -175,3 +175,59 @@ Target Report Joined RejectNegative UInt
+ Set the branch costs for conditional branch instructions. Reasonable
+ values are small, non-negative integers. The default branch cost is
+ 1.
++
++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-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 -Nrup gcc/config/s390/s390-opts.h gcc/config/s390/s390-opts.h
+--- gcc/config/s390/s390-opts.h 2018-03-27 09:33:19.764143668 -0600
++++ gcc/config/s390/s390-opts.h 2018-03-27 09:33:58.821861645 -0600
+@@ -39,4 +39,12 @@ 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_extern
++};
++
+ #endif
+diff -Nrup gcc/config/s390/s390-protos.h gcc/config/s390/s390-protos.h
+--- gcc/config/s390/s390-protos.h 2018-03-27 09:33:19.764143668 -0600
++++ gcc/config/s390/s390-protos.h 2018-03-27 09:33:58.821861645 -0600
+@@ -41,6 +41,7 @@ extern void s390_set_has_landing_pad_p (
+ extern bool s390_hard_regno_mode_ok (unsigned int, enum machine_mode);
+ extern bool s390_hard_regno_rename_ok (unsigned int, unsigned int);
+ extern int s390_class_max_nregs (enum reg_class, enum machine_mode);
++extern bool s390_return_addr_from_memory(void);
+ extern int s390_cannot_change_mode_class (enum machine_mode, enum machine_mode,
+ enum reg_class);
+ extern bool s390_function_arg_vector (enum machine_mode, const_tree);
+@@ -124,6 +125,18 @@ extern int s390_compare_and_branch_condi
+ 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 -Nrup gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c
+--- gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c 2018-03-27 09:33:58.832861566 -0600
+@@ -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 -Nrup gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c
+--- gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c 2018-03-27 09:33:58.833861558 -0600
+@@ -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 -Nrup gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c
+--- gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c 2018-03-27 09:33:58.833861558 -0600
+@@ -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 -Nrup gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c
+--- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c 2018-03-27 09:33:58.833861558 -0600
+@@ -0,0 +1,45 @@
++/* { dg-do compile } */
++/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk-extern -mindirect-branch-table" } */
++
++/* 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 -Nrup gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c
+--- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c 2018-03-27 09:33:58.834861551 -0600
+@@ -0,0 +1,42 @@
++/* { dg-do run } */
++/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
++
++/* 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 -Nrup gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c
+--- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c 2018-03-27 09:33:58.834861551 -0600
+@@ -0,0 +1,42 @@
++/* { dg-do run } */
++/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
++
++/* 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 -Nrup gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c
+--- gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c 2018-03-27 09:33:58.834861551 -0600
+@@ -0,0 +1,44 @@
++/* { 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;
++}
++
++/* 1 x foo, 1 x main
++/* { 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-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 -Nrup gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c
+--- gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c 2018-03-27 09:33:58.835861544 -0600
+@@ -0,0 +1,41 @@
++/* { 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;
++}
++
++/* 1 x foo, 1 x main
++/* { 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 -Nrup gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c
+--- gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c 2018-03-27 09:33:58.835861544 -0600
+@@ -0,0 +1,42 @@
++/* { 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 } } */
++
++/* { 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 -Nrup gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c
+--- gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c 2018-03-27 09:33:58.835861544 -0600
+@@ -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 -Nrup gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c
+--- gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c 2018-03-27 09:33:58.836861537 -0600
+@@ -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 -Nrup gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c
+--- gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c 2018-03-27 09:33:58.836861537 -0600
+@@ -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 -Nrup gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c
+--- gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c 2018-03-27 09:33:58.836861537 -0600
+@@ -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 -Nrup gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c
+--- gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c 1969-12-31 17:00:00.000000000 -0700
++++ gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c 2018-03-27 09:33:58.837861529 -0600
+@@ -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" } } */