1908 lines
54 KiB
Diff
1908 lines
54 KiB
Diff
--- gcc/config/s390/linux.h
|
|
+++ gcc/config/s390/linux.h
|
|
@@ -61,7 +61,8 @@ along with GCC; see the file COPYING3. If not see
|
|
/* Target specific assembler settings. */
|
|
|
|
#undef ASM_SPEC
|
|
-#define ASM_SPEC "%{m31&m64}%{mesa&mzarch}%{march=*}"
|
|
+#define ASM_SPEC "%{m31&m64}%{mesa&mzarch}%{march=*} \
|
|
+ %{mindirect-branch*|mfunction-return*:-mzarch}"
|
|
|
|
|
|
/* Target specific linker settings. */
|
|
--- gcc/config/s390/s390-protos.h
|
|
+++ gcc/config/s390/s390-protos.h
|
|
@@ -46,6 +46,7 @@ extern void s390_set_has_landing_pad_p (bool);
|
|
extern bool s390_hard_regno_mode_ok (unsigned int, enum machine_mode);
|
|
extern bool s390_hard_regno_rename_ok (unsigned int, unsigned int);
|
|
extern bool s390_class_max_nregs (enum reg_class, enum machine_mode);
|
|
+extern bool s390_return_addr_from_memory(void);
|
|
|
|
#ifdef RTX_CODE
|
|
extern int s390_extra_constraint_str (rtx, int, const char *);
|
|
@@ -121,6 +122,17 @@ extern int s390_branch_condition_mask (rtx);
|
|
extern int s390_compare_and_branch_condition_mask (rtx);
|
|
extern void s390_asm_declare_function_name (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);
|
|
+
|
|
#endif /* RTX_CODE */
|
|
|
|
#ifdef TREE_CODE
|
|
--- gcc/config/s390/s390.c
|
|
+++ gcc/config/s390/s390.c
|
|
@@ -393,6 +393,42 @@ struct machine_function GTY(())
|
|
bytes on a z10 (or higher) CPU. */
|
|
#define PREDICT_DISTANCE (TARGET_Z10 ? 384 : 2048)
|
|
|
|
+/* Masks per jump target register indicating which thunk need to be
|
|
+ generated. */
|
|
+static GTY(()) int indirect_branch_prez10thunk_mask = 0;
|
|
+static GTY(()) int indirect_branch_z10thunk_mask = 0;
|
|
+
|
|
+#define INDIRECT_BRANCH_NUM_OPTIONS 4
|
|
+
|
|
+enum s390_indirect_branch_option
|
|
+ {
|
|
+ s390_opt_indirect_branch_jump = 0,
|
|
+ s390_opt_indirect_branch_call,
|
|
+ s390_opt_function_return_reg,
|
|
+ s390_opt_function_return_mem
|
|
+ };
|
|
+
|
|
+enum indirect_branch s390_indirect_branch;
|
|
+enum indirect_branch s390_indirect_branch_call;
|
|
+enum indirect_branch s390_indirect_branch_jump;
|
|
+enum indirect_branch s390_function_return;
|
|
+enum indirect_branch s390_function_return_reg;
|
|
+enum indirect_branch s390_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 (void)
|
|
+{
|
|
+ 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;
|
|
@@ -1705,6 +1742,39 @@ s390_handle_option (size_t code, const char *arg, int value ATTRIBUTE_UNUSED)
|
|
}
|
|
}
|
|
|
|
+/* Convert the string argument of -mindirect-branch*= or
|
|
+ -mfunction-return*= into an enum value for further processing. */
|
|
+
|
|
+static void
|
|
+s390_indirect_branch_str_to_enum (const char *str, enum indirect_branch *opt)
|
|
+{
|
|
+ size_t i;
|
|
+ static struct
|
|
+ {
|
|
+ const char *const name;
|
|
+ enum indirect_branch value;
|
|
+ } strtoenum[] =
|
|
+ {
|
|
+ { "keep", indirect_branch_keep },
|
|
+ { "thunk", indirect_branch_thunk },
|
|
+ { "thunk-extern", indirect_branch_thunk_extern }
|
|
+ };
|
|
+
|
|
+ if (!str)
|
|
+ {
|
|
+ *opt = indirect_branch_unset;
|
|
+ return;
|
|
+ }
|
|
+
|
|
+ for (i = 0; i < ARRAY_SIZE (strtoenum); i++)
|
|
+ if (strcmp (strtoenum[i].name, str) == 0)
|
|
+ {
|
|
+ *opt = strtoenum[i].value;
|
|
+ return;
|
|
+ }
|
|
+ error ("unknown option value: %qs", str);
|
|
+}
|
|
+
|
|
void
|
|
override_options (void)
|
|
{
|
|
@@ -1720,6 +1790,53 @@ override_options (void)
|
|
target_flags &= ~MASK_ZARCH;
|
|
}
|
|
|
|
+ s390_indirect_branch_str_to_enum (s390_indirect_branch_string,
|
|
+ &s390_indirect_branch);
|
|
+ s390_indirect_branch_str_to_enum (s390_indirect_branch_call_string,
|
|
+ &s390_indirect_branch_call);
|
|
+ s390_indirect_branch_str_to_enum (s390_indirect_branch_jump_string,
|
|
+ &s390_indirect_branch_jump);
|
|
+ s390_indirect_branch_str_to_enum (s390_function_return_string,
|
|
+ &s390_function_return);
|
|
+ s390_indirect_branch_str_to_enum (s390_function_return_reg_string,
|
|
+ &s390_function_return_reg);
|
|
+ s390_indirect_branch_str_to_enum (s390_function_return_mem_string,
|
|
+ &s390_function_return_mem);
|
|
+
|
|
+ if (s390_indirect_branch > indirect_branch_keep)
|
|
+ {
|
|
+ if (s390_indirect_branch_call == indirect_branch_unset)
|
|
+ s390_indirect_branch_call = s390_indirect_branch;
|
|
+
|
|
+ if (s390_indirect_branch_jump == indirect_branch_unset)
|
|
+ s390_indirect_branch_jump = s390_indirect_branch;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if (s390_indirect_branch_call == indirect_branch_unset)
|
|
+ s390_indirect_branch_call = indirect_branch_keep;
|
|
+
|
|
+ if (s390_indirect_branch_jump == indirect_branch_unset)
|
|
+ s390_indirect_branch_jump = indirect_branch_keep;
|
|
+ }
|
|
+
|
|
+ if (s390_function_return > indirect_branch_keep)
|
|
+ {
|
|
+ if (s390_function_return_reg == indirect_branch_unset)
|
|
+ s390_function_return_reg = s390_function_return;
|
|
+
|
|
+ if (s390_function_return_mem == indirect_branch_unset)
|
|
+ s390_function_return_mem = s390_function_return;
|
|
+ }
|
|
+ else
|
|
+ {
|
|
+ if (s390_function_return_reg == indirect_branch_unset)
|
|
+ s390_function_return_reg = indirect_branch_keep;
|
|
+
|
|
+ if (s390_function_return_mem == indirect_branch_unset)
|
|
+ s390_function_return_mem = indirect_branch_keep;
|
|
+ }
|
|
+
|
|
/* Determine processor architectural level. */
|
|
if (!s390_arch_string)
|
|
{
|
|
@@ -8289,7 +8418,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)
|
|
@@ -8439,8 +8567,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 (frame_pointer,
|
|
@@ -8469,16 +8603,7 @@ s390_emit_epilogue (bool sibcall)
|
|
}
|
|
|
|
if (! sibcall)
|
|
- {
|
|
-
|
|
- /* Return to caller. */
|
|
-
|
|
- p = rtvec_alloc (2);
|
|
-
|
|
- RTVEC_ELT (p, 0) = gen_rtx_RETURN (VOIDmode);
|
|
- 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));
|
|
}
|
|
|
|
|
|
@@ -9596,6 +9721,84 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
|
|
final_end_function ();
|
|
}
|
|
|
|
+/* Output either an indirect jump or a an indirect call
|
|
+ (RETURN_ADDR_REGNO != INVALID_REGNUM) with target register REGNO
|
|
+ using a branch trampoline disabling branch target prediction. */
|
|
+
|
|
+void
|
|
+s390_indirect_branch_via_thunk (unsigned int regno,
|
|
+ unsigned int return_addr_regno,
|
|
+ rtx comparison_operator,
|
|
+ enum s390_indirect_branch_type type)
|
|
+{
|
|
+ enum s390_indirect_branch_option option;
|
|
+
|
|
+ if (type == s390_indirect_branch_type_return)
|
|
+ {
|
|
+ if (s390_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)
|
|
{
|
|
@@ -9674,6 +9877,14 @@ s390_function_ok_for_sibcall (tree decl, tree exp)
|
|
if (!TARGET_64BIT && flag_pic && decl && !targetm.binds_local_p (decl))
|
|
return false;
|
|
|
|
+ /* The thunks for indirect branches require r1 if no exrl is
|
|
+ available. r1 might not be available when doing a sibling
|
|
+ call. */
|
|
+ if (TARGET_INDIRECT_BRANCH_NOBP_CALL
|
|
+ && !TARGET_CPU_Z10
|
|
+ && !decl)
|
|
+ return false;
|
|
+
|
|
/* Register 6 on s390 is available as an argument register but unfortunately
|
|
"caller saved". This makes functions needing this register for arguments
|
|
not suitable for sibcalls. */
|
|
@@ -9707,9 +9918,13 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
|
|
{
|
|
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)
|
|
@@ -9747,26 +9962,57 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
|
|
addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM);
|
|
}
|
|
|
|
+ if (TARGET_INDIRECT_BRANCH_NOBP_CALL
|
|
+ && GET_CODE (addr_location) != SYMBOL_REF
|
|
+ && !plt_call)
|
|
+ {
|
|
+ /* Indirect branch thunks require the target to be a single GPR. */
|
|
+ addr_location = force_reg (Pmode, addr_location);
|
|
+
|
|
+ /* Without exrl the indirect branch thunks need an additional
|
|
+ register for larl;ex */
|
|
+ if (!TARGET_CPU_Z10)
|
|
+ {
|
|
+ *clobber_thunk_reg = gen_rtx_REG (Pmode, INDIRECT_BRANCH_THUNK_REGNUM);
|
|
+ *clobber_thunk_reg = gen_rtx_CLOBBER (VOIDmode, *clobber_thunk_reg);
|
|
+ }
|
|
+ }
|
|
+
|
|
addr_location = gen_rtx_MEM (QImode, addr_location);
|
|
- call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);
|
|
+ *call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);
|
|
|
|
if (result_reg != NULL_RTX)
|
|
- call = gen_rtx_SET (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)
|
|
@@ -10582,6 +10828,155 @@ s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop)
|
|
}
|
|
}
|
|
|
|
+#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 (FUNCTION_DECL,
|
|
+ get_identifier (thunk_label),
|
|
+ build_function_type_list (void_type_node, NULL_TREE));
|
|
+ DECL_RESULT (decl) = build_decl (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_ONE_ONLY (decl) = 1;
|
|
+
|
|
+ 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 */
|
|
+ fputs ("\texrl\t0,1f\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 ());
|
|
+ }
|
|
+ }
|
|
+}
|
|
+
|
|
/* Initialize GCC target structure. */
|
|
|
|
#undef TARGET_ASM_ALIGNED_HI_OP
|
|
@@ -10712,6 +11107,9 @@ s390_loop_unroll_adjust (unsigned nunroll, struct loop *loop)
|
|
#undef TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P
|
|
#define TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P hook_bool_const_tree_true
|
|
|
|
+#undef TARGET_ASM_CODE_END
|
|
+#define TARGET_ASM_CODE_END s390_code_end
|
|
+
|
|
struct gcc_target targetm = TARGET_INITIALIZER;
|
|
|
|
#include "gt-s390.h"
|
|
--- gcc/config/s390/s390.h
|
|
+++ gcc/config/s390/s390.h
|
|
@@ -1034,4 +1034,50 @@ do { \
|
|
(TARGET_LONG_DISPLACEMENT? ((d) >= -524288 && (d) <= 524287) \
|
|
: ((d) >= 0 && (d) <= 4095))
|
|
|
|
+/* 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
|
|
+};
|
|
+
|
|
+extern enum indirect_branch s390_indirect_branch;
|
|
+extern enum indirect_branch s390_indirect_branch_call;
|
|
+extern enum indirect_branch s390_indirect_branch_jump;
|
|
+extern enum indirect_branch s390_function_return;
|
|
+extern enum indirect_branch s390_function_return_reg;
|
|
+extern enum indirect_branch s390_function_return_mem;
|
|
+
|
|
+#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_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
|
|
+
|
|
+#define CASE_VALUES_THRESHOLD (TARGET_INDIRECT_BRANCH_NOBP_JUMP ? 20 : 4)
|
|
+
|
|
#endif
|
|
--- gcc/config/s390/s390.md
|
|
+++ gcc/config/s390/s390.md
|
|
@@ -155,6 +155,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.
|
|
@@ -174,6 +176,7 @@
|
|
; Floating point registers.
|
|
(FPR0_REGNUM 16)
|
|
(FPR2_REGNUM 18)
|
|
+ (FPR15_REGNUM 31)
|
|
])
|
|
|
|
;;
|
|
@@ -7699,7 +7702,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";
|
|
@@ -7762,7 +7765,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";
|
|
@@ -8061,29 +8064,120 @@
|
|
; 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 "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 "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 "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";
|
|
@@ -8091,11 +8185,45 @@
|
|
return "b\t%a0";
|
|
}
|
|
[(set (attr "op_type")
|
|
- (if_then_else (match_operand 0 "register_operand" "")
|
|
- (const_string "RR") (const_string "RX")))
|
|
+ (if_then_else (match_operand 0 "register_operand" "")
|
|
+ (const_string "RR") (const_string "RX")))
|
|
(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 "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 "type" "branch")
|
|
+ (set_attr "atype" "agen")])
|
|
+
|
|
+
|
|
(define_expand "casesi"
|
|
[(match_operand:SI 0 "general_operand" "")
|
|
(match_operand:SI 1 "general_operand" "")
|
|
@@ -8197,14 +8325,33 @@
|
|
|
|
(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"
|
|
+ && GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode
|
|
+ && !TARGET_INDIRECT_BRANCH_NOBP_CALL"
|
|
"br\t%%r1"
|
|
[(set_attr "op_type" "RR")
|
|
(set_attr "type" "branch")
|
|
(set_attr "atype" "agen")])
|
|
|
|
+(define_insn "*sibcall_br_nobp"
|
|
+ [(call (mem:QI (reg SIBCALL_REGNUM))
|
|
+ (match_operand 0 "const_int_operand" "n"))]
|
|
+ "SIBLING_CALL_P (insn)
|
|
+ && GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode
|
|
+ && 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 "";
|
|
+}
|
|
+ [(set_attr "op_type" "RIL")
|
|
+ (set_attr "type" "branch")
|
|
+ (set_attr "atype" "agen")])
|
|
+
|
|
(define_insn "*sibcall_brc"
|
|
[(call (mem:QI (match_operand 0 "bras_sym_operand" "X"))
|
|
(match_operand 1 "const_int_operand" "n"))]
|
|
@@ -8240,12 +8387,32 @@
|
|
(call (mem:QI (reg SIBCALL_REGNUM))
|
|
(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"
|
|
+ && GET_MODE (XEXP (XEXP (XEXP (PATTERN (insn), 1), 0), 0)) == Pmode
|
|
+ && !TARGET_INDIRECT_BRANCH_NOBP_CALL"
|
|
+ "br\t%%r1";
|
|
[(set_attr "op_type" "RR")
|
|
(set_attr "type" "branch")
|
|
(set_attr "atype" "agen")])
|
|
|
|
+(define_insn "*sibcall_value_br_nobp"
|
|
+ [(set (match_operand 0 "" "")
|
|
+ (call (mem:QI (reg SIBCALL_REGNUM))
|
|
+ (match_operand 1 "const_int_operand" "n")))]
|
|
+ "SIBLING_CALL_P (insn)
|
|
+ && GET_MODE (XEXP (XEXP (XEXP (PATTERN (insn), 1), 0), 0)) == Pmode
|
|
+ && 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 "";
|
|
+}
|
|
+ [(set_attr "op_type" "RIL")
|
|
+ (set_attr "type" "branch")
|
|
+ (set_attr "atype" "agen")])
|
|
+
|
|
(define_insn "*sibcall_value_brc"
|
|
[(set (match_operand 0 "" "")
|
|
(call (mem:QI (match_operand 1 "bras_sym_operand" "X"))
|
|
@@ -8308,7 +8475,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";
|
|
@@ -8322,6 +8491,45 @@
|
|
(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 "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 "type" "jsr")
|
|
+ (set_attr "atype" "agen")
|
|
+ (set_attr "z196prop" "z196_cracked")])
|
|
+
|
|
;
|
|
; call_value instruction pattern(s).
|
|
;
|
|
@@ -8369,7 +8577,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";
|
|
@@ -8487,6 +8698,49 @@
|
|
(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 "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 "type" "jsr")
|
|
+ (set_attr "atype" "agen")
|
|
+ (set_attr "z196prop" "z196_cracked")])
|
|
+
|
|
;;
|
|
;;- Atomic operations
|
|
;;
|
|
@@ -8959,12 +9213,63 @@
|
|
""
|
|
"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" "NN")
|
|
+ (set_attr "length" "6")
|
|
+ (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" "NN")
|
|
+ (set_attr "length" "6")
|
|
(set_attr "type" "jsr")
|
|
(set_attr "atype" "agen")])
|
|
|
|
--- gcc/config/s390/s390.opt
|
|
+++ gcc/config/s390/s390.opt
|
|
@@ -111,3 +111,50 @@ Warn if a single function's framesize exceeds the given framesize
|
|
mzarch
|
|
Target Report RejectNegative Negative(mesa) Mask(ZARCH)
|
|
z/Architecture
|
|
+
|
|
+
|
|
+mindirect-branch=
|
|
+Target Report RejectNegative Joined Var(s390_indirect_branch_string)
|
|
+Wrap all indirect branches into execute in order to disable branch
|
|
+prediction. Possible values are keep, thunk, and thunk-extern.
|
|
+
|
|
+mindirect-branch-jump=
|
|
+Target Report RejectNegative Joined Var(s390_indirect_branch_jump_string)
|
|
+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. Possible values are keep, thunk, and thunk-extern.
|
|
+
|
|
+mindirect-branch-call=
|
|
+Target Report RejectNegative Joined Var(s390_indirect_branch_call_string)
|
|
+Wrap all indirect calls into execute in order to disable branch prediction.
|
|
+Possible values are keep, thunk, and thunk-extern.
|
|
+
|
|
+mfunction-return=
|
|
+Target Report RejectNegative Joined Var(s390_function_return_string)
|
|
+Wrap all indirect return branches into execute in order to disable branch
|
|
+prediction. Possible values are keep, thunk, and thunk-extern.
|
|
+
|
|
+mfunction-return-mem=
|
|
+Target Report RejectNegative Joined Var(s390_function_return_mem_string)
|
|
+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. Possible values are keep, thunk,
|
|
+and thunk-extern.
|
|
+
|
|
+mfunction-return-reg=
|
|
+Target Report RejectNegative Joined Var(s390_function_return_reg_string)
|
|
+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. Possible values are keep,
|
|
+thunk, and 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. Possible values are keep,
|
|
+thunk, and thunk-extern.
|
|
--- gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c
|
|
@@ -0,0 +1,59 @@
|
|
+/* { dg-do compile } */
|
|
+/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-call=thunk-extern -mindirect-branch-table" } */
|
|
+
|
|
+int gl;
|
|
+
|
|
+void __attribute__((noinline))
|
|
+foo (int a)
|
|
+{
|
|
+ gl = a + 40;
|
|
+}
|
|
+
|
|
+int __attribute__((noinline))
|
|
+foo_value (int a)
|
|
+{
|
|
+ return a + 40;
|
|
+}
|
|
+
|
|
+void* __attribute__((noinline))
|
|
+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" } } */
|
|
--- gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c
|
|
@@ -0,0 +1,56 @@
|
|
+/* { dg-do run } */
|
|
+/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-call=thunk -mindirect-branch-table" } */
|
|
+
|
|
+int gl;
|
|
+
|
|
+void __attribute__((noinline))
|
|
+foo (int a)
|
|
+{
|
|
+ gl = a + 40;
|
|
+}
|
|
+
|
|
+int __attribute__((noinline))
|
|
+foo_value (int a)
|
|
+{
|
|
+ return a + 40;
|
|
+}
|
|
+
|
|
+void* __attribute__((noinline))
|
|
+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" } } */
|
|
--- gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c
|
|
@@ -0,0 +1,56 @@
|
|
+/* { dg-do run } */
|
|
+/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-call=thunk -mindirect-branch-table" } */
|
|
+
|
|
+int gl;
|
|
+
|
|
+void __attribute__((noinline))
|
|
+foo (int a)
|
|
+{
|
|
+ gl = a + 40;
|
|
+}
|
|
+
|
|
+int __attribute__((noinline))
|
|
+foo_value (int a)
|
|
+{
|
|
+ return a + 40;
|
|
+}
|
|
+
|
|
+void* __attribute__((noinline))
|
|
+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" } } */
|
|
--- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c
|
|
@@ -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;
|
|
+}
|
|
+
|
|
+/* The 2 gotos in bar get merged into one */
|
|
+/* { 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 "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" } } */
|
|
--- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c
|
|
@@ -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;
|
|
+}
|
|
+
|
|
+/* The 2 gotos in bar get merged into one */
|
|
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */
|
|
+/* { 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" } } */
|
|
--- gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c
|
|
@@ -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;
|
|
+}
|
|
+
|
|
+/* The 2 gotos in bar get merged into one */
|
|
+/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */
|
|
+/* { 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" } } */
|
|
--- gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c
|
|
@@ -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))
|
|
+bar (int a)
|
|
+{
|
|
+ return a + 2;
|
|
+}
|
|
+
|
|
+void __attribute__((noinline))
|
|
+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" } } */
|
|
--- gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c
|
|
@@ -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))
|
|
+bar (int a)
|
|
+{
|
|
+ return a + 2;
|
|
+}
|
|
+
|
|
+void __attribute__((noinline))
|
|
+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" } } */
|
|
--- gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c
|
|
@@ -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))
|
|
+bar (int a)
|
|
+{
|
|
+ return a + 2;
|
|
+}
|
|
+
|
|
+void __attribute__((noinline))
|
|
+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" } } */
|
|
--- gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c
|
|
@@ -0,0 +1,44 @@
|
|
+/* { dg-do compile } */
|
|
+/* { dg-options "-O3 -march=z10 --save-temps -mfunction-return-reg=thunk-extern -mindirect-branch-table" } */
|
|
+
|
|
+int gl = 0;
|
|
+
|
|
+int __attribute__((noinline))
|
|
+bar (int a)
|
|
+{
|
|
+ return a + 2;
|
|
+}
|
|
+
|
|
+void __attribute__((noinline))
|
|
+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" } } */
|
|
--- gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c
|
|
@@ -0,0 +1,41 @@
|
|
+/* { dg-do run } */
|
|
+/* { dg-options "-O3 -march=z10 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */
|
|
+
|
|
+int gl = 0;
|
|
+
|
|
+int __attribute__((noinline))
|
|
+bar (int a)
|
|
+{
|
|
+ return a + 2;
|
|
+}
|
|
+
|
|
+void __attribute__((noinline))
|
|
+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" } } */
|
|
--- gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c
|
|
@@ -0,0 +1,41 @@
|
|
+/* { dg-do run } */
|
|
+/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */
|
|
+
|
|
+int gl = 0;
|
|
+
|
|
+int __attribute__((noinline))
|
|
+bar (int a)
|
|
+{
|
|
+ return a + 2;
|
|
+}
|
|
+
|
|
+void __attribute__((noinline))
|
|
+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" } } */
|
|
--- gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c
|
|
@@ -0,0 +1,77 @@
|
|
+/* { dg-do run } */
|
|
+/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
|
|
+/* case-values-threshold will be set to 20 by the back-end when jump
|
|
+ thunk are requested. */
|
|
+
|
|
+int __attribute__((noinline)) foo1 (void) { return 1; }
|
|
+int __attribute__((noinline)) foo2 (void) { return 2; }
|
|
+int __attribute__((noinline)) foo3 (void) { return 3; }
|
|
+int __attribute__((noinline)) foo4 (void) { return 4; }
|
|
+int __attribute__((noinline)) foo5 (void) { return 5; }
|
|
+int __attribute__((noinline)) foo6 (void) { return 6; }
|
|
+int __attribute__((noinline)) foo7 (void) { return 7; }
|
|
+int __attribute__((noinline)) foo8 (void) { return 8; }
|
|
+int __attribute__((noinline)) foo9 (void) { return 9; }
|
|
+int __attribute__((noinline)) foo10 (void) { return 10; }
|
|
+int __attribute__((noinline)) foo11 (void) { return 11; }
|
|
+int __attribute__((noinline)) foo12 (void) { return 12; }
|
|
+int __attribute__((noinline)) foo13 (void) { return 13; }
|
|
+int __attribute__((noinline)) foo14 (void) { return 14; }
|
|
+int __attribute__((noinline)) foo15 (void) { return 15; }
|
|
+int __attribute__((noinline)) foo16 (void) { return 16; }
|
|
+int __attribute__((noinline)) foo17 (void) { return 17; }
|
|
+int __attribute__((noinline)) foo18 (void) { return 18; }
|
|
+int __attribute__((noinline)) foo19 (void) { return 19; }
|
|
+int __attribute__((noinline)) foo20 (void) { return 20; }
|
|
+
|
|
+
|
|
+int __attribute__((noinline))
|
|
+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" } } */
|
|
--- gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c
|
|
+++ gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c
|
|
@@ -0,0 +1,78 @@
|
|
+/* { dg-do run } */
|
|
+/* { dg-options "-O3 -march=z900 -mzarch --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
|
|
+
|
|
+/* case-values-threshold will be set to 20 by the back-end when jump
|
|
+ thunk are requested. */
|
|
+
|
|
+int __attribute__((noinline)) foo1 (void) { return 1; }
|
|
+int __attribute__((noinline)) foo2 (void) { return 2; }
|
|
+int __attribute__((noinline)) foo3 (void) { return 3; }
|
|
+int __attribute__((noinline)) foo4 (void) { return 4; }
|
|
+int __attribute__((noinline)) foo5 (void) { return 5; }
|
|
+int __attribute__((noinline)) foo6 (void) { return 6; }
|
|
+int __attribute__((noinline)) foo7 (void) { return 7; }
|
|
+int __attribute__((noinline)) foo8 (void) { return 8; }
|
|
+int __attribute__((noinline)) foo9 (void) { return 9; }
|
|
+int __attribute__((noinline)) foo10 (void) { return 10; }
|
|
+int __attribute__((noinline)) foo11 (void) { return 11; }
|
|
+int __attribute__((noinline)) foo12 (void) { return 12; }
|
|
+int __attribute__((noinline)) foo13 (void) { return 13; }
|
|
+int __attribute__((noinline)) foo14 (void) { return 14; }
|
|
+int __attribute__((noinline)) foo15 (void) { return 15; }
|
|
+int __attribute__((noinline)) foo16 (void) { return 16; }
|
|
+int __attribute__((noinline)) foo17 (void) { return 17; }
|
|
+int __attribute__((noinline)) foo18 (void) { return 18; }
|
|
+int __attribute__((noinline)) foo19 (void) { return 19; }
|
|
+int __attribute__((noinline)) foo20 (void) { return 20; }
|
|
+
|
|
+
|
|
+int __attribute__((noinline))
|
|
+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" } } */
|
|
|
|
|