/* 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
};
-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
-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);
}
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 = arm_effective_regno (XEXP (op, 0), strict);
return (((mode == E_V8QImode || mode == E_V4QImode || mode == E_V4HImode)
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;
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"