On Wed, Feb 7, 2018 at 1:01 PM, Andreas Krebbel <kreb...@linux.vnet.ibm.com> wrote: > 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. > > I plan to commit the patch tomorrow.
Do you also plan to backport this to the GCC 7 branch? > gcc/ChangeLog: > > 2018-02-07 Andreas Krebbel <kreb...@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-07 Andreas Krebbel <kreb...@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. > --- > gcc/config/s390/s390-opts.h | 9 + > gcc/config/s390/s390-protos.h | 12 + > gcc/config/s390/s390.c | 700 > +++++++++++++++++---- > gcc/config/s390/s390.h | 120 ++++ > gcc/config/s390/s390.md | 574 ++++++++++++++++- > gcc/config/s390/s390.opt | 60 ++ > .../gcc.target/s390/nobp-function-pointer-attr.c | 56 ++ > .../s390/nobp-function-pointer-nothunk.c | 59 ++ > .../gcc.target/s390/nobp-function-pointer-z10.c | 56 ++ > .../gcc.target/s390/nobp-function-pointer-z900.c | 56 ++ > .../gcc.target/s390/nobp-indirect-jump-attr.c | 42 ++ > .../s390/nobp-indirect-jump-inline-attr.c | 42 ++ > .../s390/nobp-indirect-jump-inline-z10.c | 43 ++ > .../s390/nobp-indirect-jump-inline-z900.c | 43 ++ > .../gcc.target/s390/nobp-indirect-jump-nothunk.c | 46 ++ > .../gcc.target/s390/nobp-indirect-jump-z10.c | 43 ++ > .../gcc.target/s390/nobp-indirect-jump-z900.c | 43 ++ > .../gcc.target/s390/nobp-return-attr-all.c | 46 ++ > .../gcc.target/s390/nobp-return-attr-neg.c | 40 ++ > .../gcc.target/s390/nobp-return-mem-attr.c | 46 ++ > .../gcc.target/s390/nobp-return-mem-nothunk.c | 49 ++ > .../gcc.target/s390/nobp-return-mem-z10.c | 46 ++ > .../gcc.target/s390/nobp-return-mem-z900.c | 48 ++ > .../gcc.target/s390/nobp-return-reg-attr.c | 41 ++ > .../gcc.target/s390/nobp-return-reg-mixed.c | 44 ++ > .../gcc.target/s390/nobp-return-reg-nothunk.c | 44 ++ > .../gcc.target/s390/nobp-return-reg-z10.c | 41 ++ > .../gcc.target/s390/nobp-return-reg-z900.c | 41 ++ > .../gcc.target/s390/nobp-table-jump-inline-z10.c | 78 +++ > .../gcc.target/s390/nobp-table-jump-inline-z900.c | 78 +++ > .../gcc.target/s390/nobp-table-jump-z10.c | 77 +++ > .../gcc.target/s390/nobp-table-jump-z900.c | 78 +++ > 32 files changed, 2662 insertions(+), 139 deletions(-) > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c > create mode 100644 > gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c > create mode 100644 > gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c > create mode 100644 > gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c > create mode 100644 > gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c > create mode 100644 > gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c > create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c > > diff --git a/gcc/config/s390/s390-opts.h b/gcc/config/s390/s390-opts.h > index 23632ba..aaecca7 100644 > --- a/gcc/config/s390/s390-opts.h > +++ b/gcc/config/s390/s390-opts.h > @@ -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 > diff --git a/gcc/config/s390/s390-protos.h b/gcc/config/s390/s390-protos.h > index 214062a..46f0743 100644 > --- a/gcc/config/s390/s390-protos.h > +++ b/gcc/config/s390/s390-protos.h > @@ -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 */ > diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c > index 03c93f1..62a60e2 100644 > --- a/gcc/config/s390/s390.c > +++ b/gcc/config/s390/s390.c > @@ -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) > - return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2); > + && (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) > - return_regnum = 4; > + if (!return_regnum > + || (TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION > + && !TARGET_CPU_Z10 > + && return_regnum == INDIRECT_BRANCH_THUNK_REGNUM)) > + { > + gcc_assert (INDIRECT_BRANCH_THUNK_REGNUM != 4); > + return_regnum = 4; > + } > return_reg = gen_rtx_REG (Pmode, return_regnum); > > addr = plus_constant (Pmode, frame_pointer, > @@ -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); > + } > + > > - call = gen_rtx_PARALLEL (VOIDmode, vec); > + 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, 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,7 +14645,16 @@ s390_reorg (void) > target = emit_label (XEXP (label, 0)); > INSN_ADDRESSES_NEW (target, -1); > > - target = emit_insn (s390_execute_target (insn)); > + 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" > diff --git a/gcc/config/s390/s390.h b/gcc/config/s390/s390.h > index 4564a2e..de71fd9 100644 > --- a/gcc/config/s390/s390.h > +++ b/gcc/config/s390/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 */ > diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md > index ef71132..5481f13 100644 > --- a/gcc/config/s390/s390.md > +++ b/gcc/config/s390/s390.md > @@ -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,11 +9852,74 @@ > ; casesi instruction pattern(s). > ; > > -(define_insn "casesi_jump" > - [(set (pc) (match_operand 0 "address_operand" "ZR")) > - (use (label_ref (match_operand 1 "" "")))] > +(define_expand "casesi_jump" > + [(parallel > + [(set (pc) (match_operand 0 "address_operand")) > + (use (label_ref (match_operand 1 "")))])] > "" > { > + if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK) > + { > + operands[0] = force_reg (GET_MODE (operands[0]), operands[0]); > + > + if (TARGET_CPU_Z10) > + { > + if (TARGET_64BIT) > + emit_jump_insn (gen_casesi_jump_via_thunkdi_z10 (operands[0], > + operands[1])); > + else > + emit_jump_insn (gen_casesi_jump_via_thunksi_z10 (operands[0], > + operands[1])); > + } > + else > + { > + if (TARGET_64BIT) > + emit_jump_insn (gen_casesi_jump_via_thunkdi (operands[0], > + operands[1])); > + else > + emit_jump_insn (gen_casesi_jump_via_thunksi (operands[0], > + operands[1])); > + } > + DONE; > + } > + > + 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"; > else > @@ -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_insn "*return" > + > +(define_expand "return_use" > + [(parallel > + [(return) > + (use (match_operand 0 "register_operand" "a"))])] > + "" > +{ > + if (!TARGET_CPU_Z10 > + && TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION) > + { > + if (TARGET_64BIT) > + emit_jump_insn (gen_returndi_prez10 (operands[0])); > + else > + emit_jump_insn (gen_returnsi_prez10 (operands[0])); > + DONE; > + } > +}) > + > +(define_insn "*return<mode>" > [(return) > - (use (match_operand 0 "register_operand" "a"))] > - "GET_MODE (operands[0]) == Pmode" > - "br\t%0" > - [(set_attr "op_type" "RR") > + (use (match_operand:P 0 "register_operand" "a"))] > + "TARGET_CPU_Z10 || !TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION" > +{ > + if (TARGET_INDIRECT_BRANCH_NOBP_RET) > + { > + s390_indirect_branch_via_thunk (REGNO (operands[0]), > + INVALID_REGNUM, > + NULL_RTX, > + s390_indirect_branch_type_return); > + return ""; > + } > + else > + return "br\t%0"; > +} > + [(set (attr "op_type") > + (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET") > + (const_string "RIL") > + (const_string "RR"))) > + (set (attr "mnemonic") > + (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET") > + (const_string "jg") > + (const_string "br"))) > + (set_attr "type" "jsr") > + (set_attr "atype" "agen")]) > + > +(define_insn "return<mode>_prez10" > + [(return) > + (use (match_operand:P 0 "register_operand" "a")) > + (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))] > + "!TARGET_CPU_Z10 && TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION" > +{ > + if (TARGET_INDIRECT_BRANCH_NOBP_RET) > + { > + s390_indirect_branch_via_thunk (REGNO (operands[0]), > + INVALID_REGNUM, > + NULL_RTX, > + s390_indirect_branch_type_return); > + return ""; > + } > + else > + return "br\t%0"; > +} > + [(set (attr "op_type") > + (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET") > + (const_string "RIL") > + (const_string "RR"))) > + (set (attr "mnemonic") > + (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET") > + (const_string "jg") > + (const_string "br"))) > (set_attr "type" "jsr") > (set_attr "atype" "agen")]) > > diff --git a/gcc/config/s390/s390.opt b/gcc/config/s390/s390.opt > index ea969cd..eb16f9c 100644 > --- a/gcc/config/s390/s390.opt > +++ b/gcc/config/s390/s390.opt > @@ -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. > diff --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c > b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c > new file mode 100644 > index 0000000..db9336d > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c > @@ -0,0 +1,56 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c > b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c > new file mode 100644 > index 0000000..c02b45a > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c > @@ -0,0 +1,59 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O3 -march=z10 --save-temps > -mindirect-branch-call=thunk-extern -mindirect-branch-table" } */ > + > +int gl; > + > +void __attribute__((noinline,noclone)) > +foo (int a) > +{ > + gl = a + 40; > +} > + > +int __attribute__((noinline,noclone)) > +foo_value (int a) > +{ > + return a + 40; > +} > + > +void* __attribute__((noinline,noclone)) > +get_fptr (int a) > +{ > + switch (a) > + { > + case 0: return &foo; break; > + case 1: return &foo_value; break; > + default: __builtin_abort (); > + } > +} > + > +void (*f) (int); > +int (*g) (int); > + > +int > +main () > +{ > + int res; > + > + f = get_fptr(0); > + f (2); > + if (gl != 42) > + __builtin_abort (); > + > + g = get_fptr(1); > + if (g (2) != 42) > + __builtin_abort (); > + > + return 0; > +} > + > +/* 2 x main > +/* { dg-final { scan-assembler-times > "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */ > + > +/* No thunks due to thunk-extern. */ > +/* { dg-final { scan-assembler-not "exrl" } } */ > +/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */ > + > +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ > +/* { dg-final { scan-assembler "section\t.s390_indirect_call" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c > b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c > new file mode 100644 > index 0000000..b5f13eb > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c > @@ -0,0 +1,56 @@ > +/* { dg-do run } */ > +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-call=thunk > -mindirect-branch-table" } */ > + > +int gl; > + > +void __attribute__((noinline,noclone)) > +foo (int a) > +{ > + gl = a + 40; > +} > + > +int __attribute__((noinline,noclone)) > +foo_value (int a) > +{ > + return a + 40; > +} > + > +void* __attribute__((noinline,noclone)) > +get_fptr (int a) > +{ > + switch (a) > + { > + case 0: return &foo; break; > + case 1: return &foo_value; break; > + default: __builtin_abort (); > + } > +} > + > +void (*f) (int); > +int (*g) (int); > + > +int > +main () > +{ > + int res; > + > + f = get_fptr(0); > + f (2); > + if (gl != 42) > + __builtin_abort (); > + > + g = get_fptr(1); > + if (g (2) != 42) > + __builtin_abort (); > + > + return 0; > +} > + > +/* 2 x main > +/* { dg-final { scan-assembler-times > "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */ > +/* { dg-final { scan-assembler "exrl" } } */ > + > +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ > +/* { dg-final { scan-assembler "section\t.s390_indirect_call" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c > b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c > new file mode 100644 > index 0000000..486495b > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c > @@ -0,0 +1,56 @@ > +/* { dg-do run } */ > +/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-call=thunk > -mindirect-branch-table" } */ > + > +int gl; > + > +void __attribute__((noinline,noclone)) > +foo (int a) > +{ > + gl = a + 40; > +} > + > +int __attribute__((noinline,noclone)) > +foo_value (int a) > +{ > + return a + 40; > +} > + > +void* __attribute__((noinline,noclone)) > +get_fptr (int a) > +{ > + switch (a) > + { > + case 0: return &foo; break; > + case 1: return &foo_value; break; > + default: __builtin_abort (); > + } > +} > + > +void (*f) (int); > +int (*g) (int); > + > +int > +main () > +{ > + int res; > + > + f = get_fptr(0); > + f (2); > + if (gl != 42) > + __builtin_abort (); > + > + g = get_fptr(1); > + if (g (2) != 42) > + __builtin_abort (); > + > + return 0; > +} > + > +/* 2 x main > +/* { dg-final { scan-assembler-times > "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */ > +/* { dg-final { scan-assembler "ex\t" } } */ > + > +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ > +/* { dg-final { scan-assembler "section\t.s390_indirect_call" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c > b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c > new file mode 100644 > index 0000000..c62ddf5 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c > @@ -0,0 +1,42 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c > b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c > new file mode 100644 > index 0000000..63d64c1 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c > @@ -0,0 +1,42 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c > b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c > new file mode 100644 > index 0000000..28d7837 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c > @@ -0,0 +1,43 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c > b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c > new file mode 100644 > index 0000000..3c0c007 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c > @@ -0,0 +1,43 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c > b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c > new file mode 100644 > index 0000000..05c8bb8 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c > @@ -0,0 +1,46 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c > b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c > new file mode 100644 > index 0000000..71c86fd > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c > @@ -0,0 +1,43 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c > b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c > new file mode 100644 > index 0000000..89ad799 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c > @@ -0,0 +1,43 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c > b/gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c > new file mode 100644 > index 0000000..4bf88cf > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c > @@ -0,0 +1,46 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c > b/gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c > new file mode 100644 > index 0000000..8b32bfe > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c > @@ -0,0 +1,40 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c > b/gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c > new file mode 100644 > index 0000000..39cab8b > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c > @@ -0,0 +1,46 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c > b/gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c > new file mode 100644 > index 0000000..f99f152 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c > @@ -0,0 +1,49 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c > b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c > new file mode 100644 > index 0000000..177fc32 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c > @@ -0,0 +1,46 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c > b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c > new file mode 100644 > index 0000000..0b31811 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c > @@ -0,0 +1,48 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c > b/gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c > new file mode 100644 > index 0000000..ebfc9ff > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c > @@ -0,0 +1,41 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c > b/gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c > new file mode 100644 > index 0000000..82833f7 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c > @@ -0,0 +1,44 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c > b/gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c > new file mode 100644 > index 0000000..4ea14e3 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c > @@ -0,0 +1,44 @@ > +/* { dg-do compile } */ > +/* { dg-options "-O3 -march=z10 --save-temps > -mfunction-return-reg=thunk-extern -mindirect-branch-table" } */ > + > +int gl = 0; > + > +int __attribute__((noinline,noclone)) > +bar (int a) > +{ > + return a + 2; > +} > + > +void __attribute__((noinline,noclone)) > +foo (int a) > +{ > + int i; > + > + if (a == 42) > + return; > + > + for (i = 0; i < a; i++) > + gl += bar (i); > +} > + > +int > +main () > +{ > + foo (3); > + if (gl != 9) > + __builtin_abort (); > + > + return 0; > +} > + > +/* 1 x bar > +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */ > + > +/* No thunks due to thunk-extern. */ > +/* { dg-final { scan-assembler-not "exrl" } } */ > +/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */ > + > +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ > +/* { dg-final { scan-assembler "section\t.s390_return_reg" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c > b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c > new file mode 100644 > index 0000000..42c3e74 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c > @@ -0,0 +1,41 @@ > +/* { dg-do run } */ > +/* { dg-options "-O3 -march=z10 --save-temps -mfunction-return-reg=thunk > -mindirect-branch-table" } */ > + > +int gl = 0; > + > +int __attribute__((noinline,noclone)) > +bar (int a) > +{ > + return a + 2; > +} > + > +void __attribute__((noinline,noclone)) > +foo (int a) > +{ > + int i; > + > + if (a == 42) > + return; > + > + for (i = 0; i < a; i++) > + gl += bar (i); > +} > + > +int > +main () > +{ > + foo (3); > + if (gl != 9) > + __builtin_abort (); > + > + return 0; > +} > + > +/* 1 x bar > +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */ > +/* { dg-final { scan-assembler "exrl" } } */ > + > +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ > +/* { dg-final { scan-assembler "section\t.s390_return_reg" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c > b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c > new file mode 100644 > index 0000000..3f4efa5 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c > @@ -0,0 +1,41 @@ > +/* { dg-do run } */ > +/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-reg=thunk > -mindirect-branch-table" } */ > + > +int gl = 0; > + > +int __attribute__((noinline,noclone)) > +bar (int a) > +{ > + return a + 2; > +} > + > +void __attribute__((noinline,noclone)) > +foo (int a) > +{ > + int i; > + > + if (a == 42) > + return; > + > + for (i = 0; i < a; i++) > + gl += bar (i); > +} > + > +int > +main () > +{ > + foo (3); > + if (gl != 9) > + __builtin_abort (); > + > + return 0; > +} > + > +/* 1 x bar > +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */ > +/* { dg-final { scan-assembler "ex\t" } } */ > + > +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ > +/* { dg-final { scan-assembler "section\t.s390_return_reg" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c > b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c > new file mode 100644 > index 0000000..8dfd7e4 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c > @@ -0,0 +1,78 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c > b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c > new file mode 100644 > index 0000000..46d2c54 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c > @@ -0,0 +1,78 @@ > +/* { 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" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c > b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c > new file mode 100644 > index 0000000..9dfe391 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c > @@ -0,0 +1,77 @@ > +/* { dg-do run } */ > +/* { dg-options "-O3 -march=z10 -mzarch --save-temps > -mindirect-branch-jump=thunk -mindirect-branch-table" } */ > +/* case-values-threshold will be set to 20 by the back-end when jump > + thunk are requested. */ > + > +int __attribute__((noinline,noclone)) foo1 (void) { return 1; } > +int __attribute__((noinline,noclone)) foo2 (void) { return 2; } > +int __attribute__((noinline,noclone)) foo3 (void) { return 3; } > +int __attribute__((noinline,noclone)) foo4 (void) { return 4; } > +int __attribute__((noinline,noclone)) foo5 (void) { return 5; } > +int __attribute__((noinline,noclone)) foo6 (void) { return 6; } > +int __attribute__((noinline,noclone)) foo7 (void) { return 7; } > +int __attribute__((noinline,noclone)) foo8 (void) { return 8; } > +int __attribute__((noinline,noclone)) foo9 (void) { return 9; } > +int __attribute__((noinline,noclone)) foo10 (void) { return 10; } > +int __attribute__((noinline,noclone)) foo11 (void) { return 11; } > +int __attribute__((noinline,noclone)) foo12 (void) { return 12; } > +int __attribute__((noinline,noclone)) foo13 (void) { return 13; } > +int __attribute__((noinline,noclone)) foo14 (void) { return 14; } > +int __attribute__((noinline,noclone)) foo15 (void) { return 15; } > +int __attribute__((noinline,noclone)) foo16 (void) { return 16; } > +int __attribute__((noinline,noclone)) foo17 (void) { return 17; } > +int __attribute__((noinline,noclone)) foo18 (void) { return 18; } > +int __attribute__((noinline,noclone)) foo19 (void) { return 19; } > +int __attribute__((noinline,noclone)) foo20 (void) { return 20; } > + > + > +int __attribute__((noinline,noclone)) > +bar (int a) > +{ > + int ret = 0; > + > + switch (a) > + { > + case 1: ret = foo1 (); break; > + case 2: ret = foo2 (); break; > + case 3: ret = foo3 (); break; > + case 4: ret = foo4 (); break; > + case 5: ret = foo5 (); break; > + case 6: ret = foo6 (); break; > + case 7: ret = foo7 (); break; > + case 8: ret = foo8 (); break; > + case 9: ret = foo9 (); break; > + case 10: ret = foo10 (); break; > + case 11: ret = foo11 (); break; > + case 12: ret = foo12 (); break; > + case 13: ret = foo13 (); break; > + case 14: ret = foo14 (); break; > + case 15: ret = foo15 (); break; > + case 16: ret = foo16 (); break; > + case 17: ret = foo17 (); break; > + case 18: ret = foo18 (); break; > + case 19: ret = foo19 (); break; > + case 20: ret = foo20 (); break; > + default: > + __builtin_abort (); > + } > + > + return ret; > +} > + > +int > +main () > +{ > + if (bar (3) != 3) > + __builtin_abort (); > + > + return 0; > +} > + > +/* 1 x bar > +/* { dg-final { scan-assembler-times "exrl" 1 } } */ > + > +/* { dg-final { scan-assembler "section\t.s390_indirect_jump" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */ > +/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */ > diff --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c > b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c > new file mode 100644 > index 0000000..f1439a8 > --- /dev/null > +++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c > @@ -0,0 +1,78 @@ > +/* { dg-do run } */ > +/* { dg-options "-O3 -march=z900 -mzarch --save-temps > -mindirect-branch-jump=thunk -mindirect-branch-table" } */ > + > +/* case-values-threshold will be set to 20 by the back-end when jump > + thunk are requested. */ > + > +int __attribute__((noinline,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" } } */ > -- > 2.9.1 >