/* Output routines for GCC for ARM.
- Copyright (C) 1991-2023 Free Software Foundation, Inc.
+ Copyright (C) 1991-2024 Free Software Foundation, Inc.
Contributed by Pieter `Tiggr' Schoenmakers (rcpieter@win.tue.nl)
and Martin Simmons (@harleqn.co.uk).
More major hacks by Richard Earnshaw (rearnsha@arm.com).
#include "optabs-libfuncs.h"
#include "gimplify.h"
#include "gimple.h"
+#include "gimple-iterator.h"
#include "selftest.h"
#include "tree-vectorizer.h"
#include "opts.h"
static rtx_insn *thumb1_md_asm_adjust (vec<rtx> &, vec<rtx> &,
vec<machine_mode> &,
vec<const char *> &, vec<rtx> &,
- HARD_REG_SET &, location_t);
+ vec<rtx> &, HARD_REG_SET &, location_t);
static const char *arm_identify_fpu_from_isa (sbitmap);
\f
/* Table of machine attributes. */
-static const struct attribute_spec arm_attribute_table[] =
+static const attribute_spec arm_gnu_attributes[] =
{
/* { name, min_len, max_len, decl_req, type_req, fn_type_req,
affects_type_identity, handler, exclude } */
arm_handle_cmse_nonsecure_entry, NULL },
{ "cmse_nonsecure_call", 0, 0, false, false, false, true,
arm_handle_cmse_nonsecure_call, NULL },
- { "Advanced SIMD type", 1, 1, false, true, false, true, NULL, NULL },
- { NULL, 0, 0, false, false, false, false, NULL, NULL }
+ { "Advanced SIMD type", 1, 1, false, true, false, true, NULL, NULL }
+};
+
+static const scoped_attribute_specs arm_gnu_attribute_table =
+{
+ "gnu", { arm_gnu_attributes }
+};
+
+static const scoped_attribute_specs *const arm_attribute_table[] =
+{
+ &arm_gnu_attribute_table
};
\f
/* Initialize the GCC target structure. */
#undef TARGET_FUNCTION_VALUE_REGNO_P
#define TARGET_FUNCTION_VALUE_REGNO_P arm_function_value_regno_p
+#undef TARGET_GIMPLE_FOLD_BUILTIN
+#define TARGET_GIMPLE_FOLD_BUILTIN arm_gimple_fold_builtin
+
#undef TARGET_ASM_OUTPUT_MI_THUNK
#define TARGET_ASM_OUTPUT_MI_THUNK arm_output_mi_thunk
#undef TARGET_ASM_CAN_OUTPUT_MI_THUNK
tune_params::SCHED_AUTOPREF_OFF
};
-/* Key type for Pointer Authentication extension. */
-enum aarch_key_type aarch_ra_sign_key = AARCH_KEY_A;
-
-char *accepted_branch_protection_string = NULL;
-
/* Auto-generated CPU, FPU and architecture tables. */
#include "arm-cpu-data.h"
speculation_barrier_libfunc = init_one_libfunc ("__speculation_barrier");
}
+/* Implement TARGET_GIMPLE_FOLD_BUILTIN. */
+static bool
+arm_gimple_fold_builtin (gimple_stmt_iterator *gsi)
+{
+ gcall *stmt = as_a <gcall *> (gsi_stmt (*gsi));
+ tree fndecl = gimple_call_fndecl (stmt);
+ unsigned int code = DECL_MD_FUNCTION_CODE (fndecl);
+ unsigned int subcode = code >> ARM_BUILTIN_SHIFT;
+ gimple *new_stmt = NULL;
+ switch (code & ARM_BUILTIN_CLASS)
+ {
+ case ARM_BUILTIN_GENERAL:
+ break;
+ case ARM_BUILTIN_MVE:
+ new_stmt = arm_mve::gimple_fold_builtin (subcode, stmt);
+ }
+ if (!new_stmt)
+ return false;
+
+ gsi_replace (gsi, new_stmt, true);
+ return true;
+}
+
/* On AAPCS systems, this is the "struct __va_list". */
static GTY(()) tree va_list_type;
if (opts->x_arm_branch_protection_string)
{
- aarch_validate_mbranch_protection (opts->x_arm_branch_protection_string);
+ aarch_validate_mbranch_protection (opts->x_arm_branch_protection_string,
+ "-mbranch-protection=");
if (aarch_ra_sign_key != AARCH_KEY_A)
{
if (target_thread_pointer == TP_AUTO)
{
if (arm_arch6k && !TARGET_THUMB1)
- target_thread_pointer = TP_CP15;
+ target_thread_pointer = TP_TPIDRURO;
else
target_thread_pointer = TP_SOFT;
}
}
else
{
- if (TREE_CODE (*node) == FUNCTION_TYPE
- || TREE_CODE (*node) == METHOD_TYPE)
+ if (FUNC_OR_METHOD_TYPE_P (*node))
{
if (arm_isr_value (args) == ARM_FT_UNKNOWN)
{
}
}
else if (TREE_CODE (*node) == POINTER_TYPE
- && (TREE_CODE (TREE_TYPE (*node)) == FUNCTION_TYPE
- || TREE_CODE (TREE_TYPE (*node)) == METHOD_TYPE)
+ && FUNC_OR_METHOD_TYPE_P (TREE_TYPE (*node))
&& arm_isr_value (args) != ARM_FT_UNKNOWN)
{
*node = build_variant_type_copy (*node);
{
fntype = TREE_TYPE (*node);
- if (TREE_CODE (*node) == VAR_DECL || TREE_CODE (*node) == TYPE_DECL)
+ if (VAR_P (*node) || TREE_CODE (*node) == TYPE_DECL)
decl = *node;
}
else
/* Add __attribute__ ((long_call)) to all functions, when
inside #pragma long_calls or __attribute__ ((short_call)),
when inside #pragma no_long_calls. */
- if (TREE_CODE (type) == FUNCTION_TYPE || TREE_CODE (type) == METHOD_TYPE)
+ if (FUNC_OR_METHOD_TYPE_P (type))
{
tree type_attr_list, attr_name;
type_attr_list = TYPE_ATTRIBUTES (type);
|| !DECL_COMMON (SYMBOL_REF_DECL (orig))))
{
tree decl = SYMBOL_REF_DECL (orig);
- tree init = (TREE_CODE (decl) == VAR_DECL)
+ tree init = VAR_P (decl)
? DECL_INITIAL (decl) : (TREE_CODE (decl) == CONSTRUCTOR)
? decl : 0;
int reloc = 0;
if (init && init != error_mark_node)
reloc = compute_reloc_for_constant (init);
- named_section = TREE_CODE (decl) == VAR_DECL
+ named_section = VAR_P (decl)
&& lookup_attribute ("section", DECL_ATTRIBUTES (decl));
readonly = decl_readonly_section (decl, reloc);
bool use_ldrd;
enum rtx_code code = GET_CODE (x);
+ /* If we are dealing with a MVE predicate mode, then treat it as a HImode as
+ can store and load it like any other 16-bit value. */
+ if (TARGET_HAVE_MVE && VALID_MVE_PRED_MODE (mode))
+ mode = HImode;
+
if (TARGET_HAVE_MVE && VALID_MVE_MODE (mode))
return mve_vector_mem_operand (mode, x, strict_p);
else if (REG_P (XEXP (x, 0))
&& (REGNO (XEXP (x, 0)) == FRAME_POINTER_REGNUM
|| REGNO (XEXP (x, 0)) == ARG_POINTER_REGNUM
- || (REGNO (XEXP (x, 0)) >= FIRST_VIRTUAL_REGISTER
- && REGNO (XEXP (x, 0))
- <= LAST_VIRTUAL_POINTER_REGISTER))
+ || VIRTUAL_REGISTER_P (XEXP (x, 0)))
&& GET_MODE_SIZE (mode) >= 4
&& CONST_INT_P (XEXP (x, 1))
&& (INTVAL (XEXP (x, 1)) & 3) == 0)
}
bool
-arm_legitimate_address_p (machine_mode mode, rtx x, bool strict_p)
+arm_legitimate_address_p (machine_mode mode, rtx x, bool strict_p, code_helper)
{
if (TARGET_ARM)
return arm_legitimate_address_outer_p (mode, x, SET, strict_p);
/* Only support 128-bit vectors for MVE. */
if (TARGET_HAVE_MVE
&& (!vector
- || (GET_MODE_CLASS (mode) == MODE_VECTOR_BOOL)
+ || VALID_MVE_PRED_MODE (mode)
|| n_elts * innersize != 16))
return -1;
rtx
mve_bool_vec_to_const (rtx const_vec)
{
- int n_elts = GET_MODE_NUNITS ( GET_MODE (const_vec));
- int repeat = 16 / n_elts;
- int i;
+ machine_mode mode = GET_MODE (const_vec);
+
+ if (!VECTOR_MODE_P (mode))
+ return const_vec;
+
+ unsigned n_elts = GET_MODE_NUNITS (mode);
+ unsigned el_prec = GET_MODE_PRECISION (GET_MODE_INNER (mode));
+ unsigned shift_c = 16 / n_elts;
+ unsigned i;
int hi_val = 0;
for (i = 0; i < n_elts; i++)
unsigned HOST_WIDE_INT elpart;
gcc_assert (CONST_INT_P (el));
- elpart = INTVAL (el);
+ elpart = INTVAL (el) & ((1U << el_prec) - 1);
+
+ unsigned index = BYTES_BIG_ENDIAN ? n_elts - i - 1 : i;
- for (int j = 0; j < repeat; j++)
- hi_val |= elpart << (i * repeat + j);
+ hi_val |= elpart << (index * shift_c);
}
- return gen_int_mode (hi_val, HImode);
+ /* We are using mov immediate to encode this constant which writes 32-bits
+ so we need to make sure the top 16-bits are all 0, otherwise we can't
+ guarantee we can actually write this immediate. */
+ return gen_int_mode (hi_val, SImode);
}
/* Return a non-NULL RTX iff VALS, which is a PARALLEL containing only
&& simd_immediate_valid_for_move (const_vec, mode, NULL, NULL))
/* Load using VMOV. On Cortex-A8 this takes one cycle. */
return const_vec;
- else if (TARGET_HAVE_MVE && (GET_MODE_CLASS (mode) == MODE_VECTOR_BOOL))
+ else if (TARGET_HAVE_MVE && VALID_MVE_PRED_MODE(mode))
return mve_bool_vec_to_const (const_vec);
else if ((target = neon_vdup_constant (vals, generate)) != NULL_RTX)
/* Loaded using VDUP. On Cortex-A8 the VDUP takes one NEON
return arm_coproc_mem_operand_wb (op, 0);
}
+/* In non-STRICT mode, return the register number; in STRICT mode return
+ the hard regno or the replacement if it won't be a mem. Otherwise, return
+ the original pseudo number. */
+static int
+arm_effective_regno (rtx op, bool strict)
+{
+ gcc_assert (REG_P (op));
+ if (!strict || REGNO (op) < FIRST_PSEUDO_REGISTER
+ || !reg_renumber || reg_renumber[REGNO (op)] < 0)
+ return REGNO (op);
+ return reg_renumber[REGNO (op)];
+}
+
/* This function returns TRUE on matching mode and op.
1. For given modes, check for [Rn], return TRUE for Rn <= LO_REGS.
2. For other modes, check for [Rn], return TRUE for Rn < R15 (expect R13). */
/* Match: (mem (reg)). */
if (REG_P (op))
{
- int reg_no = REGNO (op);
+ reg_no = arm_effective_regno (op, strict);
return (((mode == E_V8QImode || mode == E_V4QImode || mode == E_V4HImode)
? reg_no <= LAST_LO_REGNUM
: reg_no < LAST_ARM_REGNUM)
}
code = GET_CODE (op);
- if (code == POST_INC || code == PRE_DEC
- || code == PRE_INC || code == POST_DEC)
+ if ((code == POST_INC
+ || code == PRE_DEC
+ || code == PRE_INC
+ || code == POST_DEC)
+ && REG_P (XEXP (op, 0)))
{
- reg_no = REGNO (XEXP (op, 0));
+ reg_no = arm_effective_regno (XEXP (op, 0), strict);
return (((mode == E_V8QImode || mode == E_V4QImode || mode == E_V4HImode)
? reg_no <= LAST_LO_REGNUM
:(reg_no < LAST_ARM_REGNUM && reg_no != SP_REGNUM))
|| (reload_completed && code == PLUS && REG_P (XEXP (op, 0))
&& GET_CODE (XEXP (op, 1)) == CONST_INT))
{
- reg_no = REGNO (XEXP (op, 0));
+ reg_no = arm_effective_regno (XEXP (op, 0), strict);
if (code == PLUS)
val = INTVAL (XEXP (op, 1));
else
{
return REG_P (x) && (REGNO (x) == FRAME_POINTER_REGNUM
|| REGNO (x) == ARG_POINTER_REGNUM
- || (REGNO (x) >= FIRST_VIRTUAL_REGISTER
- && REGNO (x) <= LAST_VIRTUAL_REGISTER));
+ || VIRTUAL_REGISTER_P (x));
}
/* Return GENERAL_REGS if a scratch register required to reload x to/from
return false;
if (IS_VPR_REGNUM (regno))
- return mode == HImode
- || mode == V16BImode
- || mode == V8BImode
- || mode == V4BImode;
+ return VALID_MVE_PRED_MODE (mode);
if (TARGET_THUMB1)
/* For the Thumb we only allow values bigger than SImode in
if (GET_MODE_CLASS (mode1) == GET_MODE_CLASS (mode2))
return true;
+ if (TARGET_HAVE_MVE
+ && (VALID_MVE_PRED_MODE (mode1) && VALID_MVE_PRED_MODE (mode2)))
+ return true;
+
/* We specifically want to allow elements of "structure" modes to
be tieable to the structure. This more general condition allows
other rarer situations too. */
return true;
if (TARGET_HAVE_MVE
- && (mode == V2DImode || mode == V4SImode || mode == V8HImode
- || mode == V16QImode
- || mode == V16BImode || mode == V8BImode || mode == V4BImode))
- return true;
+ && (VALID_MVE_SI_MODE (mode) || VALID_MVE_PRED_MODE (mode)))
+ return true;
if (TARGET_HAVE_MVE_FLOAT
&& (mode == V2DFmode || mode == V4SFmode || mode == V8HFmode))
- return true;
+ return true;
return false;
}
return "";
}
+/* Output an arm casesi dispatch sequence. Used by arm_casesi_internal insn.
+ Responsible for the handling of switch statements in arm. */
+const char *
+arm_output_casesi (rtx *operands)
+{
+ char label[100];
+ rtx diff_vec = PATTERN (NEXT_INSN (as_a <rtx_insn *> (operands[2])));
+ gcc_assert (GET_CODE (diff_vec) == ADDR_DIFF_VEC);
+ output_asm_insn ("cmp\t%0, %1", operands);
+ output_asm_insn ("bhi\t%l3", operands);
+ ASM_GENERATE_INTERNAL_LABEL (label, "Lrtx", CODE_LABEL_NUMBER (operands[2]));
+ switch (GET_MODE (diff_vec))
+ {
+ case E_QImode:
+ if (ADDR_DIFF_VEC_FLAGS (diff_vec).offset_unsigned)
+ output_asm_insn ("ldrb\t%4, [%5, %0]", operands);
+ else
+ output_asm_insn ("ldrsb\t%4, [%5, %0]", operands);
+ output_asm_insn ("add\t%|pc, %|pc, %4, lsl #2", operands);
+ break;
+ case E_HImode:
+ if (REGNO (operands[4]) != REGNO (operands[5]))
+ {
+ output_asm_insn ("add\t%4, %0, %0", operands);
+ if (ADDR_DIFF_VEC_FLAGS (diff_vec).offset_unsigned)
+ output_asm_insn ("ldrh\t%4, [%5, %4]", operands);
+ else
+ output_asm_insn ("ldrsh\t%4, [%5, %4]", operands);
+ }
+ else
+ {
+ output_asm_insn ("add\t%4, %5, %0", operands);
+ if (ADDR_DIFF_VEC_FLAGS (diff_vec).offset_unsigned)
+ output_asm_insn ("ldrh\t%4, [%4, %0]", operands);
+ else
+ output_asm_insn ("ldrsh\t%4, [%4, %0]", operands);
+ }
+ output_asm_insn ("add\t%|pc, %|pc, %4, lsl #2", operands);
+ break;
+ case E_SImode:
+ if (flag_pic)
+ {
+ output_asm_insn ("ldr\t%4, [%5, %0, lsl #2]", operands);
+ output_asm_insn ("add\t%|pc, %|pc, %4", operands);
+ }
+ else
+ output_asm_insn ("ldr\t%|pc, [%5, %0, lsl #2]", operands);
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ assemble_label (asm_out_file, label);
+ output_asm_insn ("nop", operands);
+ return "";
+}
+
/* Output a Thumb-1 casesi dispatch sequence. */
const char *
thumb1_output_casesi (rtx *operands)
return "St9__va_list";
/* Half-precision floating point types. */
- if (TREE_CODE (type) == REAL_TYPE && TYPE_PRECISION (type) == 16)
+ if (SCALAR_FLOAT_TYPE_P (type) && TYPE_PRECISION (type) == 16)
{
if (TYPE_MAIN_VARIANT (type) == float16_type_node)
return NULL;
case 16: return V16BImode;
case 8: return V8BImode;
case 4: return V4BImode;
+ case 2: return V2QImode;
}
return opt_machine_mode ();
}
switch (GET_MODE_CLASS (cmp_mode))
{
case MODE_VECTOR_INT:
- emit_insn (gen_mve_vpselq (VPSELQ_S, cmp_mode, operands[0],
- operands[1], operands[2], mask));
+ emit_insn (gen_mve_q (VPSELQ_S, VPSELQ_S, cmp_mode, operands[0],
+ operands[1], operands[2], mask));
break;
case MODE_VECTOR_FLOAT:
if (TARGET_HAVE_MVE_FLOAT)
- emit_insn (gen_mve_vpselq_f (cmp_mode, operands[0],
- operands[1], operands[2], mask));
+ emit_insn (gen_mve_q_f (VPSELQ_F, cmp_mode, operands[0],
+ operands[1], operands[2], mask));
else
gcc_unreachable ();
break;
rtx_insn *
thumb1_md_asm_adjust (vec<rtx> &outputs, vec<rtx> & /*inputs*/,
vec<machine_mode> & /*input_modes*/,
- vec<const char *> &constraints, vec<rtx> & /*clobbers*/,
+ vec<const char *> &constraints,
+ vec<rtx> &, vec<rtx> & /*clobbers*/,
HARD_REG_SET & /*clobbered_regs*/, location_t /*loc*/)
{
for (unsigned i = 0, n = outputs.length (); i < n; ++i)
return default_get_mask_mode (mode);
}
+/* Output assembly to read the thread pointer from the appropriate TPIDR
+ register into DEST. If PRED_P also emit the %? that can be used to
+ output the predication code. */
+
+const char *
+arm_output_load_tpidr (rtx dst, bool pred_p)
+{
+ char buf[64];
+ int tpidr_coproc_num = -1;
+ switch (target_thread_pointer)
+ {
+ case TP_TPIDRURW:
+ tpidr_coproc_num = 2;
+ break;
+ case TP_TPIDRURO:
+ tpidr_coproc_num = 3;
+ break;
+ case TP_TPIDRPRW:
+ tpidr_coproc_num = 4;
+ break;
+ default:
+ gcc_unreachable ();
+ }
+ snprintf (buf, sizeof (buf),
+ "mrc%s\tp15, 0, %%0, c13, c0, %d\t@ load_tp_hard",
+ pred_p ? "%?" : "", tpidr_coproc_num);
+ output_asm_insn (buf, &dst);
+ return "";
+}
+
#include "gt-arm.h"