summaryrefslogtreecommitdiff
path: root/gcc/config/aarch64
diff options
context:
space:
mode:
Diffstat (limited to 'gcc/config/aarch64')
-rw-r--r--gcc/config/aarch64/aarch64-opts.h9
-rw-r--r--gcc/config/aarch64/aarch64-protos.h2
-rw-r--r--gcc/config/aarch64/aarch64.c307
-rw-r--r--gcc/config/aarch64/aarch64.h4
-rw-r--r--gcc/config/aarch64/aarch64.md10
-rw-r--r--gcc/config/aarch64/aarch64.opt20
6 files changed, 346 insertions, 6 deletions
diff --git a/gcc/config/aarch64/aarch64-opts.h b/gcc/config/aarch64/aarch64-opts.h
index 7a5c6d7664f4..b40919449e9c 100644
--- a/gcc/config/aarch64/aarch64-opts.h
+++ b/gcc/config/aarch64/aarch64-opts.h
@@ -91,4 +91,13 @@ enum aarch64_sve_vector_bits_enum {
SVE_2048 = 2048
};
+/* 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-protos.h b/gcc/config/aarch64/aarch64-protos.h
index cda2895d28e7..417b75e52b0a 100644
--- a/gcc/config/aarch64/aarch64-protos.h
+++ b/gcc/config/aarch64/aarch64-protos.h
@@ -382,6 +382,8 @@ void aarch64_split_add_offset (scalar_int_mode, rtx, rtx, rtx, rtx, rtx);
bool aarch64_mov_operand_p (rtx, machine_mode);
rtx aarch64_reverse_mask (machine_mode, unsigned int);
bool aarch64_offset_7bit_signed_scaled_p (machine_mode, poly_int64);
+const char *aarch64_output_branch_register (rtx);
+const char *aarch64_output_branch_and_link_register (rtx);
char *aarch64_output_sve_cnt_immediate (const char *, const char *, rtx);
char *aarch64_output_sve_addvl_addpl (rtx, rtx, rtx);
char *aarch64_output_sve_inc_dec_immediate (const char *, rtx);
diff --git a/gcc/config/aarch64/aarch64.c b/gcc/config/aarch64/aarch64.c
index 175f13dc2648..67a12e70399d 100644
--- a/gcc/config/aarch64/aarch64.c
+++ b/gcc/config/aarch64/aarch64.c
@@ -40,6 +40,7 @@
#include "regs.h"
#include "emit-rtl.h"
#include "recog.h"
+#include "cgraph.h"
#include "diagnostic.h"
#include "insn-attr.h"
#include "alias.h"
@@ -11062,6 +11063,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
@@ -11072,7 +11102,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)
@@ -11236,6 +11277,42 @@ aarch64_handle_attr_tune (const char *str)
return false;
}
+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;
+}
+
/* Parse an architecture extensions target attribute string specified in STR.
For example "+fp+nosimd". Show any errors if needed. Return TRUE
if successful. Update aarch64_isa_flags to reflect the ISA features
@@ -15019,6 +15096,215 @@ aarch64_output_ptrue (machine_mode mode, char suffix)
return buf;
}
+/* 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_output_branch_register (rtx call_op)
+{
+ 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_output_branch_and_link_register (rtx call_op)
+{
+ 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 ("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", &call_op);
+ }
+ return "";
+}
+
+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);
+ }
+}
+
/* Split operands into moves from op[1] + op[2] into op[0]. */
void
@@ -17390,6 +17676,16 @@ aarch64_select_early_remat_modes (sbitmap modes)
}
}
+/* Table of valid machine attributes. */
+
+static const struct attribute_spec aarch64_attribute_table[] =
+{
+ { "indirect_branch", 1, 1, true, false, false, false,
+ aarch64_handle_fndecl_attribute, NULL },
+ /* End element. */
+ { NULL, 0, 0, false, false, false, false, NULL, NULL }
+};
+
/* Target-specific selftests. */
#if CHECKING_P
@@ -17459,6 +17755,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
@@ -17471,6 +17770,12 @@ aarch64_run_selftests (void)
#undef TARGET_ASM_TRAMPOLINE_TEMPLATE
#define TARGET_ASM_TRAMPOLINE_TEMPLATE aarch64_asm_trampoline_template
+#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_BUILD_BUILTIN_VA_LIST
#define TARGET_BUILD_BUILTIN_VA_LIST aarch64_build_builtin_va_list
diff --git a/gcc/config/aarch64/aarch64.h b/gcc/config/aarch64/aarch64.h
index 976f9afae54c..8bfe7f28c0c3 100644
--- a/gcc/config/aarch64/aarch64.h
+++ b/gcc/config/aarch64/aarch64.h
@@ -682,6 +682,10 @@ typedef struct GTY (()) machine_function
struct aarch64_frame frame;
/* One entry for each hard register. */
bool reg_is_wrapped_separately[LAST_SAVED_REGNUM];
+
+ /* How to generate indirec branch. */
+ ENUM_BITFIELD (indirect_branch) indirect_branch_type : 3;
+
} machine_function;
#endif
diff --git a/gcc/config/aarch64/aarch64.md b/gcc/config/aarch64/aarch64.md
index 32a0e1f36857..a5933c892b21 100644
--- a/gcc/config/aarch64/aarch64.md
+++ b/gcc/config/aarch64/aarch64.md
@@ -298,7 +298,7 @@
(define_insn "indirect_jump"
[(set (pc) (match_operand:DI 0 "register_operand" "r"))]
""
- "br\\t%0"
+ "* return aarch64_output_branch_register (operands[0]);"
[(set_attr "type" "branch")]
)
@@ -800,7 +800,7 @@
(clobber (reg:DI LR_REGNUM))]
""
"@
- blr\\t%0
+ * return aarch64_output_branch_and_link_register (operands[0]);
bl\\t%c0"
[(set_attr "type" "call, call")]
)
@@ -826,7 +826,7 @@
(clobber (reg:DI LR_REGNUM))]
""
"@
- blr\\t%1
+ * return aarch64_output_branch_and_link_register (operands[1]);
bl\\t%c1"
[(set_attr "type" "call, call")]
)
@@ -862,7 +862,7 @@
(return)]
"SIBLING_CALL_P (insn)"
"@
- br\\t%0
+ * return aarch64_output_branch_register (operands[0]);
b\\t%c0"
[(set_attr "type" "branch, branch")]
)
@@ -875,7 +875,7 @@
(return)]
"SIBLING_CALL_P (insn)"
"@
- br\\t%1
+ * return aarch64_output_branch_register (operands[1]);
b\\t%c1"
[(set_attr "type" "branch, branch")]
)
diff --git a/gcc/config/aarch64/aarch64.opt b/gcc/config/aarch64/aarch64.opt
index 52eaf8c6f408..b266797fe5f5 100644
--- a/gcc/config/aarch64/aarch64.opt
+++ b/gcc/config/aarch64/aarch64.opt
@@ -214,3 +214,23 @@ Target RejectNegative Joined Enum(sve_vector_bits) Var(aarch64_sve_vector_bits)
mverbose-cost-dump
Common Undocumented Var(flag_aarch64_verbose_cost)
Enables verbose cost model dumping in the debug dump files.
+
+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)