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 */
......
......@@ -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 */
......@@ -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