The compiler option -mindirect-branch=<value> converts indirect
branch-and-link-register and branch-register instructions according to <value>.

The default is ``keep``, which keeps indirect branch-and-link-register and
branch-register instructions unmodified.

``thunk`` converts indirect branch-and-link-register/branch-register
instructions to a branch-and-link/branch to a function containing a retpoline
(to stop speculative execution) followed by a branch-register to the target.

``thunk-inline`` is similar to ``thunk``, but inlines the retpoline
before the branch-and-link-register/branch-register instruction.

``thunk-extern`` is also similar to ``thunk``, but does not insert the
functions containing the retpoline. When using this option, these functions
need to be provided in a separate object file. The retpoline functions exist
for each register and are named ``__aarch64_indirect_thunk_xN`` (N being the
register number).

It is also possible to override the indirect-branch setting for
individual fuctions using the function attribute ``indirect_branch``.

The actual retpoline instruction sequence, which prevents speculative
indirect branches looks like this::

        str     x30, [sp, #-16]!
        bl      101f
  100: //speculation trap
        wfe
        b       100b
  101: //do ROP
        adr     x30, 102f
        ret
  102: //non-spec code
        ldr     x30, [sp], #16

This patch has been tested with the included testcases and various other
source bases (benchmarks, retpoline-patched arm64 kernel, etc.).
---
 gcc/config/aarch64/aarch64-opts.h             |   9 +
 gcc/config/aarch64/aarch64.c                  | 298 +++++++++++++++++-
 gcc/config/aarch64/aarch64.h                  |   2 +
 gcc/config/aarch64/aarch64.opt                |  20 ++
 gcc/doc/invoke.texi                           |  20 +-
 .../gcc.target/aarch64/indirect-thunk-1.c     |  25 ++
 .../gcc.target/aarch64/indirect-thunk-2.c     |  26 ++
 .../gcc.target/aarch64/indirect-thunk-3.c     |  26 ++
 .../gcc.target/aarch64/indirect-thunk-4.c     |  27 ++
 .../gcc.target/aarch64/indirect-thunk-5.c     |  25 ++
 .../gcc.target/aarch64/indirect-thunk-6.c     |  26 ++
 .../aarch64/indirect-thunk-attr-1.c           |  28 ++
 .../aarch64/indirect-thunk-attr-2.c           |  27 ++
 .../aarch64/indirect-thunk-attr-3.c           |  29 ++
 .../aarch64/indirect-thunk-attr-4.c           |  28 ++
 .../aarch64/indirect-thunk-attr-5.c           |  24 ++
 .../aarch64/indirect-thunk-attr-6.c           |  24 ++
 .../aarch64/indirect-thunk-extern-1.c         |  22 ++
 .../aarch64/indirect-thunk-extern-2.c         |  23 ++
 .../aarch64/indirect-thunk-extern-3.c         |  23 ++
 .../aarch64/indirect-thunk-extern-4.c         |  24 ++
 .../aarch64/indirect-thunk-extern-5.c         |  20 ++
 .../aarch64/indirect-thunk-extern-6.c         |  21 ++
 .../aarch64/indirect-thunk-inline-1.c         |  26 ++
 .../aarch64/indirect-thunk-inline-2.c         |  26 ++
 .../aarch64/indirect-thunk-inline-3.c         |  26 ++
 .../aarch64/indirect-thunk-inline-4.c         |  27 ++
 .../aarch64/indirect-thunk-inline-5.c         |  23 ++
 .../aarch64/indirect-thunk-inline-6.c         |  24 ++
 29 files changed, 941 insertions(+), 8 deletions(-)
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-3.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-4.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-5.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-6.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-3.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-4.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-5.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-6.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-3.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-4.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-5.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-6.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-1.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-2.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-3.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-4.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-5.c
 create mode 100644 gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-6.c

diff --git a/gcc/config/aarch64/aarch64-opts.h 
b/gcc/config/aarch64/aarch64-opts.h
index ee7bed34924..0113533892d 100644
--- a/gcc/config/aarch64/aarch64-opts.h
+++ b/gcc/config/aarch64/aarch64-opts.h
@@ -98,4 +98,13 @@ enum stack_protector_guard {
   SSP_GLOBAL                   /* global canary */
 };
 
+/* Values for -mindirect-branch option.  */
+enum indirect_branch {
+  indirect_branch_unset = 0,
+  indirect_branch_keep,
+  indirect_branch_thunk,
+  indirect_branch_thunk_inline,
+  indirect_branch_thunk_extern
+};
+
 #endif
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 4799679f9e5..56366faeb69 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -1508,6 +1508,44 @@ handle_aarch64_vector_pcs_attribute (tree *node, tree 
name, tree,
   gcc_unreachable ();
 }
 
+/* Check whether 'indirect_branch' fndecl attribute is valid.  */
+
+static tree
+aarch64_handle_fndecl_attribute (tree *node, tree name, tree args, int,
+                                bool *no_add_attrs)
+{
+  if (TREE_CODE (*node) != FUNCTION_DECL)
+    {
+      warning (OPT_Wattributes, "%qE attribute only applies to functions",
+              name);
+      *no_add_attrs = true;
+    }
+
+  if (is_attribute_p ("indirect_branch", name))
+    {
+      tree 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;
+       }
+      else if (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;
+}
+
 /* Table of machine attributes.  */
 static const struct attribute_spec aarch64_attribute_table[] =
 {
@@ -1521,6 +1559,8 @@ static const struct attribute_spec 
aarch64_attribute_table[] =
   { "Advanced SIMD type", 1, 1, false, true,  false, true,  NULL, NULL },
   { "SVE type",                  3, 3, false, true,  false, true,  NULL, NULL 
},
   { "SVE sizeless type",  0, 0, false, true,  false, true,  NULL, NULL },
+  { "indirect_branch", 1, 1, true, false, false, false,
+                         aarch64_handle_fndecl_attribute, NULL },
   { NULL,                 0, 0, false, false, false, false, NULL, NULL }
 };
 
@@ -15227,6 +15267,35 @@ aarch64_save_restore_target_globals (tree new_tree)
     TREE_TARGET_GLOBALS (new_tree) = save_target_globals_default_opts ();
 }
 
+static void
+aarch64_set_indirect_branch_type (tree fndecl)
+{
+  if (cfun->machine->indirect_branch_type == indirect_branch_unset)
+    {
+      tree attr = lookup_attribute ("indirect_branch",
+                                   DECL_ATTRIBUTES (fndecl));
+      if (attr != NULL)
+       {
+         tree args = TREE_VALUE (attr);
+         if (args == NULL)
+           gcc_unreachable ();
+         tree cst = TREE_VALUE (args);
+         if (strcmp (TREE_STRING_POINTER (cst), "keep") == 0)
+           cfun->machine->indirect_branch_type = indirect_branch_keep;
+         else if (strcmp (TREE_STRING_POINTER (cst), "thunk") == 0)
+           cfun->machine->indirect_branch_type = indirect_branch_thunk;
+         else if (strcmp (TREE_STRING_POINTER (cst), "thunk-inline") == 0)
+           cfun->machine->indirect_branch_type = indirect_branch_thunk_inline;
+         else if (strcmp (TREE_STRING_POINTER (cst), "thunk-extern") == 0)
+           cfun->machine->indirect_branch_type = indirect_branch_thunk_extern;
+         else
+           gcc_unreachable ();
+       }
+      else
+       cfun->machine->indirect_branch_type = aarch64_indirect_branch;
+    }
+}
+
 /* Implement TARGET_SET_CURRENT_FUNCTION.  Unpack the codegen decisions
    like tuning and ISA features from the DECL_FUNCTION_SPECIFIC_TARGET
    of the function, if such exists.  This function may be called multiple
@@ -15237,7 +15306,18 @@ static void
 aarch64_set_current_function (tree fndecl)
 {
   if (!fndecl || fndecl == aarch64_previous_fndecl)
-    return;
+    {
+      if (fndecl != NULL_TREE)
+       {
+         aarch64_set_indirect_branch_type (fndecl);
+       }
+      return;
+    }
+
+  if (fndecl != NULL_TREE)
+    {
+      aarch64_set_indirect_branch_type (fndecl);
+    }
 
   tree old_tree = (aarch64_previous_fndecl
                   ? DECL_FUNCTION_SPECIFIC_TARGET (aarch64_previous_fndecl)
@@ -23659,27 +23739,225 @@ aarch64_asm_file_end ()
 #endif
 }
 
+/* Label count for call and return thunks.  It is used to make unique
+   labels in call and return thunks.  */
+static int indirectlabelno;
+
+/* Bit masks of integer registers, which contain branch target, used
+   by call and return thunks functions.  */
+static int indirect_thunks_used;
+
+#ifndef INDIRECT_LABEL
+# define INDIRECT_LABEL "LIND"
+#endif
+
+/* Fills in the label name that should be used for the indirect thunk.  */
+
+static void
+indirect_thunk_name (char name[32], int regno)
+{
+  sprintf (name, "__aarch64_indirect_thunk_%s",
+          reg_names[regno]);
+}
+
+/* Output a retpoline thunk for aarch64:
+
+       push    lr
+       bl      L2
+L1:    wfe
+       b       L1
+L2:    mov     lr, &L3
+       ret
+L3:    pop     lr
+ */
+
+static void
+output_indirect_thunk (bool save_lr)
+{
+  char indirectlabel1[32];
+  char indirectlabel2[32];
+  char indirectlabel3[32];
+
+  ASM_GENERATE_INTERNAL_LABEL (indirectlabel1, INDIRECT_LABEL,
+                              indirectlabelno++);
+  ASM_GENERATE_INTERNAL_LABEL (indirectlabel2, INDIRECT_LABEL,
+                              indirectlabelno++);
+  ASM_GENERATE_INTERNAL_LABEL (indirectlabel3, INDIRECT_LABEL,
+                              indirectlabelno++);
+
+  if (save_lr)
+    {
+      /* push lr */
+      fputs ("\tstr\tx30, [sp, #-16]!\n", asm_out_file);
+    }
+
+  /* bl L2 */
+  fputs ("\tbl\t", asm_out_file);
+  assemble_name_raw (asm_out_file, indirectlabel2);
+  fputc ('\n', asm_out_file);
+
+  /* L1: wfe/dsb loop */
+  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel1);
+  fprintf (asm_out_file, "\twfe\n");
+  fputs ("\tb\t", asm_out_file);
+  assemble_name_raw (asm_out_file, indirectlabel1);
+  fputc ('\n', asm_out_file);
+
+  /* L2: lr=&L3; ret */
+  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel2);
+  fputs ("\tadr\tx30, ", asm_out_file);
+  assemble_name_raw (asm_out_file, indirectlabel3);
+  fputc ('\n', asm_out_file);
+  fputs ("\tret\n", asm_out_file);
+
+  /* L3: */
+  ASM_OUTPUT_INTERNAL_LABEL (asm_out_file, indirectlabel3);
+
+  if (save_lr)
+    {
+      /* pop lr */
+      fputs ("\tldr\tx30, [sp], #16\n", asm_out_file);
+    }
+}
+
+static void
+output_indirect_thunk_function (int regno)
+{
+  char name[32];
+  tree decl;
+
+  /* Create __aarch64_indirect_thunk */
+  indirect_thunk_name (name, regno);
+  decl = build_decl (BUILTINS_LOCATION, FUNCTION_DECL,
+                    get_identifier (name),
+                    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;
+
+  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, name);
+  fputs ("\t.hidden\t", asm_out_file);
+  assemble_name (asm_out_file, name);
+  putc ('\n', asm_out_file);
+  ASM_DECLARE_FUNCTION_NAME (asm_out_file, name, decl);
+
+  DECL_INITIAL (decl) = make_node (BLOCK);
+  current_function_decl = decl;
+  allocate_struct_function (decl, false);
+  init_function_start (decl);
+  /* We're about to hide the function body from callees of final_* by
+     emitting it directly; tell them we're a thunk, if they care.  */
+  cfun->is_thunk = true;
+  first_function_block_is_cold = false;
+  /* Make sure unwind info is emitted for the thunk if needed.  */
+  final_start_function (emit_barrier (), asm_out_file, 1);
+
+  output_indirect_thunk (true);
+  rtx xop = gen_rtx_REG (word_mode, regno);
+  output_asm_insn ("br\t%0", &xop);
+
+  final_end_function ();
+  init_insn_lengths ();
+  free_after_compilation (cfun);
+  set_cfun (NULL);
+  current_function_decl = NULL;
+}
+
 const char *
-aarch64_indirect_branch_asm (rtx addr)
+aarch64_indirect_branch_asm (rtx call_op)
 {
-  output_asm_insn ("br\t%0", &addr);
+  char thunk_name_buf[32];
+  char *thunk_name;
+  int regno = REGNO (call_op);
+
+  if (cfun->machine->indirect_branch_type == indirect_branch_thunk_inline)
+    {
+      output_indirect_thunk (true);
+      output_asm_insn ("br\t%0", &call_op);
+    }
+  else if (cfun->machine->indirect_branch_type == indirect_branch_thunk)
+    {
+      indirect_thunks_used |= 1 << regno;
+
+      indirect_thunk_name (thunk_name_buf, regno);
+      thunk_name = thunk_name_buf;
+      fprintf (asm_out_file, "\tb\t%s\n", thunk_name);
+    }
+  else if (cfun->machine->indirect_branch_type == indirect_branch_thunk_extern)
+    {
+      indirect_thunk_name (thunk_name_buf, regno);
+      thunk_name = thunk_name_buf;
+      fprintf (asm_out_file, "\tb\t%s\n", thunk_name);
+    }
+  else
+    {
+      output_asm_insn ("br\t%0", &call_op);
+    }
   return "";
 }
 
 const char *
-aarch64_indirect_call_asm (rtx addr)
+aarch64_indirect_call_asm (rtx call_op)
 {
-  gcc_assert (REG_P (addr));
+  char thunk_name_buf[32];
+  char *thunk_name;
+  int regno = REGNO (call_op);
+
+  gcc_assert (REG_P (call_op));
   if (aarch64_harden_sls_blr_p ())
     {
-      rtx stub_label = aarch64_sls_create_blr_label (REGNO (addr));
+      rtx stub_label = aarch64_sls_create_blr_label (REGNO (call_op));
       output_asm_insn ("bl\t%0", &stub_label);
+      return "";
+    }
+
+  if (cfun->machine->indirect_branch_type == indirect_branch_thunk_inline)
+    {
+      output_indirect_thunk (true);
+      output_asm_insn ("blr\t%0", &call_op);
+    }
+  else if (cfun->machine->indirect_branch_type == indirect_branch_thunk)
+    {
+      indirect_thunks_used |= 1 << regno;
+
+      indirect_thunk_name (thunk_name_buf, regno);
+      thunk_name = thunk_name_buf;
+      fprintf (asm_out_file, "\tbl\t%s\n", thunk_name);
+    }
+  else if (cfun->machine->indirect_branch_type == indirect_branch_thunk_extern)
+    {
+      indirect_thunk_name (thunk_name_buf, regno);
+      thunk_name = thunk_name_buf;
+      fprintf (asm_out_file, "\tbl\t%s\n", thunk_name);
     }
   else
-   output_asm_insn ("blr\t%0", &addr);
+    {
+      output_asm_insn ("blr\t%0", &call_op);
+    }
   return "";
 }
 
+/* Implements TARGET_ASM_CODE_END.  */
+
+static void
+aarch64_code_end (void)
+{
+  int regno;
+
+  for (regno = R0_REGNUM; regno <= SP_REGNUM; regno++)
+    {
+      if (indirect_thunks_used & (1 << regno))
+       output_indirect_thunk_function (regno);
+    }
+}
+
 /* Target-specific selftests.  */
 
 #if CHECKING_P
@@ -23752,6 +24030,9 @@ aarch64_run_selftests (void)
 #define TARGET_ASM_CAN_OUTPUT_MI_THUNK \
   hook_bool_const_tree_hwi_hwi_const_tree_true
 
+#undef TARGET_ASM_CODE_END
+#define TARGET_ASM_CODE_END aarch64_code_end
+
 #undef TARGET_ASM_FILE_START
 #define TARGET_ASM_FILE_START aarch64_start_file
 
@@ -24203,6 +24484,9 @@ aarch64_libgcc_floating_mode_supported_p
 #undef TARGET_ATTRIBUTE_TABLE
 #define TARGET_ATTRIBUTE_TABLE aarch64_attribute_table
 
+#undef TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P
+#define TARGET_FUNCTION_ATTRIBUTE_INLINABLE_P hook_bool_const_tree_true
+
 #undef TARGET_SIMD_CLONE_COMPUTE_VECSIZE_AND_SIMDLEN
 #define TARGET_SIMD_CLONE_COMPUTE_VECSIZE_AND_SIMDLEN \
   aarch64_simd_clone_compute_vecsize_and_simdlen
diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h
index 0bdcc74ace5..d23bb8e8981 100644
--- a/gcc/config/aarch64/aarch64.h
+++ b/gcc/config/aarch64/aarch64.h
@@ -903,6 +903,8 @@ typedef struct GTY (()) machine_function
   /* One entry for each general purpose register.  */
   rtx call_via[SP_REGNUM];
   bool label_is_assembled;
+  /* How to generate indirect branch.  */
+  ENUM_BITFIELD (indirect_branch) indirect_branch_type : 3;
 } machine_function;
 #endif
 
diff --git a/gcc/config/aarch64/aarch64.opt b/gcc/config/aarch64/aarch64.opt
index 1b3d942e0f5..88b61d57600 100644
--- a/gcc/config/aarch64/aarch64.opt
+++ b/gcc/config/aarch64/aarch64.opt
@@ -138,6 +138,26 @@ mabi=
 Target RejectNegative Joined Enum(aarch64_abi) Var(aarch64_abi) 
Init(AARCH64_ABI_DEFAULT)
 Generate code that conforms to the specified ABI.
 
+mindirect-branch=
+Target Report RejectNegative Joined Enum(indirect_branch) 
Var(aarch64_indirect_branch) Init(indirect_branch_keep)
+Insert return thunk before br and blr.
+
+Enum
+Name(indirect_branch) Type(enum indirect_branch)
+Known indirect branch choices (for use with the -mindirect-branch= 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)
+
 moverride=
 Target RejectNegative ToLower Joined Var(aarch64_override_tune_string)
 -moverride=<string>    Power users only! Override CPU optimization parameters.
diff --git a/gcc/doc/invoke.texi b/gcc/doc/invoke.texi
index 671b29702ea..b0d000b3380 100644
--- a/gcc/doc/invoke.texi
+++ b/gcc/doc/invoke.texi
@@ -721,7 +721,7 @@ Objective-C and Objective-C++ Dialects}.
 -moverride=@var{string}  -mverbose-cost-dump @gol
 -mstack-protector-guard=@var{guard} -mstack-protector-guard-reg=@var{sysreg} 
@gol
 -mstack-protector-guard-offset=@var{offset} -mtrack-speculation @gol
--moutline-atomics }
+-moutline-atomics -mindirect-branch=@var{choice}}
 
 @emph{Adapteva Epiphany Options}
 @gccoptlist{-mhalf-reg-file  -mprefer-short-insn-regs @gol
@@ -18178,6 +18178,24 @@ hardware SVE vector lengths.
 
 The default is @samp{-msve-vector-bits=scalable}, which produces
 vector-length agnostic code.
+
+@item -mindirect-branch=@var{choice}
+@opindex -mindirect-branch
+Convert indirect branch-and-link-register and branch-register with 
@var{choice}.
+The default is @samp{keep}, which keeps indirect branch-and-link-register and
+branch-register instructions unmodified.
+@samp{thunk} converts indirect branch-and-link-register/branch-register
+instructions to a branch-and-link/branch to a function containing a retpoline
+(to stop speculative execution) followed by a branch-register to the target.
+@samp{thunk-inline} similar to @samp{thunk}, but inlines the retpoline
+before the branch-and-link-register/branch-register instruction.
+@samp{thunk-extern} similar to @samp{thunk}, but does not insert the functions
+containing the retpoline.  When using this option, these functions need to be
+provided in a separate object file.  The retpoline functions exist for each
+register and are named __aarch64_indirect_thunk_xN (N being the register
+number). You can control this behavior for a specific function by using the
+function attribute @code{indirect_branch}.  @xref{Function Attributes}.
+
 @end table
 
 @subsubsection @option{-march} and @option{-mcpu} Feature Modifiers
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-1.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-1.c
new file mode 100644
index 00000000000..424057e2db7
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-1.c
@@ -0,0 +1,25 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+  dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, #:lo12:.LANCHOR0\\\]" } 
} */
+/* { dg-final { scan-assembler "b\[ \t\]*__aarch64_indirect_thunk_x1" } } */
+
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "br\[ \t\]*x1" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-2.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-2.c
new file mode 100644
index 00000000000..325fe8ee875
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-2.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+void
+male_indirect_jump (long offset)
+{
+  dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "add\[ \t\]*x1, x1, :lo12:.LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, x0, lsl 3\\\]" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*__aarch64_indirect_thunk_x1" } } */
+
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "br\[ \t\]*x1" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-3.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-3.c
new file mode 100644
index 00000000000..17c0601060c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-3.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+int
+male_indirect_jump (long offset)
+{
+  dispatch(offset);
+  return 0;
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, #:lo12:.LANCHOR0\\\]" } 
} */
+/* { dg-final { scan-assembler "bl\[ \t\]*__aarch64_indirect_thunk_x1" } } */
+
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "br\[ \t\]*x1" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-4.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-4.c
new file mode 100644
index 00000000000..42ae4322c04
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-4.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+int
+male_indirect_jump (long offset)
+{
+  dispatch[offset](offset);
+  return 0;
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "add\[ \t\]*x1, x1, :lo12:.LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, x0, lsl 3\\\]" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*__aarch64_indirect_thunk_x1" } } */
+
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "br\[ \t\]*x1" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-5.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-5.c
new file mode 100644
index 00000000000..e84350d51a1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-5.c
@@ -0,0 +1,25 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x0, _GLOBAL_OFFSET_TABLE_" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x16, \\\[x0, 
#:gotpage_lo15:bar\\\]" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*__aarch64_indirect_thunk_x16" } } */
+
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "br\[ \t\]*x16" } } */
+
+/* { dg-final { scan-assembler-not "__aarch64_indirect_thunk\n" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-6.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-6.c
new file mode 100644
index 00000000000..4c21735dc3c
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-6.c
@@ -0,0 +1,26 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+  bar ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x0, _GLOBAL_OFFSET_TABLE_" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x0, \\\[x0, #:gotpage_lo15:bar\\\]" 
} } */
+/* { dg-final { scan-assembler "bl\[ \t\]*__aarch64_indirect_thunk_x0" } } */
+
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "br\[ \t\]*x0" } } */
+
+/* { dg-final { scan-assembler-not "__aarch64_indirect_thunk\n" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-1.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-1.c
new file mode 100644
index 00000000000..92260b85ef2
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-1.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+extern void male_indirect_jump (long)
+  __attribute__ ((indirect_branch("thunk")));
+
+void
+male_indirect_jump (long offset)
+{
+  dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, #:lo12:.LANCHOR0\\\]" } 
} */
+/* { dg-final { scan-assembler "b\[ \t\]*__aarch64_indirect_thunk_x1" } } */
+
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "br\[ \t\]*x1" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-2.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-2.c
new file mode 100644
index 00000000000..89b3f8b310f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-2.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+__attribute__ ((indirect_branch("thunk")))
+void
+male_indirect_jump (long offset)
+{
+  dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "add\[ \t\]*x1, x1, :lo12:.LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, x0, lsl 3\\\]" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*__aarch64_indirect_thunk_x1" } } */
+
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "br\[ \t\]*x1" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-3.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-3.c
new file mode 100644
index 00000000000..47f5e1a8fcc
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-3.c
@@ -0,0 +1,29 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+extern int male_indirect_jump (long)
+  __attribute__ ((indirect_branch("thunk-inline")));
+
+int
+male_indirect_jump (long offset)
+{
+  dispatch(offset);
+  return 0;
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, #:lo12:.LANCHOR0\\\]" } 
} */
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "blr\[ \t\]*x1" } } */
+
+/* { dg-final { scan-assembler-not "__aarch64_indirect_thunk" } } */
+
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-4.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-4.c
new file mode 100644
index 00000000000..35d640e36e4
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-4.c
@@ -0,0 +1,28 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+__attribute__ ((indirect_branch("thunk-inline")))
+int
+male_indirect_jump (long offset)
+{
+  dispatch[offset](offset);
+  return 0;
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "add\[ \t\]*x1, x1, :lo12:.LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, x0, lsl 3\\\]" } } */
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "blr\[ \t\]*x1" } } */
+
+/* { dg-final { scan-assembler-not "__aarch64_indirect_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-5.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-5.c
new file mode 100644
index 00000000000..5dd9bf44d2b
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-5.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+extern int male_indirect_jump (long)
+  __attribute__ ((indirect_branch("thunk-extern")));
+
+int
+male_indirect_jump (long offset)
+{
+  dispatch(offset);
+  return 0;
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, #:lo12:.LANCHOR0\\\]" } 
} */
+/* { dg-final { scan-assembler "bl\[ \t\]*__aarch64_indirect_thunk_x1" } } */
+
+/* { dg-final { scan-assembler-not "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler-not "wfe" } } */
+/* { dg-final { scan-assembler-not "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler-not "adr\[ \t\]*x30, .LIND2" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-6.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-6.c
new file mode 100644
index 00000000000..5179a55dafd
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-attr-6.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+__attribute__ ((indirect_branch("thunk-extern")))
+int
+male_indirect_jump (long offset)
+{
+  dispatch[offset](offset);
+  return 0;
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "add\[ \t\]*x1, x1, :lo12:.LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, x0, lsl 3\\\]" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*__aarch64_indirect_thunk_x1" } } */
+
+/* { dg-final { scan-assembler-not "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler-not "wfe" } } */
+/* { dg-final { scan-assembler-not "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler-not "adr\[ \t\]*x30, .LIND2" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-1.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-1.c
new file mode 100644
index 00000000000..931d2136407
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-1.c
@@ -0,0 +1,22 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+  dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, #:lo12:.LANCHOR0\\\]" } 
} */
+/* { dg-final { scan-assembler "b\[ \t\]*__aarch64_indirect_thunk_x1" } } */
+
+/* { dg-final { scan-assembler-not "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler-not "wfe" } } */
+/* { dg-final { scan-assembler-not "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler-not "adrp\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler-not "add\[ \t\]*x30, x30, :lo12:.LIND2" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-2.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-2.c
new file mode 100644
index 00000000000..93aaec3d624
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-2.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+void
+male_indirect_jump (long offset)
+{
+  dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "add\[ \t\]*x1, x1, :lo12:.LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, x0, lsl 3\\\]" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*__aarch64_indirect_thunk_x1" } } */
+
+/* { dg-final { scan-assembler-not "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler-not "wfe" } } */
+/* { dg-final { scan-assembler-not "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler-not "adrp\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler-not "add\[ \t\]*x30, x30, :lo12:.LIND2" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-3.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-3.c
new file mode 100644
index 00000000000..66a1e80320e
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-3.c
@@ -0,0 +1,23 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+int
+male_indirect_jump (long offset)
+{
+  dispatch(offset);
+  return 0;
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, #:lo12:.LANCHOR0\\\]" } 
} */
+/* { dg-final { scan-assembler "bl\[ \t\]*__aarch64_indirect_thunk_x1" } } */
+
+/* { dg-final { scan-assembler-not "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler-not "wfe" } } */
+/* { dg-final { scan-assembler-not "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler-not "adrp\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler-not "add\[ \t\]*x30, x30, :lo12:.LIND2" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-4.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-4.c
new file mode 100644
index 00000000000..5a26bb21e57
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-4.c
@@ -0,0 +1,24 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-extern -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+int
+male_indirect_jump (long offset)
+{
+  dispatch[offset](offset);
+  return 0;
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "add\[ \t\]*x1, x1, :lo12:.LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, x0, lsl 3\\\]" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*__aarch64_indirect_thunk_x1" } } */
+
+/* { dg-final { scan-assembler-not "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler-not "wfe" } } */
+/* { dg-final { scan-assembler-not "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler-not "adrp\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler-not "add\[ \t\]*x30, x30, :lo12:.LIND2" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-5.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-5.c
new file mode 100644
index 00000000000..6a3288e001f
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-5.c
@@ -0,0 +1,20 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x0, _GLOBAL_OFFSET_TABLE_" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x16, \\\[x0, 
#:gotpage_lo15:bar\\\]" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*__aarch64_indirect_thunk_x16" } } */
+
+/* { dg-final { scan-assembler-not "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler-not "wfe" } } */
+/* { dg-final { scan-assembler-not "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler-not "adrp\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler-not "add\[ \t\]*x30, x30, :lo12:.LIND2" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-6.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-6.c
new file mode 100644
index 00000000000..635b53b35b9
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-extern-6.c
@@ -0,0 +1,21 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-extern" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+  bar ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x0, _GLOBAL_OFFSET_TABLE_" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x0, \\\[x0, #:gotpage_lo15:bar\\\]" 
} } */
+/* { dg-final { scan-assembler "bl\[ \t\]*__aarch64_indirect_thunk_x0" } } */
+
+/* { dg-final { scan-assembler-not "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler-not "wfe" } } */
+/* { dg-final { scan-assembler-not "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler-not "adrp\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler-not "add\[ \t\]*x30, x30, :lo12:.LIND2" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-1.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-1.c
new file mode 100644
index 00000000000..4ed67ca19a1
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-1.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+void
+male_indirect_jump (long offset)
+{
+  dispatch(offset);
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x16, #:lo12:.LANCHOR0\\\]" 
} } */
+/* { dg-final { scan-assembler "mov\[ \t\]*x16, x1" } } */
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "br\[ \t\]*x16" } } */
+
+/* { dg-final { scan-assembler-not "__aarch64_indirect_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-2.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-2.c
new file mode 100644
index 00000000000..059f73b7129
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-2.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+void
+male_indirect_jump (long offset)
+{
+  dispatch[offset](offset);
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "add\[ \t\]*x1, x1, :lo12:.LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, x0, lsl 3\\\]" } } */
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "br\[ \t\]*x1" } } */
+
+/* { dg-final { scan-assembler-not "__aarch64_indirect_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-3.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-3.c
new file mode 100644
index 00000000000..81c60b7ef65
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-3.c
@@ -0,0 +1,26 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch;
+
+int
+male_indirect_jump (long offset)
+{
+  dispatch(offset);
+  return 0;
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, #:lo12:.LANCHOR0\\\]" } 
} */
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "blr\[ \t\]*x1" } } */
+
+/* { dg-final { scan-assembler-not "__aarch64_indirect_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-4.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-4.c
new file mode 100644
index 00000000000..ff1e3da0afe
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-4.c
@@ -0,0 +1,27 @@
+/* { dg-do compile } */
+/* { dg-options "-O2 -mindirect-branch=thunk-inline -fno-pic" } */
+
+typedef void (*dispatch_t)(long offset);
+
+dispatch_t dispatch[256];
+
+int
+male_indirect_jump (long offset)
+{
+  dispatch[offset](offset);
+  return 0;
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x1, .LANCHOR0" } } */
+/* { dg-final { scan-assembler "add\[ \t\]*x1, x1, :lo12:.LANCHOR0" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x1, \\\[x1, x0, lsl 3\\\]" } } */
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "blr\[ \t\]*x1" } } */
+
+/* { dg-final { scan-assembler-not "__aarch64_indirect_thunk" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-5.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-5.c
new file mode 100644
index 00000000000..05c8093d7ff
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-5.c
@@ -0,0 +1,23 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+
+extern void bar (void);
+
+void
+foo (void)
+{
+  bar ();
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x0, _GLOBAL_OFFSET_TABLE_" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x16, \\\[x0, 
#:gotpage_lo15:bar\\\]" } } */
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "br\[ \t\]*x16" } } */
+
+/* { dg-final { scan-assembler-not "__aarch64_indirect_thunk\n" } } */
diff --git a/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-6.c 
b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-6.c
new file mode 100644
index 00000000000..9cedefadd8d
--- /dev/null
+++ b/gcc/testsuite/gcc.target/aarch64/indirect-thunk-inline-6.c
@@ -0,0 +1,24 @@
+/* { dg-do compile { target *-*-linux* } } */
+/* { dg-options "-O2 -fpic -fno-plt -mindirect-branch=thunk-inline" } */
+
+extern void bar (void);
+
+int
+foo (void)
+{
+  bar ();
+  return 0;
+}
+
+/* { dg-final { scan-assembler "adrp\[ \t\]*x0, _GLOBAL_OFFSET_TABLE_" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x0, \\\[x0, #:gotpage_lo15:bar\\\]" 
} } */
+/* { dg-final { scan-assembler "str\[ \t\]*x30, \\\[sp, #-16\\\]!" } } */
+/* { dg-final { scan-assembler "bl\[ \t\]*\.LIND1" } } */
+/* { dg-final { scan-assembler "wfe" } } */
+/* { dg-final { scan-assembler "b\[ \t\]*\.LIND0" } } */
+/* { dg-final { scan-assembler "adr\[ \t\]*x30, .LIND2" } } */
+/* { dg-final { scan-assembler "ret" } } */
+/* { dg-final { scan-assembler "ldr\[ \t\]*x30, \\\[sp\\\], #16" } } */
+/* { dg-final { scan-assembler "blr\[ \t\]*x0" } } */
+
+/* { dg-final { scan-assembler-not "__aarch64_indirect_thunk\n" } } */
-- 
2.29.2

Reply via email to