S/390: Disable prediction of indirect branches

Message ID 20180207120112.16852-1-krebbel@linux.vnet.ibm.com
State Superseded
Headers show
Series
  • S/390: Disable prediction of indirect branches
Related show

Commit Message

Andreas Krebbel Feb. 7, 2018, 12:01 p.m.
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.

gcc/ChangeLog:

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

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

gcc/testsuite/ChangeLog:

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

	* gcc.target/s390/nobp-function-pointer-attr.c: New test.
	* gcc.target/s390/nobp-function-pointer-nothunk.c: New test.
	* gcc.target/s390/nobp-function-pointer-z10.c: New test.
	* gcc.target/s390/nobp-function-pointer-z900.c: New test.
	* gcc.target/s390/nobp-indirect-jump-attr.c: New test.
	* gcc.target/s390/nobp-indirect-jump-inline-attr.c: New test.
	* gcc.target/s390/nobp-indirect-jump-inline-z10.c: New test.
	* gcc.target/s390/nobp-indirect-jump-inline-z900.c: New test.
	* gcc.target/s390/nobp-indirect-jump-nothunk.c: New test.
	* gcc.target/s390/nobp-indirect-jump-z10.c: New test.
	* gcc.target/s390/nobp-indirect-jump-z900.c: New test.
	* gcc.target/s390/nobp-return-attr-all.c: New test.
	* gcc.target/s390/nobp-return-attr-neg.c: New test.
	* gcc.target/s390/nobp-return-mem-attr.c: New test.
	* gcc.target/s390/nobp-return-mem-nothunk.c: New test.
	* gcc.target/s390/nobp-return-mem-z10.c: New test.
	* gcc.target/s390/nobp-return-mem-z900.c: New test.
	* gcc.target/s390/nobp-return-reg-attr.c: New test.
	* gcc.target/s390/nobp-return-reg-mixed.c: New test.
	* gcc.target/s390/nobp-return-reg-nothunk.c: New test.
	* gcc.target/s390/nobp-return-reg-z10.c: New test.
	* gcc.target/s390/nobp-return-reg-z900.c: New test.
	* gcc.target/s390/nobp-table-jump-inline-z10.c: New test.
	* gcc.target/s390/nobp-table-jump-inline-z900.c: New test.
	* gcc.target/s390/nobp-table-jump-z10.c: New test.
	* gcc.target/s390/nobp-table-jump-z900.c: New test.
---
 gcc/config/s390/s390-opts.h                        |   9 +
 gcc/config/s390/s390-protos.h                      |  12 +
 gcc/config/s390/s390.c                             | 700 +++++++++++++++++----
 gcc/config/s390/s390.h                             | 120 ++++
 gcc/config/s390/s390.md                            | 574 ++++++++++++++++-
 gcc/config/s390/s390.opt                           |  60 ++
 .../gcc.target/s390/nobp-function-pointer-attr.c   |  56 ++
 .../s390/nobp-function-pointer-nothunk.c           |  59 ++
 .../gcc.target/s390/nobp-function-pointer-z10.c    |  56 ++
 .../gcc.target/s390/nobp-function-pointer-z900.c   |  56 ++
 .../gcc.target/s390/nobp-indirect-jump-attr.c      |  42 ++
 .../s390/nobp-indirect-jump-inline-attr.c          |  42 ++
 .../s390/nobp-indirect-jump-inline-z10.c           |  43 ++
 .../s390/nobp-indirect-jump-inline-z900.c          |  43 ++
 .../gcc.target/s390/nobp-indirect-jump-nothunk.c   |  46 ++
 .../gcc.target/s390/nobp-indirect-jump-z10.c       |  43 ++
 .../gcc.target/s390/nobp-indirect-jump-z900.c      |  43 ++
 .../gcc.target/s390/nobp-return-attr-all.c         |  46 ++
 .../gcc.target/s390/nobp-return-attr-neg.c         |  40 ++
 .../gcc.target/s390/nobp-return-mem-attr.c         |  46 ++
 .../gcc.target/s390/nobp-return-mem-nothunk.c      |  49 ++
 .../gcc.target/s390/nobp-return-mem-z10.c          |  46 ++
 .../gcc.target/s390/nobp-return-mem-z900.c         |  48 ++
 .../gcc.target/s390/nobp-return-reg-attr.c         |  41 ++
 .../gcc.target/s390/nobp-return-reg-mixed.c        |  44 ++
 .../gcc.target/s390/nobp-return-reg-nothunk.c      |  44 ++
 .../gcc.target/s390/nobp-return-reg-z10.c          |  41 ++
 .../gcc.target/s390/nobp-return-reg-z900.c         |  41 ++
 .../gcc.target/s390/nobp-table-jump-inline-z10.c   |  78 +++
 .../gcc.target/s390/nobp-table-jump-inline-z900.c  |  78 +++
 .../gcc.target/s390/nobp-table-jump-z10.c          |  77 +++
 .../gcc.target/s390/nobp-table-jump-z900.c         |  78 +++
 32 files changed, 2662 insertions(+), 139 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c
 create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c

-- 
2.9.1

Comments

Richard Biener Feb. 8, 2018, 11:33 a.m. | #1
On Wed, Feb 7, 2018 at 1:01 PM, Andreas Krebbel
<krebbel@linux.vnet.ibm.com> wrote:
> This patch implements GCC support for mitigating vulnerability

> CVE-2017-5715 known as Spectre #2 on IBM Z.

>

> In order to disable prediction of indirect branches the implementation

> makes use of an IBM Z specific feature - the execute instruction.

> Performing an indirect branch via execute prevents the branch from

> being subject to dynamic branch prediction.

>

> The implementation tries to stay close to the x86 solution regarding

> user interface.

>

> x86 style options supported (without thunk-inline):

>

> -mindirect-branch=(keep|thunk|thunk-extern)

> -mfunction-return=(keep|thunk|thunk-extern)

>

> IBM Z specific options:

>

> -mindirect-branch-jump=(keep|thunk|thunk-extern|thunk-inline)

> -mindirect-branch-call=(keep|thunk|thunk-extern)

> -mfunction-return-reg=(keep|thunk|thunk-extern)

> -mfunction-return-mem=(keep|thunk|thunk-extern)

>

> These options allow us to enable/disable the branch conversion at a

> finer granularity.

>

> -mindirect-branch sets the value of -mindirect-branch-jump and

>  -mindirect-branch-call.

>

> -mfunction-return sets the value of -mfunction-return-reg and

>  -mfunction-return-mem.

>

> All these options are supported on GCC command line as well as

> function attributes.

>

> 'thunk' triggers the generation of out of line thunks (expolines) and

> replaces the formerly indirect branch with a direct branch to the

> thunk.  Depending on the -march= setting two different types of thunks

> are generated.  With -march=z10 or higher exrl (execute relative long)

> is being used while targeting older machines makes use of larl/ex

> instead.  From a security perspective the exrl variant is preferable.

>

> 'thunk-extern' does the branch replacement like 'thunk' but does not

> emit the thunks.

>

> 'thunk-inline' is only available for indirect jumps.  It should be used

> in environments where correct CFI is important - known as user space.

>

> Additionally the patch introduces the -mindirect-branch-table option

> which generates tables pointing to the locations which have been

> modified.  This is supposed to allow reverting the changes without

> re-compilation in situations where it isn't required. The sections are

> split up into one section per option.

>

> I plan to commit the patch tomorrow.


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

> gcc/ChangeLog:

>

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

>

>         * config/s390/s390-opts.h (enum indirect_branch): Define.

>         * config/s390/s390-protos.h (s390_return_addr_from_memory)

>         (s390_indirect_branch_via_thunk)

>         (s390_indirect_branch_via_inline_thunk): Add function prototypes.

>         (enum s390_indirect_branch_type): Define.

>         * config/s390/s390.c (struct s390_frame_layout, struct

>         machine_function): Remove.

>         (indirect_branch_prez10thunk_mask, indirect_branch_z10thunk_mask)

>         (indirect_branch_table_label_no, indirect_branch_table_name):

>         Define variables.

>         (INDIRECT_BRANCH_NUM_OPTIONS): Define macro.

>         (enum s390_indirect_branch_option): Define.

>         (s390_return_addr_from_memory): New function.

>         (s390_handle_string_attribute): New function.

>         (s390_attribute_table): Add new attribute handler.

>         (s390_execute_label): Handle UNSPEC_EXECUTE_JUMP patterns.

>         (s390_indirect_branch_via_thunk): New function.

>         (s390_indirect_branch_via_inline_thunk): New function.

>         (s390_function_ok_for_sibcall): When jumping via thunk disallow

>         sibling call optimization for non z10 compiles.

>         (s390_emit_call): Force indirect branch target to be a single

>         register.  Add r1 clobber for non-z10 compiles.

>         (s390_emit_epilogue): Emit return jump via return_use expander.

>         (s390_reorg): Handle JUMP_INSNs as execute targets.

>         (s390_option_override_internal): Perform validity checks for the

>         new command line options.

>         (s390_indirect_branch_attrvalue): New function.

>         (s390_indirect_branch_settings): New function.

>         (s390_set_current_function): Invoke s390_indirect_branch_settings.

>         (s390_output_indirect_thunk_function):  New function.

>         (s390_code_end): Implement target hook.

>         (s390_case_values_threshold): Implement target hook.

>         (TARGET_ASM_CODE_END, TARGET_CASE_VALUES_THRESHOLD): Define target

>         macros.

>         * config/s390/s390.h (struct s390_frame_layout)

>         (struct machine_function): Move here from s390.c.

>         (TARGET_INDIRECT_BRANCH_NOBP_RET)

>         (TARGET_INDIRECT_BRANCH_NOBP_JUMP)

>         (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK)

>         (TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK)

>         (TARGET_INDIRECT_BRANCH_NOBP_CALL)

>         (TARGET_DEFAULT_INDIRECT_BRANCH_TABLE)

>         (TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL)

>         (TARGET_INDIRECT_BRANCH_THUNK_NAME_EX)

>         (TARGET_INDIRECT_BRANCH_TABLE): Define macros.

>         * config/s390/s390.md (UNSPEC_EXECUTE_JUMP)

>         (INDIRECT_BRANCH_THUNK_REGNUM): Define constants.

>         (mnemonic attribute): Add values which aren't recognized

>         automatically.

>         ("*cjump_long", "*icjump_long", "*basr", "*basr_r"): Disable

>         pattern for branch conversion.  Fix mnemonic attribute.

>         ("*c<code>", "*sibcall_br", "*sibcall_value_br", "*return"): Emit

>         indirect branch via thunk if requested.

>         ("indirect_jump", "<code>"): Expand patterns for branch conversion.

>         ("*indirect_jump"): Disable for branch conversion using out of

>         line thunks.

>         ("indirect_jump_via_thunk<mode>_z10")

>         ("indirect_jump_via_thunk<mode>")

>         ("indirect_jump_via_inlinethunk<mode>_z10")

>         ("indirect_jump_via_inlinethunk<mode>", "*casesi_jump")

>         ("casesi_jump_via_thunk<mode>_z10", "casesi_jump_via_thunk<mode>")

>         ("casesi_jump_via_inlinethunk<mode>_z10")

>         ("casesi_jump_via_inlinethunk<mode>", "*basr_via_thunk<mode>_z10")

>         ("*basr_via_thunk<mode>", "*basr_r_via_thunk_z10")

>         ("*basr_r_via_thunk", "return<mode>_prez10"): New pattern.

>         ("*indirect2_jump"): Disable for branch conversion.

>         ("casesi_jump"): Turn into expander and expand patterns for branch

>         conversion.

>         ("return_use"): New expander.

>         ("*return"): Emit return via thunk and rename it to ...

>         ("*return<mode>"): ... this one.

>         * config/s390/s390.opt: Add new options and and enum for the

>         option values.

>

> gcc/testsuite/ChangeLog:

>

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

>

>         * gcc.target/s390/nobp-function-pointer-attr.c: New test.

>         * gcc.target/s390/nobp-function-pointer-nothunk.c: New test.

>         * gcc.target/s390/nobp-function-pointer-z10.c: New test.

>         * gcc.target/s390/nobp-function-pointer-z900.c: New test.

>         * gcc.target/s390/nobp-indirect-jump-attr.c: New test.

>         * gcc.target/s390/nobp-indirect-jump-inline-attr.c: New test.

>         * gcc.target/s390/nobp-indirect-jump-inline-z10.c: New test.

>         * gcc.target/s390/nobp-indirect-jump-inline-z900.c: New test.

>         * gcc.target/s390/nobp-indirect-jump-nothunk.c: New test.

>         * gcc.target/s390/nobp-indirect-jump-z10.c: New test.

>         * gcc.target/s390/nobp-indirect-jump-z900.c: New test.

>         * gcc.target/s390/nobp-return-attr-all.c: New test.

>         * gcc.target/s390/nobp-return-attr-neg.c: New test.

>         * gcc.target/s390/nobp-return-mem-attr.c: New test.

>         * gcc.target/s390/nobp-return-mem-nothunk.c: New test.

>         * gcc.target/s390/nobp-return-mem-z10.c: New test.

>         * gcc.target/s390/nobp-return-mem-z900.c: New test.

>         * gcc.target/s390/nobp-return-reg-attr.c: New test.

>         * gcc.target/s390/nobp-return-reg-mixed.c: New test.

>         * gcc.target/s390/nobp-return-reg-nothunk.c: New test.

>         * gcc.target/s390/nobp-return-reg-z10.c: New test.

>         * gcc.target/s390/nobp-return-reg-z900.c: New test.

>         * gcc.target/s390/nobp-table-jump-inline-z10.c: New test.

>         * gcc.target/s390/nobp-table-jump-inline-z900.c: New test.

>         * gcc.target/s390/nobp-table-jump-z10.c: New test.

>         * gcc.target/s390/nobp-table-jump-z900.c: New test.

> ---

>  gcc/config/s390/s390-opts.h                        |   9 +

>  gcc/config/s390/s390-protos.h                      |  12 +

>  gcc/config/s390/s390.c                             | 700 +++++++++++++++++----

>  gcc/config/s390/s390.h                             | 120 ++++

>  gcc/config/s390/s390.md                            | 574 ++++++++++++++++-

>  gcc/config/s390/s390.opt                           |  60 ++

>  .../gcc.target/s390/nobp-function-pointer-attr.c   |  56 ++

>  .../s390/nobp-function-pointer-nothunk.c           |  59 ++

>  .../gcc.target/s390/nobp-function-pointer-z10.c    |  56 ++

>  .../gcc.target/s390/nobp-function-pointer-z900.c   |  56 ++

>  .../gcc.target/s390/nobp-indirect-jump-attr.c      |  42 ++

>  .../s390/nobp-indirect-jump-inline-attr.c          |  42 ++

>  .../s390/nobp-indirect-jump-inline-z10.c           |  43 ++

>  .../s390/nobp-indirect-jump-inline-z900.c          |  43 ++

>  .../gcc.target/s390/nobp-indirect-jump-nothunk.c   |  46 ++

>  .../gcc.target/s390/nobp-indirect-jump-z10.c       |  43 ++

>  .../gcc.target/s390/nobp-indirect-jump-z900.c      |  43 ++

>  .../gcc.target/s390/nobp-return-attr-all.c         |  46 ++

>  .../gcc.target/s390/nobp-return-attr-neg.c         |  40 ++

>  .../gcc.target/s390/nobp-return-mem-attr.c         |  46 ++

>  .../gcc.target/s390/nobp-return-mem-nothunk.c      |  49 ++

>  .../gcc.target/s390/nobp-return-mem-z10.c          |  46 ++

>  .../gcc.target/s390/nobp-return-mem-z900.c         |  48 ++

>  .../gcc.target/s390/nobp-return-reg-attr.c         |  41 ++

>  .../gcc.target/s390/nobp-return-reg-mixed.c        |  44 ++

>  .../gcc.target/s390/nobp-return-reg-nothunk.c      |  44 ++

>  .../gcc.target/s390/nobp-return-reg-z10.c          |  41 ++

>  .../gcc.target/s390/nobp-return-reg-z900.c         |  41 ++

>  .../gcc.target/s390/nobp-table-jump-inline-z10.c   |  78 +++

>  .../gcc.target/s390/nobp-table-jump-inline-z900.c  |  78 +++

>  .../gcc.target/s390/nobp-table-jump-z10.c          |  77 +++

>  .../gcc.target/s390/nobp-table-jump-z900.c         |  78 +++

>  32 files changed, 2662 insertions(+), 139 deletions(-)

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c

>  create mode 100644 gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c

>

> diff --git a/gcc/config/s390/s390-opts.h b/gcc/config/s390/s390-opts.h

> index 23632ba..aaecca7 100644

> --- a/gcc/config/s390/s390-opts.h

> +++ b/gcc/config/s390/s390-opts.h

> @@ -43,4 +43,13 @@ enum processor_type

>    PROCESSOR_max

>  };

>

> +

> +/* Values for -mindirect-branch and -mfunction-return options.  */

> +enum indirect_branch {

> +  indirect_branch_unset = 0,

> +  indirect_branch_keep,

> +  indirect_branch_thunk,

> +  indirect_branch_thunk_inline,

> +  indirect_branch_thunk_extern

> +};

>  #endif

> diff --git a/gcc/config/s390/s390-protos.h b/gcc/config/s390/s390-protos.h

> index 214062a..46f0743 100644

> --- a/gcc/config/s390/s390-protos.h

> +++ b/gcc/config/s390/s390-protos.h

> @@ -50,6 +50,7 @@ extern void s390_set_has_landing_pad_p (bool);

>  extern bool s390_hard_regno_rename_ok (unsigned int, unsigned int);

>  extern int s390_class_max_nregs (enum reg_class, machine_mode);

>  extern bool s390_function_arg_vector (machine_mode, const_tree);

> +extern bool s390_return_addr_from_memory(void);

>  #if S390_USE_TARGET_ATTRIBUTE

>  extern tree s390_valid_target_attribute_tree (tree args,

>                                               struct gcc_options *opts,

> @@ -145,6 +146,17 @@ extern int s390_compare_and_branch_condition_mask (rtx);

>  extern bool s390_extzv_shift_ok (int, int, unsigned HOST_WIDE_INT);

>  extern void s390_asm_output_function_label (FILE *, const char *, tree);

>

> +enum s390_indirect_branch_type

> +  {

> +    s390_indirect_branch_type_jump = 0,

> +    s390_indirect_branch_type_call,

> +    s390_indirect_branch_type_return

> +  };

> +extern void s390_indirect_branch_via_thunk (unsigned int regno,

> +                                           unsigned int return_addr_regno,

> +                                           rtx comparison_operator,

> +                                           enum s390_indirect_branch_type type);

> +extern void s390_indirect_branch_via_inline_thunk (rtx execute_target);

>  #endif /* RTX_CODE */

>

>  /* s390-c.c routines */

> diff --git a/gcc/config/s390/s390.c b/gcc/config/s390/s390.c

> index 03c93f1..62a60e2 100644

> --- a/gcc/config/s390/s390.c

> +++ b/gcc/config/s390/s390.c

> @@ -399,84 +399,6 @@ struct s390_address

>    bool literal_pool;

>  };

>

> -/* The following structure is embedded in the machine

> -   specific part of struct function.  */

> -

> -struct GTY (()) s390_frame_layout

> -{

> -  /* Offset within stack frame.  */

> -  HOST_WIDE_INT gprs_offset;

> -  HOST_WIDE_INT f0_offset;

> -  HOST_WIDE_INT f4_offset;

> -  HOST_WIDE_INT f8_offset;

> -  HOST_WIDE_INT backchain_offset;

> -

> -  /* Number of first and last gpr where slots in the register

> -     save area are reserved for.  */

> -  int first_save_gpr_slot;

> -  int last_save_gpr_slot;

> -

> -  /* Location (FP register number) where GPRs (r0-r15) should

> -     be saved to.

> -      0 - does not need to be saved at all

> -     -1 - stack slot  */

> -#define SAVE_SLOT_NONE   0

> -#define SAVE_SLOT_STACK -1

> -  signed char gpr_save_slots[16];

> -

> -  /* Number of first and last gpr to be saved, restored.  */

> -  int first_save_gpr;

> -  int first_restore_gpr;

> -  int last_save_gpr;

> -  int last_restore_gpr;

> -

> -  /* Bits standing for floating point registers. Set, if the

> -     respective register has to be saved. Starting with reg 16 (f0)

> -     at the rightmost bit.

> -     Bit 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0

> -     fpr 15 13 11  9 14 12 10  8  7  5  3  1  6  4  2  0

> -     reg 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16  */

> -  unsigned int fpr_bitmap;

> -

> -  /* Number of floating point registers f8-f15 which must be saved.  */

> -  int high_fprs;

> -

> -  /* Set if return address needs to be saved.

> -     This flag is set by s390_return_addr_rtx if it could not use

> -     the initial value of r14 and therefore depends on r14 saved

> -     to the stack.  */

> -  bool save_return_addr_p;

> -

> -  /* Size of stack frame.  */

> -  HOST_WIDE_INT frame_size;

> -};

> -

> -/* Define the structure for the machine field in struct function.  */

> -

> -struct GTY(()) machine_function

> -{

> -  struct s390_frame_layout frame_layout;

> -

> -  /* Literal pool base register.  */

> -  rtx base_reg;

> -

> -  /* True if we may need to perform branch splitting.  */

> -  bool split_branches_pending_p;

> -

> -  bool has_landing_pad_p;

> -

> -  /* True if the current function may contain a tbegin clobbering

> -     FPRs.  */

> -  bool tbegin_p;

> -

> -  /* For -fsplit-stack support: A stack local which holds a pointer to

> -     the stack arguments for a function with a variable number of

> -     arguments.  This is set at the start of the function and is used

> -     to initialize the overflow_arg_area field of the va_list

> -     structure.  */

> -  rtx split_stack_varargs_pointer;

> -};

> -

>  /* Few accessor macros for struct cfun->machine->s390_frame_layout.  */

>

>  #define cfun_frame_layout (cfun->machine->frame_layout)

> @@ -517,6 +439,33 @@ struct GTY(()) machine_function

>     bytes on a z10 (or higher) CPU.  */

>  #define PREDICT_DISTANCE (TARGET_Z10 ? 384 : 2048)

>

> +/* Masks per jump target register indicating which thunk need to be

> +   generated.  */

> +static GTY(()) int indirect_branch_prez10thunk_mask = 0;

> +static GTY(()) int indirect_branch_z10thunk_mask = 0;

> +

> +#define INDIRECT_BRANCH_NUM_OPTIONS 4

> +

> +enum s390_indirect_branch_option

> +  {

> +    s390_opt_indirect_branch_jump = 0,

> +    s390_opt_indirect_branch_call,

> +    s390_opt_function_return_reg,

> +    s390_opt_function_return_mem

> +  };

> +

> +static GTY(()) int indirect_branch_table_label_no[INDIRECT_BRANCH_NUM_OPTIONS] = { 0 };

> +const char *indirect_branch_table_label[INDIRECT_BRANCH_NUM_OPTIONS] = \

> +  { "LJUMP", "LCALL", "LRETREG", "LRETMEM" };

> +const char *indirect_branch_table_name[INDIRECT_BRANCH_NUM_OPTIONS] =  \

> +  { ".s390_indirect_jump", ".s390_indirect_call",

> +    ".s390_return_reg", ".s390_return_mem" };

> +

> +bool

> +s390_return_addr_from_memory ()

> +{

> +  return cfun_gpr_save_slot(RETURN_REGNUM) == SAVE_SLOT_STACK;

> +}

>

>  /* Indicate which ABI has been used for passing vector args.

>     0 - no vector type arguments have been passed where the ABI is relevant

> @@ -1179,11 +1128,83 @@ s390_handle_vectorbool_attribute (tree *node, tree name ATTRIBUTE_UNUSED,

>    return NULL_TREE;

>  }

>

> +/* Check syntax of function decl attributes having a string type value.  */

> +

> +static tree

> +s390_handle_string_attribute (tree *node, tree name ATTRIBUTE_UNUSED,

> +                             tree args ATTRIBUTE_UNUSED,

> +                             int flags ATTRIBUTE_UNUSED,

> +                             bool *no_add_attrs)

> +{

> +  tree cst;

> +

> +  if (TREE_CODE (*node) != FUNCTION_DECL)

> +    {

> +      warning (OPT_Wattributes, "%qE attribute only applies to functions",

> +              name);

> +      *no_add_attrs = true;

> +    }

> +

> +  cst = TREE_VALUE (args);

> +

> +  if (TREE_CODE (cst) != STRING_CST)

> +    {

> +      warning (OPT_Wattributes,

> +              "%qE attribute requires a string constant argument",

> +              name);

> +      *no_add_attrs = true;

> +    }

> +

> +  if (is_attribute_p ("indirect_branch", name)

> +      || is_attribute_p ("indirect_branch_call", name)

> +      || is_attribute_p ("function_return", name)

> +      || is_attribute_p ("function_return_reg", name)

> +      || is_attribute_p ("function_return_mem", name))

> +    {

> +      if (strcmp (TREE_STRING_POINTER (cst), "keep") != 0

> +         && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0

> +         && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0)

> +      {

> +       warning (OPT_Wattributes,

> +                "argument to %qE attribute is not "

> +                "(keep|thunk|thunk-extern)", name);

> +       *no_add_attrs = true;

> +      }

> +    }

> +

> +  if (is_attribute_p ("indirect_branch_jump", name)

> +      && strcmp (TREE_STRING_POINTER (cst), "keep") != 0

> +      && strcmp (TREE_STRING_POINTER (cst), "thunk") != 0

> +      && strcmp (TREE_STRING_POINTER (cst), "thunk-inline") != 0

> +      && strcmp (TREE_STRING_POINTER (cst), "thunk-extern") != 0)

> +    {

> +      warning (OPT_Wattributes,

> +              "argument to %qE attribute is not "

> +              "(keep|thunk|thunk-inline|thunk-extern)", name);

> +      *no_add_attrs = true;

> +    }

> +

> +  return NULL_TREE;

> +}

> +

>  static const struct attribute_spec s390_attribute_table[] = {

>    { "hotpatch", 2, 2, true, false, false, false,

>      s390_handle_hotpatch_attribute, NULL },

>    { "s390_vector_bool", 0, 0, false, true, false, true,

>      s390_handle_vectorbool_attribute, NULL },

> +  { "indirect_branch", 1, 1, true, false, false, false,

> +    s390_handle_string_attribute, NULL },

> +  { "indirect_branch_jump", 1, 1, true, false, false, false,

> +    s390_handle_string_attribute, NULL },

> +  { "indirect_branch_call", 1, 1, true, false, false, false,

> +    s390_handle_string_attribute, NULL },

> +  { "function_return", 1, 1, true, false, false, false,

> +    s390_handle_string_attribute, NULL },

> +  { "function_return_reg", 1, 1, true, false, false, false,

> +    s390_handle_string_attribute, NULL },

> +  { "function_return_mem", 1, 1, true, false, false, false,

> +    s390_handle_string_attribute, NULL },

> +

>    /* End element.  */

>    { NULL,        0, 0, false, false, false, false, NULL, NULL }

>  };

> @@ -8733,11 +8754,25 @@ s390_find_constant (struct constant_pool *pool, rtx val,

>  static rtx

>  s390_execute_label (rtx insn)

>  {

> -  if (NONJUMP_INSN_P (insn)

> +  if (INSN_P (insn)

>        && GET_CODE (PATTERN (insn)) == PARALLEL

>        && GET_CODE (XVECEXP (PATTERN (insn), 0, 0)) == UNSPEC

> -      && XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE)

> -    return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2);

> +      && (XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE

> +         || XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE_JUMP))

> +    {

> +      if (XINT (XVECEXP (PATTERN (insn), 0, 0), 1) == UNSPEC_EXECUTE)

> +       return XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 2);

> +      else

> +       {

> +         gcc_assert (JUMP_P (insn));

> +         /* For jump insns as execute target:

> +            - There is one operand less in the parallel (the

> +              modification register of the execute is always 0).

> +            - The execute target label is wrapped into an

> +              if_then_else in order to hide it from jump analysis.  */

> +         return XEXP (XVECEXP (XVECEXP (PATTERN (insn), 0, 0), 0, 0), 0);

> +       }

> +    }

>

>    return NULL_RTX;

>  }

> @@ -11681,7 +11716,6 @@ s390_emit_epilogue (bool sibcall)

>    rtx frame_pointer, return_reg, cfa_restores = NULL_RTX;

>    int area_bottom, area_top, offset = 0;

>    int next_offset;

> -  rtvec p;

>    int i;

>

>    if (TARGET_TPF_PROFILING)

> @@ -11837,8 +11871,14 @@ s390_emit_epilogue (bool sibcall)

>           && s390_tune <= PROCESSOR_2097_Z10)

>         {

>           int return_regnum = find_unused_clobbered_reg();

> -         if (!return_regnum)

> -           return_regnum = 4;

> +         if (!return_regnum

> +             || (TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION

> +                 && !TARGET_CPU_Z10

> +                 && return_regnum == INDIRECT_BRANCH_THUNK_REGNUM))

> +           {

> +             gcc_assert (INDIRECT_BRANCH_THUNK_REGNUM != 4);

> +             return_regnum = 4;

> +           }

>           return_reg = gen_rtx_REG (Pmode, return_regnum);

>

>           addr = plus_constant (Pmode, frame_pointer,

> @@ -11875,16 +11915,7 @@ s390_emit_epilogue (bool sibcall)

>    s390_restore_gprs_from_fprs ();

>

>    if (! sibcall)

> -    {

> -

> -      /* Return to caller.  */

> -

> -      p = rtvec_alloc (2);

> -

> -      RTVEC_ELT (p, 0) = ret_rtx;

> -      RTVEC_ELT (p, 1) = gen_rtx_USE (VOIDmode, return_reg);

> -      emit_jump_insn (gen_rtx_PARALLEL (VOIDmode, p));

> -    }

> +    emit_jump_insn (gen_return_use (return_reg));

>  }

>

>  /* Implement TARGET_SET_UP_BY_PROLOGUE.  */

> @@ -13475,6 +13506,112 @@ s390_output_mi_thunk (FILE *file, tree thunk ATTRIBUTE_UNUSED,

>    final_end_function ();

>  }

>

> +/* Output either an indirect jump or a an indirect call

> +   (RETURN_ADDR_REGNO != INVALID_REGNUM) with target register REGNO

> +   using a branch trampoline disabling branch target prediction.  */

> +

> +void

> +s390_indirect_branch_via_thunk (unsigned int regno,

> +                               unsigned int return_addr_regno,

> +                               rtx comparison_operator,

> +                               enum s390_indirect_branch_type type)

> +{

> +  enum s390_indirect_branch_option option;

> +

> +  if (type == s390_indirect_branch_type_return)

> +    {

> +      if (s390_return_addr_from_memory ())

> +       option = s390_opt_function_return_mem;

> +      else

> +       option = s390_opt_function_return_reg;

> +    }

> +  else if (type == s390_indirect_branch_type_jump)

> +    option = s390_opt_indirect_branch_jump;

> +  else if (type == s390_indirect_branch_type_call)

> +    option = s390_opt_indirect_branch_call;

> +  else

> +    gcc_unreachable ();

> +

> +  if (TARGET_INDIRECT_BRANCH_TABLE)

> +    {

> +      char label[32];

> +

> +      ASM_GENERATE_INTERNAL_LABEL (label,

> +                                  indirect_branch_table_label[option],

> +                                  indirect_branch_table_label_no[option]++);

> +      ASM_OUTPUT_LABEL (asm_out_file, label);

> +    }

> +

> +  if (return_addr_regno != INVALID_REGNUM)

> +    {

> +      gcc_assert (comparison_operator == NULL_RTX);

> +      fprintf (asm_out_file, " \tbrasl\t%%r%d,", return_addr_regno);

> +    }

> +  else

> +    {

> +      fputs (" \tjg", asm_out_file);

> +      if (comparison_operator != NULL_RTX)

> +       print_operand (asm_out_file, comparison_operator, 'C');

> +

> +      fputs ("\t", asm_out_file);

> +    }

> +

> +  if (TARGET_CPU_Z10)

> +    fprintf (asm_out_file,

> +            TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL "\n",

> +            regno);

> +  else

> +    fprintf (asm_out_file,

> +            TARGET_INDIRECT_BRANCH_THUNK_NAME_EX "\n",

> +            INDIRECT_BRANCH_THUNK_REGNUM, regno);

> +

> +  if ((option == s390_opt_indirect_branch_jump

> +       && cfun->machine->indirect_branch_jump == indirect_branch_thunk)

> +      || (option == s390_opt_indirect_branch_call

> +         && cfun->machine->indirect_branch_call == indirect_branch_thunk)

> +      || (option == s390_opt_function_return_reg

> +         && cfun->machine->function_return_reg == indirect_branch_thunk)

> +      || (option == s390_opt_function_return_mem

> +         && cfun->machine->function_return_mem == indirect_branch_thunk))

> +    {

> +      if (TARGET_CPU_Z10)

> +       indirect_branch_z10thunk_mask |= (1 << regno);

> +      else

> +       indirect_branch_prez10thunk_mask |= (1 << regno);

> +    }

> +}

> +

> +/* Output an inline thunk for indirect jumps.  EXECUTE_TARGET can

> +   either be an address register or a label pointing to the location

> +   of the jump instruction.  */

> +

> +void

> +s390_indirect_branch_via_inline_thunk (rtx execute_target)

> +{

> +  if (TARGET_INDIRECT_BRANCH_TABLE)

> +    {

> +      char label[32];

> +

> +      ASM_GENERATE_INTERNAL_LABEL (label,

> +                                  indirect_branch_table_label[s390_opt_indirect_branch_jump],

> +                                  indirect_branch_table_label_no[s390_opt_indirect_branch_jump]++);

> +      ASM_OUTPUT_LABEL (asm_out_file, label);

> +    }

> +

> +  if (!TARGET_ZARCH)

> +    fputs ("\t.machinemode zarch\n", asm_out_file);

> +

> +  if (REG_P (execute_target))

> +    fprintf (asm_out_file, "\tex\t%%r0,0(%%r%d)\n", REGNO (execute_target));

> +  else

> +    output_asm_insn ("\texrl\t%%r0,%0", &execute_target);

> +

> +  if (!TARGET_ZARCH)

> +    fputs ("\t.machinemode esa\n", asm_out_file);

> +

> +  fputs ("0:\tj\t0b\n", asm_out_file);

> +}

> +

>  static bool

>  s390_valid_pointer_mode (scalar_int_mode mode)

>  {

> @@ -13576,6 +13713,14 @@ s390_function_ok_for_sibcall (tree decl, tree exp)

>    if (!TARGET_64BIT && flag_pic && decl && !targetm.binds_local_p (decl))

>      return false;

>

> +  /* The thunks for indirect branches require r1 if no exrl is

> +     available.  r1 might not be available when doing a sibling

> +     call.  */

> +  if (TARGET_INDIRECT_BRANCH_NOBP_CALL

> +      && !TARGET_CPU_Z10

> +      && !decl)

> +    return false;

> +

>    /* Register 6 on s390 is available as an argument register but unfortunately

>       "caller saved". This makes functions needing this register for arguments

>       not suitable for sibcalls.  */

> @@ -13609,9 +13754,13 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,

>  {

>    bool plt_call = false;

>    rtx_insn *insn;

> -  rtx call;

> -  rtx clobber;

> -  rtvec vec;

> +  rtx vec[4] = { NULL_RTX };

> +  int elts = 0;

> +  rtx *call = &vec[0];

> +  rtx *clobber_ret_reg = &vec[1];

> +  rtx *use = &vec[2];

> +  rtx *clobber_thunk_reg = &vec[3];

> +  int i;

>

>    /* Direct function calls need special treatment.  */

>    if (GET_CODE (addr_location) == SYMBOL_REF)

> @@ -13663,26 +13812,58 @@ s390_emit_call (rtx addr_location, rtx tls_call, rtx result_reg,

>        addr_location = gen_rtx_REG (Pmode, SIBCALL_REGNUM);

>      }

>

> +  if (TARGET_INDIRECT_BRANCH_NOBP_CALL

> +      && GET_CODE (addr_location) != SYMBOL_REF

> +      && !plt_call)

> +    {

> +      /* Indirect branch thunks require the target to be a single GPR.  */

> +      addr_location = force_reg (Pmode, addr_location);

> +

> +      /* Without exrl the indirect branch thunks need an additional

> +        register for larl;ex */

> +      if (!TARGET_CPU_Z10)

> +       {

> +         *clobber_thunk_reg = gen_rtx_REG (Pmode, INDIRECT_BRANCH_THUNK_REGNUM);

> +         *clobber_thunk_reg = gen_rtx_CLOBBER (VOIDmode, *clobber_thunk_reg);

> +       }

> +    }

> +

>    addr_location = gen_rtx_MEM (QImode, addr_location);

> -  call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);

> +  *call = gen_rtx_CALL (VOIDmode, addr_location, const0_rtx);

>

>    if (result_reg != NULL_RTX)

> -    call = gen_rtx_SET (result_reg, call);

> +    *call = gen_rtx_SET (result_reg, *call);

>

>    if (retaddr_reg != NULL_RTX)

>      {

> -      clobber = gen_rtx_CLOBBER (VOIDmode, retaddr_reg);

> +      *clobber_ret_reg = gen_rtx_CLOBBER (VOIDmode, retaddr_reg);

>

>        if (tls_call != NULL_RTX)

> -       vec = gen_rtvec (3, call, clobber,

> -                        gen_rtx_USE (VOIDmode, tls_call));

> -      else

> -       vec = gen_rtvec (2, call, clobber);

> +       *use = gen_rtx_USE (VOIDmode, tls_call);

> +    }

> +

>

> -      call = gen_rtx_PARALLEL (VOIDmode, vec);

> +  for (i = 0; i < 4; i++)

> +    if (vec[i] != NULL_RTX)

> +      elts++;

> +

> +  if (elts > 1)

> +    {

> +      rtvec v;

> +      int e = 0;

> +

> +      v = rtvec_alloc (elts);

> +      for (i = 0; i < 4; i++)

> +       if (vec[i] != NULL_RTX)

> +         {

> +           RTVEC_ELT (v, e) = vec[i];

> +           e++;

> +         }

> +

> +      *call = gen_rtx_PARALLEL (VOIDmode, v);

>      }

>

> -  insn = emit_call_insn (call);

> +  insn = emit_call_insn (*call);

>

>    /* 31-bit PLT stubs and tls calls use the GOT register implicitly.  */

>    if ((!TARGET_64BIT && plt_call) || tls_call != NULL_RTX)

> @@ -14464,7 +14645,16 @@ s390_reorg (void)

>           target = emit_label (XEXP (label, 0));

>           INSN_ADDRESSES_NEW (target, -1);

>

> -         target = emit_insn (s390_execute_target (insn));

> +         if (JUMP_P (insn))

> +           {

> +             target = emit_jump_insn (s390_execute_target (insn));

> +             /* This is important in order to keep a table jump

> +                pointing at the jump table label.  Only this makes it

> +                being recognized as table jump.  */

> +             JUMP_LABEL (target) = JUMP_LABEL (insn);

> +           }

> +         else

> +           target = emit_insn (s390_execute_target (insn));

>           INSN_ADDRESSES_NEW (target, -1);

>         }

>      }

> @@ -15199,6 +15389,42 @@ s390_option_override_internal (bool main_args_p,

>    if (TARGET_64BIT && !TARGET_ZARCH_P (opts->x_target_flags))

>      error ("64-bit ABI not supported in ESA/390 mode");

>

> +  if (opts->x_s390_indirect_branch == indirect_branch_thunk_inline

> +      || opts->x_s390_indirect_branch_call == indirect_branch_thunk_inline

> +      || opts->x_s390_function_return == indirect_branch_thunk_inline

> +      || opts->x_s390_function_return_reg == indirect_branch_thunk_inline

> +      || opts->x_s390_function_return_mem == indirect_branch_thunk_inline)

> +    error ("thunk-inline is only supported with -mindirect-branch-jump");

> +

> +  if (opts->x_s390_indirect_branch != indirect_branch_keep)

> +    {

> +      if (!opts_set->x_s390_indirect_branch_call)

> +       opts->x_s390_indirect_branch_call = opts->x_s390_indirect_branch;

> +

> +      if (!opts_set->x_s390_indirect_branch_jump)

> +       opts->x_s390_indirect_branch_jump = opts->x_s390_indirect_branch;

> +    }

> +

> +  if (opts->x_s390_function_return != indirect_branch_keep)

> +    {

> +      if (!opts_set->x_s390_function_return_reg)

> +       opts->x_s390_function_return_reg = opts->x_s390_function_return;

> +

> +      if (!opts_set->x_s390_function_return_mem)

> +       opts->x_s390_function_return_mem = opts->x_s390_function_return;

> +    }

> +

> +  if (!TARGET_CPU_ZARCH)

> +    {

> +      if (opts->x_s390_indirect_branch_call != indirect_branch_keep

> +         || opts->x_s390_indirect_branch_jump != indirect_branch_keep)

> +       error ("-mindirect-branch* options require -march=z900 or higher");

> +      if (opts->x_s390_function_return_reg != indirect_branch_keep

> +         || opts->x_s390_function_return_mem != indirect_branch_keep)

> +       error ("-mfunction-return* options require -march=z900 or higher");

> +    }

> +

> +

>    /* Enable hardware transactions if available and not explicitly

>       disabled by user.  E.g. with -m31 -march=zEC12 -mzarch */

>    if (!TARGET_OPT_HTM_P (opts_set->x_target_flags))

> @@ -15811,6 +16037,78 @@ s390_can_inline_p (tree caller, tree callee)

>    return ret;

>  }

>

> +/* Set VAL to correct enum value according to the indirect-branch or

> +   function-return attribute in ATTR.  */

> +

> +static inline void

> +s390_indirect_branch_attrvalue (tree attr, enum indirect_branch *val)

> +{

> +  const char *str = TREE_STRING_POINTER (TREE_VALUE (TREE_VALUE (attr)));

> +  if (strcmp (str, "keep") == 0)

> +    *val = indirect_branch_keep;

> +  else if (strcmp (str, "thunk") == 0)

> +    *val = indirect_branch_thunk;

> +  else if (strcmp (str, "thunk-inline") == 0)

> +    *val = indirect_branch_thunk_inline;

> +  else if (strcmp (str, "thunk-extern") == 0)

> +    *val = indirect_branch_thunk_extern;

> +}

> +

> +/* Memorize the setting for -mindirect-branch* and -mfunction-return*

> +   from either the cmdline or the function attributes in

> +   cfun->machine.  */

> +

> +static void

> +s390_indirect_branch_settings (tree fndecl)

> +{

> +  tree attr;

> +

> +  if (!fndecl)

> +    return;

> +

> +  /* Initialize with the cmdline options and let the attributes

> +     override it.  */

> +  cfun->machine->indirect_branch_jump = s390_indirect_branch_jump;

> +  cfun->machine->indirect_branch_call = s390_indirect_branch_call;

> +

> +  cfun->machine->function_return_reg = s390_function_return_reg;

> +  cfun->machine->function_return_mem = s390_function_return_mem;

> +

> +  if ((attr = lookup_attribute ("indirect_branch",

> +                               DECL_ATTRIBUTES (fndecl))))

> +    {

> +      s390_indirect_branch_attrvalue (attr,

> +                                     &cfun->machine->indirect_branch_jump);

> +      s390_indirect_branch_attrvalue (attr,

> +                                     &cfun->machine->indirect_branch_call);

> +    }

> +

> +  if ((attr = lookup_attribute ("indirect_branch_jump",

> +                               DECL_ATTRIBUTES (fndecl))))

> +    s390_indirect_branch_attrvalue (attr, &cfun->machine->indirect_branch_jump);

> +

> +  if ((attr = lookup_attribute ("indirect_branch_call",

> +                               DECL_ATTRIBUTES (fndecl))))

> +    s390_indirect_branch_attrvalue (attr, &cfun->machine->indirect_branch_call);

> +

> +  if ((attr = lookup_attribute ("function_return",

> +                               DECL_ATTRIBUTES (fndecl))))

> +    {

> +      s390_indirect_branch_attrvalue (attr,

> +                                     &cfun->machine->function_return_reg);

> +      s390_indirect_branch_attrvalue (attr,

> +                                     &cfun->machine->function_return_mem);

> +    }

> +

> +  if ((attr = lookup_attribute ("function_return_reg",

> +                               DECL_ATTRIBUTES (fndecl))))

> +    s390_indirect_branch_attrvalue (attr, &cfun->machine->function_return_reg);

> +

> +  if ((attr = lookup_attribute ("function_return_mem",

> +                               DECL_ATTRIBUTES (fndecl))))

> +    s390_indirect_branch_attrvalue (attr, &cfun->machine->function_return_mem);

> +}

> +

>  /* Restore targets globals from NEW_TREE and invalidate s390_previous_fndecl

>     cache.  */

>

> @@ -15861,6 +16159,8 @@ s390_set_current_function (tree fndecl)

>    if (old_tree != new_tree)

>      s390_activate_target_options (new_tree);

>    s390_previous_fndecl = fndecl;

> +

> +  s390_indirect_branch_settings (fndecl);

>  }

>  #endif

>

> @@ -16159,6 +16459,186 @@ s390_asan_shadow_offset (void)

>    return TARGET_64BIT ? HOST_WIDE_INT_1U << 52 : HOST_WIDE_INT_UC (0x20000000);

>  }

>

> +#ifdef HAVE_GAS_HIDDEN

> +# define USE_HIDDEN_LINKONCE 1

> +#else

> +# define USE_HIDDEN_LINKONCE 0

> +#endif

> +

> +/* Output an indirect branch trampoline for target register REGNO.  */

> +

> +static void

> +s390_output_indirect_thunk_function (unsigned int regno, bool z10_p)

> +{

> +  tree decl;

> +  char thunk_label[32];

> +  int i;

> +

> +  if (z10_p)

> +    sprintf (thunk_label, TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL, regno);

> +  else

> +    sprintf (thunk_label, TARGET_INDIRECT_BRANCH_THUNK_NAME_EX,

> +            INDIRECT_BRANCH_THUNK_REGNUM, regno);

> +

> +  decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,

> +                    get_identifier (thunk_label),

> +                    build_function_type_list (void_type_node, NULL_TREE));

> +  DECL_RESULT (decl) = build_decl (BUILTINS_LOCATION, RESULT_DECL,

> +                                  NULL_TREE, void_type_node);

> +  TREE_PUBLIC (decl) = 1;

> +  TREE_STATIC (decl) = 1;

> +  DECL_IGNORED_P (decl) = 1;

> +

> +  if (USE_HIDDEN_LINKONCE)

> +    {

> +      cgraph_node::create (decl)->set_comdat_group (DECL_ASSEMBLER_NAME (decl));

> +

> +      targetm.asm_out.unique_section (decl, 0);

> +      switch_to_section (get_named_section (decl, NULL, 0));

> +

> +      targetm.asm_out.globalize_label (asm_out_file, thunk_label);

> +      fputs ("\t.hidden\t", asm_out_file);

> +      assemble_name (asm_out_file, thunk_label);

> +      putc ('\n', asm_out_file);

> +      ASM_DECLARE_FUNCTION_NAME (asm_out_file, thunk_label, decl);

> +    }

> +  else

> +    {

> +      switch_to_section (text_section);

> +      ASM_OUTPUT_LABEL (asm_out_file, thunk_label);

> +    }

> +

> +  DECL_INITIAL (decl) = make_node (BLOCK);

> +  current_function_decl = decl;

> +  allocate_struct_function (decl, false);

> +  init_function_start (decl);

> +  cfun->is_thunk = true;

> +  first_function_block_is_cold = false;

> +  final_start_function (emit_barrier (), asm_out_file, 1);

> +

> +  /* This makes CFI at least usable for indirect jumps.

> +

> +     Stopping in the thunk: backtrace will point to the thunk target

> +     is if it was interrupted by a signal.  For a call this means that

> +     the call chain will be: caller->callee->thunk   */

> +  if (flag_asynchronous_unwind_tables)

> +    {

> +      fputs ("\t.cfi_signal_frame\n", asm_out_file);

> +      fprintf (asm_out_file, "\t.cfi_return_column %d\n", regno);

> +      for (i = 0; i < FPR15_REGNUM; i++)

> +       fprintf (asm_out_file, "\t.cfi_same_value %s\n", reg_names[i]);

> +    }

> +

> +  if (z10_p)

> +    {

> +      /* exrl  0,1f  */

> +

> +      /* We generate a thunk for z10 compiled code although z10 is

> +        currently not enabled.  Tell the assembler to accept the

> +        instruction.  */

> +      if (!TARGET_CPU_Z10)

> +       {

> +         fputs ("\t.machine push\n", asm_out_file);

> +         fputs ("\t.machine z10\n", asm_out_file);

> +       }

> +      /* We use exrl even if -mzarch hasn't been specified on the

> +        command line so we have to tell the assembler to accept

> +        it.  */

> +      if (!TARGET_ZARCH)

> +       fputs ("\t.machinemode zarch\n", asm_out_file);

> +

> +      fputs ("\texrl\t0,1f\n", asm_out_file);

> +

> +      if (!TARGET_ZARCH)

> +       fputs ("\t.machinemode esa\n", asm_out_file);

> +

> +      if (!TARGET_CPU_Z10)

> +       fputs ("\t.machine pop\n", asm_out_file);

> +    }

> +  else if (TARGET_CPU_ZARCH)

> +    {

> +      /* larl %r1,1f  */

> +      fprintf (asm_out_file, "\tlarl\t%%r%d,1f\n",

> +              INDIRECT_BRANCH_THUNK_REGNUM);

> +

> +      /* ex 0,0(%r1)  */

> +      fprintf (asm_out_file, "\tex\t0,0(%%r%d)\n",

> +              INDIRECT_BRANCH_THUNK_REGNUM);

> +    }

> +  else

> +    gcc_unreachable ();

> +

> +  /* 0:    j 0b  */

> +  fputs ("0:\tj\t0b\n", asm_out_file);

> +

> +  /* 1:    br <regno>  */

> +  fprintf (asm_out_file, "1:\tbr\t%%r%d\n", regno);

> +

> +  final_end_function ();

> +  init_insn_lengths ();

> +  free_after_compilation (cfun);

> +  set_cfun (NULL);

> +  current_function_decl = NULL;

> +}

> +

> +/* Implement the asm.code_end target hook.  */

> +

> +static void

> +s390_code_end (void)

> +{

> +  int i;

> +

> +  for (i = 1; i < 16; i++)

> +    {

> +      if (indirect_branch_z10thunk_mask & (1 << i))

> +       s390_output_indirect_thunk_function (i, true);

> +

> +      if (indirect_branch_prez10thunk_mask & (1 << i))

> +       s390_output_indirect_thunk_function (i, false);

> +    }

> +

> +  if (TARGET_INDIRECT_BRANCH_TABLE)

> +    {

> +      int o;

> +      int i;

> +

> +      for (o = 0; o < INDIRECT_BRANCH_NUM_OPTIONS; o++)

> +       {

> +         if (indirect_branch_table_label_no[o] == 0)

> +           continue;

> +

> +         switch_to_section (get_section (indirect_branch_table_name[o],

> +                                         0,

> +                                         NULL_TREE));

> +         for (i = 0; i < indirect_branch_table_label_no[o]; i++)

> +           {

> +             char label_start[32];

> +

> +             ASM_GENERATE_INTERNAL_LABEL (label_start,

> +                                          indirect_branch_table_label[o], i);

> +

> +             fputs ("\t.long\t", asm_out_file);

> +             assemble_name_raw (asm_out_file, label_start);

> +             fputs ("-.\n", asm_out_file);

> +           }

> +         switch_to_section (current_function_section ());

> +       }

> +    }

> +}

> +

> +/* Implement the TARGET_CASE_VALUES_THRESHOLD target hook.  */

> +

> +unsigned int

> +s390_case_values_threshold (void)

> +{

> +  /* Disabling branch prediction for indirect jumps makes jump tables

> +     much more expensive.  */

> +  if (TARGET_INDIRECT_BRANCH_NOBP_JUMP)

> +    return 20;

> +

> +  return default_case_values_threshold ();

> +}

> +

>  /* Initialize GCC target structure.  */

>

>  #undef  TARGET_ASM_ALIGNED_HI_OP

> @@ -16441,6 +16921,12 @@ s390_asan_shadow_offset (void)

>  #undef TARGET_CONSTANT_ALIGNMENT

>  #define TARGET_CONSTANT_ALIGNMENT s390_constant_alignment

>

> +#undef TARGET_ASM_CODE_END

> +#define TARGET_ASM_CODE_END s390_code_end

> +

> +#undef TARGET_CASE_VALUES_THRESHOLD

> +#define TARGET_CASE_VALUES_THRESHOLD s390_case_values_threshold

> +

>  struct gcc_target targetm = TARGET_INITIALIZER;

>

>  #include "gt-s390.h"

> diff --git a/gcc/config/s390/s390.h b/gcc/config/s390/s390.h

> index 4564a2e..de71fd9 100644

> --- a/gcc/config/s390/s390.h

> +++ b/gcc/config/s390/s390.h

> @@ -1033,4 +1033,124 @@ extern const int processor_flags_table[];

>      s390_register_target_pragmas ();           \

>    } while (0)

>

> +#ifndef USED_FOR_TARGET

> +/* The following structure is embedded in the machine

> +   specific part of struct function.  */

> +

> +struct GTY (()) s390_frame_layout

> +{

> +  /* Offset within stack frame.  */

> +  HOST_WIDE_INT gprs_offset;

> +  HOST_WIDE_INT f0_offset;

> +  HOST_WIDE_INT f4_offset;

> +  HOST_WIDE_INT f8_offset;

> +  HOST_WIDE_INT backchain_offset;

> +

> +  /* Number of first and last gpr where slots in the register

> +     save area are reserved for.  */

> +  int first_save_gpr_slot;

> +  int last_save_gpr_slot;

> +

> +  /* Location (FP register number) where GPRs (r0-r15) should

> +     be saved to.

> +      0 - does not need to be saved at all

> +     -1 - stack slot  */

> +#define SAVE_SLOT_NONE   0

> +#define SAVE_SLOT_STACK -1

> +  signed char gpr_save_slots[16];

> +

> +  /* Number of first and last gpr to be saved, restored.  */

> +  int first_save_gpr;

> +  int first_restore_gpr;

> +  int last_save_gpr;

> +  int last_restore_gpr;

> +

> +  /* Bits standing for floating point registers. Set, if the

> +     respective register has to be saved. Starting with reg 16 (f0)

> +     at the rightmost bit.

> +     Bit 15 14 13 12 11 10  9  8  7  6  5  4  3  2  1  0

> +     fpr 15 13 11  9 14 12 10  8  7  5  3  1  6  4  2  0

> +     reg 31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16  */

> +  unsigned int fpr_bitmap;

> +

> +  /* Number of floating point registers f8-f15 which must be saved.  */

> +  int high_fprs;

> +

> +  /* Set if return address needs to be saved.

> +     This flag is set by s390_return_addr_rtx if it could not use

> +     the initial value of r14 and therefore depends on r14 saved

> +     to the stack.  */

> +  bool save_return_addr_p;

> +

> +  /* Size of stack frame.  */

> +  HOST_WIDE_INT frame_size;

> +};

> +

> +

> +/* Define the structure for the machine field in struct function.  */

> +

> +struct GTY(()) machine_function

> +{

> +  struct s390_frame_layout frame_layout;

> +

> +  /* Literal pool base register.  */

> +  rtx base_reg;

> +

> +  /* True if we may need to perform branch splitting.  */

> +  bool split_branches_pending_p;

> +

> +  bool has_landing_pad_p;

> +

> +  /* True if the current function may contain a tbegin clobbering

> +     FPRs.  */

> +  bool tbegin_p;

> +

> +  /* For -fsplit-stack support: A stack local which holds a pointer to

> +     the stack arguments for a function with a variable number of

> +     arguments.  This is set at the start of the function and is used

> +     to initialize the overflow_arg_area field of the va_list

> +     structure.  */

> +  rtx split_stack_varargs_pointer;

> +

> +  enum indirect_branch indirect_branch_jump;

> +  enum indirect_branch indirect_branch_call;

> +

> +  enum indirect_branch function_return_mem;

> +  enum indirect_branch function_return_reg;

> +};

> +#endif

> +

> +#define TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION                         \

> +  (cfun->machine->function_return_reg != indirect_branch_keep          \

> +   || cfun->machine->function_return_mem != indirect_branch_keep)

> +

> +#define TARGET_INDIRECT_BRANCH_NOBP_RET                                        \

> +  ((cfun->machine->function_return_reg != indirect_branch_keep         \

> +    && !s390_return_addr_from_memory ())                               \

> +   || (cfun->machine->function_return_mem != indirect_branch_keep      \

> +       && s390_return_addr_from_memory ()))

> +

> +#define TARGET_INDIRECT_BRANCH_NOBP_JUMP                               \

> +  (cfun->machine->indirect_branch_jump != indirect_branch_keep)

> +

> +#define TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK                         \

> +  (cfun->machine->indirect_branch_jump == indirect_branch_thunk                \

> +   || cfun->machine->indirect_branch_jump == indirect_branch_thunk_extern)

> +

> +#define TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK                  \

> +  (cfun->machine->indirect_branch_jump == indirect_branch_thunk_inline)

> +

> +#define TARGET_INDIRECT_BRANCH_NOBP_CALL                       \

> +  (cfun->machine->indirect_branch_call != indirect_branch_keep)

> +

> +#ifndef TARGET_DEFAULT_INDIRECT_BRANCH_TABLE

> +#define TARGET_DEFAULT_INDIRECT_BRANCH_TABLE 0

> +#endif

> +

> +#define TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL "__s390_indirect_jump_r%d"

> +#define TARGET_INDIRECT_BRANCH_THUNK_NAME_EX   "__s390_indirect_jump_r%duse_r%d"

> +

> +#define TARGET_INDIRECT_BRANCH_TABLE s390_indirect_branch_table

> +

> +

>  #endif /* S390_H */

> diff --git a/gcc/config/s390/s390.md b/gcc/config/s390/s390.md

> index ef71132..5481f13 100644

> --- a/gcc/config/s390/s390.md

> +++ b/gcc/config/s390/s390.md

> @@ -89,6 +89,7 @@

>     UNSPEC_LTREF

>     UNSPEC_INSN

>     UNSPEC_EXECUTE

> +   UNSPEC_EXECUTE_JUMP

>

>     ; Atomic Support

>     UNSPEC_MB

> @@ -302,6 +303,8 @@

>    [

>     ; Sibling call register.

>     (SIBCALL_REGNUM              1)

> +   ; A call-clobbered reg which can be used in indirect branch thunks

> +   (INDIRECT_BRANCH_THUNK_REGNUM 1)

>     ; Literal pool base register.

>     (BASE_REGNUM                        13)

>     ; Return address register.

> @@ -471,7 +474,10 @@

>                           z196_cracked"

>               (const_string "none"))

>

> -(define_attr "mnemonic" "bcr_flush,unknown" (const_string "unknown"))

> +; mnemonics which only get defined through if_then_else currently

> +; don't get added to the list values automatically and hence need to

> +; be listed here.

> +(define_attr "mnemonic" "b,bas,bc,bcr_flush,unknown" (const_string "unknown"))

>

>  ;; Length in bytes.

>

> @@ -9075,7 +9081,7 @@

>            (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)])

>            (match_operand 0 "address_operand" "ZQZR")

>            (pc)))]

> -  ""

> +  "!TARGET_INDIRECT_BRANCH_NOBP_JUMP"

>  {

>    if (get_attr_op_type (insn) == OP_TYPE_RR)

>      return "b%C1r\t%0";

> @@ -9085,6 +9091,9 @@

>    [(set (attr "op_type")

>          (if_then_else (match_operand 0 "register_operand" "")

>                        (const_string "RR") (const_string "RX")))

> +   (set (attr "mnemonic")

> +        (if_then_else (match_operand 0 "register_operand" "")

> +                      (const_string "bcr") (const_string "bc")))

>     (set_attr "type"  "branch")

>     (set_attr "atype" "agen")])

>

> @@ -9096,8 +9105,26 @@

>            (ANY_RETURN)

>            (pc)))]

>    "s390_can_use_<code>_insn ()"

> -  "b%C0r\t%%r14"

> -  [(set_attr "op_type" "RR")

> +{

> +  if (TARGET_INDIRECT_BRANCH_NOBP_RET)

> +    {

> +      s390_indirect_branch_via_thunk (RETURN_REGNUM,

> +                                     INVALID_REGNUM,

> +                                     operands[0],

> +                                     s390_indirect_branch_type_return);

> +      return "";

> +    }

> +  else

> +    return "b%C0r\t%%r14";

> +}

> +  [(set (attr "op_type")

> +       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")

> +                     (const_string "RIL")

> +                     (const_string "RR")))

> +   (set (attr "mnemonic")

> +       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")

> +                     (const_string "brcl")

> +                     (const_string "bcr")))

>     (set_attr "type"  "jsr")

>     (set_attr "atype" "agen")])

>

> @@ -9150,7 +9177,7 @@

>            (match_operator 1 "s390_comparison" [(reg CC_REGNUM) (const_int 0)])

>            (pc)

>            (match_operand 0 "address_operand" "ZQZR")))]

> -  ""

> +  "!TARGET_INDIRECT_BRANCH_NOBP_JUMP"

>  {

>    if (get_attr_op_type (insn) == OP_TYPE_RR)

>      return "b%D1r\t%0";

> @@ -9160,6 +9187,9 @@

>    [(set (attr "op_type")

>          (if_then_else (match_operand 0 "register_operand" "")

>                        (const_string "RR") (const_string "RX")))

> +   (set (attr "mnemonic")

> +        (if_then_else (match_operand 0 "register_operand" "")

> +                      (const_string "bcr") (const_string "bc")))

>     (set_attr "type"  "branch")

>     (set_attr "atype" "agen")])

>

> @@ -9664,21 +9694,144 @@

>      ;

>    else

>      operands[0] = force_reg (Pmode, operands[0]);

> +

> +  if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK)

> +    {

> +      operands[0] = force_reg (Pmode, operands[0]);

> +      if (TARGET_CPU_Z10)

> +       {

> +         if (TARGET_64BIT)

> +           emit_jump_insn (gen_indirect_jump_via_thunkdi_z10 (operands[0]));

> +         else

> +           emit_jump_insn (gen_indirect_jump_via_thunksi_z10 (operands[0]));

> +       }

> +      else

> +       {

> +         if (TARGET_64BIT)

> +           emit_jump_insn (gen_indirect_jump_via_thunkdi (operands[0]));

> +         else

> +           emit_jump_insn (gen_indirect_jump_via_thunksi (operands[0]));

> +       }

> +      DONE;

> +    }

> +

> +  if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK)

> +    {

> +      operands[0] = force_reg (Pmode, operands[0]);

> +      rtx label_ref = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());

> +      if (TARGET_CPU_Z10)

> +       {

> +         if (TARGET_64BIT)

> +           emit_jump_insn (gen_indirect_jump_via_inlinethunkdi_z10 (operands[0],

> +                                                                    label_ref));

> +         else

> +           emit_jump_insn (gen_indirect_jump_via_inlinethunksi_z10 (operands[0],

> +                                                                    label_ref));

> +       }

> +      else

> +       {

> +         if (TARGET_64BIT)

> +           emit_jump_insn (gen_indirect_jump_via_inlinethunkdi (operands[0],

> +                                                                label_ref,

> +                                                                force_reg (Pmode, label_ref)));

> +         else

> +           emit_jump_insn (gen_indirect_jump_via_inlinethunksi (operands[0],

> +                                                                label_ref,

> +                                                                force_reg (Pmode, label_ref)));

> +       }

> +      DONE;

> +    }

>  })

>

> -; The first constraint must be an "extra address constraint" in order

> -; to trigger address reloading in LRA/reload

>  (define_insn "*indirect_jump"

>    [(set (pc)

> -       (match_operand 0 "address_operand" "ZR,a"))]

> - ""

> - "@

> -  b\t%a0

> -  br\t%0"

> - [(set_attr "op_type" "RX,RR")

> +       (match_operand 0 "address_operand" "ZR"))]

> + "!TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK"

> +{

> +  if (get_attr_op_type (insn) == OP_TYPE_RR)

> +    return "br\t%0";

> +  else

> +    return "b\t%a0";

> +}

> + [(set (attr "op_type")

> +       (if_then_else (match_operand 0 "register_operand" "")

> +                    (const_string "RR") (const_string "RX")))

> +  (set (attr "mnemonic")

> +       (if_then_else (match_operand 0 "register_operand" "")

> +                    (const_string "br") (const_string "b")))

>    (set_attr "type"  "branch")

> -  (set_attr "atype" "agen")

> -  (set_attr "cpu_facility" "*")])

> +  (set_attr "atype" "agen")])

> +

> +(define_insn "indirect_jump_via_thunk<mode>_z10"

> +  [(set (pc)

> +       (match_operand:P 0 "register_operand" "a"))]

> + "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK

> +  && TARGET_CPU_Z10"

> +{

> +  s390_indirect_branch_via_thunk (REGNO (operands[0]),

> +                                 INVALID_REGNUM,

> +                                 NULL_RTX,

> +                                 s390_indirect_branch_type_jump);

> +  return "";

> +}

> + [(set_attr "op_type"  "RIL")

> +  (set_attr "mnemonic" "jg")

> +  (set_attr "type"  "branch")

> +  (set_attr "atype" "agen")])

> +

> +(define_insn "indirect_jump_via_thunk<mode>"

> +  [(set (pc)

> +       (match_operand:P 0 "register_operand" " a"))

> +   (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]

> + "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK

> +  && !TARGET_CPU_Z10"

> +{

> +  s390_indirect_branch_via_thunk (REGNO (operands[0]),

> +                                 INVALID_REGNUM,

> +                                 NULL_RTX,

> +                                 s390_indirect_branch_type_jump);

> +  return "";

> +}

> + [(set_attr "op_type"  "RIL")

> +  (set_attr "mnemonic" "jg")

> +  (set_attr "type"  "branch")

> +  (set_attr "atype" "agen")])

> +

> +

> +; The label_ref is wrapped into an if_then_else in order to hide it

> +; from mark_jump_label.  Without this the label_ref would become the

> +; ONLY jump target of that jump breaking the control flow graph.

> +(define_insn "indirect_jump_via_inlinethunk<mode>_z10"

> +  [(unspec [(if_then_else (match_operand:P 1 "larl_operand" "X")

> +                         (const_int 0)

> +                         (const_int 0))

> +           (const_int 0)] UNSPEC_EXECUTE_JUMP)

> +   (set (pc) (match_operand:P 0 "register_operand" "a"))]

> +  "TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK

> +   && TARGET_CPU_Z10"

> +{

> +  s390_indirect_branch_via_inline_thunk (operands[1]);

> +  return "";

> +}

> +  [(set_attr "op_type" "RIL")

> +   (set_attr "type"    "branch")

> +   (set_attr "length"  "10")])

> +

> +(define_insn "indirect_jump_via_inlinethunk<mode>"

> +  [(unspec [(if_then_else (match_operand:P 1 "larl_operand" "X")

> +                         (const_int 0)

> +                         (const_int 0))

> +           (match_operand:P 2 "register_operand" "a")] UNSPEC_EXECUTE_JUMP)

> +   (set (pc) (match_operand:P 0 "register_operand" "a"))]

> +  "TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK

> +   && !TARGET_CPU_Z10"

> +{

> +  s390_indirect_branch_via_inline_thunk (operands[2]);

> +  return "";

> +}

> +  [(set_attr "op_type" "RX")

> +   (set_attr "type"    "branch")

> +   (set_attr "length"  "8")])

>

>  ; FIXME: LRA does not appear to be able to deal with MEMs being

>  ; checked against address constraints like ZR above.  So make this a

> @@ -9686,7 +9839,7 @@

>  (define_insn "*indirect2_jump"

>    [(set (pc)

>         (match_operand 0 "nonimmediate_operand" "a,T"))]

> - ""

> + "!TARGET_INDIRECT_BRANCH_NOBP_JUMP"

>   "@

>    br\t%0

>    bi\t%0"

> @@ -9699,11 +9852,74 @@

>  ; casesi instruction pattern(s).

>  ;

>

> -(define_insn "casesi_jump"

> - [(set (pc) (match_operand 0 "address_operand" "ZR"))

> -   (use (label_ref (match_operand 1 "" "")))]

> +(define_expand "casesi_jump"

> +  [(parallel

> +    [(set (pc) (match_operand 0 "address_operand"))

> +     (use (label_ref (match_operand 1 "")))])]

>    ""

>  {

> +  if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK)

> +    {

> +      operands[0] = force_reg (GET_MODE (operands[0]), operands[0]);

> +

> +      if (TARGET_CPU_Z10)

> +       {

> +         if (TARGET_64BIT)

> +           emit_jump_insn (gen_casesi_jump_via_thunkdi_z10 (operands[0],

> +                                                            operands[1]));

> +         else

> +           emit_jump_insn (gen_casesi_jump_via_thunksi_z10 (operands[0],

> +                                                            operands[1]));

> +       }

> +      else

> +       {

> +         if (TARGET_64BIT)

> +           emit_jump_insn (gen_casesi_jump_via_thunkdi (operands[0],

> +                                                        operands[1]));

> +         else

> +           emit_jump_insn (gen_casesi_jump_via_thunksi (operands[0],

> +                                                        operands[1]));

> +       }

> +      DONE;

> +    }

> +

> +    if (TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK)

> +    {

> +      operands[0] = force_reg (Pmode, operands[0]);

> +      rtx label_ref = gen_rtx_LABEL_REF (VOIDmode, gen_label_rtx ());

> +      if (TARGET_CPU_Z10)

> +       {

> +         if (TARGET_64BIT)

> +           emit_jump_insn (gen_casesi_jump_via_inlinethunkdi_z10 (operands[0],

> +                                                                  operands[1],

> +                                                                  label_ref));

> +         else

> +           emit_jump_insn (gen_casesi_jump_via_inlinethunksi_z10 (operands[0],

> +                                                                  operands[1],

> +                                                                  label_ref));

> +       }

> +      else

> +       {

> +         if (TARGET_64BIT)

> +           emit_jump_insn (gen_casesi_jump_via_inlinethunkdi (operands[0],

> +                                                              operands[1],

> +                                                              label_ref,

> +                                                              force_reg (Pmode, label_ref)));

> +         else

> +           emit_jump_insn (gen_casesi_jump_via_inlinethunksi (operands[0],

> +                                                              operands[1],

> +                                                              label_ref,

> +                                                              force_reg (Pmode, label_ref)));

> +       }

> +      DONE;

> +    }

> +})

> +

> +(define_insn "*casesi_jump"

> + [(set (pc) (match_operand 0 "address_operand" "ZR"))

> +  (use (label_ref (match_operand 1 "" "")))]

> + "!TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK"

> +{

>    if (get_attr_op_type (insn) == OP_TYPE_RR)

>      return "br\t%0";

>    else

> @@ -9712,9 +9928,85 @@

>    [(set (attr "op_type")

>          (if_then_else (match_operand 0 "register_operand" "")

>                        (const_string "RR") (const_string "RX")))

> +   (set (attr "mnemonic")

> +        (if_then_else (match_operand 0 "register_operand" "")

> +                      (const_string "br") (const_string "b")))

> +   (set_attr "type"  "branch")

> +   (set_attr "atype" "agen")])

> +

> +(define_insn "casesi_jump_via_thunk<mode>_z10"

> + [(set (pc) (match_operand:P 0 "register_operand" "a"))

> +  (use (label_ref (match_operand 1 "" "")))]

> + "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK

> +  && TARGET_CPU_Z10"

> +{

> +  s390_indirect_branch_via_thunk (REGNO (operands[0]),

> +                                 INVALID_REGNUM,

> +                                 NULL_RTX,

> +                                 s390_indirect_branch_type_jump);

> +  return "";

> +}

> +  [(set_attr "op_type" "RIL")

> +   (set_attr "mnemonic" "jg")

> +   (set_attr "type"  "branch")

> +   (set_attr "atype" "agen")])

> +

> +(define_insn "casesi_jump_via_thunk<mode>"

> + [(set (pc) (match_operand:P 0 "register_operand" "a"))

> +  (use (label_ref (match_operand 1 "" "")))

> +  (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]

> + "TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK

> +  && !TARGET_CPU_Z10"

> +{

> +  s390_indirect_branch_via_thunk (REGNO (operands[0]),

> +                                 INVALID_REGNUM,

> +                                 NULL_RTX,

> +                                 s390_indirect_branch_type_jump);

> +  return "";

> +}

> +  [(set_attr "op_type" "RIL")

> +   (set_attr "mnemonic" "jg")

>     (set_attr "type"  "branch")

>     (set_attr "atype" "agen")])

>

> +

> +; The label_ref is wrapped into an if_then_else in order to hide it

> +; from mark_jump_label.  Without this the label_ref would become the

> +; ONLY jump target of that jump breaking the control flow graph.

> +(define_insn "casesi_jump_via_inlinethunk<mode>_z10"

> +  [(unspec [(if_then_else (match_operand:P 2 "larl_operand" "X")

> +                         (const_int 0)

> +                         (const_int 0))

> +           (const_int 0)] UNSPEC_EXECUTE_JUMP)

> +   (set (pc) (match_operand:P 0 "register_operand" "a"))

> +   (use (label_ref (match_operand 1 "" "")))]

> +  "TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK

> +   && TARGET_CPU_Z10"

> +{

> +  s390_indirect_branch_via_inline_thunk (operands[2]);

> +  return "";

> +}

> +  [(set_attr "op_type" "RIL")

> +   (set_attr "type"    "cs")

> +   (set_attr "length"  "10")])

> +

> +(define_insn "casesi_jump_via_inlinethunk<mode>"

> +  [(unspec [(if_then_else (match_operand:P 2 "larl_operand" "X")

> +                         (const_int 0)

> +                         (const_int 0))

> +           (match_operand:P 3 "register_operand" "a")] UNSPEC_EXECUTE_JUMP)

> +   (set (pc) (match_operand:P 0 "register_operand" "a"))

> +   (use (label_ref (match_operand 1 "" "")))]

> +  "TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK

> +   && !TARGET_CPU_Z10"

> +{

> +  s390_indirect_branch_via_inline_thunk (operands[3]);

> +  return "";

> +}

> +  [(set_attr "op_type" "RX")

> +   (set_attr "type"    "cs")

> +   (set_attr "length"  "8")])

> +

>  (define_expand "casesi"

>    [(match_operand:SI 0 "general_operand" "")

>     (match_operand:SI 1 "general_operand" "")

> @@ -9819,8 +10111,27 @@

>           (match_operand 0 "const_int_operand" "n"))]

>    "SIBLING_CALL_P (insn)

>     && GET_MODE (XEXP (XEXP (PATTERN (insn), 0), 0)) == Pmode"

> -  "br\t%%r1"

> -  [(set_attr "op_type" "RR")

> +{

> +  if (TARGET_INDIRECT_BRANCH_NOBP_CALL)

> +    {

> +      gcc_assert (TARGET_CPU_Z10);

> +      s390_indirect_branch_via_thunk (SIBCALL_REGNUM,

> +                                     INVALID_REGNUM,

> +                                     NULL_RTX,

> +                                     s390_indirect_branch_type_call);

> +      return "";

> +    }

> +  else

> +    return "br\t%%r1";

> +}

> + [(set (attr "op_type")

> +       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")

> +                    (const_string "RIL")

> +                    (const_string "RR")))

> +  (set (attr "mnemonic")

> +       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")

> +                    (const_string "jg")

> +                    (const_string "br")))

>     (set_attr "type"  "branch")

>     (set_attr "atype" "agen")])

>

> @@ -9860,8 +10171,27 @@

>               (match_operand 1 "const_int_operand" "n")))]

>    "SIBLING_CALL_P (insn)

>     && GET_MODE (XEXP (XEXP (XEXP (PATTERN (insn), 1), 0), 0)) == Pmode"

> -  "br\t%%r1"

> -  [(set_attr "op_type" "RR")

> +{

> +  if (TARGET_INDIRECT_BRANCH_NOBP_CALL)

> +    {

> +      gcc_assert (TARGET_CPU_Z10);

> +      s390_indirect_branch_via_thunk (SIBCALL_REGNUM,

> +                                     INVALID_REGNUM,

> +                                     NULL_RTX,

> +                                     s390_indirect_branch_type_call);

> +      return "";

> +    }

> +  else

> +    return "br\t%%r1";

> +}

> +  [(set (attr "op_type")

> +       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")

> +                    (const_string "RIL")

> +                    (const_string "RR")))

> +   (set (attr "mnemonic")

> +       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_CALL")

> +                    (const_string "jg")

> +                    (const_string "br")))

>     (set_attr "type"  "branch")

>     (set_attr "atype" "agen")])

>

> @@ -9927,7 +10257,9 @@

>    [(call (mem:QI (match_operand 0 "address_operand" "ZR"))

>           (match_operand 1 "const_int_operand" "n"))

>     (clobber (match_operand 2 "register_operand" "=r"))]

> -  "!SIBLING_CALL_P (insn) && GET_MODE (operands[2]) == Pmode"

> +  "!TARGET_INDIRECT_BRANCH_NOBP_CALL

> +   && !SIBLING_CALL_P (insn)

> +   && GET_MODE (operands[2]) == Pmode"

>  {

>    if (get_attr_op_type (insn) == OP_TYPE_RR)

>      return "basr\t%2,%0";

> @@ -9937,6 +10269,50 @@

>    [(set (attr "op_type")

>          (if_then_else (match_operand 0 "register_operand" "")

>                        (const_string "RR") (const_string "RX")))

> +   (set (attr "mnemonic")

> +        (if_then_else (match_operand 0 "register_operand" "")

> +                      (const_string "basr") (const_string "bas")))

> +   (set_attr "type"  "jsr")

> +   (set_attr "atype" "agen")

> +   (set_attr "z196prop" "z196_cracked")])

> +

> +(define_insn "*basr_via_thunk<mode>_z10"

> +  [(call (mem:QI (match_operand:P 0 "register_operand" "a"))

> +         (match_operand 1 "const_int_operand"          "n"))

> +   (clobber (match_operand:P 2 "register_operand"    "=&r"))]

> +  "TARGET_INDIRECT_BRANCH_NOBP_CALL

> +   && TARGET_CPU_Z10

> +   && !SIBLING_CALL_P (insn)"

> +{

> +  s390_indirect_branch_via_thunk (REGNO (operands[0]),

> +                                 REGNO (operands[2]),

> +                                 NULL_RTX,

> +                                 s390_indirect_branch_type_call);

> +  return "";

> +}

> +  [(set_attr "op_type" "RIL")

> +   (set_attr "mnemonic" "brasl")

> +   (set_attr "type"  "jsr")

> +   (set_attr "atype" "agen")

> +   (set_attr "z196prop" "z196_cracked")])

> +

> +(define_insn "*basr_via_thunk<mode>"

> +  [(call (mem:QI (match_operand:P 0 "register_operand" "a"))

> +         (match_operand 1 "const_int_operand"          "n"))

> +   (clobber (match_operand:P 2 "register_operand"    "=&r"))

> +   (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]

> +  "TARGET_INDIRECT_BRANCH_NOBP_CALL

> +   && !TARGET_CPU_Z10

> +   && !SIBLING_CALL_P (insn)"

> +{

> +  s390_indirect_branch_via_thunk (REGNO (operands[0]),

> +                                 REGNO (operands[2]),

> +                                 NULL_RTX,

> +                                 s390_indirect_branch_type_call);

> +  return "";

> +}

> +  [(set_attr "op_type" "RIL")

> +   (set_attr "mnemonic" "brasl")

>     (set_attr "type"  "jsr")

>     (set_attr "atype" "agen")

>     (set_attr "z196prop" "z196_cracked")])

> @@ -9988,7 +10364,9 @@

>          (call (mem:QI (match_operand 1 "address_operand" "ZR"))

>                (match_operand 2 "const_int_operand" "n")))

>     (clobber (match_operand 3 "register_operand" "=r"))]

> -  "!SIBLING_CALL_P (insn) && GET_MODE (operands[3]) == Pmode"

> +  "!TARGET_INDIRECT_BRANCH_NOBP_CALL

> +   && !SIBLING_CALL_P (insn)

> +   && GET_MODE (operands[3]) == Pmode"

>  {

>    if (get_attr_op_type (insn) == OP_TYPE_RR)

>      return "basr\t%3,%1";

> @@ -9998,6 +10376,54 @@

>    [(set (attr "op_type")

>          (if_then_else (match_operand 1 "register_operand" "")

>                        (const_string "RR") (const_string "RX")))

> +   (set (attr "mnemonic")

> +        (if_then_else (match_operand 1 "register_operand" "")

> +                      (const_string "basr") (const_string "bas")))

> +   (set_attr "type"  "jsr")

> +   (set_attr "atype" "agen")

> +   (set_attr "z196prop" "z196_cracked")])

> +

> +(define_insn "*basr_r_via_thunk_z10"

> +  [(set (match_operand 0 "" "")

> +        (call (mem:QI (match_operand 1 "register_operand" "a"))

> +              (match_operand 2 "const_int_operand"        "n")))

> +   (clobber (match_operand 3 "register_operand"         "=&r"))]

> +  "TARGET_INDIRECT_BRANCH_NOBP_CALL

> +   && TARGET_CPU_Z10

> +   && !SIBLING_CALL_P (insn)

> +   && GET_MODE (operands[3]) == Pmode"

> +{

> +  s390_indirect_branch_via_thunk (REGNO (operands[1]),

> +                                 REGNO (operands[3]),

> +                                 NULL_RTX,

> +                                 s390_indirect_branch_type_call);

> +  return "";

> +}

> +  [(set_attr "op_type" "RIL")

> +   (set_attr "mnemonic" "brasl")

> +   (set_attr "type"  "jsr")

> +   (set_attr "atype" "agen")

> +   (set_attr "z196prop" "z196_cracked")])

> +

> +(define_insn "*basr_r_via_thunk"

> +  [(set (match_operand 0 "" "")

> +        (call (mem:QI (match_operand 1 "register_operand" "a"))

> +              (match_operand 2 "const_int_operand"        "n")))

> +   (clobber (match_operand 3 "register_operand"         "=&r"))

> +   (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]

> +  "TARGET_INDIRECT_BRANCH_NOBP_CALL

> +   && !TARGET_CPU_Z10

> +   && !SIBLING_CALL_P (insn)

> +   && GET_MODE (operands[3]) == Pmode"

> +{

> +  s390_indirect_branch_via_thunk (REGNO (operands[1]),

> +                                 REGNO (operands[3]),

> +                                 NULL_RTX,

> +                                 s390_indirect_branch_type_call);

> +  return "";

> +}

> +  [(set_attr "op_type" "RIL")

> +   (set_attr "mnemonic"  "brasl")

>     (set_attr "type"  "jsr")

>     (set_attr "atype" "agen")

>     (set_attr "z196prop" "z196_cracked")])

> @@ -10734,17 +11160,101 @@

>  (define_insn "<code>"

>    [(ANY_RETURN)]

>    "s390_can_use_<code>_insn ()"

> -  "br\t%%r14"

> -  [(set_attr "op_type" "RR")

> +{

> +  if (TARGET_INDIRECT_BRANCH_NOBP_RET)

> +    {

> +      /* The target is always r14 so there is no clobber

> +        of r1 needed for pre z10 targets.  */

> +      s390_indirect_branch_via_thunk (RETURN_REGNUM,

> +                                     INVALID_REGNUM,

> +                                     NULL_RTX,

> +                                     s390_indirect_branch_type_return);

> +      return "";

> +    }

> +  else

> +    return "br\t%%r14";

> +}

> +  [(set (attr "op_type")

> +       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")

> +                     (const_string "RIL")

> +                     (const_string "RR")))

> +   (set (attr "mnemonic")

> +       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")

> +                     (const_string "jg")

> +                     (const_string "br")))

>     (set_attr "type"    "jsr")

>     (set_attr "atype"   "agen")])

>

> -(define_insn "*return"

> +

> +(define_expand "return_use"

> +  [(parallel

> +    [(return)

> +     (use (match_operand 0 "register_operand" "a"))])]

> +  ""

> +{

> +  if (!TARGET_CPU_Z10

> +      && TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION)

> +    {

> +      if (TARGET_64BIT)

> +        emit_jump_insn (gen_returndi_prez10 (operands[0]));

> +      else

> +        emit_jump_insn (gen_returnsi_prez10 (operands[0]));

> +      DONE;

> +    }

> +})

> +

> +(define_insn "*return<mode>"

>    [(return)

> -   (use (match_operand 0 "register_operand" "a"))]

> -  "GET_MODE (operands[0]) == Pmode"

> -  "br\t%0"

> -  [(set_attr "op_type" "RR")

> +   (use (match_operand:P 0 "register_operand" "a"))]

> +  "TARGET_CPU_Z10 || !TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION"

> +{

> +  if (TARGET_INDIRECT_BRANCH_NOBP_RET)

> +    {

> +      s390_indirect_branch_via_thunk (REGNO (operands[0]),

> +                                      INVALID_REGNUM,

> +                                      NULL_RTX,

> +                                      s390_indirect_branch_type_return);

> +      return "";

> +    }

> +  else

> +    return "br\t%0";

> +}

> +  [(set (attr "op_type")

> +       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")

> +                     (const_string "RIL")

> +                     (const_string "RR")))

> +   (set (attr "mnemonic")

> +       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")

> +                     (const_string "jg")

> +                     (const_string "br")))

> +   (set_attr "type"    "jsr")

> +   (set_attr "atype"   "agen")])

> +

> +(define_insn "return<mode>_prez10"

> +  [(return)

> +   (use (match_operand:P 0 "register_operand" "a"))

> +   (clobber (reg:P INDIRECT_BRANCH_THUNK_REGNUM))]

> +  "!TARGET_CPU_Z10 && TARGET_INDIRECT_BRANCH_NOBP_RET_OPTION"

> +{

> +  if (TARGET_INDIRECT_BRANCH_NOBP_RET)

> +    {

> +      s390_indirect_branch_via_thunk (REGNO (operands[0]),

> +                                      INVALID_REGNUM,

> +                                      NULL_RTX,

> +                                      s390_indirect_branch_type_return);

> +      return "";

> +    }

> +  else

> +    return "br\t%0";

> +}

> +  [(set (attr "op_type")

> +       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")

> +                     (const_string "RIL")

> +                     (const_string "RR")))

> +   (set (attr "mnemonic")

> +       (if_then_else (match_test "TARGET_INDIRECT_BRANCH_NOBP_RET")

> +                     (const_string "jg")

> +                     (const_string "br")))

>     (set_attr "type"    "jsr")

>     (set_attr "atype"   "agen")])

>

> diff --git a/gcc/config/s390/s390.opt b/gcc/config/s390/s390.opt

> index ea969cd..eb16f9c 100644

> --- a/gcc/config/s390/s390.opt

> +++ b/gcc/config/s390/s390.opt

> @@ -233,3 +233,63 @@ Use LRA instead of reload.

>  mpic-data-is-text-relative

>  Target Report Var(s390_pic_data_is_text_relative) Init(TARGET_DEFAULT_PIC_DATA_IS_TEXT_RELATIVE)

>  Assume data segments are relative to text segment.

> +

> +

> +mindirect-branch=

> +Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch) Init(indirect_branch_keep)

> +Wrap all indirect branches into execute in order to disable branch

> +prediction.

> +

> +mindirect-branch-jump=

> +Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch_jump) Init(indirect_branch_keep)

> +Wrap indirect table jumps and computed gotos into execute in order to

> +disable branch prediction.  Using thunk or thunk-extern with this

> +option requires the thunks to be considered signal handlers to order to

> +generate correct CFI.  For environments where unwinding (e.g. for

> +exceptions) is required please use thunk-inline instead.

> +

> +mindirect-branch-call=

> +Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_indirect_branch_call) Init(indirect_branch_keep)

> +Wrap all indirect calls into execute in order to disable branch prediction.

> +

> +mfunction-return=

> +Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return) Init(indirect_branch_keep)

> +Wrap all indirect return branches into execute in order to disable branch

> +prediction.

> +

> +mfunction-return-mem=

> +Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return_mem) Init(indirect_branch_keep)

> +Wrap indirect return branches into execute in order to disable branch

> +prediction. This affects only branches where the return address is

> +going to be restored from memory.

> +

> +mfunction-return-reg=

> +Target Report RejectNegative Joined Enum(indirect_branch) Var(s390_function_return_reg) Init(indirect_branch_keep)

> +Wrap indirect return branches into execute in order to disable branch

> +prediction. This affects only branches where the return address

> +doesn't need to be restored from memory.

> +

> +Enum

> +Name(indirect_branch) Type(enum indirect_branch)

> +Known indirect branch choices (for use with the -mindirect-branch=/-mfunction-return= options):

> +

> +EnumValue

> +Enum(indirect_branch) String(keep) Value(indirect_branch_keep)

> +

> +EnumValue

> +Enum(indirect_branch) String(thunk) Value(indirect_branch_thunk)

> +

> +EnumValue

> +Enum(indirect_branch) String(thunk-inline) Value(indirect_branch_thunk_inline)

> +

> +EnumValue

> +Enum(indirect_branch) String(thunk-extern) Value(indirect_branch_thunk_extern)

> +

> +mindirect-branch-table

> +Target Report Var(s390_indirect_branch_table) Init(TARGET_DEFAULT_INDIRECT_BRANCH_TABLE)

> +Generate sections .s390_indirect_jump, .s390_indirect_call,

> +.s390_return_reg, and .s390_return_mem to contain the indirect branch

> +locations which have been patched as part of using one of the

> +-mindirect-branch* or -mfunction-return* options.  The sections

> +consist of an array of 32 bit elements. Each entry holds the offset

> +from the entry to the patched location.

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c

> new file mode 100644

> index 0000000..db9336d

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c

> @@ -0,0 +1,56 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3  -march=z10 --save-temps -mindirect-branch-table" } */

> +

> +int gl;

> +

> +void __attribute__((noinline,noclone))

> +foo (int a)

> +{

> +  gl = a + 40;

> +}

> +

> +int __attribute__((noinline,noclone))

> +foo_value (int a)

> +{

> +  return a + 40;

> +}

> +

> +void* __attribute__((noinline,noclone))

> +get_fptr (int a)

> +{

> +  switch (a)

> +    {

> +    case 0: return &foo; break;

> +    case 1: return &foo_value; break;

> +    default: __builtin_abort ();

> +    }

> +}

> +

> +void (*f) (int);

> +int (*g) (int);

> +

> +int __attribute__((indirect_branch_call("thunk")))

> +main ()

> +{

> +  int res;

> +

> +  f = get_fptr(0);

> +  f (2);

> +  if (gl != 42)

> +    __builtin_abort ();

> +

> +  g = get_fptr(1);

> +  if (g (2) != 42)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 2 x main

> +/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */

> +/* { dg-final { scan-assembler "exrl" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c

> new file mode 100644

> index 0000000..c02b45a

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c

> @@ -0,0 +1,59 @@

> +/* { dg-do compile } */

> +/* { dg-options "-O3  -march=z10 --save-temps -mindirect-branch-call=thunk-extern -mindirect-branch-table" } */

> +

> +int gl;

> +

> +void __attribute__((noinline,noclone))

> +foo (int a)

> +{

> +  gl = a + 40;

> +}

> +

> +int __attribute__((noinline,noclone))

> +foo_value (int a)

> +{

> +  return a + 40;

> +}

> +

> +void*  __attribute__((noinline,noclone))

> +get_fptr (int a)

> +{

> +  switch (a)

> +    {

> +    case 0: return &foo; break;

> +    case 1: return &foo_value; break;

> +    default: __builtin_abort ();

> +    }

> +}

> +

> +void (*f) (int);

> +int (*g) (int);

> +

> +int

> +main ()

> +{

> +  int res;

> +

> +  f = get_fptr(0);

> +  f (2);

> +  if (gl != 42)

> +    __builtin_abort ();

> +

> +  g = get_fptr(1);

> +  if (g (2) != 42)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 2 x main

> +/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */

> +

> +/* No thunks due to thunk-extern.  */

> +/* { dg-final { scan-assembler-not "exrl" } } */

> +/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c

> new file mode 100644

> index 0000000..b5f13eb

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c

> @@ -0,0 +1,56 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3  -march=z10 --save-temps -mindirect-branch-call=thunk -mindirect-branch-table" } */

> +

> +int gl;

> +

> +void __attribute__((noinline,noclone))

> +foo (int a)

> +{

> +  gl = a + 40;

> +}

> +

> +int __attribute__((noinline,noclone))

> +foo_value (int a)

> +{

> +  return a + 40;

> +}

> +

> +void*  __attribute__((noinline,noclone))

> +get_fptr (int a)

> +{

> +  switch (a)

> +    {

> +    case 0: return &foo; break;

> +    case 1: return &foo_value; break;

> +    default: __builtin_abort ();

> +    }

> +}

> +

> +void (*f) (int);

> +int (*g) (int);

> +

> +int

> +main ()

> +{

> +  int res;

> +

> +  f = get_fptr(0);

> +  f (2);

> +  if (gl != 42)

> +    __builtin_abort ();

> +

> +  g = get_fptr(1);

> +  if (g (2) != 42)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 2 x main

> +/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */

> +/* { dg-final { scan-assembler "exrl" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c

> new file mode 100644

> index 0000000..486495b

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c

> @@ -0,0 +1,56 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3  -march=z900 --save-temps -mindirect-branch-call=thunk -mindirect-branch-table" } */

> +

> +int gl;

> +

> +void __attribute__((noinline,noclone))

> +foo (int a)

> +{

> +  gl = a + 40;

> +}

> +

> +int __attribute__((noinline,noclone))

> +foo_value (int a)

> +{

> +  return a + 40;

> +}

> +

> +void*  __attribute__((noinline,noclone))

> +get_fptr (int a)

> +{

> +  switch (a)

> +    {

> +    case 0: return &foo; break;

> +    case 1: return &foo_value; break;

> +    default: __builtin_abort ();

> +    }

> +}

> +

> +void (*f) (int);

> +int (*g) (int);

> +

> +int

> +main ()

> +{

> +  int res;

> +

> +  f = get_fptr(0);

> +  f (2);

> +  if (gl != 42)

> +    __builtin_abort ();

> +

> +  g = get_fptr(1);

> +  if (g (2) != 42)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 2 x main

> +/* { dg-final { scan-assembler-times "brasl\t%r\[0-9\]*,__s390_indirect_jump" 2 } } */

> +/* { dg-final { scan-assembler "ex\t" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c

> new file mode 100644

> index 0000000..c62ddf5

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c

> @@ -0,0 +1,42 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-table" } */

> +/* { dg-require-effective-target label_values } */

> +

> +/* This is a copy of the gcc.c-torture/execute/20040302-1.c

> +   testcase.  */

> +

> +int code[]={0,0,0,0,1};

> +

> +void

> +foo(int x) {

> +  volatile int b;

> +  b = 0xffffffff;

> +}

> +

> +void __attribute__((indirect_branch_jump("thunk")))

> +bar(int *pc) {

> +  static const void *l[] = {&&lab0, &&end};

> +

> +  foo(0);

> +  goto *l[*pc];

> + lab0:

> +  foo(0);

> +  pc++;

> +  goto *l[*pc];

> + end:

> +  return;

> +}

> +

> +int main() {

> +  bar(code);

> +  return 0;

> +}

> +

> +/* 2x bar */

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */

> +/* { dg-final { scan-assembler "exrl" } } */

> +

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c

> new file mode 100644

> index 0000000..63d64c1

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c

> @@ -0,0 +1,42 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-table" } */

> +/* { dg-require-effective-target label_values } */

> +

> +/* This is a copy of the gcc.c-torture/execute/20040302-1.c

> +   testcase.  */

> +

> +int code[]={0,0,0,0,1};

> +

> +void foo(int x) {

> +  volatile int b;

> +  b = 0xffffffff;

> +}

> +

> +void __attribute__((indirect_branch_jump("thunk-inline")))

> +bar(int *pc) {

> +  static const void *l[] = {&&lab0, &&end};

> +

> +  foo(0);

> +  goto *l[*pc];

> + lab0:

> +  foo(0);

> +  pc++;

> +  goto *l[*pc];

> + end:

> +  return;

> +}

> +

> +int

> +main() {

> +  bar(code);

> +  return 0;

> +}

> +

> +/* The two gotos in bar get merged.  */

> +/* { dg-final { scan-assembler-times "exrl" 1 } } */

> +

> +/* { dg-final { scan-assembler-not "jg\t__s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c

> new file mode 100644

> index 0000000..28d7837

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c

> @@ -0,0 +1,43 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */

> +/* { dg-require-effective-target label_values } */

> +

> +/* This is a copy of the gcc.c-torture/execute/20040302-1.c

> +   testcase.  */

> +

> +int code[]={0,0,0,0,1};

> +

> +void

> +foo(int x) {

> +  volatile int b;

> +  b = 0xffffffff;

> +}

> +

> +void

> +bar(int *pc) {

> +  static const void *l[] = {&&lab0, &&end};

> +

> +  foo(0);

> +  goto *l[*pc];

> + lab0:

> +  foo(0);

> +  pc++;

> +  goto *l[*pc];

> + end:

> +  return;

> +}

> +

> +int

> +main() {

> +  bar(code);

> +  return 0;

> +}

> +

> +/* The two gotos in bar get merged.  */

> +/* { dg-final { scan-assembler-times "exrl" 1 } } */

> +

> +/* { dg-final { scan-assembler-not "jg\t__s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c

> new file mode 100644

> index 0000000..3c0c007

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c

> @@ -0,0 +1,43 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */

> +/* { dg-require-effective-target label_values } */

> +

> +/* This is a copy of the gcc.c-torture/execute/20040302-1.c

> +   testcase.  */

> +

> +int code[]={0,0,0,0,1};

> +

> +void

> +foo(int x) {

> +  volatile int b;

> +  b = 0xffffffff;

> +}

> +

> +void

> +bar(int *pc) {

> +  static const void *l[] = {&&lab0, &&end};

> +

> +  foo(0);

> +  goto *l[*pc];

> + lab0:

> +  foo(0);

> +  pc++;

> +  goto *l[*pc];

> + end:

> +  return;

> +}

> +

> +int

> +main() {

> +  bar(code);

> +  return 0;

> +}

> +

> +/* The two gotos in bar get merged.  */

> +/* { dg-final { scan-assembler-times "\tex\t" 1 } } */

> +

> +/* { dg-final { scan-assembler-not "jg\t__s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c

> new file mode 100644

> index 0000000..05c8bb8

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c

> @@ -0,0 +1,46 @@

> +/* { dg-do compile } */

> +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk-extern -mindirect-branch-table" } */

> +/* { dg-require-effective-target label_values } */

> +

> +/* This is a copy of the gcc.c-torture/execute/20040302-1.c

> +   testcase.  */

> +

> +int code[]={0,0,0,0,1};

> +

> +void

> +foo(int x) {

> +  volatile int b;

> +  b = 0xffffffff;

> +}

> +

> +void

> +bar(int *pc) {

> +  static const void *l[] = {&&lab0, &&end};

> +

> +  foo(0);

> +  goto *l[*pc];

> + lab0:

> +  foo(0);

> +  pc++;

> +  goto *l[*pc];

> + end:

> +  return;

> +}

> +

> +int

> +main() {

> +  bar(code);

> +  return 0;

> +}

> +

> +/* 2 x bar

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */

> +

> +/* No thunks due to thunk-extern.  */

> +/* { dg-final { scan-assembler-not "exrl" } } */

> +/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */

> +

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c

> new file mode 100644

> index 0000000..71c86fd

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c

> @@ -0,0 +1,43 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */

> +/* { dg-require-effective-target label_values } */

> +

> +/* This is a copy of the gcc.c-torture/execute/20040302-1.c

> +   testcase.  */

> +

> +int code[]={0,0,0,0,1};

> +

> +void

> +foo(int x) {

> +  volatile int b;

> +  b = 0xffffffff;

> +}

> +

> +void

> +bar(int *pc) {

> +  static const void *l[] = {&&lab0, &&end};

> +

> +  foo(0);

> +  goto *l[*pc];

> + lab0:

> +  foo(0);

> +  pc++;

> +  goto *l[*pc];

> + end:

> +  return;

> +}

> +

> +int

> +main() {

> +  bar(code);

> +  return 0;

> +}

> +

> +/* 2x bar */

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */

> +/* { dg-final { scan-assembler "exrl" } } */

> +

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c

> new file mode 100644

> index 0000000..89ad799

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c

> @@ -0,0 +1,43 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z900 --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */

> +/* { dg-require-effective-target label_values } */

> +

> +/* This is a copy of the gcc.c-torture/execute/20040302-1.c

> +   testcase.  */

> +

> +int code[]={0,0,0,0,1};

> +

> +void

> +foo(int x) {

> +  volatile int b;

> +  b = 0xffffffff;

> +}

> +

> +void

> +bar(int *pc) {

> +  static const void *l[] = {&&lab0, &&end};

> +

> +  foo(0);

> +  goto *l[*pc];

> + lab0:

> +  foo(0);

> +  pc++;

> +  goto *l[*pc];

> + end:

> +  return;

> +}

> +

> +int

> +main() {

> +  bar(code);

> +  return 0;

> +}

> +

> +/* 2 x bar

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */

> +/* { dg-final { scan-assembler "ex\t" } } */

> +

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c b/gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c

> new file mode 100644

> index 0000000..4bf88cf

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c

> @@ -0,0 +1,46 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-table" } */

> +

> +int gl = 0;

> +

> +int __attribute__((noinline,noclone))

> +bar (int a)

> +{

> +  return a + 2;

> +}

> +

> +void __attribute__((function_return("thunk"),noinline,noclone))

> +foo (int a)

> +{

> +  int i;

> +

> +  if (a == 42)

> +    return;

> +

> +  for (i = 0; i < a; i++)

> +    gl += bar (i);

> +}

> +

> +int

> +main ()

> +{

> +  foo (3);

> +  if (gl != 9)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* With -march=z10 -mzarch the shrink wrapped returns use compare and

> +   swap relative to jump to the exit block instead of making use of

> +   the conditional return pattern.

> +   FIXME: Use compare and branch register for that!!!! */

> +

> +/* 2 x foo

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */

> +/* { dg-final { scan-assembler "exrl" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c b/gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c

> new file mode 100644

> index 0000000..8b32bfe

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c

> @@ -0,0 +1,40 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mfunction-return-mem=thunk -mindirect-branch-table" } */

> +

> +int gl = 0;

> +

> +int __attribute__((noinline,noclone))

> +bar (int a)

> +{

> +  return a + 2;

> +}

> +

> +void __attribute__((function_return("keep"),noinline,noclone))

> +foo (int a)

> +{

> +  int i;

> +

> +  if (a == 42)

> +    return;

> +

> +  for (i = 0; i < a; i++)

> +    gl += bar (i);

> +}

> +

> +int __attribute__((function_return("keep")))

> +main ()

> +{

> +  foo (3);

> +  if (gl != 9)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* { dg-final { scan-assembler-not "jg\t__s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "exrl" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c b/gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c

> new file mode 100644

> index 0000000..39cab8b

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c

> @@ -0,0 +1,46 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-table" } */

> +

> +int gl = 0;

> +

> +int __attribute__((noinline,noclone))

> +bar (int a)

> +{

> +  return a + 2;

> +}

> +

> +void __attribute__((function_return_mem("thunk"),noinline,noclone))

> +foo (int a)

> +{

> +  int i;

> +

> +  if (a == 42)

> +    return;

> +

> +  for (i = 0; i < a; i++)

> +    gl += bar (i);

> +}

> +

> +int

> +main ()

> +{

> +  foo (3);

> +  if (gl != 9)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* With -march=z10 -mzarch the shrink wrapped returns use compare and

> +   swap relative to jump to the exit block instead of making use of

> +   the conditional return pattern.

> +   FIXME: Use compare and branch register for that!!!! */

> +

> +/* 2 x foo

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */

> +/* { dg-final { scan-assembler "exrl" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c b/gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c

> new file mode 100644

> index 0000000..f99f152

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c

> @@ -0,0 +1,49 @@

> +/* { dg-do compile } */

> +/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mfunction-return-mem=thunk-extern -mindirect-branch-table" } */

> +

> +int gl = 0;

> +

> +int __attribute__((noinline,noclone))

> +bar (int a)

> +{

> +  return a + 2;

> +}

> +

> +void __attribute__((noinline,noclone))

> +foo (int a)

> +{

> +  int i;

> +

> +  if (a == 42)

> +    return;

> +

> +  for (i = 0; i < a; i++)

> +    gl += bar (i);

> +}

> +

> +int

> +main ()

> +{

> +  foo (3);

> +  if (gl != 9)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* With -march=z10 -mzarch the shrink wrapped returns use compare and

> +   swap relative to jump to the exit block instead of making use of

> +   the conditional return pattern.

> +   FIXME: Use compare and branch register for that!!!! */

> +

> +/* 2 x foo, 1 x main

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 3 } } */

> +

> +/* No thunks due to thunk-extern.  */

> +/* { dg-final { scan-assembler-not "exrl" } } */

> +/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c

> new file mode 100644

> index 0000000..177fc32

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c

> @@ -0,0 +1,46 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mfunction-return-mem=thunk -mindirect-branch-table" } */

> +

> +int gl = 0;

> +

> +int __attribute__((noinline,noclone))

> +bar (int a)

> +{

> +  return a + 2;

> +}

> +

> +void __attribute__((noinline,noclone))

> +foo (int a)

> +{

> +  int i;

> +

> +  if (a == 42)

> +    return;

> +

> +  for (i = 0; i < a; i++)

> +    gl += bar (i);

> +}

> +

> +int

> +main ()

> +{

> +  foo (3);

> +  if (gl != 9)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* With -march=z10 -mzarch the shrink wrapped returns use compare and

> +   swap relative to jump to the exit block instead of making use of

> +   the conditional return pattern.

> +   FIXME: Use compare and branch register for that!!!! */

> +

> +/* 2 x foo, 1 x main

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 3 } } */

> +/* { dg-final { scan-assembler "exrl" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c

> new file mode 100644

> index 0000000..0b31811

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c

> @@ -0,0 +1,48 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-mem=thunk -mindirect-branch-table" } */

> +

> +int gl = 0;

> +

> +int __attribute__((noinline,noclone))

> +bar (int a)

> +{

> +  return a + 2;

> +}

> +

> +void __attribute__((noinline,noclone))

> +foo (int a)

> +{

> +  int i;

> +

> +  if (a == 42)

> +    return;

> +

> +  for (i = 0; i < a; i++)

> +    gl += bar (i);

> +}

> +

> +int

> +main ()

> +{

> +  foo (3);

> +  if (gl != 9)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 1 x foo, 1 x main

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */

> +

> +/* 1 x foo, conditional return, shrink wrapped

> +/* { dg-final { scan-assembler "jge\t__s390_indirect_jump" } } */

> +

> +/* 1 x foo, conditional return, shrink wrapped

> +/* { dg-final { scan-assembler "jgle\t__s390_indirect_jump" } } */

> +

> +/* { dg-final { scan-assembler "ex\t" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c

> new file mode 100644

> index 0000000..ebfc9ff

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c

> @@ -0,0 +1,41 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z10 --save-temps -mindirect-branch-table" } */

> +

> +int gl = 0;

> +

> +int __attribute__((function_return_reg("thunk"),noinline,noclone))

> +bar (int a)

> +{

> +  return a + 2;

> +}

> +

> +void __attribute__((noinline,noclone))

> +foo (int a)

> +{

> +  int i;

> +

> +  if (a == 42)

> +    return;

> +

> +  for (i = 0; i < a; i++)

> +    gl += bar (i);

> +}

> +

> +int

> +main ()

> +{

> +  foo (3);

> +  if (gl != 9)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 1 x bar

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */

> +/* { dg-final { scan-assembler "exrl" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c

> new file mode 100644

> index 0000000..82833f7

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c

> @@ -0,0 +1,44 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */

> +

> +/* We have to generate different thunks for indirect branches

> +   depending on whether the code is compiled for pre z10 machines or

> +   later.  This testcase makes sure this works within the same compile

> +   unit.  */

> +

> +int __attribute__((noinline,noclone,target("arch=z10")))

> +bar (int a)

> +{

> +  return a + 2;

> +}

> +

> +int __attribute__((noinline,noclone,target("arch=z9-ec")))

> +foo (int a)

> +{

> +  return a + 3;

> +}

> +

> +int

> +main ()

> +{

> +  if (bar (42) != 44)

> +    __builtin_abort ();

> +

> +  if (foo (42) != 45)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 1 x bar, 1 x foo */

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 2 } } */

> +/* 1 x foo */

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump_r1use" 1 } } */

> +

> +/* { dg-final { scan-assembler-times "ex\t" 1 } } */

> +/* { dg-final { scan-assembler-times "exrl\t" 1 } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c

> new file mode 100644

> index 0000000..4ea14e3

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c

> @@ -0,0 +1,44 @@

> +/* { dg-do compile } */

> +/* { dg-options "-O3 -march=z10 --save-temps -mfunction-return-reg=thunk-extern -mindirect-branch-table" } */

> +

> +int gl = 0;

> +

> +int __attribute__((noinline,noclone))

> +bar (int a)

> +{

> +  return a + 2;

> +}

> +

> +void __attribute__((noinline,noclone))

> +foo (int a)

> +{

> +  int i;

> +

> +  if (a == 42)

> +    return;

> +

> +  for (i = 0; i < a; i++)

> +    gl += bar (i);

> +}

> +

> +int

> +main ()

> +{

> +  foo (3);

> +  if (gl != 9)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 1 x bar

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */

> +

> +/* No thunks due to thunk-extern.  */

> +/* { dg-final { scan-assembler-not "exrl" } } */

> +/* { dg-final { scan-assembler-not ".globl __s390_indirect_jump" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c

> new file mode 100644

> index 0000000..42c3e74

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c

> @@ -0,0 +1,41 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z10 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */

> +

> +int gl = 0;

> +

> +int __attribute__((noinline,noclone))

> +bar (int a)

> +{

> +  return a + 2;

> +}

> +

> +void __attribute__((noinline,noclone))

> +foo (int a)

> +{

> +  int i;

> +

> +  if (a == 42)

> +    return;

> +

> +  for (i = 0; i < a; i++)

> +    gl += bar (i);

> +}

> +

> +int

> +main ()

> +{

> +  foo (3);

> +  if (gl != 9)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 1 x bar

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */

> +/* { dg-final { scan-assembler "exrl" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c

> new file mode 100644

> index 0000000..3f4efa5

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c

> @@ -0,0 +1,41 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z900 --save-temps -mfunction-return-reg=thunk -mindirect-branch-table" } */

> +

> +int gl = 0;

> +

> +int __attribute__((noinline,noclone))

> +bar (int a)

> +{

> +  return a + 2;

> +}

> +

> +void __attribute__((noinline,noclone))

> +foo (int a)

> +{

> +  int i;

> +

> +  if (a == 42)

> +    return;

> +

> +  for (i = 0; i < a; i++)

> +    gl += bar (i);

> +}

> +

> +int

> +main ()

> +{

> +  foo (3);

> +  if (gl != 9)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 1 x bar

> +/* { dg-final { scan-assembler-times "jg\t__s390_indirect_jump" 1 } } */

> +/* { dg-final { scan-assembler "ex\t" } } */

> +

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler     "section\t.s390_return_reg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_mem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c

> new file mode 100644

> index 0000000..8dfd7e4

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c

> @@ -0,0 +1,78 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */

> +

> +/* case-values-threshold will be set to 20 by the back-end when jump

> +   thunk are requested.  */

> +

> +int __attribute__((noinline,noclone)) foo1 (void) { return 1; }

> +int __attribute__((noinline,noclone)) foo2 (void) { return 2; }

> +int __attribute__((noinline,noclone)) foo3 (void) { return 3; }

> +int __attribute__((noinline,noclone)) foo4 (void) { return 4; }

> +int __attribute__((noinline,noclone)) foo5 (void) { return 5; }

> +int __attribute__((noinline,noclone)) foo6 (void) { return 6; }

> +int __attribute__((noinline,noclone)) foo7 (void) { return 7; }

> +int __attribute__((noinline,noclone)) foo8 (void) { return 8; }

> +int __attribute__((noinline,noclone)) foo9 (void) { return 9; }

> +int __attribute__((noinline,noclone)) foo10 (void) { return 10; }

> +int __attribute__((noinline,noclone)) foo11 (void) { return 11; }

> +int __attribute__((noinline,noclone)) foo12 (void) { return 12; }

> +int __attribute__((noinline,noclone)) foo13 (void) { return 13; }

> +int __attribute__((noinline,noclone)) foo14 (void) { return 14; }

> +int __attribute__((noinline,noclone)) foo15 (void) { return 15; }

> +int __attribute__((noinline,noclone)) foo16 (void) { return 16; }

> +int __attribute__((noinline,noclone)) foo17 (void) { return 17; }

> +int __attribute__((noinline,noclone)) foo18 (void) { return 18; }

> +int __attribute__((noinline,noclone)) foo19 (void) { return 19; }

> +int __attribute__((noinline,noclone)) foo20 (void) { return 20; }

> +

> +

> +int __attribute__((noinline,noclone))

> +bar (int a)

> +{

> +  int ret = 0;

> +

> +  switch (a)

> +    {

> +    case 1: ret = foo1 (); break;

> +    case 2: ret = foo2 (); break;

> +    case 3: ret = foo3 (); break;

> +    case 4: ret = foo4 (); break;

> +    case 5: ret = foo5 (); break;

> +    case 6: ret = foo6 (); break;

> +    case 7: ret = foo7 (); break;

> +    case 8: ret = foo8 (); break;

> +    case 9: ret = foo9 (); break;

> +    case 10: ret = foo10 (); break;

> +    case 11: ret = foo11 (); break;

> +    case 12: ret = foo12 (); break;

> +    case 13: ret = foo13 (); break;

> +    case 14: ret = foo14 (); break;

> +    case 15: ret = foo15 (); break;

> +    case 16: ret = foo16 (); break;

> +    case 17: ret = foo17 (); break;

> +    case 18: ret = foo18 (); break;

> +    case 19: ret = foo19 (); break;

> +    case 20: ret = foo20 (); break;

> +    default:

> +      __builtin_abort ();

> +    }

> +

> +  return ret;

> +}

> +

> +int

> +main ()

> +{

> +  if (bar (3) != 3)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 1 x bar

> +/* { dg-final { scan-assembler-times "exrl" 1 } } */

> +

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c

> new file mode 100644

> index 0000000..46d2c54

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c

> @@ -0,0 +1,78 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z900 -mzarch --save-temps -mindirect-branch-jump=thunk-inline -mindirect-branch-table" } */

> +

> +/* case-values-threshold will be set to 20 by the back-end when jump

> +   thunk are requested.  */

> +

> +int __attribute__((noinline,noclone)) foo1 (void) { return 1; }

> +int __attribute__((noinline,noclone)) foo2 (void) { return 2; }

> +int __attribute__((noinline,noclone)) foo3 (void) { return 3; }

> +int __attribute__((noinline,noclone)) foo4 (void) { return 4; }

> +int __attribute__((noinline,noclone)) foo5 (void) { return 5; }

> +int __attribute__((noinline,noclone)) foo6 (void) { return 6; }

> +int __attribute__((noinline,noclone)) foo7 (void) { return 7; }

> +int __attribute__((noinline,noclone)) foo8 (void) { return 8; }

> +int __attribute__((noinline,noclone)) foo9 (void) { return 9; }

> +int __attribute__((noinline,noclone)) foo10 (void) { return 10; }

> +int __attribute__((noinline,noclone)) foo11 (void) { return 11; }

> +int __attribute__((noinline,noclone)) foo12 (void) { return 12; }

> +int __attribute__((noinline,noclone)) foo13 (void) { return 13; }

> +int __attribute__((noinline,noclone)) foo14 (void) { return 14; }

> +int __attribute__((noinline,noclone)) foo15 (void) { return 15; }

> +int __attribute__((noinline,noclone)) foo16 (void) { return 16; }

> +int __attribute__((noinline,noclone)) foo17 (void) { return 17; }

> +int __attribute__((noinline,noclone)) foo18 (void) { return 18; }

> +int __attribute__((noinline,noclone)) foo19 (void) { return 19; }

> +int __attribute__((noinline,noclone)) foo20 (void) { return 20; }

> +

> +

> +int __attribute__((noinline,noclone))

> +bar (int a)

> +{

> +  int ret = 0;

> +

> +  switch (a)

> +    {

> +    case 1: ret = foo1 (); break;

> +    case 2: ret = foo2 (); break;

> +    case 3: ret = foo3 (); break;

> +    case 4: ret = foo4 (); break;

> +    case 5: ret = foo5 (); break;

> +    case 6: ret = foo6 (); break;

> +    case 7: ret = foo7 (); break;

> +    case 8: ret = foo8 (); break;

> +    case 9: ret = foo9 (); break;

> +    case 10: ret = foo10 (); break;

> +    case 11: ret = foo11 (); break;

> +    case 12: ret = foo12 (); break;

> +    case 13: ret = foo13 (); break;

> +    case 14: ret = foo14 (); break;

> +    case 15: ret = foo15 (); break;

> +    case 16: ret = foo16 (); break;

> +    case 17: ret = foo17 (); break;

> +    case 18: ret = foo18 (); break;

> +    case 19: ret = foo19 (); break;

> +    case 20: ret = foo20 (); break;

> +    default:

> +      __builtin_abort ();

> +    }

> +

> +  return ret;

> +}

> +

> +int

> +main ()

> +{

> +  if (bar (3) != 3)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 1 x bar

> +/* { dg-final { scan-assembler-times "\tex\t" 1 } } */

> +

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c

> new file mode 100644

> index 0000000..9dfe391

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c

> @@ -0,0 +1,77 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z10 -mzarch --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */

> +/* case-values-threshold will be set to 20 by the back-end when jump

> +   thunk are requested.  */

> +

> +int __attribute__((noinline,noclone)) foo1 (void) { return 1; }

> +int __attribute__((noinline,noclone)) foo2 (void) { return 2; }

> +int __attribute__((noinline,noclone)) foo3 (void) { return 3; }

> +int __attribute__((noinline,noclone)) foo4 (void) { return 4; }

> +int __attribute__((noinline,noclone)) foo5 (void) { return 5; }

> +int __attribute__((noinline,noclone)) foo6 (void) { return 6; }

> +int __attribute__((noinline,noclone)) foo7 (void) { return 7; }

> +int __attribute__((noinline,noclone)) foo8 (void) { return 8; }

> +int __attribute__((noinline,noclone)) foo9 (void) { return 9; }

> +int __attribute__((noinline,noclone)) foo10 (void) { return 10; }

> +int __attribute__((noinline,noclone)) foo11 (void) { return 11; }

> +int __attribute__((noinline,noclone)) foo12 (void) { return 12; }

> +int __attribute__((noinline,noclone)) foo13 (void) { return 13; }

> +int __attribute__((noinline,noclone)) foo14 (void) { return 14; }

> +int __attribute__((noinline,noclone)) foo15 (void) { return 15; }

> +int __attribute__((noinline,noclone)) foo16 (void) { return 16; }

> +int __attribute__((noinline,noclone)) foo17 (void) { return 17; }

> +int __attribute__((noinline,noclone)) foo18 (void) { return 18; }

> +int __attribute__((noinline,noclone)) foo19 (void) { return 19; }

> +int __attribute__((noinline,noclone)) foo20 (void) { return 20; }

> +

> +

> +int __attribute__((noinline,noclone))

> +bar (int a)

> +{

> +  int ret = 0;

> +

> +  switch (a)

> +    {

> +    case 1: ret = foo1 (); break;

> +    case 2: ret = foo2 (); break;

> +    case 3: ret = foo3 (); break;

> +    case 4: ret = foo4 (); break;

> +    case 5: ret = foo5 (); break;

> +    case 6: ret = foo6 (); break;

> +    case 7: ret = foo7 (); break;

> +    case 8: ret = foo8 (); break;

> +    case 9: ret = foo9 (); break;

> +    case 10: ret = foo10 (); break;

> +    case 11: ret = foo11 (); break;

> +    case 12: ret = foo12 (); break;

> +    case 13: ret = foo13 (); break;

> +    case 14: ret = foo14 (); break;

> +    case 15: ret = foo15 (); break;

> +    case 16: ret = foo16 (); break;

> +    case 17: ret = foo17 (); break;

> +    case 18: ret = foo18 (); break;

> +    case 19: ret = foo19 (); break;

> +    case 20: ret = foo20 (); break;

> +    default:

> +      __builtin_abort ();

> +    }

> +

> +  return ret;

> +}

> +

> +int

> +main ()

> +{

> +  if (bar (3) != 3)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 1 x bar

> +/* { dg-final { scan-assembler-times "exrl" 1 } } */

> +

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */

> diff --git a/gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c

> new file mode 100644

> index 0000000..f1439a8

> --- /dev/null

> +++ b/gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c

> @@ -0,0 +1,78 @@

> +/* { dg-do run } */

> +/* { dg-options "-O3 -march=z900 -mzarch --save-temps -mindirect-branch-jump=thunk -mindirect-branch-table" } */

> +

> +/* case-values-threshold will be set to 20 by the back-end when jump

> +   thunk are requested.  */

> +

> +int __attribute__((noinline,noclone)) foo1 (void) { return 1; }

> +int __attribute__((noinline,noclone)) foo2 (void) { return 2; }

> +int __attribute__((noinline,noclone)) foo3 (void) { return 3; }

> +int __attribute__((noinline,noclone)) foo4 (void) { return 4; }

> +int __attribute__((noinline,noclone)) foo5 (void) { return 5; }

> +int __attribute__((noinline,noclone)) foo6 (void) { return 6; }

> +int __attribute__((noinline,noclone)) foo7 (void) { return 7; }

> +int __attribute__((noinline,noclone)) foo8 (void) { return 8; }

> +int __attribute__((noinline,noclone)) foo9 (void) { return 9; }

> +int __attribute__((noinline,noclone)) foo10 (void) { return 10; }

> +int __attribute__((noinline,noclone)) foo11 (void) { return 11; }

> +int __attribute__((noinline,noclone)) foo12 (void) { return 12; }

> +int __attribute__((noinline,noclone)) foo13 (void) { return 13; }

> +int __attribute__((noinline,noclone)) foo14 (void) { return 14; }

> +int __attribute__((noinline,noclone)) foo15 (void) { return 15; }

> +int __attribute__((noinline,noclone)) foo16 (void) { return 16; }

> +int __attribute__((noinline,noclone)) foo17 (void) { return 17; }

> +int __attribute__((noinline,noclone)) foo18 (void) { return 18; }

> +int __attribute__((noinline,noclone)) foo19 (void) { return 19; }

> +int __attribute__((noinline,noclone)) foo20 (void) { return 20; }

> +

> +

> +int __attribute__((noinline,noclone))

> +bar (int a)

> +{

> +  int ret = 0;

> +

> +  switch (a)

> +    {

> +    case 1: ret = foo1 (); break;

> +    case 2: ret = foo2 (); break;

> +    case 3: ret = foo3 (); break;

> +    case 4: ret = foo4 (); break;

> +    case 5: ret = foo5 (); break;

> +    case 6: ret = foo6 (); break;

> +    case 7: ret = foo7 (); break;

> +    case 8: ret = foo8 (); break;

> +    case 9: ret = foo9 (); break;

> +    case 10: ret = foo10 (); break;

> +    case 11: ret = foo11 (); break;

> +    case 12: ret = foo12 (); break;

> +    case 13: ret = foo13 (); break;

> +    case 14: ret = foo14 (); break;

> +    case 15: ret = foo15 (); break;

> +    case 16: ret = foo16 (); break;

> +    case 17: ret = foo17 (); break;

> +    case 18: ret = foo18 (); break;

> +    case 19: ret = foo19 (); break;

> +    case 20: ret = foo20 (); break;

> +    default:

> +      __builtin_abort ();

> +    }

> +

> +  return ret;

> +}

> +

> +int

> +main ()

> +{

> +  if (bar (3) != 3)

> +    __builtin_abort ();

> +

> +  return 0;

> +}

> +

> +/* 1 x bar

> +/* { dg-final { scan-assembler-times "ex\t" 1 } } */

> +

> +/* { dg-final { scan-assembler     "section\t.s390_indirect_jump" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_indirect_call" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_fromreg" } } */

> +/* { dg-final { scan-assembler-not "section\t.s390_return_frommem" } } */

> --

> 2.9.1

>
Andreas Krebbel Feb. 8, 2018, 12:17 p.m. | #2
On 02/08/2018 12:33 PM, Richard Biener wrote:
> On Wed, Feb 7, 2018 at 1:01 PM, Andreas Krebbel

> <krebbel@linux.vnet.ibm.com> wrote:

>> This patch implements GCC support for mitigating vulnerability

>> CVE-2017-5715 known as Spectre #2 on IBM Z.

>>

>> In order to disable prediction of indirect branches the implementation

>> makes use of an IBM Z specific feature - the execute instruction.

>> Performing an indirect branch via execute prevents the branch from

>> being subject to dynamic branch prediction.

>>

>> The implementation tries to stay close to the x86 solution regarding

>> user interface.

>>

>> x86 style options supported (without thunk-inline):

>>

>> -mindirect-branch=(keep|thunk|thunk-extern)

>> -mfunction-return=(keep|thunk|thunk-extern)

>>

>> IBM Z specific options:

>>

>> -mindirect-branch-jump=(keep|thunk|thunk-extern|thunk-inline)

>> -mindirect-branch-call=(keep|thunk|thunk-extern)

>> -mfunction-return-reg=(keep|thunk|thunk-extern)

>> -mfunction-return-mem=(keep|thunk|thunk-extern)

>>

>> These options allow us to enable/disable the branch conversion at a

>> finer granularity.

>>

>> -mindirect-branch sets the value of -mindirect-branch-jump and

>>  -mindirect-branch-call.

>>

>> -mfunction-return sets the value of -mfunction-return-reg and

>>  -mfunction-return-mem.

>>

>> All these options are supported on GCC command line as well as

>> function attributes.

>>

>> 'thunk' triggers the generation of out of line thunks (expolines) and

>> replaces the formerly indirect branch with a direct branch to the

>> thunk.  Depending on the -march= setting two different types of thunks

>> are generated.  With -march=z10 or higher exrl (execute relative long)

>> is being used while targeting older machines makes use of larl/ex

>> instead.  From a security perspective the exrl variant is preferable.

>>

>> 'thunk-extern' does the branch replacement like 'thunk' but does not

>> emit the thunks.

>>

>> 'thunk-inline' is only available for indirect jumps.  It should be used

>> in environments where correct CFI is important - known as user space.

>>

>> Additionally the patch introduces the -mindirect-branch-table option

>> which generates tables pointing to the locations which have been

>> modified.  This is supposed to allow reverting the changes without

>> re-compilation in situations where it isn't required. The sections are

>> split up into one section per option.

>>

>> I plan to commit the patch tomorrow.

> 

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


Yes, I'm working on it.

-Andreas-
H.J. Lu Feb. 8, 2018, 7:57 p.m. | #3
On Thu, Feb 8, 2018 at 4:17 AM, Andreas Krebbel
<krebbel@linux.vnet.ibm.com> wrote:
> On 02/08/2018 12:33 PM, Richard Biener wrote:

>> On Wed, Feb 7, 2018 at 1:01 PM, Andreas Krebbel

>> <krebbel@linux.vnet.ibm.com> wrote:

>>> This patch implements GCC support for mitigating vulnerability

>>> CVE-2017-5715 known as Spectre #2 on IBM Z.

>>>

>>> In order to disable prediction of indirect branches the implementation

>>> makes use of an IBM Z specific feature - the execute instruction.

>>> Performing an indirect branch via execute prevents the branch from

>>> being subject to dynamic branch prediction.

>>>

>>> The implementation tries to stay close to the x86 solution regarding

>>> user interface.

>>>

>>> x86 style options supported (without thunk-inline):

>>>

>>> -mindirect-branch=(keep|thunk|thunk-extern)

>>> -mfunction-return=(keep|thunk|thunk-extern)

>>>

>>> IBM Z specific options:

>>>

>>> -mindirect-branch-jump=(keep|thunk|thunk-extern|thunk-inline)

>>> -mindirect-branch-call=(keep|thunk|thunk-extern)

>>> -mfunction-return-reg=(keep|thunk|thunk-extern)

>>> -mfunction-return-mem=(keep|thunk|thunk-extern)

>>>

>>> These options allow us to enable/disable the branch conversion at a

>>> finer granularity.

>>>

>>> -mindirect-branch sets the value of -mindirect-branch-jump and

>>>  -mindirect-branch-call.

>>>

>>> -mfunction-return sets the value of -mfunction-return-reg and

>>>  -mfunction-return-mem.

>>>

>>> All these options are supported on GCC command line as well as

>>> function attributes.

>>>

>>> 'thunk' triggers the generation of out of line thunks (expolines) and

>>> replaces the formerly indirect branch with a direct branch to the

>>> thunk.  Depending on the -march= setting two different types of thunks

>>> are generated.  With -march=z10 or higher exrl (execute relative long)

>>> is being used while targeting older machines makes use of larl/ex

>>> instead.  From a security perspective the exrl variant is preferable.

>>>

>>> 'thunk-extern' does the branch replacement like 'thunk' but does not

>>> emit the thunks.

>>>

>>> 'thunk-inline' is only available for indirect jumps.  It should be used

>>> in environments where correct CFI is important - known as user space.

>>>

>>> Additionally the patch introduces the -mindirect-branch-table option

>>> which generates tables pointing to the locations which have been

>>> modified.  This is supposed to allow reverting the changes without

>>> re-compilation in situations where it isn't required. The sections are

>>> split up into one section per option.

>>>

>>> I plan to commit the patch tomorrow.

>>

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

>

> Yes, I'm working on it.

>


This breaks glibc build:

/export/gnu/import/git/toolchain/build/compilers/s390x-linux-gnu/glibc/s390x-linux-gnu/libc_pic.os:
In function `__cmsg_nxthdr':
/export/ssd/git/toolchain/build/compilers/s390x-linux-gnu/glibc-src/s390x-linux-gnu/socket/../sysdeps/unix/sysv/linux/cmsg_nxthdr.c:39:
undefined reference to `__s390_indirect_jump_r1use_r14'
/export/ssd/git/toolchain/build/compilers/s390x-linux-gnu/glibc-src/s390x-linux-gnu/socket/../sysdeps/unix/sysv/linux/cmsg_nxthdr.c:39:
undefined reference to `__s390_indirect_jump_r1use_r14'
collect2: error: ld returned 1 exit status
make[4]: *** [../Makerules:765:
/export/gnu/import/git/toolchain/build/compilers/s390x-linux-gnu/glibc/s390x-linux-gnu/libc.so]
Error 1
make[4]: Leaving directory
'/export/ssd/git/toolchain/build/compilers/s390x-linux-gnu/glibc-src/s390x-linux-gnu/elf'
make[3]: *** [Makefile:215: elf/subdir_lib] Error 2
make[3]: Leaving directory
'/export/ssd/git/toolchain/build/compilers/s390x-linux-gnu/glibc-src/s390x-linux-gnu'
make[2]: *** [Makefile:9: all] Error 2
make[2]: Leaving directory
'/export/ssd/git/toolchain/build/compilers/s390x-linux-gnu/glibc/s390x-linux-gnu'


-- 
H.J.
H.J. Lu Feb. 8, 2018, 8:14 p.m. | #4
On Thu, Feb 8, 2018 at 11:57 AM, H.J. Lu <hjl.tools@gmail.com> wrote:
> On Thu, Feb 8, 2018 at 4:17 AM, Andreas Krebbel

> <krebbel@linux.vnet.ibm.com> wrote:

>> On 02/08/2018 12:33 PM, Richard Biener wrote:

>>> On Wed, Feb 7, 2018 at 1:01 PM, Andreas Krebbel

>>> <krebbel@linux.vnet.ibm.com> wrote:

>>>> This patch implements GCC support for mitigating vulnerability

>>>> CVE-2017-5715 known as Spectre #2 on IBM Z.

>>>>

>>>> In order to disable prediction of indirect branches the implementation

>>>> makes use of an IBM Z specific feature - the execute instruction.

>>>> Performing an indirect branch via execute prevents the branch from

>>>> being subject to dynamic branch prediction.

>>>>

>>>> The implementation tries to stay close to the x86 solution regarding

>>>> user interface.

>>>>

>>>> x86 style options supported (without thunk-inline):

>>>>

>>>> -mindirect-branch=(keep|thunk|thunk-extern)

>>>> -mfunction-return=(keep|thunk|thunk-extern)

>>>>

>>>> IBM Z specific options:

>>>>

>>>> -mindirect-branch-jump=(keep|thunk|thunk-extern|thunk-inline)

>>>> -mindirect-branch-call=(keep|thunk|thunk-extern)

>>>> -mfunction-return-reg=(keep|thunk|thunk-extern)

>>>> -mfunction-return-mem=(keep|thunk|thunk-extern)

>>>>

>>>> These options allow us to enable/disable the branch conversion at a

>>>> finer granularity.

>>>>

>>>> -mindirect-branch sets the value of -mindirect-branch-jump and

>>>>  -mindirect-branch-call.

>>>>

>>>> -mfunction-return sets the value of -mfunction-return-reg and

>>>>  -mfunction-return-mem.

>>>>

>>>> All these options are supported on GCC command line as well as

>>>> function attributes.

>>>>

>>>> 'thunk' triggers the generation of out of line thunks (expolines) and

>>>> replaces the formerly indirect branch with a direct branch to the

>>>> thunk.  Depending on the -march= setting two different types of thunks

>>>> are generated.  With -march=z10 or higher exrl (execute relative long)

>>>> is being used while targeting older machines makes use of larl/ex

>>>> instead.  From a security perspective the exrl variant is preferable.

>>>>

>>>> 'thunk-extern' does the branch replacement like 'thunk' but does not

>>>> emit the thunks.

>>>>

>>>> 'thunk-inline' is only available for indirect jumps.  It should be used

>>>> in environments where correct CFI is important - known as user space.

>>>>

>>>> Additionally the patch introduces the -mindirect-branch-table option

>>>> which generates tables pointing to the locations which have been

>>>> modified.  This is supposed to allow reverting the changes without

>>>> re-compilation in situations where it isn't required. The sections are

>>>> split up into one section per option.

>>>>

>>>> I plan to commit the patch tomorrow.

>>>

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

>>

>> Yes, I'm working on it.

>>

>

> This breaks glibc build:

>

> /export/gnu/import/git/toolchain/build/compilers/s390x-linux-gnu/glibc/s390x-linux-gnu/libc_pic.os:

> In function `__cmsg_nxthdr':

> /export/ssd/git/toolchain/build/compilers/s390x-linux-gnu/glibc-src/s390x-linux-gnu/socket/../sysdeps/unix/sysv/linux/cmsg_nxthdr.c:39:

> undefined reference to `__s390_indirect_jump_r1use_r14'

> /export/ssd/git/toolchain/build/compilers/s390x-linux-gnu/glibc-src/s390x-linux-gnu/socket/../sysdeps/unix/sysv/linux/cmsg_nxthdr.c:39:

> undefined reference to `__s390_indirect_jump_r1use_r14'

> collect2: error: ld returned 1 exit status

> make[4]: *** [../Makerules:765:

> /export/gnu/import/git/toolchain/build/compilers/s390x-linux-gnu/glibc/s390x-linux-gnu/libc.so]

> Error 1

> make[4]: Leaving directory

> '/export/ssd/git/toolchain/build/compilers/s390x-linux-gnu/glibc-src/s390x-linux-gnu/elf'

> make[3]: *** [Makefile:215: elf/subdir_lib] Error 2

> make[3]: Leaving directory

> '/export/ssd/git/toolchain/build/compilers/s390x-linux-gnu/glibc-src/s390x-linux-gnu'

> make[2]: *** [Makefile:9: all] Error 2

> make[2]: Leaving directory

> '/export/ssd/git/toolchain/build/compilers/s390x-linux-gnu/glibc/s390x-linux-gnu'


I opened:

https://gcc.gnu.org/bugzilla/show_bug.cgi?id=84295

-- 
H.J.

Patch

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" } } */