On Wed, Feb 7, 2018 at 1:01 PM, Andreas Krebbel
<kreb...@linux.vnet.ibm.com> wrote:
> This patch implements GCC support for mitigating vulnerability
> CVE-2017-5715 known as Spectre #2 on IBM Z.
>
> In order to disable prediction of indirect branches the implementation
> makes use of an IBM Z specific feature - the execute instruction.
> Performing an indirect branch via execute prevents the branch from
> being subject to dynamic branch prediction.
>
> The implementation tries to stay close to the x86 solution regarding
> user interface.
>
> x86 style options supported (without thunk-inline):
>
> -mindirect-branch=(keep|thunk|thunk-extern)
> -mfunction-return=(keep|thunk|thunk-extern)
>
> IBM Z specific options:
>
> -mindirect-branch-jump=(keep|thunk|thunk-extern|thunk-inline)
> -mindirect-branch-call=(keep|thunk|thunk-extern)
> -mfunction-return-reg=(keep|thunk|thunk-extern)
> -mfunction-return-mem=(keep|thunk|thunk-extern)
>
> These options allow us to enable/disable the branch conversion at a
> finer granularity.
>
> -mindirect-branch sets the value of -mindirect-branch-jump and
>  -mindirect-branch-call.
>
> -mfunction-return sets the value of -mfunction-return-reg and
>  -mfunction-return-mem.
>
> All these options are supported on GCC command line as well as
> function attributes.
>
> 'thunk' triggers the generation of out of line thunks (expolines) and
> replaces the formerly indirect branch with a direct branch to the
> thunk.  Depending on the -march= setting two different types of thunks
> are generated.  With -march=z10 or higher exrl (execute relative long)
> is being used while targeting older machines makes use of larl/ex
> instead.  From a security perspective the exrl variant is preferable.
>
> 'thunk-extern' does the branch replacement like 'thunk' but does not
> emit the thunks.
>
> 'thunk-inline' is only available for indirect jumps.  It should be used
> in environments where correct CFI is important - known as user space.
>
> Additionally the patch introduces the -mindirect-branch-table option
> which generates tables pointing to the locations which have been
> modified.  This is supposed to allow reverting the changes without
> re-compilation in situations where it isn't required. The sections are
> split up into one section per option.
>
> I plan to commit the patch tomorrow.

Do you also plan to backport this to the GCC 7 branch?

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

Reply via email to