Commit 84b4c7b5 by Andreas Krebbel Committed by Andreas Krebbel

S/390: Disable prediction of indirect branches

This patch implements GCC support for mitigating vulnerability
CVE-2017-5715 known as Spectre #2 on IBM Z.

In order to disable prediction of indirect branches the implementation
makes use of an IBM Z specific feature - the execute instruction.
Performing an indirect branch via execute prevents the branch from
being subject to dynamic branch prediction.

The implementation tries to stay close to the x86 solution regarding
user interface.

x86 style options supported (without thunk-inline):

-mindirect-branch=(keep|thunk|thunk-extern)
-mfunction-return=(keep|thunk|thunk-extern)

IBM Z specific options:

-mindirect-branch-jump=(keep|thunk|thunk-extern|thunk-inline)
-mindirect-branch-call=(keep|thunk|thunk-extern)
-mfunction-return-reg=(keep|thunk|thunk-extern)
-mfunction-return-mem=(keep|thunk|thunk-extern)

These options allow us to enable/disable the branch conversion at a
finer granularity.

-mindirect-branch sets the value of -mindirect-branch-jump and
 -mindirect-branch-call.

-mfunction-return sets the value of -mfunction-return-reg and
 -mfunction-return-mem.

All these options are supported on GCC command line as well as
function attributes.

'thunk' triggers the generation of out of line thunks (expolines) and
replaces the formerly indirect branch with a direct branch to the
thunk.  Depending on the -march= setting two different types of thunks
are generated.  With -march=z10 or higher exrl (execute relative long)
is being used while targeting older machines makes use of larl/ex
instead.  From a security perspective the exrl variant is preferable.

'thunk-extern' does the branch replacement like 'thunk' but does not
emit the thunks.

'thunk-inline' is only available for indirect jumps.  It should be used
in environments where correct CFI is important - known as user space.

Additionally the patch introduces the -mindirect-branch-table option
which generates tables pointing to the locations which have been
modified.  This is supposed to allow reverting the changes without
re-compilation in situations where it isn't required. The sections are
split up into one section per option.

gcc/ChangeLog:

2018-02-08  Andreas Krebbel  <krebbel@linux.vnet.ibm.com>

	* config/s390/s390-opts.h (enum indirect_branch): Define.
	* config/s390/s390-protos.h (s390_return_addr_from_memory)
	(s390_indirect_branch_via_thunk)
	(s390_indirect_branch_via_inline_thunk): Add function prototypes.
	(enum s390_indirect_branch_type): Define.
	* config/s390/s390.c (struct s390_frame_layout, struct
	machine_function): Remove.
	(indirect_branch_prez10thunk_mask, indirect_branch_z10thunk_mask)
	(indirect_branch_table_label_no, indirect_branch_table_name):
	Define variables.
	(INDIRECT_BRANCH_NUM_OPTIONS): Define macro.
	(enum s390_indirect_branch_option): Define.
	(s390_return_addr_from_memory): New function.
	(s390_handle_string_attribute): New function.
	(s390_attribute_table): Add new attribute handler.
	(s390_execute_label): Handle UNSPEC_EXECUTE_JUMP patterns.
	(s390_indirect_branch_via_thunk): New function.
	(s390_indirect_branch_via_inline_thunk): New function.
	(s390_function_ok_for_sibcall): When jumping via thunk disallow
	sibling call optimization for non z10 compiles.
	(s390_emit_call): Force indirect branch target to be a single
	register.  Add r1 clobber for non-z10 compiles.
	(s390_emit_epilogue): Emit return jump via return_use expander.
	(s390_reorg): Handle JUMP_INSNs as execute targets.
	(s390_option_override_internal): Perform validity checks for the
	new command line options.
	(s390_indirect_branch_attrvalue): New function.
	(s390_indirect_branch_settings): New function.
	(s390_set_current_function): Invoke s390_indirect_branch_settings.
	(s390_output_indirect_thunk_function):  New function.
	(s390_code_end): Implement target hook.
	(s390_case_values_threshold): Implement target hook.
	(TARGET_ASM_CODE_END, TARGET_CASE_VALUES_THRESHOLD): Define target
	macros.
	* config/s390/s390.h (struct s390_frame_layout)
	(struct	machine_function): Move here from s390.c.
	(TARGET_INDIRECT_BRANCH_NOBP_RET)
	(TARGET_INDIRECT_BRANCH_NOBP_JUMP)
	(TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK)
	(TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK)
	(TARGET_INDIRECT_BRANCH_NOBP_CALL)
	(TARGET_DEFAULT_INDIRECT_BRANCH_TABLE)
	(TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL)
	(TARGET_INDIRECT_BRANCH_THUNK_NAME_EX)
	(TARGET_INDIRECT_BRANCH_TABLE): Define macros.
	* config/s390/s390.md (UNSPEC_EXECUTE_JUMP)
	(INDIRECT_BRANCH_THUNK_REGNUM): Define constants.
	(mnemonic attribute): Add values which aren't recognized
	automatically.
	("*cjump_long", "*icjump_long", "*basr", "*basr_r"): Disable
	pattern for branch conversion.  Fix mnemonic attribute.
	("*c<code>", "*sibcall_br", "*sibcall_value_br", "*return"): Emit
	indirect branch via thunk if requested.
	("indirect_jump", "<code>"): Expand patterns for branch conversion.
	("*indirect_jump"): Disable for branch conversion using out of
	line thunks.
	("indirect_jump_via_thunk<mode>_z10")
	("indirect_jump_via_thunk<mode>")
	("indirect_jump_via_inlinethunk<mode>_z10")
	("indirect_jump_via_inlinethunk<mode>", "*casesi_jump")
	("casesi_jump_via_thunk<mode>_z10", "casesi_jump_via_thunk<mode>")
	("casesi_jump_via_inlinethunk<mode>_z10")
	("casesi_jump_via_inlinethunk<mode>", "*basr_via_thunk<mode>_z10")
	("*basr_via_thunk<mode>", "*basr_r_via_thunk_z10")
	("*basr_r_via_thunk", "return<mode>_prez10"): New pattern.
	("*indirect2_jump"): Disable for branch conversion.
	("casesi_jump"): Turn into expander and expand patterns for branch
	conversion.
	("return_use"): New expander.
	("*return"): Emit return via thunk and rename it to ...
	("*return<mode>"): ... this one.
	* config/s390/s390.opt: Add new options and and enum for the
	option values.

gcc/testsuite/ChangeLog:

2018-02-08  Andreas Krebbel  <krebbel@linux.vnet.ibm.com>

	* gcc.target/s390/nobp-function-pointer-attr.c: New test.
	* gcc.target/s390/nobp-function-pointer-nothunk.c: New test.
	* gcc.target/s390/nobp-function-pointer-z10.c: New test.
	* gcc.target/s390/nobp-function-pointer-z900.c: New test.
	* gcc.target/s390/nobp-indirect-jump-attr.c: New test.
	* gcc.target/s390/nobp-indirect-jump-inline-attr.c: New test.
	* gcc.target/s390/nobp-indirect-jump-inline-z10.c: New test.
	* gcc.target/s390/nobp-indirect-jump-inline-z900.c: New test.
	* gcc.target/s390/nobp-indirect-jump-nothunk.c: New test.
	* gcc.target/s390/nobp-indirect-jump-z10.c: New test.
	* gcc.target/s390/nobp-indirect-jump-z900.c: New test.
	* gcc.target/s390/nobp-return-attr-all.c: New test.
	* gcc.target/s390/nobp-return-attr-neg.c: New test.
	* gcc.target/s390/nobp-return-mem-attr.c: New test.
	* gcc.target/s390/nobp-return-mem-nothunk.c: New test.
	* gcc.target/s390/nobp-return-mem-z10.c: New test.
	* gcc.target/s390/nobp-return-mem-z900.c: New test.
	* gcc.target/s390/nobp-return-reg-attr.c: New test.
	* gcc.target/s390/nobp-return-reg-mixed.c: New test.
	* gcc.target/s390/nobp-return-reg-nothunk.c: New test.
	* gcc.target/s390/nobp-return-reg-z10.c: New test.
	* gcc.target/s390/nobp-return-reg-z900.c: New test.
	* gcc.target/s390/nobp-table-jump-inline-z10.c: New test.
	* gcc.target/s390/nobp-table-jump-inline-z900.c: New test.
	* gcc.target/s390/nobp-table-jump-z10.c: New test.
	* gcc.target/s390/nobp-table-jump-z900.c: New test.

From-SVN: r257489
parent 98a05c03
2018-02-08 Andreas Krebbel <krebbel@linux.vnet.ibm.com>
* config/s390/s390-opts.h (enum indirect_branch): Define.
* config/s390/s390-protos.h (s390_return_addr_from_memory)
(s390_indirect_branch_via_thunk)
(s390_indirect_branch_via_inline_thunk): Add function prototypes.
(enum s390_indirect_branch_type): Define.
* config/s390/s390.c (struct s390_frame_layout, struct
machine_function): Remove.
(indirect_branch_prez10thunk_mask, indirect_branch_z10thunk_mask)
(indirect_branch_table_label_no, indirect_branch_table_name):
Define variables.
(INDIRECT_BRANCH_NUM_OPTIONS): Define macro.
(enum s390_indirect_branch_option): Define.
(s390_return_addr_from_memory): New function.
(s390_handle_string_attribute): New function.
(s390_attribute_table): Add new attribute handler.
(s390_execute_label): Handle UNSPEC_EXECUTE_JUMP patterns.
(s390_indirect_branch_via_thunk): New function.
(s390_indirect_branch_via_inline_thunk): New function.
(s390_function_ok_for_sibcall): When jumping via thunk disallow
sibling call optimization for non z10 compiles.
(s390_emit_call): Force indirect branch target to be a single
register. Add r1 clobber for non-z10 compiles.
(s390_emit_epilogue): Emit return jump via return_use expander.
(s390_reorg): Handle JUMP_INSNs as execute targets.
(s390_option_override_internal): Perform validity checks for the
new command line options.
(s390_indirect_branch_attrvalue): New function.
(s390_indirect_branch_settings): New function.
(s390_set_current_function): Invoke s390_indirect_branch_settings.
(s390_output_indirect_thunk_function): New function.
(s390_code_end): Implement target hook.
(s390_case_values_threshold): Implement target hook.
(TARGET_ASM_CODE_END, TARGET_CASE_VALUES_THRESHOLD): Define target
macros.
* config/s390/s390.h (struct s390_frame_layout)
(struct machine_function): Move here from s390.c.
(TARGET_INDIRECT_BRANCH_NOBP_RET)
(TARGET_INDIRECT_BRANCH_NOBP_JUMP)
(TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK)
(TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK)
(TARGET_INDIRECT_BRANCH_NOBP_CALL)
(TARGET_DEFAULT_INDIRECT_BRANCH_TABLE)
(TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL)
(TARGET_INDIRECT_BRANCH_THUNK_NAME_EX)
(TARGET_INDIRECT_BRANCH_TABLE): Define macros.
* config/s390/s390.md (UNSPEC_EXECUTE_JUMP)
(INDIRECT_BRANCH_THUNK_REGNUM): Define constants.
(mnemonic attribute): Add values which aren't recognized
automatically.
("*cjump_long", "*icjump_long", "*basr", "*basr_r"): Disable
pattern for branch conversion. Fix mnemonic attribute.
("*c<code>", "*sibcall_br", "*sibcall_value_br", "*return"): Emit
indirect branch via thunk if requested.
("indirect_jump", "<code>"): Expand patterns for branch conversion.
("*indirect_jump"): Disable for branch conversion using out of
line thunks.
("indirect_jump_via_thunk<mode>_z10")
("indirect_jump_via_thunk<mode>")
("indirect_jump_via_inlinethunk<mode>_z10")
("indirect_jump_via_inlinethunk<mode>", "*casesi_jump")
("casesi_jump_via_thunk<mode>_z10", "casesi_jump_via_thunk<mode>")
("casesi_jump_via_inlinethunk<mode>_z10")
("casesi_jump_via_inlinethunk<mode>", "*basr_via_thunk<mode>_z10")
("*basr_via_thunk<mode>", "*basr_r_via_thunk_z10")
("*basr_r_via_thunk", "return<mode>_prez10"): New pattern.
("*indirect2_jump"): Disable for branch conversion.
("casesi_jump"): Turn into expander and expand patterns for branch
conversion.
("return_use"): New expander.
("*return"): Emit return via thunk and rename it to ...
("*return<mode>"): ... this one.
* config/s390/s390.opt: Add new options and and enum for the
option values.
2018-02-08 Richard Sandiford <richard.sandiford@linaro.org>
* lra-constraints.c (match_reload): Unconditionally use
......
......@@ -43,4 +43,13 @@ enum processor_type
PROCESSOR_max
};
/* Values for -mindirect-branch and -mfunction-return options. */
enum indirect_branch {
indirect_branch_unset = 0,
indirect_branch_keep,
indirect_branch_thunk,
indirect_branch_thunk_inline,
indirect_branch_thunk_extern
};
#endif
......@@ -50,6 +50,7 @@ extern void s390_set_has_landing_pad_p (bool);
extern bool s390_hard_regno_rename_ok (unsigned int, unsigned int);
extern int s390_class_max_nregs (enum reg_class, machine_mode);
extern bool s390_function_arg_vector (machine_mode, const_tree);
extern bool s390_return_addr_from_memory(void);
#if S390_USE_TARGET_ATTRIBUTE
extern tree s390_valid_target_attribute_tree (tree args,
struct gcc_options *opts,
......@@ -145,6 +146,17 @@ extern int s390_compare_and_branch_condition_mask (rtx);
extern bool s390_extzv_shift_ok (int, int, unsigned HOST_WIDE_INT);
extern void s390_asm_output_function_label (FILE *, const char *, tree);
enum s390_indirect_branch_type
{
s390_indirect_branch_type_jump = 0,
s390_indirect_branch_type_call,
s390_indirect_branch_type_return
};
extern void s390_indirect_branch_via_thunk (unsigned int regno,
unsigned int return_addr_regno,
rtx comparison_operator,
enum s390_indirect_branch_type type);
extern void s390_indirect_branch_via_inline_thunk (rtx execute_target);
#endif /* RTX_CODE */
/* s390-c.c routines */
......
......@@ -399,84 +399,6 @@ struct s390_address
bool literal_pool;
};
/* The following structure is embedded in the machine
specific part of struct function. */
struct GTY (()) s390_frame_layout
{
/* Offset within stack frame. */
HOST_WIDE_INT gprs_offset;
HOST_WIDE_INT f0_offset;
HOST_WIDE_INT f4_offset;
HOST_WIDE_INT f8_offset;
HOST_WIDE_INT backchain_offset;
/* Number of first and last gpr where slots in the register
save area are reserved for. */
int first_save_gpr_slot;
int last_save_gpr_slot;
/* Location (FP register number) where GPRs (r0-r15) should
be saved to.
0 - does not need to be saved at all
-1 - stack slot */
#define SAVE_SLOT_NONE 0
#define SAVE_SLOT_STACK -1
signed char gpr_save_slots[16];
/* Number of first and last gpr to be saved, restored. */
int first_save_gpr;
int first_restore_gpr;
int last_save_gpr;
int last_restore_gpr;
/* Bits standing for floating point registers. Set, if the
respective register has to be saved. Starting with reg 16 (f0)
at the rightmost bit.
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
fpr 15 13 11 9 14 12 10 8 7 5 3 1 6 4 2 0
reg 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 */
unsigned int fpr_bitmap;
/* Number of floating point registers f8-f15 which must be saved. */
int high_fprs;
/* Set if return address needs to be saved.
This flag is set by s390_return_addr_rtx if it could not use
the initial value of r14 and therefore depends on r14 saved
to the stack. */
bool save_return_addr_p;
/* Size of stack frame. */
HOST_WIDE_INT frame_size;
};
/* Define the structure for the machine field in struct function. */
struct GTY(()) machine_function
{
struct s390_frame_layout frame_layout;
/* Literal pool base register. */
rtx base_reg;
/* True if we may need to perform branch splitting. */
bool split_branches_pending_p;
bool has_landing_pad_p;
/* True if the current function may contain a tbegin clobbering
FPRs. */
bool tbegin_p;
/* For -fsplit-stack support: A stack local which holds a pointer to
the stack arguments for a function with a variable number of
arguments. This is set at the start of the function and is used
to initialize the overflow_arg_area field of the va_list
structure. */
rtx split_stack_varargs_pointer;
};
/* Few accessor macros for struct cfun->machine->s390_frame_layout. */
#define cfun_frame_layout (cfun->machine->frame_layout)
......@@ -517,6 +439,33 @@ struct GTY(()) machine_function
bytes on a z10 (or higher) CPU. */
#define PREDICT_DISTANCE (TARGET_Z10 ? 384 : 2048)
/* Masks per jump target register indicating which thunk need to be
generated. */
static GTY(()) int indirect_branch_prez10thunk_mask = 0;
static GTY(()) int indirect_branch_z10thunk_mask = 0;
#define INDIRECT_BRANCH_NUM_OPTIONS 4
enum s390_indirect_branch_option
{
s390_opt_indirect_branch_jump = 0,
s390_opt_indirect_branch_call,
s390_opt_function_return_reg,
s390_opt_function_return_mem
};
static GTY(()) int indirect_branch_table_label_no[INDIRECT_BRANCH_NUM_OPTIONS] = { 0 };
const char *indirect_branch_table_label[INDIRECT_BRANCH_NUM_OPTIONS] = \
{ "LJUMP", "LCALL", "LRETREG", "LRETMEM" };
const char *indirect_branch_table_name[INDIRECT_BRANCH_NUM_OPTIONS] = \
{ ".s390_indirect_jump", ".s390_indirect_call",
".s390_return_reg", ".s390_return_mem" };
bool
s390_return_addr_from_memory ()
{
return cfun_gpr_save_slot(RETURN_REGNUM) == SAVE_SLOT_STACK;
}
/* Indicate which ABI has been used for passing vector args.
0 - no vector type arguments have been passed where the ABI is relevant
......@@ -1179,11 +1128,83 @@ s390_handle_vectorbool_attribute (tree *node, tree name ATTRIBUTE_UNUSED,
return NULL_TREE;
}
/* Check syntax of function decl attributes having a string type value. */
static tree
s390_handle_string_attribute (tree *node, tree name ATTRIBUTE_UNUSED,
tree args ATTRIBUTE_UNUSED,
int flags ATTRIBUTE_UNUSED,
bool *no_add_attrs)
{
tree cst;
if (TREE_CODE (*node) != FUNCTION_DECL)
{
warning (OPT_Wattributes, "%qE attribute only applies to functions",
name);
*no_add_attrs = true;
}
cst = TREE_VALUE (args);
if (TREE_CODE (cst) != STRING_CST)
{
warning (OPT_Wattributes,
"%qE attribute requires a string constant argument",
name);
*no_add_attrs = true;
}
if (is_attribute_p ("indirect_branch", name)
|| is_attribute_p ("indirect_branch_call", name)
|| is_attribute_p ("function_return", name)
|| is_attribute_p ("function_return_reg", name)
|| is_attribute_p ("function_return_mem", name))
{
if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0
&& strcmp (TREE_STRING_POINTER (cst), "thunk") != 0
&& strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0)
{
warning (OPT_Wattributes,
"argument to %qE attribute is not "
"(keep|thunk|thunk-extern)", name);
*no_add_attrs = true;
}
}
if (is_attribute_p ("indirect_branch_jump", name)
&& strcmp (TREE_STRING_POINTER (cst), "keep") != 0
&& strcmp (TREE_STRING_POINTER (cst), "thunk") != 0
&& strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0
&& strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0)
{
warning (OPT_Wattributes,
"argument to %qE attribute is not "
"(keep|thunk|thunk-inline|thunk-extern)", name);
*no_add_attrs = true;
}
return NULL_TREE;
}
static const struct attribute_spec s390_attribute_table[] = {
{ "hotpatch", 2, 2, true, false, false, false,
s390_handle_hotpatch_attribute, NULL },
{ "s390_vector_bool", 0, 0, false, true, false, true,
s390_handle_vectorbool_attribute, NULL },
{ "indirect_branch", 1, 1, true, false, false, false,
s390_handle_string_attribute, NULL },
{ "indirect_branch_jump", 1, 1, true, false, false, false,
s390_handle_string_attribute, NULL },
{ "indirect_branch_call", 1, 1, true, false, false, false,
s390_handle_string_attribute, NULL },
{ "function_return", 1, 1, true, false, false, false,
s390_handle_string_attribute, NULL },
{ "function_return_reg", 1, 1, true, false, false, false,
s390_handle_string_attribute, NULL },
{ "function_return_mem", 1, 1, true, false, false, false,
s390_handle_string_attribute, NULL },
/* End element. */
{ NULL, 0, 0, false, false, false, false, NULL, NULL }
};
......@@ -8733,11 +8754,25 @@ s390_find_constant (struct constant_pool *pool, rtx val,
static rtx
s390_execute_label (rtx insn)
{
if (NONJUMP_INSN_P (insn)
if (INSN_P (insn)
&& GET_CODE (PATTERN (insn)) == PARALLEL
&& GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC
&& XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE)
&& (XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE
|| XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE_JUMP))
{
if (XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE)
return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2);
else
{
gcc_assert (JUMP_P (insn));
/* For jump insns as execute target:
- There is one operand less in the parallel (the
modification register of the execute is always 0).
- The execute target label is wrapped into an
if_then_else in order to hide it from jump analysis. */
return XEXP (XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 0), 0);
}
}
return NULL_RTX;
}
......@@ -11681,7 +11716,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)
......@@ -11837,8 +11871,14 @@ s390_emit_epilogue (bool sibcall)
&& s390_tune <= PROCESSOR_2097_Z10)
{
int return_regnum = find_unused_clobbered_reg();
if (!return_regnum)
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,
......@@ -11875,16 +11915,7 @@ s390_emit_epilogue (bool sibcall)
s390_restore_gprs_from_fprs ();
if (! sibcall)
{
/* Return to caller. */
p = rtvec_alloc (2);
RTVEC_ELT (p, 0) = ret_rtx;
RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg);
emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));
}
emit_jump_insn (gen_return_use (return_reg));
}
/* Implement TARGET_SET_UP_BY_PROLOGUE. */
......@@ -13475,6 +13506,112 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,
final_end_function ();
}
/* Output either an indirect jump or a an indirect call
(RETURN_ADDR_REGNO != INVALID_REGNUM) with target register REGNO
using a branch trampoline disabling branch target prediction. */
void
s390_indirect_branch_via_thunk (unsigned int regno,
unsigned int return_addr_regno,
rtx comparison_operator,
enum s390_indirect_branch_type type)
{
enum s390_indirect_branch_option option;
if (type == s390_indirect_branch_type_return)
{
if (s390_return_addr_from_memory ())
option = s390_opt_function_return_mem;
else
option = s390_opt_function_return_reg;
}
else if (type == s390_indirect_branch_type_jump)
option = s390_opt_indirect_branch_jump;
else if (type == s390_indirect_branch_type_call)
option = s390_opt_indirect_branch_call;
else
gcc_unreachable ();
if (TARGET_INDIRECT_BRANCH_TABLE)
{
char label[32];
ASM_GENERATE_INTERNAL_LABEL (label,
indirect_branch_table_label[option],
indirect_branch_table_label_no[option]++);
ASM_OUTPUT_LABEL (asm_out_file, label);
}
if (return_addr_regno != INVALID_REGNUM)
{
gcc_assert (comparison_operator == NULL_RTX);
fprintf (asm_out_file, " \tbrasl\t%%r%d,", return_addr_regno);
}
else
{
fputs (" \tjg", asm_out_file);
if (comparison_operator != NULL_RTX)
print_operand (asm_out_file, comparison_operator, 'C');
fputs ("\t", asm_out_file);
}
if (TARGET_CPU_Z10)
fprintf (asm_out_file,
TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL "\n",
regno);
else
fprintf (asm_out_file,
TARGET_INDIRECT_BRANCH_THUNK_NAME_EX "\n",
INDIRECT_BRANCH_THUNK_REGNUM, regno);
if ((option == s390_opt_indirect_branch_jump
&& cfun->machine->indirect_branch_jump == indirect_branch_thunk)
|| (option == s390_opt_indirect_branch_call
&& cfun->machine->indirect_branch_call == indirect_branch_thunk)
|| (option == s390_opt_function_return_reg
&& cfun->machine->function_return_reg == indirect_branch_thunk)
|| (option == s390_opt_function_return_mem
&& cfun->machine->function_return_mem == indirect_branch_thunk))
{
if (TARGET_CPU_Z10)
indirect_branch_z10thunk_mask |= (1 << regno);
else
indirect_branch_prez10thunk_mask |= (1 << regno);
}
}
/* Output an inline thunk for indirect jumps. EXECUTE_TARGET can
either be an address register or a label pointing to the location
of the jump instruction. */
void
s390_indirect_branch_via_inline_thunk (rtx execute_target)
{
if (TARGET_INDIRECT_BRANCH_TABLE)
{
char label[32];
ASM_GENERATE_INTERNAL_LABEL (label,
indirect_branch_table_label[s390_opt_indirect_branch_jump],
indirect_branch_table_label_no[s390_opt_indirect_branch_jump]++);
ASM_OUTPUT_LABEL (asm_out_file, label);
}
if (!TARGET_ZARCH)
fputs ("\t.machinemode zarch\n", asm_out_file);
if (REG_P (execute_target))
fprintf (asm_out_file, "\tex\t%%r0,0(%%r%d)\n", REGNO (execute_target));
else
output_asm_insn ("\texrl\t%%r0,%0", &execute_target);
if (!TARGET_ZARCH)
fputs ("\t.machinemode esa\n", asm_out_file);
fputs ("0:\tj\t0b\n", asm_out_file);
}
static bool
s390_valid_pointer_mode (scalar_int_mode mode)
{
......@@ -13576,6 +13713,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. */
......@@ -13609,9 +13754,13 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
{
bool plt_call = false;
rtx_insn *insn;
rtx call;
rtx clobber;
rtvec vec;
rtx vec[4] = { NULL_RTX };
int elts = 0;
rtx *call = &vec[0];
rtx *clobber_ret_reg = &vec[1];
rtx *use = &vec[2];
rtx *clobber_thunk_reg = &vec[3];
int i;
/* Direct function calls need special treatment. */
if (GET_CODE (addr_location) == SYMBOL_REF)
......@@ -13663,26 +13812,58 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,
addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM);
}
if (TARGET_INDIRECT_BRANCH_NOBP_CALL
&& GET_CODE (addr_location) != SYMBOL_REF
&& !plt_call)
{
/* Indirect branch thunks require the target to be a single GPR. */
addr_location = force_reg (Pmode, addr_location);
/* Without exrl the indirect branch thunks need an additional
register for larl;ex */
if (!TARGET_CPU_Z10)
{
*clobber_thunk_reg = gen_rtx_REG (Pmode, INDIRECT_BRANCH_THUNK_REGNUM);
*clobber_thunk_reg = gen_rtx_CLOBBER (VOIDmode, *clobber_thunk_reg);
}
}
addr_location = gen_rtx_MEM (QImode, addr_location);
call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);
*call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);
if (result_reg != NULL_RTX)
call = gen_rtx_SET (result_reg, call);
*call = gen_rtx_SET (result_reg, *call);
if (retaddr_reg != NULL_RTX)
{
clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg);
*clobber_ret_reg = gen_rtx_CLOBBER (VOIDmode, retaddr_reg);
if (tls_call != NULL_RTX)
vec = gen_rtvec (3, call, clobber,
gen_rtx_USE (VOIDmode, tls_call));
else
vec = gen_rtvec (2, call, clobber);
*use = gen_rtx_USE (VOIDmode, tls_call);
}
for (i = 0; i < 4; i++)
if (vec[i] != NULL_RTX)
elts++;
if (elts > 1)
{
rtvec v;
int e = 0;
v = rtvec_alloc (elts);
for (i = 0; i < 4; i++)
if (vec[i] != NULL_RTX)
{
RTVEC_ELT (v, e) = vec[i];
e++;
}
call = gen_rtx_PARALLEL (VOIDmode, vec);
*call = gen_rtx_PARALLEL (VOIDmode, v);
}
insn = emit_call_insn (call);
insn = emit_call_insn (*call);
/* 31-bit PLT stubs and tls calls use the GOT register implicitly. */
if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX)
......@@ -14464,6 +14645,15 @@ s390_reorg (void)
target = emit_label (XEXP (label, 0));
INSN_ADDRESSES_NEW (target, -1);
if (JUMP_P (insn))
{
target = emit_jump_insn (s390_execute_target (insn));
/* This is important in order to keep a table jump
pointing at the jump table label. Only this makes it
being recognized as table jump. */
JUMP_LABEL (target) = JUMP_LABEL (insn);
}
else
target = emit_insn (s390_execute_target (insn));
INSN_ADDRESSES_NEW (target, -1);
}
......@@ -15199,6 +15389,42 @@ s390_option_override_internal (bool main_args_p,
if (TARGET_64BIT && !TARGET_ZARCH_P (opts->x_target_flags))
error ("64-bit ABI not supported in ESA/390 mode");
if (opts->x_s390_indirect_branch == indirect_branch_thunk_inline
|| opts->x_s390_indirect_branch_call == indirect_branch_thunk_inline
|| opts->x_s390_function_return == indirect_branch_thunk_inline
|| opts->x_s390_function_return_reg == indirect_branch_thunk_inline
|| opts->x_s390_function_return_mem == indirect_branch_thunk_inline)
error ("thunk-inline is only supported with -mindirect-branch-jump");
if (opts->x_s390_indirect_branch != indirect_branch_keep)
{
if (!opts_set->x_s390_indirect_branch_call)
opts->x_s390_indirect_branch_call = opts->x_s390_indirect_branch;
if (!opts_set->x_s390_indirect_branch_jump)
opts->x_s390_indirect_branch_jump = opts->x_s390_indirect_branch;
}
if (opts->x_s390_function_return != indirect_branch_keep)
{
if (!opts_set->x_s390_function_return_reg)
opts->x_s390_function_return_reg = opts->x_s390_function_return;
if (!opts_set->x_s390_function_return_mem)
opts->x_s390_function_return_mem = opts->x_s390_function_return;
}
if (!TARGET_CPU_ZARCH)
{
if (opts->x_s390_indirect_branch_call != indirect_branch_keep
|| opts->x_s390_indirect_branch_jump != indirect_branch_keep)
error ("-mindirect-branch* options require -march=z900 or higher");
if (opts->x_s390_function_return_reg != indirect_branch_keep
|| opts->x_s390_function_return_mem != indirect_branch_keep)
error ("-mfunction-return* options require -march=z900 or higher");
}
/* Enable hardware transactions if available and not explicitly
disabled by user. E.g. with -m31 -march=zEC12 -mzarch */
if (!TARGET_OPT_HTM_P (opts_set->x_target_flags))
......@@ -15811,6 +16037,78 @@ s390_can_inline_p (tree caller, tree callee)
return ret;
}
/* Set VAL to correct enum value according to the indirect-branch or
function-return attribute in ATTR. */
static inline void
s390_indirect_branch_attrvalue (tree attr, enum indirect_branch *val)
{
const char *str = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)));
if (strcmp (str, "keep") == 0)
*val = indirect_branch_keep;
else if (strcmp (str, "thunk") == 0)
*val = indirect_branch_thunk;
else if (strcmp (str, "thunk-inline") == 0)
*val = indirect_branch_thunk_inline;
else if (strcmp (str, "thunk-extern") == 0)
*val = indirect_branch_thunk_extern;
}
/* Memorize the setting for -mindirect-branch* and -mfunction-return*
from either the cmdline or the function attributes in
cfun->machine. */
static void
s390_indirect_branch_settings (tree fndecl)
{
tree attr;
if (!fndecl)
return;
/* Initialize with the cmdline options and let the attributes
override it. */
cfun->machine->indirect_branch_jump = s390_indirect_branch_jump;
cfun->machine->indirect_branch_call = s390_indirect_branch_call;
cfun->machine->function_return_reg = s390_function_return_reg;
cfun->machine->function_return_mem = s390_function_return_mem;
if ((attr = lookup_attribute ("indirect_branch",
DECL_ATTRIBUTES (fndecl))))
{
s390_indirect_branch_attrvalue (attr,
&cfun->machine->indirect_branch_jump);
s390_indirect_branch_attrvalue (attr,
&cfun->machine->indirect_branch_call);
}
if ((attr = lookup_attribute ("indirect_branch_jump",
DECL_ATTRIBUTES (fndecl))))
s390_indirect_branch_attrvalue (attr, &cfun->machine->indirect_branch_jump);
if ((attr = lookup_attribute ("indirect_branch_call",
DECL_ATTRIBUTES (fndecl))))
s390_indirect_branch_attrvalue (attr, &cfun->machine->indirect_branch_call);
if ((attr = lookup_attribute ("function_return",
DECL_ATTRIBUTES (fndecl))))
{
s390_indirect_branch_attrvalue (attr,
&cfun->machine->function_return_reg);
s390_indirect_branch_attrvalue (attr,
&cfun->machine->function_return_mem);
}
if ((attr = lookup_attribute ("function_return_reg",
DECL_ATTRIBUTES (fndecl))))
s390_indirect_branch_attrvalue (attr, &cfun->machine->function_return_reg);
if ((attr = lookup_attribute ("function_return_mem",
DECL_ATTRIBUTES (fndecl))))
s390_indirect_branch_attrvalue (attr, &cfun->machine->function_return_mem);
}
/* Restore targets globals from NEW_TREE and invalidate s390_previous_fndecl
cache. */
......@@ -15861,6 +16159,8 @@ s390_set_current_function (tree fndecl)
if (old_tree != new_tree)
s390_activate_target_options (new_tree);
s390_previous_fndecl = fndecl;
s390_indirect_branch_settings (fndecl);
}
#endif
......@@ -16159,6 +16459,186 @@ s390_asan_shadow_offset (void)
return TARGET_64BIT ? HOST_WIDE_INT_1U << 52 : HOST_WIDE_INT_UC (0x20000000);
}
#ifdef HAVE_GAS_HIDDEN
# define USE_HIDDEN_LINKONCE 1
#else
# define USE_HIDDEN_LINKONCE 0
#endif
/* Output an indirect branch trampoline for target register REGNO. */
static void
s390_output_indirect_thunk_function (unsigned int regno, bool z10_p)
{
tree decl;
char thunk_label[32];
int i;
if (z10_p)
sprintf (thunk_label, TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL, regno);
else
sprintf (thunk_label, TARGET_INDIRECT_BRANCH_THUNK_NAME_EX,
INDIRECT_BRANCH_THUNK_REGNUM, regno);
decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
get_identifier (thunk_label),
build_function_type_list (void_type_node, NULL_TREE));
DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL,
NULL_TREE, void_type_node);
TREE_PUBLIC (decl) = 1;
TREE_STATIC (decl) = 1;
DECL_IGNORED_P (decl) = 1;
if (USE_HIDDEN_LINKONCE)
{
cgraph_node::create (decl)->set_comdat_group (DECL_ASSEMBLER_NAME (decl));
targetm.asm_out.unique_section (decl, 0);
switch_to_section (get_named_section (decl, NULL, 0));
targetm.asm_out.globalize_label (asm_out_file, thunk_label);
fputs ("\t.hidden\t", asm_out_file);
assemble_name (asm_out_file, thunk_label);
putc ('\n', asm_out_file);
ASM_DECLARE_FUNCTION_NAME (asm_out_file, thunk_label, decl);
}
else
{
switch_to_section (text_section);
ASM_OUTPUT_LABEL (asm_out_file, thunk_label);
}
DECL_INITIAL (decl) = make_node (BLOCK);
current_function_decl = decl;
allocate_struct_function (decl, false);
init_function_start (decl);
cfun->is_thunk = true;
first_function_block_is_cold = false;
final_start_function (emit_barrier (), asm_out_file, 1);
/* This makes CFI at least usable for indirect jumps.
Stopping in the thunk: backtrace will point to the thunk target
is if it was interrupted by a signal. For a call this means that
the call chain will be: caller->callee->thunk */
if (flag_asynchronous_unwind_tables)
{
fputs ("\t.cfi_signal_frame\n", asm_out_file);
fprintf (asm_out_file, "\t.cfi_return_column %d\n", regno);
for (i = 0; i < FPR15_REGNUM; i++)
fprintf (asm_out_file, "\t.cfi_same_value %s\n", reg_names[i]);
}
if (z10_p)
{
/* exrl 0,1f */
/* We generate a thunk for z10 compiled code although z10 is
currently not enabled. Tell the assembler to accept the
instruction. */
if (!TARGET_CPU_Z10)
{
fputs ("\t.machine push\n", asm_out_file);
fputs ("\t.machine z10\n", asm_out_file);
}
/* We use exrl even if -mzarch hasn't been specified on the
command line so we have to tell the assembler to accept
it. */
if (!TARGET_ZARCH)
fputs ("\t.machinemode zarch\n", asm_out_file);
fputs ("\texrl\t0,1f\n", asm_out_file);
if (!TARGET_ZARCH)
fputs ("\t.machinemode esa\n", asm_out_file);
if (!TARGET_CPU_Z10)
fputs ("\t.machine pop\n", asm_out_file);
}
else if (TARGET_CPU_ZARCH)
{
/* larl %r1,1f */
fprintf (asm_out_file, "\tlarl\t%%r%d,1f\n",
INDIRECT_BRANCH_THUNK_REGNUM);
/* ex 0,0(%r1) */
fprintf (asm_out_file, "\tex\t0,0(%%r%d)\n",
INDIRECT_BRANCH_THUNK_REGNUM);
}
else
gcc_unreachable ();
/* 0: j 0b */
fputs ("0:\tj\t0b\n", asm_out_file);
/* 1: br <regno> */
fprintf (asm_out_file, "1:\tbr\t%%r%d\n", regno);
final_end_function ();
init_insn_lengths ();
free_after_compilation (cfun);
set_cfun (NULL);
current_function_decl = NULL;
}
/* Implement the asm.code_end target hook. */
static void
s390_code_end (void)
{
int i;
for (i = 1; i < 16; i++)
{
if (indirect_branch_z10thunk_mask & (1 << i))
s390_output_indirect_thunk_function (i, true);
if (indirect_branch_prez10thunk_mask & (1 << i))
s390_output_indirect_thunk_function (i, false);
}
if (TARGET_INDIRECT_BRANCH_TABLE)
{
int o;
int i;
for (o = 0; o < INDIRECT_BRANCH_NUM_OPTIONS; o++)
{
if (indirect_branch_table_label_no[o] == 0)
continue;
switch_to_section (get_section (indirect_branch_table_name[o],
0,
NULL_TREE));
for (i = 0; i < indirect_branch_table_label_no[o]; i++)
{
char label_start[32];
ASM_GENERATE_INTERNAL_LABEL (label_start,
indirect_branch_table_label[o], i);
fputs ("\t.long\t", asm_out_file);
assemble_name_raw (asm_out_file, label_start);
fputs ("-.\n", asm_out_file);
}
switch_to_section (current_function_section ());
}
}
}
/* Implement the TARGET_CASE_VALUES_THRESHOLD target hook. */
unsigned int
s390_case_values_threshold (void)
{
/* Disabling branch prediction for indirect jumps makes jump tables
much more expensive. */
if (TARGET_INDIRECT_BRANCH_NOBP_JUMP)
return 20;
return default_case_values_threshold ();
}
/* Initialize GCC target structure. */
#undef TARGET_ASM_ALIGNED_HI_OP
......@@ -16441,6 +16921,12 @@ s390_asan_shadow_offset (void)
#undef TARGET_CONSTANT_ALIGNMENT
#define TARGET_CONSTANT_ALIGNMENT s390_constant_alignment
#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"
......@@ -1033,4 +1033,124 @@ extern const int processor_flags_table[];
s390_register_target_pragmas (); \
} while (0)
#ifndef USED_FOR_TARGET
/* The following structure is embedded in the machine
specific part of struct function. */
struct GTY (()) s390_frame_layout
{
/* Offset within stack frame. */
HOST_WIDE_INT gprs_offset;
HOST_WIDE_INT f0_offset;
HOST_WIDE_INT f4_offset;
HOST_WIDE_INT f8_offset;
HOST_WIDE_INT backchain_offset;
/* Number of first and last gpr where slots in the register
save area are reserved for. */
int first_save_gpr_slot;
int last_save_gpr_slot;
/* Location (FP register number) where GPRs (r0-r15) should
be saved to.
0 - does not need to be saved at all
-1 - stack slot */
#define SAVE_SLOT_NONE 0
#define SAVE_SLOT_STACK -1
signed char gpr_save_slots[16];
/* Number of first and last gpr to be saved, restored. */
int first_save_gpr;
int first_restore_gpr;
int last_save_gpr;
int last_restore_gpr;
/* Bits standing for floating point registers. Set, if the
respective register has to be saved. Starting with reg 16 (f0)
at the rightmost bit.
Bit 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
fpr 15 13 11 9 14 12 10 8 7 5 3 1 6 4 2 0
reg 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16 */
unsigned int fpr_bitmap;
/* Number of floating point registers f8-f15 which must be saved. */
int high_fprs;
/* Set if return address needs to be saved.
This flag is set by s390_return_addr_rtx if it could not use
the initial value of r14 and therefore depends on r14 saved
to the stack. */
bool save_return_addr_p;
/* Size of stack frame. */
HOST_WIDE_INT frame_size;
};
/* Define the structure for the machine field in struct function. */
struct GTY(()) machine_function
{
struct s390_frame_layout frame_layout;
/* Literal pool base register. */
rtx base_reg;
/* True if we may need to perform branch splitting. */
bool split_branches_pending_p;
bool has_landing_pad_p;
/* True if the current function may contain a tbegin clobbering
FPRs. */
bool tbegin_p;
/* For -fsplit-stack support: A stack local which holds a pointer to
the stack arguments for a function with a variable number of
arguments. This is set at the start of the function and is used
to initialize the overflow_arg_area field of the va_list
structure. */
rtx split_stack_varargs_pointer;
enum indirect_branch indirect_branch_jump;
enum indirect_branch indirect_branch_call;
enum indirect_branch function_return_mem;
enum indirect_branch function_return_reg;
};
#endif
#define TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION \
(cfun->machine->function_return_reg != indirect_branch_keep \
|| cfun->machine->function_return_mem != indirect_branch_keep)
#define TARGET_INDIRECT_BRANCH_NOBP_RET \
((cfun->machine->function_return_reg != indirect_branch_keep \
&& !s390_return_addr_from_memory ()) \
|| (cfun->machine->function_return_mem != indirect_branch_keep \
&& s390_return_addr_from_memory ()))
#define TARGET_INDIRECT_BRANCH_NOBP_JUMP \
(cfun->machine->indirect_branch_jump != indirect_branch_keep)
#define TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK \
(cfun->machine->indirect_branch_jump == indirect_branch_thunk \
|| cfun->machine->indirect_branch_jump == indirect_branch_thunk_extern)
#define TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK \
(cfun->machine->indirect_branch_jump == indirect_branch_thunk_inline)
#define TARGET_INDIRECT_BRANCH_NOBP_CALL \
(cfun->machine->indirect_branch_call != indirect_branch_keep)
#ifndef TARGET_DEFAULT_INDIRECT_BRANCH_TABLE
#define TARGET_DEFAULT_INDIRECT_BRANCH_TABLE 0
#endif
#define TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL "__s390_indirect_jump_r%d"
#define TARGET_INDIRECT_BRANCH_THUNK_NAME_EX "__s390_indirect_jump_r%duse_r%d"
#define TARGET_INDIRECT_BRANCH_TABLE s390_indirect_branch_table
#endif /* S390_H */
......@@ -89,6 +89,7 @@
UNSPEC_LTREF
UNSPEC_INSN
UNSPEC_EXECUTE
UNSPEC_EXECUTE_JUMP
; Atomic Support
UNSPEC_MB
......@@ -302,6 +303,8 @@
[
; Sibling call register.
(SIBCALL_REGNUM 1)
; A call-clobbered reg which can be used in indirect branch thunks
(INDIRECT_BRANCH_THUNK_REGNUM 1)
; Literal pool base register.
(BASE_REGNUM 13)
; Return address register.
......@@ -471,7 +474,10 @@
z196_cracked"
(const_string "none"))
(define_attr "mnemonic" "bcr_flush,unknown" (const_string "unknown"))
; mnemonics which only get defined through if_then_else currently
; don't get added to the list values automatically and hence need to
; be listed here.
(define_attr "mnemonic" "b,bas,bc,bcr_flush,unknown" (const_string "unknown"))
;; Length in bytes.
......@@ -9075,7 +9081,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";
......@@ -9085,6 +9091,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")])
......@@ -9096,8 +9105,26 @@
(ANY_RETURN)
(pc)))]
"s390_can_use_<code>_insn ()"
"b%C0r\t%%r14"
[(set_attr "op_type" "RR")
{
if (TARGET_INDIRECT_BRANCH_NOBP_RET)
{
s390_indirect_branch_via_thunk (RETURN_REGNUM,
INVALID_REGNUM,
operands[0],
s390_indirect_branch_type_return);
return "";
}
else
return "b%C0r\t%%r14";
}
[(set (attr "op_type")
(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
(const_string "RIL")
(const_string "RR")))
(set (attr "mnemonic")
(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
(const_string "brcl")
(const_string "bcr")))
(set_attr "type" "jsr")
(set_attr "atype" "agen")])
......@@ -9150,7 +9177,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";
......@@ -9160,6 +9187,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")])
......@@ -9664,21 +9694,144 @@
;
else
operands[0] = force_reg (Pmode, operands[0]);
if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK)
{
operands[0] = force_reg (Pmode, operands[0]);
if (TARGET_CPU_Z10)
{
if (TARGET_64BIT)
emit_jump_insn (gen_indirect_jump_via_thunkdi_z10 (operands[0]));
else
emit_jump_insn (gen_indirect_jump_via_thunksi_z10 (operands[0]));
}
else
{
if (TARGET_64BIT)
emit_jump_insn (gen_indirect_jump_via_thunkdi (operands[0]));
else
emit_jump_insn (gen_indirect_jump_via_thunksi (operands[0]));
}
DONE;
}
if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK)
{
operands[0] = force_reg (Pmode, operands[0]);
rtx label_ref = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
if (TARGET_CPU_Z10)
{
if (TARGET_64BIT)
emit_jump_insn (gen_indirect_jump_via_inlinethunkdi_z10 (operands[0],
label_ref));
else
emit_jump_insn (gen_indirect_jump_via_inlinethunksi_z10 (operands[0],
label_ref));
}
else
{
if (TARGET_64BIT)
emit_jump_insn (gen_indirect_jump_via_inlinethunkdi (operands[0],
label_ref,
force_reg (Pmode, label_ref)));
else
emit_jump_insn (gen_indirect_jump_via_inlinethunksi (operands[0],
label_ref,
force_reg (Pmode, label_ref)));
}
DONE;
}
})
; The first constraint must be an "extra address constraint" in order
; to trigger address reloading in LRA/reload
(define_insn "*indirect_jump"
[(set (pc)
(match_operand 0 "address_operand" "ZR,a"))]
""
"@
b\t%a0
br\t%0"
[(set_attr "op_type" "RX,RR")
(match_operand 0 "address_operand" "ZR"))]
"!TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK"
{
if (get_attr_op_type (insn) == OP_TYPE_RR)
return "br\t%0";
else
return "b\t%a0";
}
[(set (attr "op_type")
(if_then_else (match_operand 0 "register_operand" "")
(const_string "RR") (const_string "RX")))
(set (attr "mnemonic")
(if_then_else (match_operand 0 "register_operand" "")
(const_string "br") (const_string "b")))
(set_attr "type" "branch")
(set_attr "atype" "agen")
(set_attr "cpu_facility" "*")])
(set_attr "atype" "agen")])
(define_insn "indirect_jump_via_thunk<mode>_z10"
[(set (pc)
(match_operand:P 0 "register_operand" "a"))]
"TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK
&& TARGET_CPU_Z10"
{
s390_indirect_branch_via_thunk (REGNO (operands[0]),
INVALID_REGNUM,
NULL_RTX,
s390_indirect_branch_type_jump);
return "";
}
[(set_attr "op_type" "RIL")
(set_attr "mnemonic" "jg")
(set_attr "type" "branch")
(set_attr "atype" "agen")])
(define_insn "indirect_jump_via_thunk<mode>"
[(set (pc)
(match_operand:P 0 "register_operand" " a"))
(clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]
"TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK
&& !TARGET_CPU_Z10"
{
s390_indirect_branch_via_thunk (REGNO (operands[0]),
INVALID_REGNUM,
NULL_RTX,
s390_indirect_branch_type_jump);
return "";
}
[(set_attr "op_type" "RIL")
(set_attr "mnemonic" "jg")
(set_attr "type" "branch")
(set_attr "atype" "agen")])
; The label_ref is wrapped into an if_then_else in order to hide it
; from mark_jump_label. Without this the label_ref would become the
; ONLY jump target of that jump breaking the control flow graph.
(define_insn "indirect_jump_via_inlinethunk<mode>_z10"
[(unspec [(if_then_else (match_operand:P 1 "larl_operand" "X")
(const_int 0)
(const_int 0))
(const_int 0)] UNSPEC_EXECUTE_JUMP)
(set (pc) (match_operand:P 0 "register_operand" "a"))]
"TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK
&& TARGET_CPU_Z10"
{
s390_indirect_branch_via_inline_thunk (operands[1]);
return "";
}
[(set_attr "op_type" "RIL")
(set_attr "type" "branch")
(set_attr "length" "10")])
(define_insn "indirect_jump_via_inlinethunk<mode>"
[(unspec [(if_then_else (match_operand:P 1 "larl_operand" "X")
(const_int 0)
(const_int 0))
(match_operand:P 2 "register_operand" "a")] UNSPEC_EXECUTE_JUMP)
(set (pc) (match_operand:P 0 "register_operand" "a"))]
"TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK
&& !TARGET_CPU_Z10"
{
s390_indirect_branch_via_inline_thunk (operands[2]);
return "";
}
[(set_attr "op_type" "RX")
(set_attr "type" "branch")
(set_attr "length" "8")])
; FIXME: LRA does not appear to be able to deal with MEMs being
; checked against address constraints like ZR above. So make this a
......@@ -9686,7 +9839,7 @@
(define_insn "*indirect2_jump"
[(set (pc)
(match_operand 0 "nonimmediate_operand" "a,T"))]
""
"!TARGET_INDIRECT_BRANCH_NOBP_JUMP"
"@
br\t%0
bi\t%0"
......@@ -9699,10 +9852,73 @@
; casesi instruction pattern(s).
;
(define_insn "casesi_jump"
(define_expand "casesi_jump"
[(parallel
[(set (pc) (match_operand 0 "address_operand"))
(use (label_ref (match_operand 1 "")))])]
""
{
if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK)
{
operands[0] = force_reg (GET_MODE (operands[0]), operands[0]);
if (TARGET_CPU_Z10)
{
if (TARGET_64BIT)
emit_jump_insn (gen_casesi_jump_via_thunkdi_z10 (operands[0],
operands[1]));
else
emit_jump_insn (gen_casesi_jump_via_thunksi_z10 (operands[0],
operands[1]));
}
else
{
if (TARGET_64BIT)
emit_jump_insn (gen_casesi_jump_via_thunkdi (operands[0],
operands[1]));
else
emit_jump_insn (gen_casesi_jump_via_thunksi (operands[0],
operands[1]));
}
DONE;
}
if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK)
{
operands[0] = force_reg (Pmode, operands[0]);
rtx label_ref = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());
if (TARGET_CPU_Z10)
{
if (TARGET_64BIT)
emit_jump_insn (gen_casesi_jump_via_inlinethunkdi_z10 (operands[0],
operands[1],
label_ref));
else
emit_jump_insn (gen_casesi_jump_via_inlinethunksi_z10 (operands[0],
operands[1],
label_ref));
}
else
{
if (TARGET_64BIT)
emit_jump_insn (gen_casesi_jump_via_inlinethunkdi (operands[0],
operands[1],
label_ref,
force_reg (Pmode, label_ref)));
else
emit_jump_insn (gen_casesi_jump_via_inlinethunksi (operands[0],
operands[1],
label_ref,
force_reg (Pmode, label_ref)));
}
DONE;
}
})
(define_insn "*casesi_jump"
[(set (pc) (match_operand 0 "address_operand" "ZR"))
(use (label_ref (match_operand 1 "" "")))]
""
"!TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK"
{
if (get_attr_op_type (insn) == OP_TYPE_RR)
return "br\t%0";
......@@ -9712,9 +9928,85 @@
[(set (attr "op_type")
(if_then_else (match_operand 0 "register_operand" "")
(const_string "RR") (const_string "RX")))
(set (attr "mnemonic")
(if_then_else (match_operand 0 "register_operand" "")
(const_string "br") (const_string "b")))
(set_attr "type" "branch")
(set_attr "atype" "agen")])
(define_insn "casesi_jump_via_thunk<mode>_z10"
[(set (pc) (match_operand:P 0 "register_operand" "a"))
(use (label_ref (match_operand 1 "" "")))]
"TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK
&& TARGET_CPU_Z10"
{
s390_indirect_branch_via_thunk (REGNO (operands[0]),
INVALID_REGNUM,
NULL_RTX,
s390_indirect_branch_type_jump);
return "";
}
[(set_attr "op_type" "RIL")
(set_attr "mnemonic" "jg")
(set_attr "type" "branch")
(set_attr "atype" "agen")])
(define_insn "casesi_jump_via_thunk<mode>"
[(set (pc) (match_operand:P 0 "register_operand" "a"))
(use (label_ref (match_operand 1 "" "")))
(clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]
"TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK
&& !TARGET_CPU_Z10"
{
s390_indirect_branch_via_thunk (REGNO (operands[0]),
INVALID_REGNUM,
NULL_RTX,
s390_indirect_branch_type_jump);
return "";
}
[(set_attr "op_type" "RIL")
(set_attr "mnemonic" "jg")
(set_attr "type" "branch")
(set_attr "atype" "agen")])
; The label_ref is wrapped into an if_then_else in order to hide it
; from mark_jump_label. Without this the label_ref would become the
; ONLY jump target of that jump breaking the control flow graph.
(define_insn "casesi_jump_via_inlinethunk<mode>_z10"
[(unspec [(if_then_else (match_operand:P 2 "larl_operand" "X")
(const_int 0)
(const_int 0))
(const_int 0)] UNSPEC_EXECUTE_JUMP)
(set (pc) (match_operand:P 0 "register_operand" "a"))
(use (label_ref (match_operand 1 "" "")))]
"TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK
&& TARGET_CPU_Z10"
{
s390_indirect_branch_via_inline_thunk (operands[2]);
return "";
}
[(set_attr "op_type" "RIL")
(set_attr "type" "cs")
(set_attr "length" "10")])
(define_insn "casesi_jump_via_inlinethunk<mode>"
[(unspec [(if_then_else (match_operand:P 2 "larl_operand" "X")
(const_int 0)
(const_int 0))
(match_operand:P 3 "register_operand" "a")] UNSPEC_EXECUTE_JUMP)
(set (pc) (match_operand:P 0 "register_operand" "a"))
(use (label_ref (match_operand 1 "" "")))]
"TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK
&& !TARGET_CPU_Z10"
{
s390_indirect_branch_via_inline_thunk (operands[3]);
return "";
}
[(set_attr "op_type" "RX")
(set_attr "type" "cs")
(set_attr "length" "8")])
(define_expand "casesi"
[(match_operand:SI 0 "general_operand" "")
(match_operand:SI 1 "general_operand" "")
......@@ -9819,8 +10111,27 @@
(match_operand 0 "const_int_operand" "n"))]
"SIBLING_CALL_P (insn)
&& GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode"
"br\t%%r1"
[(set_attr "op_type" "RR")
{
if (TARGET_INDIRECT_BRANCH_NOBP_CALL)
{
gcc_assert (TARGET_CPU_Z10);
s390_indirect_branch_via_thunk (SIBCALL_REGNUM,
INVALID_REGNUM,
NULL_RTX,
s390_indirect_branch_type_call);
return "";
}
else
return "br\t%%r1";
}
[(set (attr "op_type")
(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")
(const_string "RIL")
(const_string "RR")))
(set (attr "mnemonic")
(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")
(const_string "jg")
(const_string "br")))
(set_attr "type" "branch")
(set_attr "atype" "agen")])
......@@ -9860,8 +10171,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")])
......@@ -9927,7 +10257,9 @@
[(call (mem:QI (match_operand 0 "address_operand" "ZR"))
(match_operand 1 "const_int_operand" "n"))
(clobber (match_operand 2 "register_operand" "=r"))]
"!SIBLING_CALL_P (insn) && GET_MODE (operands[2]) == Pmode"
"!TARGET_INDIRECT_BRANCH_NOBP_CALL
&& !SIBLING_CALL_P (insn)
&& GET_MODE (operands[2]) == Pmode"
{
if (get_attr_op_type (insn) == OP_TYPE_RR)
return "basr\t%2,%0";
......@@ -9937,6 +10269,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")])
......@@ -9988,7 +10364,9 @@
(call (mem:QI (match_operand 1 "address_operand" "ZR"))
(match_operand 2 "const_int_operand" "n")))
(clobber (match_operand 3 "register_operand" "=r"))]
"!SIBLING_CALL_P (insn) && GET_MODE (operands[3]) == Pmode"
"!TARGET_INDIRECT_BRANCH_NOBP_CALL
&& !SIBLING_CALL_P (insn)
&& GET_MODE (operands[3]) == Pmode"
{
if (get_attr_op_type (insn) == OP_TYPE_RR)
return "basr\t%3,%1";
......@@ -9998,6 +10376,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")])
......@@ -10734,17 +11160,101 @@
(define_insn "<code>"
[(ANY_RETURN)]
"s390_can_use_<code>_insn ()"
"br\t%%r14"
[(set_attr "op_type" "RR")
{
if (TARGET_INDIRECT_BRANCH_NOBP_RET)
{
/* The target is always r14 so there is no clobber
of r1 needed for pre z10 targets. */
s390_indirect_branch_via_thunk (RETURN_REGNUM,
INVALID_REGNUM,
NULL_RTX,
s390_indirect_branch_type_return);
return "";
}
else
return "br\t%%r14";
}
[(set (attr "op_type")
(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
(const_string "RIL")
(const_string "RR")))
(set (attr "mnemonic")
(if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")
(const_string "jg")
(const_string "br")))
(set_attr "type" "jsr")
(set_attr "atype" "agen")])
(define_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: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"
(define_insn "return<mode>_prez10"
[(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"))
(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")])
......
......@@ -233,3 +233,63 @@ Use LRA instead of reload.
mpic-data-is-text-relative
Target Report Var(s390_pic_data_is_text_relative) Init(TARGET_DEFAULT_PIC_DATA_IS_TEXT_RELATIVE)
Assume data segments are relative to text segment.
mindirect-branch=
Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch) Init(indirect_branch_keep)
Wrap all indirect branches into execute in order to disable branch
prediction.
mindirect-branch-jump=
Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch_jump) Init(indirect_branch_keep)
Wrap indirect table jumps and computed gotos into execute in order to
disable branch prediction. Using thunk or thunk-extern with this
option requires the thunks to be considered signal handlers to order to
generate correct CFI. For environments where unwinding (e.g. for
exceptions) is required please use thunk-inline instead.
mindirect-branch-call=
Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch_call) Init(indirect_branch_keep)
Wrap all indirect calls into execute in order to disable branch prediction.
mfunction-return=
Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return) Init(indirect_branch_keep)
Wrap all indirect return branches into execute in order to disable branch
prediction.
mfunction-return-mem=
Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return_mem) Init(indirect_branch_keep)
Wrap indirect return branches into execute in order to disable branch
prediction. This affects only branches where the return address is
going to be restored from memory.
mfunction-return-reg=
Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return_reg) Init(indirect_branch_keep)
Wrap indirect return branches into execute in order to disable branch
prediction. This affects only branches where the return address
doesn't need to be restored from memory.
Enum
Name(indirect_branch) Type(enum indirect_branch)
Known indirect branch choices (for use with the -mindirect-branch=/-mfunction-return= options):
EnumValue
Enum(indirect_branch) String(keep) Value(indirect_branch_keep)
EnumValue
Enum(indirect_branch) String(thunk) Value(indirect_branch_thunk)
EnumValue
Enum(indirect_branch) String(thunk-inline) Value(indirect_branch_thunk_inline)
EnumValue
Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern)
mindirect-branch-table
Target Report Var(s390_indirect_branch_table) Init(TARGET_DEFAULT_INDIRECT_BRANCH_TABLE)
Generate sections .s390_indirect_jump, .s390_indirect_call,
.s390_return_reg, and .s390_return_mem to contain the indirect branch
locations which have been patched as part of using one of the
-mindirect-branch* or -mfunction-return* options. The sections
consist of an array of 32 bit elements. Each entry holds the offset
from the entry to the patched location.
2018-02-08 Andreas Krebbel <krebbel@linux.vnet.ibm.com>
* gcc.target/s390/nobp-function-pointer-attr.c: New test.
* gcc.target/s390/nobp-function-pointer-nothunk.c: New test.
* gcc.target/s390/nobp-function-pointer-z10.c: New test.
* gcc.target/s390/nobp-function-pointer-z900.c: New test.
* gcc.target/s390/nobp-indirect-jump-attr.c: New test.
* gcc.target/s390/nobp-indirect-jump-inline-attr.c: New test.
* gcc.target/s390/nobp-indirect-jump-inline-z10.c: New test.
* gcc.target/s390/nobp-indirect-jump-inline-z900.c: New test.
* gcc.target/s390/nobp-indirect-jump-nothunk.c: New test.
* gcc.target/s390/nobp-indirect-jump-z10.c: New test.
* gcc.target/s390/nobp-indirect-jump-z900.c: New test.
* gcc.target/s390/nobp-return-attr-all.c: New test.
* gcc.target/s390/nobp-return-attr-neg.c: New test.
* gcc.target/s390/nobp-return-mem-attr.c: New test.
* gcc.target/s390/nobp-return-mem-nothunk.c: New test.
* gcc.target/s390/nobp-return-mem-z10.c: New test.
* gcc.target/s390/nobp-return-mem-z900.c: New test.
* gcc.target/s390/nobp-return-reg-attr.c: New test.
* gcc.target/s390/nobp-return-reg-mixed.c: New test.
* gcc.target/s390/nobp-return-reg-nothunk.c: New test.
* gcc.target/s390/nobp-return-reg-z10.c: New test.
* gcc.target/s390/nobp-return-reg-z900.c: New test.
* gcc.target/s390/nobp-table-jump-inline-z10.c: New test.
* gcc.target/s390/nobp-table-jump-inline-z900.c: New test.
* gcc.target/s390/nobp-table-jump-z10.c: New test.
* gcc.target/s390/nobp-table-jump-z900.c: New test.
2018-02-08 Richard Biener <rguenther@suse.de>
PR tree-optimization/84233
......
/* { dg-do run } */
/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-table" } */
int gl;
void __attribute__((noinline,noclone))
foo (int a)
{
gl = a + 40;
}
int __attribute__((noinline,noclone))
foo_value (int a)
{
return a + 40;
}
void* __attribute__((noinline,noclone))
get_fptr (int a)
{
switch (a)
{
case 0: return &foo; break;
case 1: return &foo_value; break;
default: __builtin_abort ();
}
}
void (*f) (int);
int (*g) (int);
int __attribute__((indirect_branch_call("thunk")))
main ()
{
int res;
f = get_fptr(0);
f (2);
if (gl != 42)
__builtin_abort ();
g = get_fptr(1);
if (g (2) != 42)
__builtin_abort ();
return 0;
}
/* 2 x main
/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */
/* { dg-final { scan-assembler "exrl" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
/* { 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" } } */
/* { 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" } } */
/* { 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" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-table" } */
/* { dg-require-effective-target label_values } */
/* This is a copy of the gcc.c-torture/execute/20040302-1.c
testcase. */
int code[]={0,0,0,0,1};
void
foo(int x) {
volatile int b;
b = 0xffffffff;
}
void __attribute__((indirect_branch_jump("thunk")))
bar(int *pc) {
static const void *l[] = {&&lab0, &&end};
foo(0);
goto *l[*pc];
lab0:
foo(0);
pc++;
goto *l[*pc];
end:
return;
}
int main() {
bar(code);
return 0;
}
/* 2x bar */
/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
/* { dg-final { scan-assembler "exrl" } } */
/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-table" } */
/* { dg-require-effective-target label_values } */
/* This is a copy of the gcc.c-torture/execute/20040302-1.c
testcase. */
int code[]={0,0,0,0,1};
void foo(int x) {
volatile int b;
b = 0xffffffff;
}
void __attribute__((indirect_branch_jump("thunk-inline")))
bar(int *pc) {
static const void *l[] = {&&lab0, &&end};
foo(0);
goto *l[*pc];
lab0:
foo(0);
pc++;
goto *l[*pc];
end:
return;
}
int
main() {
bar(code);
return 0;
}
/* The two gotos in bar get merged. */
/* { dg-final { scan-assembler-times "exrl" 1 } } */
/* { dg-final { scan-assembler-not "jg\t__s390_indirect_jump" } } */
/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */
/* { dg-require-effective-target label_values } */
/* This is a copy of the gcc.c-torture/execute/20040302-1.c
testcase. */
int code[]={0,0,0,0,1};
void
foo(int x) {
volatile int b;
b = 0xffffffff;
}
void
bar(int *pc) {
static const void *l[] = {&&lab0, &&end};
foo(0);
goto *l[*pc];
lab0:
foo(0);
pc++;
goto *l[*pc];
end:
return;
}
int
main() {
bar(code);
return 0;
}
/* The two gotos in bar get merged. */
/* { dg-final { scan-assembler-times "exrl" 1 } } */
/* { dg-final { scan-assembler-not "jg\t__s390_indirect_jump" } } */
/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */
/* { dg-require-effective-target label_values } */
/* This is a copy of the gcc.c-torture/execute/20040302-1.c
testcase. */
int code[]={0,0,0,0,1};
void
foo(int x) {
volatile int b;
b = 0xffffffff;
}
void
bar(int *pc) {
static const void *l[] = {&&lab0, &&end};
foo(0);
goto *l[*pc];
lab0:
foo(0);
pc++;
goto *l[*pc];
end:
return;
}
int
main() {
bar(code);
return 0;
}
/* The two gotos in bar get merged. */
/* { dg-final { scan-assembler-times "\tex\t" 1 } } */
/* { dg-final { scan-assembler-not "jg\t__s390_indirect_jump" } } */
/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
/* { dg-do compile } */
/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk-extern -mindirect-branch-table" } */
/* { dg-require-effective-target label_values } */
/* This is a copy of the gcc.c-torture/execute/20040302-1.c
testcase. */
int code[]={0,0,0,0,1};
void
foo(int x) {
volatile int b;
b = 0xffffffff;
}
void
bar(int *pc) {
static const void *l[] = {&&lab0, &&end};
foo(0);
goto *l[*pc];
lab0:
foo(0);
pc++;
goto *l[*pc];
end:
return;
}
int
main() {
bar(code);
return 0;
}
/* 2 x bar
/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
/* No thunks due to thunk-extern. */
/* { dg-final { scan-assembler-not "exrl" } } */
/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */
/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
/* { dg-require-effective-target label_values } */
/* This is a copy of the gcc.c-torture/execute/20040302-1.c
testcase. */
int code[]={0,0,0,0,1};
void
foo(int x) {
volatile int b;
b = 0xffffffff;
}
void
bar(int *pc) {
static const void *l[] = {&&lab0, &&end};
foo(0);
goto *l[*pc];
lab0:
foo(0);
pc++;
goto *l[*pc];
end:
return;
}
int
main() {
bar(code);
return 0;
}
/* 2x bar */
/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
/* { dg-final { scan-assembler "exrl" } } */
/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */
/* { dg-require-effective-target label_values } */
/* This is a copy of the gcc.c-torture/execute/20040302-1.c
testcase. */
int code[]={0,0,0,0,1};
void
foo(int x) {
volatile int b;
b = 0xffffffff;
}
void
bar(int *pc) {
static const void *l[] = {&&lab0, &&end};
foo(0);
goto *l[*pc];
lab0:
foo(0);
pc++;
goto *l[*pc];
end:
return;
}
int
main() {
bar(code);
return 0;
}
/* 2 x bar
/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
/* { dg-final { scan-assembler "ex\t" } } */
/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-table" } */
int gl = 0;
int __attribute__((noinline,noclone))
bar (int a)
{
return a + 2;
}
void __attribute__((function_return("thunk"),noinline,noclone))
foo (int a)
{
int i;
if (a == 42)
return;
for (i = 0; i < a; i++)
gl += bar (i);
}
int
main ()
{
foo (3);
if (gl != 9)
__builtin_abort ();
return 0;
}
/* With -march=z10 -mzarch the shrink wrapped returns use compare and
swap relative to jump to the exit block instead of making use of
the conditional return pattern.
FIXME: Use compare and branch register for that!!!! */
/* 2 x foo
/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
/* { dg-final { scan-assembler "exrl" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler "section\t.s390_return_mem" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mfunction-return-mem=thunk -mindirect-branch-table" } */
int gl = 0;
int __attribute__((noinline,noclone))
bar (int a)
{
return a + 2;
}
void __attribute__((function_return("keep"),noinline,noclone))
foo (int a)
{
int i;
if (a == 42)
return;
for (i = 0; i < a; i++)
gl += bar (i);
}
int __attribute__((function_return("keep")))
main ()
{
foo (3);
if (gl != 9)
__builtin_abort ();
return 0;
}
/* { dg-final { scan-assembler-not "jg\t__s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "exrl" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-table" } */
int gl = 0;
int __attribute__((noinline,noclone))
bar (int a)
{
return a + 2;
}
void __attribute__((function_return_mem("thunk"),noinline,noclone))
foo (int a)
{
int i;
if (a == 42)
return;
for (i = 0; i < a; i++)
gl += bar (i);
}
int
main ()
{
foo (3);
if (gl != 9)
__builtin_abort ();
return 0;
}
/* With -march=z10 -mzarch the shrink wrapped returns use compare and
swap relative to jump to the exit block instead of making use of
the conditional return pattern.
FIXME: Use compare and branch register for that!!!! */
/* 2 x foo
/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
/* { dg-final { scan-assembler "exrl" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler "section\t.s390_return_mem" } } */
/* { dg-do compile } */
/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mfunction-return-mem=thunk-extern -mindirect-branch-table" } */
int gl = 0;
int __attribute__((noinline,noclone))
bar (int a)
{
return a + 2;
}
void __attribute__((noinline,noclone))
foo (int a)
{
int i;
if (a == 42)
return;
for (i = 0; i < a; i++)
gl += bar (i);
}
int
main ()
{
foo (3);
if (gl != 9)
__builtin_abort ();
return 0;
}
/* With -march=z10 -mzarch the shrink wrapped returns use compare and
swap relative to jump to the exit block instead of making use of
the conditional return pattern.
FIXME: Use compare and branch register for that!!!! */
/* 2 x foo, 1 x main
/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 3 } } */
/* No thunks due to thunk-extern. */
/* { dg-final { scan-assembler-not "exrl" } } */
/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler "section\t.s390_return_mem" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mfunction-return-mem=thunk -mindirect-branch-table" } */
int gl = 0;
int __attribute__((noinline,noclone))
bar (int a)
{
return a + 2;
}
void __attribute__((noinline,noclone))
foo (int a)
{
int i;
if (a == 42)
return;
for (i = 0; i < a; i++)
gl += bar (i);
}
int
main ()
{
foo (3);
if (gl != 9)
__builtin_abort ();
return 0;
}
/* With -march=z10 -mzarch the shrink wrapped returns use compare and
swap relative to jump to the exit block instead of making use of
the conditional return pattern.
FIXME: Use compare and branch register for that!!!! */
/* 2 x foo, 1 x main
/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 3 } } */
/* { dg-final { scan-assembler "exrl" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler "section\t.s390_return_mem" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-mem=thunk -mindirect-branch-table" } */
int gl = 0;
int __attribute__((noinline,noclone))
bar (int a)
{
return a + 2;
}
void __attribute__((noinline,noclone))
foo (int a)
{
int i;
if (a == 42)
return;
for (i = 0; i < a; i++)
gl += bar (i);
}
int
main ()
{
foo (3);
if (gl != 9)
__builtin_abort ();
return 0;
}
/* 1 x foo, 1 x main
/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
/* 1 x foo, conditional return, shrink wrapped
/* { dg-final { scan-assembler "jge\t__s390_indirect_jump" } } */
/* 1 x foo, conditional return, shrink wrapped
/* { dg-final { scan-assembler "jgle\t__s390_indirect_jump" } } */
/* { dg-final { scan-assembler "ex\t" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler "section\t.s390_return_mem" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-table" } */
int gl = 0;
int __attribute__((function_return_reg("thunk"),noinline,noclone))
bar (int a)
{
return a + 2;
}
void __attribute__((noinline,noclone))
foo (int a)
{
int i;
if (a == 42)
return;
for (i = 0; i < a; i++)
gl += bar (i);
}
int
main ()
{
foo (3);
if (gl != 9)
__builtin_abort ();
return 0;
}
/* 1 x bar
/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */
/* { dg-final { scan-assembler "exrl" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */
/* We have to generate different thunks for indirect branches
depending on whether the code is compiled for pre z10 machines or
later. This testcase makes sure this works within the same compile
unit. */
int __attribute__((noinline,noclone,target("arch=z10")))
bar (int a)
{
return a + 2;
}
int __attribute__((noinline,noclone,target("arch=z9-ec")))
foo (int a)
{
return a + 3;
}
int
main ()
{
if (bar (42) != 44)
__builtin_abort ();
if (foo (42) != 45)
__builtin_abort ();
return 0;
}
/* 1 x bar, 1 x foo */
/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */
/* 1 x foo */
/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump_r1use" 1 } } */
/* { dg-final { scan-assembler-times "ex\t" 1 } } */
/* { dg-final { scan-assembler-times "exrl\t" 1 } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler "section\t.s390_return_reg" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */
/* { 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" } } */
/* { 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" } } */
/* { 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" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */
/* case-values-threshold will be set to 20 by the back-end when jump
thunk are requested. */
int __attribute__((noinline,noclone)) foo1 (void) { return 1; }
int __attribute__((noinline,noclone)) foo2 (void) { return 2; }
int __attribute__((noinline,noclone)) foo3 (void) { return 3; }
int __attribute__((noinline,noclone)) foo4 (void) { return 4; }
int __attribute__((noinline,noclone)) foo5 (void) { return 5; }
int __attribute__((noinline,noclone)) foo6 (void) { return 6; }
int __attribute__((noinline,noclone)) foo7 (void) { return 7; }
int __attribute__((noinline,noclone)) foo8 (void) { return 8; }
int __attribute__((noinline,noclone)) foo9 (void) { return 9; }
int __attribute__((noinline,noclone)) foo10 (void) { return 10; }
int __attribute__((noinline,noclone)) foo11 (void) { return 11; }
int __attribute__((noinline,noclone)) foo12 (void) { return 12; }
int __attribute__((noinline,noclone)) foo13 (void) { return 13; }
int __attribute__((noinline,noclone)) foo14 (void) { return 14; }
int __attribute__((noinline,noclone)) foo15 (void) { return 15; }
int __attribute__((noinline,noclone)) foo16 (void) { return 16; }
int __attribute__((noinline,noclone)) foo17 (void) { return 17; }
int __attribute__((noinline,noclone)) foo18 (void) { return 18; }
int __attribute__((noinline,noclone)) foo19 (void) { return 19; }
int __attribute__((noinline,noclone)) foo20 (void) { return 20; }
int __attribute__((noinline,noclone))
bar (int a)
{
int ret = 0;
switch (a)
{
case 1: ret = foo1 (); break;
case 2: ret = foo2 (); break;
case 3: ret = foo3 (); break;
case 4: ret = foo4 (); break;
case 5: ret = foo5 (); break;
case 6: ret = foo6 (); break;
case 7: ret = foo7 (); break;
case 8: ret = foo8 (); break;
case 9: ret = foo9 (); break;
case 10: ret = foo10 (); break;
case 11: ret = foo11 (); break;
case 12: ret = foo12 (); break;
case 13: ret = foo13 (); break;
case 14: ret = foo14 (); break;
case 15: ret = foo15 (); break;
case 16: ret = foo16 (); break;
case 17: ret = foo17 (); break;
case 18: ret = foo18 (); break;
case 19: ret = foo19 (); break;
case 20: ret = foo20 (); break;
default:
__builtin_abort ();
}
return ret;
}
int
main ()
{
if (bar (3) != 3)
__builtin_abort ();
return 0;
}
/* 1 x bar
/* { dg-final { scan-assembler-times "exrl" 1 } } */
/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */
/* { dg-do run } */
/* { dg-options "-O3 -march=z900 -mzarch --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */
/* case-values-threshold will be set to 20 by the back-end when jump
thunk are requested. */
int __attribute__((noinline,noclone)) foo1 (void) { return 1; }
int __attribute__((noinline,noclone)) foo2 (void) { return 2; }
int __attribute__((noinline,noclone)) foo3 (void) { return 3; }
int __attribute__((noinline,noclone)) foo4 (void) { return 4; }
int __attribute__((noinline,noclone)) foo5 (void) { return 5; }
int __attribute__((noinline,noclone)) foo6 (void) { return 6; }
int __attribute__((noinline,noclone)) foo7 (void) { return 7; }
int __attribute__((noinline,noclone)) foo8 (void) { return 8; }
int __attribute__((noinline,noclone)) foo9 (void) { return 9; }
int __attribute__((noinline,noclone)) foo10 (void) { return 10; }
int __attribute__((noinline,noclone)) foo11 (void) { return 11; }
int __attribute__((noinline,noclone)) foo12 (void) { return 12; }
int __attribute__((noinline,noclone)) foo13 (void) { return 13; }
int __attribute__((noinline,noclone)) foo14 (void) { return 14; }
int __attribute__((noinline,noclone)) foo15 (void) { return 15; }
int __attribute__((noinline,noclone)) foo16 (void) { return 16; }
int __attribute__((noinline,noclone)) foo17 (void) { return 17; }
int __attribute__((noinline,noclone)) foo18 (void) { return 18; }
int __attribute__((noinline,noclone)) foo19 (void) { return 19; }
int __attribute__((noinline,noclone)) foo20 (void) { return 20; }
int __attribute__((noinline,noclone))
bar (int a)
{
int ret = 0;
switch (a)
{
case 1: ret = foo1 (); break;
case 2: ret = foo2 (); break;
case 3: ret = foo3 (); break;
case 4: ret = foo4 (); break;
case 5: ret = foo5 (); break;
case 6: ret = foo6 (); break;
case 7: ret = foo7 (); break;
case 8: ret = foo8 (); break;
case 9: ret = foo9 (); break;
case 10: ret = foo10 (); break;
case 11: ret = foo11 (); break;
case 12: ret = foo12 (); break;
case 13: ret = foo13 (); break;
case 14: ret = foo14 (); break;
case 15: ret = foo15 (); break;
case 16: ret = foo16 (); break;
case 17: ret = foo17 (); break;
case 18: ret = foo18 (); break;
case 19: ret = foo19 (); break;
case 20: ret = foo20 (); break;
default:
__builtin_abort ();
}
return ret;
}
int
main ()
{
if (bar (3) != 3)
__builtin_abort ();
return 0;
}
/* 1 x bar
/* { dg-final { scan-assembler-times "\tex\t" 1 } } */
/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */
/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */
/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */
/* { 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" } } */
/* { 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" } } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment