+#ifdef HAVE_GAS_HIDDEN
+# define USE_HIDDEN_LINKONCE 1
+#else
+# define USE_HIDDEN_LINKONCE 0
+#endif
+
+/* Output an indirect branch trampoline for target register REGNO. */
+
+static void
+s390_output_indirect_thunk_function (unsigned int regno, bool z10_p)
+{
+ tree decl;
+ char thunk_label[32];
+ int i;
+
+ if (z10_p)
+ sprintf (thunk_label, TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL, regno);
+ else
+ sprintf (thunk_label, TARGET_INDIRECT_BRANCH_THUNK_NAME_EX,
+ INDIRECT_BRANCH_THUNK_REGNUM, regno);
+
+ decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
+ get_identifier (thunk_label),
+ build_function_type_list (void_type_node, NULL_TREE));
+ DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL,
+ NULL_TREE, void_type_node);
+ TREE_PUBLIC (decl) = 1;
+ TREE_STATIC (decl) = 1;
+ DECL_IGNORED_P (decl) = 1;
+
+ if (USE_HIDDEN_LINKONCE)
+ {
+ cgraph_node::create (decl)->set_comdat_group (DECL_ASSEMBLER_NAME (decl));
+
+ targetm.asm_out.unique_section (decl, 0);
+ switch_to_section (get_named_section (decl, NULL, 0));
+
+ targetm.asm_out.globalize_label (asm_out_file, thunk_label);
+ fputs ("\t.hidden\t", asm_out_file);
+ assemble_name (asm_out_file, thunk_label);
+ putc ('\n', asm_out_file);
+ ASM_DECLARE_FUNCTION_NAME (asm_out_file, thunk_label, decl);
+ }
+ else
+ {
+ switch_to_section (text_section);
+ ASM_OUTPUT_LABEL (asm_out_file, thunk_label);
+ }
+
+ DECL_INITIAL (decl) = make_node (BLOCK);
+ current_function_decl = decl;
+ allocate_struct_function (decl, false);
+ init_function_start (decl);
+ cfun->is_thunk = true;
+ first_function_block_is_cold = false;
+ final_start_function (emit_barrier (), asm_out_file, 1);
+
+ /* This makes CFI at least usable for indirect jumps.
+
+ Stopping in the thunk: backtrace will point to the thunk target
+ is if it was interrupted by a signal. For a call this means that
+ the call chain will be: caller->callee->thunk */
+ if (flag_asynchronous_unwind_tables)
+ {
+ fputs ("\t.cfi_signal_frame\n", asm_out_file);
+ fprintf (asm_out_file, "\t.cfi_return_column %d\n", regno);
+ for (i = 0; i < FPR15_REGNUM; i++)
+ fprintf (asm_out_file, "\t.cfi_same_value %s\n", reg_names[i]);
+ }
+
+ if (z10_p)
+ {
+ /* exrl 0,1f */
+
+ /* We generate a thunk for z10 compiled code although z10 is
+ currently not enabled. Tell the assembler to accept the
+ instruction. */
+ if (!TARGET_CPU_Z10)
+ {
+ fputs ("\t.machine push\n", asm_out_file);
+ fputs ("\t.machine z10\n", asm_out_file);
+ }
+ /* We use exrl even if -mzarch hasn't been specified on the
+ command line so we have to tell the assembler to accept
+ it. */
+ if (!TARGET_ZARCH)
+ fputs ("\t.machinemode zarch\n", asm_out_file);
+
+ fputs ("\texrl\t0,1f\n", asm_out_file);
+
+ if (!TARGET_ZARCH)
+ fputs ("\t.machinemode esa\n", asm_out_file);
+
+ if (!TARGET_CPU_Z10)
+ fputs ("\t.machine pop\n", asm_out_file);
+ }
+ else if (TARGET_CPU_ZARCH)
+ {
+ /* larl %r1,1f */
+ fprintf (asm_out_file, "\tlarl\t%%r%d,1f\n",
+ INDIRECT_BRANCH_THUNK_REGNUM);
+
+ /* ex 0,0(%r1) */
+ fprintf (asm_out_file, "\tex\t0,0(%%r%d)\n",
+ INDIRECT_BRANCH_THUNK_REGNUM);
+ }
+ else
+ gcc_unreachable ();
+
+ /* 0: j 0b */
+ fputs ("0:\tj\t0b\n", asm_out_file);
+
+ /* 1: br <regno> */
+ fprintf (asm_out_file, "1:\tbr\t%%r%d\n", regno);
+
+ final_end_function ();
+ init_insn_lengths ();
+ free_after_compilation (cfun);
+ set_cfun (NULL);
+ current_function_decl = NULL;
+}
+
+/* Implement the asm.code_end target hook. */
+
+static void
+s390_code_end (void)
+{
+ int i;
+
+ for (i = 1; i < 16; i++)
+ {
+ if (indirect_branch_z10thunk_mask & (1 << i))
+ s390_output_indirect_thunk_function (i, true);
+
+ if (indirect_branch_prez10thunk_mask & (1 << i))
+ s390_output_indirect_thunk_function (i, false);
+ }
+
+ if (TARGET_INDIRECT_BRANCH_TABLE)
+ {
+ int o;
+ int i;
+
+ for (o = 0; o < INDIRECT_BRANCH_NUM_OPTIONS; o++)
+ {
+ if (indirect_branch_table_label_no[o] == 0)
+ continue;
+
+ switch_to_section (get_section (indirect_branch_table_name[o],
+ 0,
+ NULL_TREE));
+ for (i = 0; i < indirect_branch_table_label_no[o]; i++)
+ {
+ char label_start[32];
+
+ ASM_GENERATE_INTERNAL_LABEL (label_start,
+ indirect_branch_table_label[o], i);
+
+ fputs ("\t.long\t", asm_out_file);
+ assemble_name_raw (asm_out_file, label_start);
+ fputs ("-.\n", asm_out_file);
+ }
+ switch_to_section (current_function_section ());
+ }
+ }
+}
+
+/* Implement the TARGET_CASE_VALUES_THRESHOLD target hook. */
+
+unsigned int
+s390_case_values_threshold (void)
+{
+ /* Disabling branch prediction for indirect jumps makes jump tables
+ much more expensive. */
+ if (TARGET_INDIRECT_BRANCH_NOBP_JUMP)
+ return 20;
+
+ return default_case_values_threshold ();
+}
+