This is the mail archive of the
gcc-patches@gcc.gnu.org
mailing list for the GCC project.
Re: [PATCH] S/390: Disable prediction of indirect branches
- From: Richard Biener <richard dot guenther at gmail dot com>
- To: Andreas Krebbel <krebbel at linux dot vnet dot ibm dot com>
- Cc: GCC Patches <gcc-patches at gcc dot gnu dot org>
- Date: Thu, 8 Feb 2018 12:33:35 +0100
- Subject: Re: [PATCH] S/390: Disable prediction of indirect branches
- Authentication-results: sourceware.org; auth=none
- References: <20180207120112.16852-1-krebbel@linux.vnet.ibm.com>
On Wed, Feb 7, 2018 at 1:01 PM, Andreas Krebbel
<krebbel@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 <krebbel@linux.vnet.ibm.com>
>
> * config/s390/s390-opts.h (enum indirect_branch): Define.
> * config/s390/s390-protos.h (s390_return_addr_from_memory)
> (s390_indirect_branch_via_thunk)
> (s390_indirect_branch_via_inline_thunk): Add function prototypes.
> (enum s390_indirect_branch_type): Define.
> * config/s390/s390.c (struct s390_frame_layout, struct
> machine_function): Remove.
> (indirect_branch_prez10thunk_mask, indirect_branch_z10thunk_mask)
> (indirect_branch_table_label_no, indirect_branch_table_name):
> Define variables.
> (INDIRECT_BRANCH_NUM_OPTIONS): Define macro.
> (enum s390_indirect_branch_option): Define.
> (s390_return_addr_from_memory): New function.
> (s390_handle_string_attribute): New function.
> (s390_attribute_table): Add new attribute handler.
> (s390_execute_label): Handle UNSPEC_EXECUTE_JUMP patterns.
> (s390_indirect_branch_via_thunk): New function.
> (s390_indirect_branch_via_inline_thunk): New function.
> (s390_function_ok_for_sibcall): When jumping via thunk disallow
> sibling call optimization for non z10 compiles.
> (s390_emit_call): Force indirect branch target to be a single
> register. Add r1 clobber for non-z10 compiles.
> (s390_emit_epilogue): Emit return jump via return_use expander.
> (s390_reorg): Handle JUMP_INSNs as execute targets.
> (s390_option_override_internal): Perform validity checks for the
> new command line options.
> (s390_indirect_branch_attrvalue): New function.
> (s390_indirect_branch_settings): New function.
> (s390_set_current_function): Invoke s390_indirect_branch_settings.
> (s390_output_indirect_thunk_function): New function.
> (s390_code_end): Implement target hook.
> (s390_case_values_threshold): Implement target hook.
> (TARGET_ASM_CODE_END, TARGET_CASE_VALUES_THRESHOLD): Define target
> macros.
> * config/s390/s390.h (struct s390_frame_layout)
> (struct machine_function): Move here from s390.c.
> (TARGET_INDIRECT_BRANCH_NOBP_RET)
> (TARGET_INDIRECT_BRANCH_NOBP_JUMP)
> (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK)
> (TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK)
> (TARGET_INDIRECT_BRANCH_NOBP_CALL)
> (TARGET_DEFAULT_INDIRECT_BRANCH_TABLE)
> (TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL)
> (TARGET_INDIRECT_BRANCH_THUNK_NAME_EX)
> (TARGET_INDIRECT_BRANCH_TABLE): Define macros.
> * config/s390/s390.md (UNSPEC_EXECUTE_JUMP)
> (INDIRECT_BRANCH_THUNK_REGNUM): Define constants.
> (mnemonic attribute): Add values which aren't recognized
> automatically.
> ("*cjump_long", "*icjump_long", "*basr", "*basr_r"): Disable
> pattern for branch conversion. Fix mnemonic attribute.
> ("*c<code>", "*sibcall_br", "*sibcall_value_br", "*return"): Emit
> indirect branch via thunk if requested.
> ("indirect_jump", "<code>"): Expand patterns for branch conversion.
> ("*indirect_jump"): Disable for branch conversion using out of
> line thunks.
> ("indirect_jump_via_thunk<mode>_z10")
> ("indirect_jump_via_thunk<mode>")
> ("indirect_jump_via_inlinethunk<mode>_z10")
> ("indirect_jump_via_inlinethunk<mode>", "*casesi_jump")
> ("casesi_jump_via_thunk<mode>_z10", "casesi_jump_via_thunk<mode>")
> ("casesi_jump_via_inlinethunk<mode>_z10")
> ("casesi_jump_via_inlinethunk<mode>", "*basr_via_thunk<mode>_z10")
> ("*basr_via_thunk<mode>", "*basr_r_via_thunk_z10")
> ("*basr_r_via_thunk", "return<mode>_prez10"): New pattern.
> ("*indirect2_jump"): Disable for branch conversion.
> ("casesi_jump"): Turn into expander and expand patterns for branch
> conversion.
> ("return_use"): New expander.
> ("*return"): Emit return via thunk and rename it to ...
> ("*return<mode>"): ... this one.
> * config/s390/s390.opt: Add new options and and enum for the
> option values.
>
> gcc/testsuite/ChangeLog:
>
> 2018-02-07 Andreas Krebbel <krebbel@linux.vnet.ibm.com>
>
> * gcc.target/s390/nobp-function-pointer-attr.c: New test.
> * gcc.target/s390/nobp-function-pointer-nothunk.c: New test.
> * gcc.target/s390/nobp-function-pointer-z10.c: New test.
> * gcc.target/s390/nobp-function-pointer-z900.c: New test.
> * gcc.target/s390/nobp-indirect-jump-attr.c: New test.
> * gcc.target/s390/nobp-indirect-jump-inline-attr.c: New test.
> * gcc.target/s390/nobp-indirect-jump-inline-z10.c: New test.
> * gcc.target/s390/nobp-indirect-jump-inline-z900.c: New test.
> * gcc.target/s390/nobp-indirect-jump-nothunk.c: New test.
> * gcc.target/s390/nobp-indirect-jump-z10.c: New test.
> * gcc.target/s390/nobp-indirect-jump-z900.c: New test.
> * gcc.target/s390/nobp-return-attr-all.c: New test.
> * gcc.target/s390/nobp-return-attr-neg.c: New test.
> * gcc.target/s390/nobp-return-mem-attr.c: New test.
> * gcc.target/s390/nobp-return-mem-nothunk.c: New test.
> * gcc.target/s390/nobp-return-mem-z10.c: New test.
> * gcc.target/s390/nobp-return-mem-z900.c: New test.
> * gcc.target/s390/nobp-return-reg-attr.c: New test.
> * gcc.target/s390/nobp-return-reg-mixed.c: New test.
> * gcc.target/s390/nobp-return-reg-nothunk.c: New test.
> * gcc.target/s390/nobp-return-reg-z10.c: New test.
> * gcc.target/s390/nobp-return-reg-z900.c: New test.
> * gcc.target/s390/nobp-table-jump-inline-z10.c: New test.
> * gcc.target/s390/nobp-table-jump-inline-z900.c: New test.
> * gcc.target/s390/nobp-table-jump-z10.c: New test.
> * gcc.target/s390/nobp-table-jump-z900.c: New test.
> ---
> 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
>