S/390: Disable prediction of indirect branches
authorAndreas Krebbel <krebbel@linux.vnet.ibm.com>
Thu, 8 Feb 2018 14:45:53 +0000 (14:45 +0000)
committerAndreas Krebbel <krebbel@gcc.gnu.org>
Thu, 8 Feb 2018 14:45:53 +0000 (14:45 +0000)
This patch implements GCC support for mitigating vulnerability
CVE-2017-5715 known as Spectre #2 on IBM Z.

In order to disable prediction of indirect branches the implementation
makes use of an IBM Z specific feature - the execute instruction.
Performing an indirect branch via execute prevents the branch from
being subject to dynamic branch prediction.

The implementation tries to stay close to the x86 solution regarding
user interface.

x86 style options supported (without thunk-inline):

-mindirect-branch=(keep|thunk|thunk-extern)
-mfunction-return=(keep|thunk|thunk-extern)

IBM Z specific options:

-mindirect-branch-jump=(keep|thunk|thunk-extern|thunk-inline)
-mindirect-branch-call=(keep|thunk|thunk-extern)
-mfunction-return-reg=(keep|thunk|thunk-extern)
-mfunction-return-mem=(keep|thunk|thunk-extern)

These options allow us to enable/disable the branch conversion at a
finer granularity.

-mindirect-branch sets the value of -mindirect-branch-jump and
 -mindirect-branch-call.

-mfunction-return sets the value of -mfunction-return-reg and
 -mfunction-return-mem.

All these options are supported on GCC command line as well as
function attributes.

'thunk' triggers the generation of out of line thunks (expolines) and
replaces the formerly indirect branch with a direct branch to the
thunk.  Depending on the -march= setting two different types of thunks
are generated.  With -march=z10 or higher exrl (execute relative long)
is being used while targeting older machines makes use of larl/ex
instead.  From a security perspective the exrl variant is preferable.

'thunk-extern' does the branch replacement like 'thunk' but does not
emit the thunks.

'thunk-inline' is only available for indirect jumps.  It should be used
in environments where correct CFI is important - known as user space.

Additionally the patch introduces the -mindirect-branch-table option
which generates tables pointing to the locations which have been
modified.  This is supposed to allow reverting the changes without
re-compilation in situations where it isn't required. The sections are
split up into one section per option.

gcc/ChangeLog:

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

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

gcc/testsuite/ChangeLog:

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

* gcc.target/s390/nobp-function-pointer-attr.c: New test.
* gcc.target/s390/nobp-function-pointer-nothunk.c: New test.
* gcc.target/s390/nobp-function-pointer-z10.c: New test.
* gcc.target/s390/nobp-function-pointer-z900.c: New test.
* gcc.target/s390/nobp-indirect-jump-attr.c: New test.
* gcc.target/s390/nobp-indirect-jump-inline-attr.c: New test.
* gcc.target/s390/nobp-indirect-jump-inline-z10.c: New test.
* gcc.target/s390/nobp-indirect-jump-inline-z900.c: New test.
* gcc.target/s390/nobp-indirect-jump-nothunk.c: New test.
* gcc.target/s390/nobp-indirect-jump-z10.c: New test.
* gcc.target/s390/nobp-indirect-jump-z900.c: New test.
* gcc.target/s390/nobp-return-attr-all.c: New test.
* gcc.target/s390/nobp-return-attr-neg.c: New test.
* gcc.target/s390/nobp-return-mem-attr.c: New test.
* gcc.target/s390/nobp-return-mem-nothunk.c: New test.
* gcc.target/s390/nobp-return-mem-z10.c: New test.
* gcc.target/s390/nobp-return-mem-z900.c: New test.
* gcc.target/s390/nobp-return-reg-attr.c: New test.
* gcc.target/s390/nobp-return-reg-mixed.c: New test.
* gcc.target/s390/nobp-return-reg-nothunk.c: New test.
* gcc.target/s390/nobp-return-reg-z10.c: New test.
* gcc.target/s390/nobp-return-reg-z900.c: New test.
* gcc.target/s390/nobp-table-jump-inline-z10.c: New test.
* gcc.target/s390/nobp-table-jump-inline-z900.c: New test.
* gcc.target/s390/nobp-table-jump-z10.c: New test.
* gcc.target/s390/nobp-table-jump-z900.c: New test.

From-SVN: r257489

34 files changed:
gcc/ChangeLog
gcc/config/s390/s390-opts.h
gcc/config/s390/s390-protos.h
gcc/config/s390/s390.c
gcc/config/s390/s390.h
gcc/config/s390/s390.md
gcc/config/s390/s390.opt
gcc/testsuite/ChangeLog
gcc/testsuite/gcc.target/s390/nobp-function-pointer-attr.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-function-pointer-nothunk.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-function-pointer-z10.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-function-pointer-z900.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-indirect-jump-attr.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-attr.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z10.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-indirect-jump-inline-z900.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-indirect-jump-nothunk.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z10.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-indirect-jump-z900.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-return-attr-all.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-return-attr-neg.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-return-mem-attr.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-return-mem-nothunk.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-return-mem-z10.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-return-mem-z900.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-return-reg-attr.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-return-reg-mixed.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-return-reg-nothunk.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-return-reg-z10.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-return-reg-z900.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z10.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-table-jump-inline-z900.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-table-jump-z10.c [new file with mode: 0644]
gcc/testsuite/gcc.target/s390/nobp-table-jump-z900.c [new file with mode: 0644]

index 803e0305c2a9f8ea4605ec6033559251ce659f92..c83df9769a1608c816c0949ff9bc512397794d69 100644 (file)
@@ -1,3 +1,79 @@
+2018-02-08  Andreas Krebbel  <krebbel@linux.vnet.ibm.com>
+
+       * config/s390/s390-opts.h (enum indirect_branch): Define.
+       * config/s390/s390-protos.h (s390_return_addr_from_memory)
+       (s390_indirect_branch_via_thunk)
+       (s390_indirect_branch_via_inline_thunk): Add function prototypes.
+       (enum s390_indirect_branch_type): Define.
+       * config/s390/s390.c (struct s390_frame_layout, struct
+       machine_function): Remove.
+       (indirect_branch_prez10thunk_mask, indirect_branch_z10thunk_mask)
+       (indirect_branch_table_label_no, indirect_branch_table_name):
+       Define variables.
+       (INDIRECT_BRANCH_NUM_OPTIONS): Define macro.
+       (enum s390_indirect_branch_option): Define.
+       (s390_return_addr_from_memory): New function.
+       (s390_handle_string_attribute): New function.
+       (s390_attribute_table): Add new attribute handler.
+       (s390_execute_label): Handle UNSPEC_EXECUTE_JUMP patterns.
+       (s390_indirect_branch_via_thunk): New function.
+       (s390_indirect_branch_via_inline_thunk): New function.
+       (s390_function_ok_for_sibcall): When jumping via thunk disallow
+       sibling call optimization for non z10 compiles.
+       (s390_emit_call): Force indirect branch target to be a single
+       register.  Add r1 clobber for non-z10 compiles.
+       (s390_emit_epilogue): Emit return jump via return_use expander.
+       (s390_reorg): Handle JUMP_INSNs as execute targets.
+       (s390_option_override_internal): Perform validity checks for the
+       new command line options.
+       (s390_indirect_branch_attrvalue): New function.
+       (s390_indirect_branch_settings): New function.
+       (s390_set_current_function): Invoke s390_indirect_branch_settings.
+       (s390_output_indirect_thunk_function):  New function.
+       (s390_code_end): Implement target hook.
+       (s390_case_values_threshold): Implement target hook.
+       (TARGET_ASM_CODE_END, TARGET_CASE_VALUES_THRESHOLD): Define target
+       macros.
+       * config/s390/s390.h (struct s390_frame_layout)
+       (struct machine_function): Move here from s390.c.
+       (TARGET_INDIRECT_BRANCH_NOBP_RET)
+       (TARGET_INDIRECT_BRANCH_NOBP_JUMP)
+       (TARGET_INDIRECT_BRANCH_NOBP_JUMP_THUNK)
+       (TARGET_INDIRECT_BRANCH_NOBP_JUMP_INLINE_THUNK)
+       (TARGET_INDIRECT_BRANCH_NOBP_CALL)
+       (TARGET_DEFAULT_INDIRECT_BRANCH_TABLE)
+       (TARGET_INDIRECT_BRANCH_THUNK_NAME_EXRL)
+       (TARGET_INDIRECT_BRANCH_THUNK_NAME_EX)
+       (TARGET_INDIRECT_BRANCH_TABLE): Define macros.
+       * config/s390/s390.md (UNSPEC_EXECUTE_JUMP)
+       (INDIRECT_BRANCH_THUNK_REGNUM): Define constants.
+       (mnemonic attribute): Add values which aren't recognized
+       automatically.
+       ("*cjump_long", "*icjump_long", "*basr", "*basr_r"): Disable
+       pattern for branch conversion.  Fix mnemonic attribute.
+       ("*c<code>", "*sibcall_br", "*sibcall_value_br", "*return"): Emit
+       indirect branch via thunk if requested.
+       ("indirect_jump", "<code>"): Expand patterns for branch conversion.
+       ("*indirect_jump"): Disable for branch conversion using out of
+       line thunks.
+       ("indirect_jump_via_thunk<mode>_z10")
+       ("indirect_jump_via_thunk<mode>")
+       ("indirect_jump_via_inlinethunk<mode>_z10")
+       ("indirect_jump_via_inlinethunk<mode>", "*casesi_jump")
+       ("casesi_jump_via_thunk<mode>_z10", "casesi_jump_via_thunk<mode>")
+       ("casesi_jump_via_inlinethunk<mode>_z10")
+       ("casesi_jump_via_inlinethunk<mode>", "*basr_via_thunk<mode>_z10")
+       ("*basr_via_thunk<mode>", "*basr_r_via_thunk_z10")
+       ("*basr_r_via_thunk", "return<mode>_prez10"): New pattern.
+       ("*indirect2_jump"): Disable for branch conversion.
+       ("casesi_jump"): Turn into expander and expand patterns for branch
+       conversion.
+       ("return_use"): New expander.
+       ("*return"): Emit return via thunk and rename it to ...
+       ("*return<mode>"): ... this one.
+       * config/s390/s390.opt: Add new options and and enum for the
+       option values.
+
 2018-02-08  Richard Sandiford  <richard.sandiford@linaro.org>
 
        * lra-constraints.c (match_reload): Unconditionally use
index 23632ba94aca1518673df6f9478796f7f8a3d0f1..aaecca7e4b2dfde97f980cab6e14f4ed34697d96 100644 (file)
@@ -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
index 214062a54e16f93c888ddcd1363eafce77149293..46f0743461d81072b93bc7f834a3e30cbc7c1dfd 100644 (file)
@@ -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 */
index 03c93f1dfff9cda4a1a3c50f3c6beeb55ca84a97..62a60e2c9539d8582300255b7d8d5e59a3050685 100644 (file)
@@ -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"
index 4564a2e348e1f406b30f64419aa370f07c35eb83..de71fd927e21c7d06485cdd9ab9a86a64b02b531 100644 (file)
@@ -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 */
index ef7113288ac1d6fefe67ce27a5a08874e9faeed5..5481f1375efdce4e4e0d9a1277f26856038072cb 100644 (file)
@@ -89,6 +89,7 @@
    UNSPEC_LTREF
    UNSPEC_INSN
    UNSPEC_EXECUTE
+   UNSPEC_EXECUTE_JUMP
 
    ; Atomic Support
    UNSPEC_MB
   [
    ; 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.
                          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.
 
           (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";
   [(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")])
 
           (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")])
 
           (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";
   [(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")])
 
     ;
   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
 (define_insn "*indirect2_jump"
   [(set (pc)
        (match_operand 0 "nonimmediate_operand" "a,T"))]
- ""
+ "!TARGET_INDIRECT_BRANCH_NOBP_JUMP"
  "@
   br\t%0
   bi\t%0"
 ; 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";
   [(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" "")
          (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")])
 
              (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")])
 
   [(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";
   [(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")])
         (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";
   [(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")])
 (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")])
 
index ea969cdb24652a6ce32497966009cb3ef6a7c7d2..eb16f9c821fb05ec8b3d8cfa5cb4027d61181e09 100644 (file)
@@ -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.
index 5b83f3aff8e61da03595827914189c8cd12a4358..5f45c55b663ab9ddc8c04062fff18712571caac8 100644 (file)
@@ -1,3 +1,32 @@
+2018-02-08  Andreas Krebbel  <krebbel@linux.vnet.ibm.com>
+
+       * gcc.target/s390/nobp-function-pointer-attr.c: New test.
+       * gcc.target/s390/nobp-function-pointer-nothunk.c: New test.
+       * gcc.target/s390/nobp-function-pointer-z10.c: New test.
+       * gcc.target/s390/nobp-function-pointer-z900.c: New test.
+       * gcc.target/s390/nobp-indirect-jump-attr.c: New test.
+       * gcc.target/s390/nobp-indirect-jump-inline-attr.c: New test.
+       * gcc.target/s390/nobp-indirect-jump-inline-z10.c: New test.
+       * gcc.target/s390/nobp-indirect-jump-inline-z900.c: New test.
+       * gcc.target/s390/nobp-indirect-jump-nothunk.c: New test.
+       * gcc.target/s390/nobp-indirect-jump-z10.c: New test.
+       * gcc.target/s390/nobp-indirect-jump-z900.c: New test.
+       * gcc.target/s390/nobp-return-attr-all.c: New test.
+       * gcc.target/s390/nobp-return-attr-neg.c: New test.
+       * gcc.target/s390/nobp-return-mem-attr.c: New test.
+       * gcc.target/s390/nobp-return-mem-nothunk.c: New test.
+       * gcc.target/s390/nobp-return-mem-z10.c: New test.
+       * gcc.target/s390/nobp-return-mem-z900.c: New test.
+       * gcc.target/s390/nobp-return-reg-attr.c: New test.
+       * gcc.target/s390/nobp-return-reg-mixed.c: New test.
+       * gcc.target/s390/nobp-return-reg-nothunk.c: New test.
+       * gcc.target/s390/nobp-return-reg-z10.c: New test.
+       * gcc.target/s390/nobp-return-reg-z900.c: New test.
+       * gcc.target/s390/nobp-table-jump-inline-z10.c: New test.
+       * gcc.target/s390/nobp-table-jump-inline-z900.c: New test.
+       * gcc.target/s390/nobp-table-jump-z10.c: New test.
+       * gcc.target/s390/nobp-table-jump-z900.c: New test.
+
 2018-02-08  Richard Biener  <rguenther@suse.de>
 
        PR tree-optimization/84233
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 (file)
index 0000000..db9336d
--- /dev/null
@@ -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 (file)
index 0000000..c02b45a
--- /dev/null
@@ -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 (file)
index 0000000..b5f13eb
--- /dev/null
@@ -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 (file)
index 0000000..486495b
--- /dev/null
@@ -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 (file)
index 0000000..c62ddf5
--- /dev/null
@@ -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 (file)
index 0000000..63d64c1
--- /dev/null
@@ -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 (file)
index 0000000..28d7837
--- /dev/null
@@ -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 (file)
index 0000000..3c0c007
--- /dev/null
@@ -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 (file)
index 0000000..05c8bb8
--- /dev/null
@@ -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 (file)
index 0000000..71c86fd
--- /dev/null
@@ -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 (file)
index 0000000..89ad799
--- /dev/null
@@ -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 (file)
index 0000000..4bf88cf
--- /dev/null
@@ -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 (file)
index 0000000..8b32bfe
--- /dev/null
@@ -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 (file)
index 0000000..39cab8b
--- /dev/null
@@ -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 (file)
index 0000000..f99f152
--- /dev/null
@@ -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 (file)
index 0000000..177fc32
--- /dev/null
@@ -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 (file)
index 0000000..0b31811
--- /dev/null
@@ -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 (file)
index 0000000..ebfc9ff
--- /dev/null
@@ -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 (file)
index 0000000..82833f7
--- /dev/null
@@ -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 (file)
index 0000000..4ea14e3
--- /dev/null
@@ -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 (file)
index 0000000..42c3e74
--- /dev/null
@@ -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 (file)
index 0000000..3f4efa5
--- /dev/null
@@ -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 (file)
index 0000000..8dfd7e4
--- /dev/null
@@ -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 (file)
index 0000000..46d2c54
--- /dev/null
@@ -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 (file)
index 0000000..9dfe391
--- /dev/null
@@ -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 (file)
index 0000000..f1439a8
--- /dev/null
@@ -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" } } */