X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-aarch64.c;h=5f5ec1b3dbcba62fa20d339126e50311f651a952;hb=dc63d5682e48ab64724a2da2ee803cd13bf5f0c0;hp=779db31828bf93d75f7e8eb088f80793eca493a2;hpb=6837a663c55602490ed095e5891e0c4deff4b9db;p=binutils-gdb.git diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c index 779db31828b..5f5ec1b3dbc 100644 --- a/gas/config/tc-aarch64.c +++ b/gas/config/tc-aarch64.c @@ -1,6 +1,6 @@ /* tc-aarch64.c -- Assemble for the AArch64 ISA - Copyright (C) 2009-2022 Free Software Foundation, Inc. + Copyright (C) 2009-2023 Free Software Foundation, Inc. Contributed by ARM Ltd. This file is part of GAS. @@ -31,15 +31,13 @@ #ifdef OBJ_ELF #include "elf/aarch64.h" #include "dw2gencfi.h" +#include "sframe.h" +#include "gen-sframe.h" #endif +#include "dw2gencfi.h" #include "dwarf2dbg.h" -/* Types of processor to assemble for. */ -#ifndef CPU_DEFAULT -#define CPU_DEFAULT AARCH64_ARCH_V8 -#endif - #define streq(a, b) (strcmp (a, b) == 0) #define END_OF_INSN '\0' @@ -53,7 +51,7 @@ static const aarch64_feature_set *mcpu_cpu_opt = NULL; static const aarch64_feature_set *march_cpu_opt = NULL; /* Constants for known architecture features. */ -static const aarch64_feature_set cpu_default = CPU_DEFAULT; +static const aarch64_feature_set cpu_default = AARCH64_ARCH_FEATURES (V8A); /* Currently active instruction sequence. */ static aarch64_instr_sequence *insn_sequence = NULL; @@ -61,21 +59,30 @@ static aarch64_instr_sequence *insn_sequence = NULL; #ifdef OBJ_ELF /* Pre-defined "_GLOBAL_OFFSET_TABLE_" */ static symbolS *GOT_symbol; +#endif /* Which ABI to use. */ enum aarch64_abi_type { AARCH64_ABI_NONE = 0, AARCH64_ABI_LP64 = 1, - AARCH64_ABI_ILP32 = 2 + AARCH64_ABI_ILP32 = 2, + AARCH64_ABI_LLP64 = 3 }; +unsigned int aarch64_sframe_cfa_sp_reg; +/* The other CFA base register for SFrame stack trace info. */ +unsigned int aarch64_sframe_cfa_fp_reg; +unsigned int aarch64_sframe_cfa_ra_reg; + #ifndef DEFAULT_ARCH #define DEFAULT_ARCH "aarch64" #endif +#ifdef OBJ_ELF /* DEFAULT_ARCH is initialized in gas/configure.tgt. */ static const char *default_arch = DEFAULT_ARCH; +#endif /* AArch64 ABI for the output file. */ static enum aarch64_abi_type aarch64_abi = AARCH64_ABI_NONE; @@ -85,7 +92,10 @@ static enum aarch64_abi_type aarch64_abi = AARCH64_ABI_NONE; 64-bit model, in which the C int type is 32-bits but the C long type and all pointer types are 64-bit objects (LP64). */ #define ilp32_p (aarch64_abi == AARCH64_ABI_ILP32) -#endif + +/* When non zero, C types int and long are 32 bit, + pointers, however are 64 bit */ +#define llp64_p (aarch64_abi == AARCH64_ABI_LLP64) enum vector_el_type { @@ -99,17 +109,6 @@ enum vector_el_type NT_merge }; -/* SME horizontal or vertical slice indicator, encoded in "V". - Values: - 0 - Horizontal - 1 - vertical -*/ -enum sme_hv_slice -{ - HV_horizontal = 0, - HV_vertical = 1 -}; - /* Bits for DEFINED field in vector_type_el. */ #define NTA_HASTYPE 1 #define NTA_HASINDEX 2 @@ -119,6 +118,7 @@ struct vector_type_el { enum vector_el_type type; unsigned char defined; + unsigned element_size; unsigned width; int64_t index; }; @@ -140,11 +140,7 @@ struct aarch64_instruction /* libopcodes structure for instruction intermediate representation. */ aarch64_inst base; /* Record assembly errors found during the parsing. */ - struct - { - enum aarch64_operand_error_kind kind; - const char *error; - } parsing_error; + aarch64_operand_error parsing_error; /* The condition that appears in the assembly line. */ int cond; /* Relocation information (including the GAS internal fixup). */ @@ -160,6 +156,32 @@ static aarch64_instruction inst; static bool parse_operands (char *, const aarch64_opcode *); static bool programmer_friendly_fixup (aarch64_instruction *); +/* If an AARCH64_OPDE_SYNTAX_ERROR has no error string, its first three + data fields contain the following information: + + data[0].i: + A mask of register types that would have been acceptable as bare + operands, outside of a register list. In addition, SEF_DEFAULT_ERROR + is set if a general parsing error occured for an operand (that is, + an error not related to registers, and having no error string). + + data[1].i: + A mask of register types that would have been acceptable inside + a register list. In addition, SEF_IN_REGLIST is set if the + operand contained a '{' and if we got to the point of trying + to parse a register inside a list. + + data[2].i: + The mask associated with the register that was actually seen, or 0 + if none. A nonzero value describes a register inside a register + list if data[1].i & SEF_IN_REGLIST, otherwise it describes a bare + register. + + The idea is that stringless errors from multiple opcode templates can + be ORed together to give a summary of the available alternatives. */ +#define SEF_DEFAULT_ERROR (1U << 31) +#define SEF_IN_REGLIST (1U << 31) + /* Diagnostics inline function utilities. These are lightweight utilities which should only be called by parse_operands @@ -180,8 +202,8 @@ static bool programmer_friendly_fixup (aarch64_instruction *); static inline void clear_error (void) { + memset (&inst.parsing_error, 0, sizeof (inst.parsing_error)); inst.parsing_error.kind = AARCH64_OPDE_NIL; - inst.parsing_error.error = NULL; } static inline bool @@ -190,21 +212,11 @@ error_p (void) return inst.parsing_error.kind != AARCH64_OPDE_NIL; } -static inline const char * -get_error_message (void) -{ - return inst.parsing_error.error; -} - -static inline enum aarch64_operand_error_kind -get_error_kind (void) -{ - return inst.parsing_error.kind; -} - static inline void set_error (enum aarch64_operand_error_kind kind, const char *error) { + memset (&inst.parsing_error, 0, sizeof (inst.parsing_error)); + inst.parsing_error.index = -1; inst.parsing_error.kind = kind; inst.parsing_error.error = error; } @@ -221,6 +233,7 @@ static inline void set_default_error (void) { set_error (AARCH64_OPDE_SYNTAX_ERROR, NULL); + inst.parsing_error.data[0].i = SEF_DEFAULT_ERROR; } static inline void @@ -272,66 +285,88 @@ struct reloc_entry BASIC_REG_TYPE(R_64) /* x[0-30] */ \ BASIC_REG_TYPE(SP_32) /* wsp */ \ BASIC_REG_TYPE(SP_64) /* sp */ \ - BASIC_REG_TYPE(Z_32) /* wzr */ \ - BASIC_REG_TYPE(Z_64) /* xzr */ \ + BASIC_REG_TYPE(ZR_32) /* wzr */ \ + BASIC_REG_TYPE(ZR_64) /* xzr */ \ BASIC_REG_TYPE(FP_B) /* b[0-31] *//* NOTE: keep FP_[BHSDQ] consecutive! */\ BASIC_REG_TYPE(FP_H) /* h[0-31] */ \ BASIC_REG_TYPE(FP_S) /* s[0-31] */ \ BASIC_REG_TYPE(FP_D) /* d[0-31] */ \ BASIC_REG_TYPE(FP_Q) /* q[0-31] */ \ - BASIC_REG_TYPE(VN) /* v[0-31] */ \ - BASIC_REG_TYPE(ZN) /* z[0-31] */ \ - BASIC_REG_TYPE(PN) /* p[0-15] */ \ - BASIC_REG_TYPE(ZA) /* za[0-15] */ \ - BASIC_REG_TYPE(ZAH) /* za[0-15]h */ \ - BASIC_REG_TYPE(ZAV) /* za[0-15]v */ \ + BASIC_REG_TYPE(V) /* v[0-31] */ \ + BASIC_REG_TYPE(Z) /* z[0-31] */ \ + BASIC_REG_TYPE(P) /* p[0-15] */ \ + BASIC_REG_TYPE(PN) /* pn[0-15] */ \ + BASIC_REG_TYPE(ZA) /* za */ \ + BASIC_REG_TYPE(ZAT) /* za[0-15] (ZA tile) */ \ + BASIC_REG_TYPE(ZATH) /* za[0-15]h (ZA tile horizontal slice) */ \ + BASIC_REG_TYPE(ZATV) /* za[0-15]v (ZA tile vertical slice) */ \ + BASIC_REG_TYPE(ZT0) /* zt0 */ \ /* Typecheck: any 64-bit int reg (inc SP exc XZR). */ \ MULTI_REG_TYPE(R64_SP, REG_TYPE(R_64) | REG_TYPE(SP_64)) \ /* Typecheck: same, plus SVE registers. */ \ MULTI_REG_TYPE(SVE_BASE, REG_TYPE(R_64) | REG_TYPE(SP_64) \ - | REG_TYPE(ZN)) \ + | REG_TYPE(Z)) \ /* Typecheck: x[0-30], w[0-30] or [xw]zr. */ \ - MULTI_REG_TYPE(R_Z, REG_TYPE(R_32) | REG_TYPE(R_64) \ - | REG_TYPE(Z_32) | REG_TYPE(Z_64)) \ + MULTI_REG_TYPE(R_ZR, REG_TYPE(R_32) | REG_TYPE(R_64) \ + | REG_TYPE(ZR_32) | REG_TYPE(ZR_64)) \ /* Typecheck: same, plus SVE registers. */ \ MULTI_REG_TYPE(SVE_OFFSET, REG_TYPE(R_32) | REG_TYPE(R_64) \ - | REG_TYPE(Z_32) | REG_TYPE(Z_64) \ - | REG_TYPE(ZN)) \ + | REG_TYPE(ZR_32) | REG_TYPE(ZR_64) \ + | REG_TYPE(Z)) \ /* Typecheck: x[0-30], w[0-30] or {w}sp. */ \ MULTI_REG_TYPE(R_SP, REG_TYPE(R_32) | REG_TYPE(R_64) \ | REG_TYPE(SP_32) | REG_TYPE(SP_64)) \ /* Typecheck: any int (inc {W}SP inc [WX]ZR). */ \ - MULTI_REG_TYPE(R_Z_SP, REG_TYPE(R_32) | REG_TYPE(R_64) \ + MULTI_REG_TYPE(R_ZR_SP, REG_TYPE(R_32) | REG_TYPE(R_64) \ | REG_TYPE(SP_32) | REG_TYPE(SP_64) \ - | REG_TYPE(Z_32) | REG_TYPE(Z_64)) \ + | REG_TYPE(ZR_32) | REG_TYPE(ZR_64)) \ /* Typecheck: any [BHSDQ]P FP. */ \ MULTI_REG_TYPE(BHSDQ, REG_TYPE(FP_B) | REG_TYPE(FP_H) \ | REG_TYPE(FP_S) | REG_TYPE(FP_D) | REG_TYPE(FP_Q)) \ /* Typecheck: any int or [BHSDQ]P FP or V reg (exc SP inc [WX]ZR). */ \ - MULTI_REG_TYPE(R_Z_BHSDQ_V, REG_TYPE(R_32) | REG_TYPE(R_64) \ - | REG_TYPE(Z_32) | REG_TYPE(Z_64) | REG_TYPE(VN) \ + MULTI_REG_TYPE(R_ZR_BHSDQ_V, REG_TYPE(R_32) | REG_TYPE(R_64) \ + | REG_TYPE(ZR_32) | REG_TYPE(ZR_64) | REG_TYPE(V) \ | REG_TYPE(FP_B) | REG_TYPE(FP_H) \ | REG_TYPE(FP_S) | REG_TYPE(FP_D) | REG_TYPE(FP_Q)) \ /* Typecheck: as above, but also Zn, Pn, and {W}SP. This should only \ be used for SVE instructions, since Zn and Pn are valid symbols \ in other contexts. */ \ - MULTI_REG_TYPE(R_Z_SP_BHSDQ_VZP, REG_TYPE(R_32) | REG_TYPE(R_64) \ + MULTI_REG_TYPE(R_ZR_SP_BHSDQ_VZP, REG_TYPE(R_32) | REG_TYPE(R_64) \ | REG_TYPE(SP_32) | REG_TYPE(SP_64) \ - | REG_TYPE(Z_32) | REG_TYPE(Z_64) | REG_TYPE(VN) \ + | REG_TYPE(ZR_32) | REG_TYPE(ZR_64) | REG_TYPE(V) \ | REG_TYPE(FP_B) | REG_TYPE(FP_H) \ | REG_TYPE(FP_S) | REG_TYPE(FP_D) | REG_TYPE(FP_Q) \ - | REG_TYPE(ZN) | REG_TYPE(PN)) \ + | REG_TYPE(Z) | REG_TYPE(P)) \ + /* Likewise, but with predicate-as-counter registers added. */ \ + MULTI_REG_TYPE(R_ZR_SP_BHSDQ_VZP_PN, REG_TYPE(R_32) | REG_TYPE(R_64) \ + | REG_TYPE(SP_32) | REG_TYPE(SP_64) \ + | REG_TYPE(ZR_32) | REG_TYPE(ZR_64) | REG_TYPE(V) \ + | REG_TYPE(FP_B) | REG_TYPE(FP_H) \ + | REG_TYPE(FP_S) | REG_TYPE(FP_D) | REG_TYPE(FP_Q) \ + | REG_TYPE(Z) | REG_TYPE(P) | REG_TYPE(PN)) \ /* Any integer register; used for error messages only. */ \ MULTI_REG_TYPE(R_N, REG_TYPE(R_32) | REG_TYPE(R_64) \ | REG_TYPE(SP_32) | REG_TYPE(SP_64) \ - | REG_TYPE(Z_32) | REG_TYPE(Z_64)) \ + | REG_TYPE(ZR_32) | REG_TYPE(ZR_64)) \ + /* Any vector register. */ \ + MULTI_REG_TYPE(VZ, REG_TYPE(V) | REG_TYPE(Z)) \ + /* An SVE vector or predicate register. */ \ + MULTI_REG_TYPE(ZP, REG_TYPE(Z) | REG_TYPE(P)) \ + /* Any vector or predicate register. */ \ + MULTI_REG_TYPE(VZP, REG_TYPE(V) | REG_TYPE(Z) | REG_TYPE(P)) \ + /* The whole of ZA or a single tile. */ \ + MULTI_REG_TYPE(ZA_ZAT, REG_TYPE(ZA) | REG_TYPE(ZAT)) \ + /* A horizontal or vertical slice of a ZA tile. */ \ + MULTI_REG_TYPE(ZATHV, REG_TYPE(ZATH) | REG_TYPE(ZATV)) \ /* Pseudo type to mark the end of the enumerator sequence. */ \ - BASIC_REG_TYPE(MAX) + END_REG_TYPE(MAX) #undef BASIC_REG_TYPE #define BASIC_REG_TYPE(T) REG_TYPE_##T, #undef MULTI_REG_TYPE #define MULTI_REG_TYPE(T,V) BASIC_REG_TYPE(T) +#undef END_REG_TYPE +#define END_REG_TYPE(T) BASIC_REG_TYPE(T) /* Register type enumerators. */ typedef enum aarch64_reg_type_ @@ -346,6 +381,8 @@ typedef enum aarch64_reg_type_ #define REG_TYPE(T) (1 << REG_TYPE_##T) #undef MULTI_REG_TYPE #define MULTI_REG_TYPE(T,V) V, +#undef END_REG_TYPE +#define END_REG_TYPE(T) 0 /* Structure for a hash table entry for a register. */ typedef struct @@ -365,84 +402,146 @@ static const unsigned reg_type_masks[] = #undef BASIC_REG_TYPE #undef REG_TYPE #undef MULTI_REG_TYPE +#undef END_REG_TYPE #undef AARCH64_REG_TYPES -/* Diagnostics used when we don't get a register of the expected type. - Note: this has to synchronized with aarch64_reg_type definitions - above. */ +/* We expected one of the registers in MASK to be specified. If a register + of some kind was specified, SEEN is a mask that contains that register, + otherwise it is zero. + + If it is possible to provide a relatively pithy message that describes + the error exactly, return a string that does so, reporting the error + against "operand %d". Return null otherwise. + + From a QoI perspective, any REG_TYPE_* that is passed as the first + argument to set_expected_reg_error should generally have its own message. + Providing messages for combinations of such REG_TYPE_*s can be useful if + it is possible to summarize the combination in a relatively natural way. + On the other hand, it seems better to avoid long lists of unrelated + things. */ + static const char * -get_reg_expected_msg (aarch64_reg_type reg_type) +get_reg_expected_msg (unsigned int mask, unsigned int seen) +{ + /* First handle messages that use SEEN. */ + if ((mask & reg_type_masks[REG_TYPE_ZAT]) + && (seen & reg_type_masks[REG_TYPE_ZATHV])) + return N_("expected an unsuffixed ZA tile at operand %d"); + + if ((mask & reg_type_masks[REG_TYPE_ZATHV]) + && (seen & reg_type_masks[REG_TYPE_ZAT])) + return N_("missing horizontal or vertical suffix at operand %d"); + + if ((mask & reg_type_masks[REG_TYPE_ZA]) + && (seen & (reg_type_masks[REG_TYPE_ZAT] + | reg_type_masks[REG_TYPE_ZATHV]))) + return N_("expected 'za' rather than a ZA tile at operand %d"); + + if ((mask & reg_type_masks[REG_TYPE_PN]) + && (seen & reg_type_masks[REG_TYPE_P])) + return N_("expected a predicate-as-counter rather than predicate-as-mask" + " register at operand %d"); + + if ((mask & reg_type_masks[REG_TYPE_P]) + && (seen & reg_type_masks[REG_TYPE_PN])) + return N_("expected a predicate-as-mask rather than predicate-as-counter" + " register at operand %d"); + + /* Integer, zero and stack registers. */ + if (mask == reg_type_masks[REG_TYPE_R_64]) + return N_("expected a 64-bit integer register at operand %d"); + if (mask == reg_type_masks[REG_TYPE_R_ZR]) + return N_("expected an integer or zero register at operand %d"); + if (mask == reg_type_masks[REG_TYPE_R_SP]) + return N_("expected an integer or stack pointer register at operand %d"); + + /* Floating-point and SIMD registers. */ + if (mask == reg_type_masks[REG_TYPE_BHSDQ]) + return N_("expected a scalar SIMD or floating-point register" + " at operand %d"); + if (mask == reg_type_masks[REG_TYPE_V]) + return N_("expected an Advanced SIMD vector register at operand %d"); + if (mask == reg_type_masks[REG_TYPE_Z]) + return N_("expected an SVE vector register at operand %d"); + if (mask == reg_type_masks[REG_TYPE_P] + || mask == (reg_type_masks[REG_TYPE_P] | reg_type_masks[REG_TYPE_PN])) + /* Use this error for "predicate-as-mask only" and "either kind of + predicate". We report a more specific error if P is used where + PN is expected, and vice versa, so the issue at this point is + "predicate-like" vs. "not predicate-like". */ + return N_("expected an SVE predicate register at operand %d"); + if (mask == reg_type_masks[REG_TYPE_PN]) + return N_("expected an SVE predicate-as-counter register at operand %d"); + if (mask == reg_type_masks[REG_TYPE_VZ]) + return N_("expected a vector register at operand %d"); + if (mask == reg_type_masks[REG_TYPE_ZP]) + return N_("expected an SVE vector or predicate register at operand %d"); + if (mask == reg_type_masks[REG_TYPE_VZP]) + return N_("expected a vector or predicate register at operand %d"); + + /* SME-related registers. */ + if (mask == reg_type_masks[REG_TYPE_ZA]) + return N_("expected a ZA array vector at operand %d"); + if (mask == (reg_type_masks[REG_TYPE_ZA_ZAT] | reg_type_masks[REG_TYPE_ZT0])) + return N_("expected ZT0 or a ZA mask at operand %d"); + if (mask == reg_type_masks[REG_TYPE_ZAT]) + return N_("expected a ZA tile at operand %d"); + if (mask == reg_type_masks[REG_TYPE_ZATHV]) + return N_("expected a ZA tile slice at operand %d"); + + /* Integer and vector combos. */ + if (mask == (reg_type_masks[REG_TYPE_R_ZR] | reg_type_masks[REG_TYPE_V])) + return N_("expected an integer register or Advanced SIMD vector register" + " at operand %d"); + if (mask == (reg_type_masks[REG_TYPE_R_ZR] | reg_type_masks[REG_TYPE_Z])) + return N_("expected an integer register or SVE vector register" + " at operand %d"); + if (mask == (reg_type_masks[REG_TYPE_R_ZR] | reg_type_masks[REG_TYPE_VZ])) + return N_("expected an integer or vector register at operand %d"); + if (mask == (reg_type_masks[REG_TYPE_R_ZR] | reg_type_masks[REG_TYPE_P])) + return N_("expected an integer or predicate register at operand %d"); + if (mask == (reg_type_masks[REG_TYPE_R_ZR] | reg_type_masks[REG_TYPE_VZP])) + return N_("expected an integer, vector or predicate register" + " at operand %d"); + + /* SVE and SME combos. */ + if (mask == (reg_type_masks[REG_TYPE_Z] | reg_type_masks[REG_TYPE_ZATHV])) + return N_("expected an SVE vector register or ZA tile slice" + " at operand %d"); + + return NULL; +} + +/* Record that we expected a register of type TYPE but didn't see one. + REG is the register that we actually saw, or null if we didn't see a + recognized register. FLAGS is SEF_IN_REGLIST if we are parsing the + contents of a register list, otherwise it is zero. */ + +static inline void +set_expected_reg_error (aarch64_reg_type type, const reg_entry *reg, + unsigned int flags) { - const char *msg; + assert (flags == 0 || flags == SEF_IN_REGLIST); + set_error (AARCH64_OPDE_SYNTAX_ERROR, NULL); + if (flags & SEF_IN_REGLIST) + inst.parsing_error.data[1].i = reg_type_masks[type] | flags; + else + inst.parsing_error.data[0].i = reg_type_masks[type]; + if (reg) + inst.parsing_error.data[2].i = reg_type_masks[reg->type]; +} - switch (reg_type) - { - case REG_TYPE_R_32: - msg = N_("integer 32-bit register expected"); - break; - case REG_TYPE_R_64: - msg = N_("integer 64-bit register expected"); - break; - case REG_TYPE_R_N: - msg = N_("integer register expected"); - break; - case REG_TYPE_R64_SP: - msg = N_("64-bit integer or SP register expected"); - break; - case REG_TYPE_SVE_BASE: - msg = N_("base register expected"); - break; - case REG_TYPE_R_Z: - msg = N_("integer or zero register expected"); - break; - case REG_TYPE_SVE_OFFSET: - msg = N_("offset register expected"); - break; - case REG_TYPE_R_SP: - msg = N_("integer or SP register expected"); - break; - case REG_TYPE_R_Z_SP: - msg = N_("integer, zero or SP register expected"); - break; - case REG_TYPE_FP_B: - msg = N_("8-bit SIMD scalar register expected"); - break; - case REG_TYPE_FP_H: - msg = N_("16-bit SIMD scalar or floating-point half precision " - "register expected"); - break; - case REG_TYPE_FP_S: - msg = N_("32-bit SIMD scalar or floating-point single precision " - "register expected"); - break; - case REG_TYPE_FP_D: - msg = N_("64-bit SIMD scalar or floating-point double precision " - "register expected"); - break; - case REG_TYPE_FP_Q: - msg = N_("128-bit SIMD scalar or floating-point quad precision " - "register expected"); - break; - case REG_TYPE_R_Z_BHSDQ_V: - case REG_TYPE_R_Z_SP_BHSDQ_VZP: - msg = N_("register expected"); - break; - case REG_TYPE_BHSDQ: /* any [BHSDQ]P FP */ - msg = N_("SIMD scalar or floating-point register expected"); - break; - case REG_TYPE_VN: /* any V reg */ - msg = N_("vector register expected"); - break; - case REG_TYPE_ZN: - msg = N_("SVE vector register expected"); - break; - case REG_TYPE_PN: - msg = N_("SVE predicate register expected"); - break; - default: - as_fatal (_("invalid register type %d"), reg_type); - } - return msg; +/* Record that we expected a register list containing registers of type TYPE, + but didn't see the opening '{'. If we saw a register instead, REG is the + register that we saw, otherwise it is null. */ + +static inline void +set_expected_reglist_error (aarch64_reg_type type, const reg_entry *reg) +{ + set_error (AARCH64_OPDE_SYNTAX_ERROR, NULL); + inst.parsing_error.data[1].i = reg_type_masks[type]; + if (reg) + inst.parsing_error.data[2].i = reg_type_masks[reg->type]; } /* Some well known registers that we refer to directly elsewhere. */ @@ -565,25 +664,18 @@ static bool in_aarch64_get_expression = false; #define ALLOW_ABSENT false #define REJECT_ABSENT true -/* Fifth argument to aarch64_get_expression. */ -#define NORMAL_RESOLUTION false - /* Return TRUE if the string pointed by *STR is successfully parsed as an valid expression; *EP will be filled with the information of such an expression. Otherwise return FALSE. If ALLOW_IMMEDIATE_PREFIX is true then skip a '#' at the start. - If REJECT_ABSENT is true then trat missing expressions as an error. - If DEFER_RESOLUTION is true, then do not resolve expressions against - constant symbols. Necessary if the expression is part of a fixup - that uses a reloc that must be emitted. */ + If REJECT_ABSENT is true then trat missing expressions as an error. */ static bool aarch64_get_expression (expressionS * ep, char ** str, bool allow_immediate_prefix, - bool reject_absent, - bool defer_resolution) + bool reject_absent) { char *save_in; segT seg; @@ -603,10 +695,7 @@ aarch64_get_expression (expressionS * ep, save_in = input_line_pointer; input_line_pointer = *str; in_aarch64_get_expression = true; - if (defer_resolution) - seg = deferred_expression (ep); - else - seg = expression (ep); + seg = expression (ep); in_aarch64_get_expression = false; if (ep->X_op == O_illegal || (reject_absent && ep->X_op == O_absent)) @@ -701,6 +790,81 @@ first_error_fmt (const char *format, ...) } } +/* Internal helper routine converting a vector_type_el structure *VECTYPE + to a corresponding operand qualifier. */ + +static inline aarch64_opnd_qualifier_t +vectype_to_qualifier (const struct vector_type_el *vectype) +{ + /* Element size in bytes indexed by vector_el_type. */ + const unsigned char ele_size[5] + = {1, 2, 4, 8, 16}; + const unsigned int ele_base [5] = + { + AARCH64_OPND_QLF_V_4B, + AARCH64_OPND_QLF_V_2H, + AARCH64_OPND_QLF_V_2S, + AARCH64_OPND_QLF_V_1D, + AARCH64_OPND_QLF_V_1Q + }; + + if (!vectype->defined || vectype->type == NT_invtype) + goto vectype_conversion_fail; + + if (vectype->type == NT_zero) + return AARCH64_OPND_QLF_P_Z; + if (vectype->type == NT_merge) + return AARCH64_OPND_QLF_P_M; + + gas_assert (vectype->type >= NT_b && vectype->type <= NT_q); + + if (vectype->defined & (NTA_HASINDEX | NTA_HASVARWIDTH)) + { + /* Special case S_4B. */ + if (vectype->type == NT_b && vectype->width == 4) + return AARCH64_OPND_QLF_S_4B; + + /* Special case S_2H. */ + if (vectype->type == NT_h && vectype->width == 2) + return AARCH64_OPND_QLF_S_2H; + + /* Vector element register. */ + return AARCH64_OPND_QLF_S_B + vectype->type; + } + else + { + /* Vector register. */ + int reg_size = ele_size[vectype->type] * vectype->width; + unsigned offset; + unsigned shift; + if (reg_size != 16 && reg_size != 8 && reg_size != 4) + goto vectype_conversion_fail; + + /* The conversion is by calculating the offset from the base operand + qualifier for the vector type. The operand qualifiers are regular + enough that the offset can established by shifting the vector width by + a vector-type dependent amount. */ + shift = 0; + if (vectype->type == NT_b) + shift = 3; + else if (vectype->type == NT_h || vectype->type == NT_s) + shift = 2; + else if (vectype->type >= NT_d) + shift = 1; + else + gas_assert (0); + + offset = ele_base [vectype->type] + (vectype->width >> shift); + gas_assert (AARCH64_OPND_QLF_V_4B <= offset + && offset <= AARCH64_OPND_QLF_V_1Q); + return offset; + } + + vectype_conversion_fail: + first_error (_("bad vector arrangement type")); + return AARCH64_OPND_QLF_NIL; +} + /* Register parsing. */ /* Generic register parser which is called by other specialized @@ -740,6 +904,38 @@ parse_reg (char **ccp) return reg; } +/* Return the operand qualifier associated with all uses of REG, or + AARCH64_OPND_QLF_NIL if none. AARCH64_OPND_QLF_NIL means either + that qualifiers don't apply to REG or that qualifiers are added + using suffixes. */ + +static aarch64_opnd_qualifier_t +inherent_reg_qualifier (const reg_entry *reg) +{ + switch (reg->type) + { + case REG_TYPE_R_32: + case REG_TYPE_SP_32: + case REG_TYPE_ZR_32: + return AARCH64_OPND_QLF_W; + + case REG_TYPE_R_64: + case REG_TYPE_SP_64: + case REG_TYPE_ZR_64: + return AARCH64_OPND_QLF_X; + + case REG_TYPE_FP_B: + case REG_TYPE_FP_H: + case REG_TYPE_FP_S: + case REG_TYPE_FP_D: + case REG_TYPE_FP_Q: + return AARCH64_OPND_QLF_S_B + (reg->type - REG_TYPE_FP_B); + + default: + return AARCH64_OPND_QLF_NIL; + } +} + /* Return TRUE if REG->TYPE is a valid type of TYPE; otherwise return FALSE. */ static bool @@ -767,20 +963,8 @@ aarch64_addr_reg_parse (char **ccp, aarch64_reg_type reg_type, switch (reg->type) { - case REG_TYPE_R_32: - case REG_TYPE_SP_32: - case REG_TYPE_Z_32: - *qualifier = AARCH64_OPND_QLF_W; - break; - - case REG_TYPE_R_64: - case REG_TYPE_SP_64: - case REG_TYPE_Z_64: - *qualifier = AARCH64_OPND_QLF_X; - break; - - case REG_TYPE_ZN: - if ((reg_type_masks[reg_type] & (1 << REG_TYPE_ZN)) == 0 + case REG_TYPE_Z: + if ((reg_type_masks[reg_type] & (1 << REG_TYPE_Z)) == 0 || str[0] != '.') return NULL; switch (TOLOWER (str[1])) @@ -798,7 +982,10 @@ aarch64_addr_reg_parse (char **ccp, aarch64_reg_type reg_type, break; default: - return NULL; + if (!aarch64_check_reg_type (reg, REG_TYPE_R_ZR_SP)) + return NULL; + *qualifier = inherent_reg_qualifier (reg); + break; } *ccp = str; @@ -815,7 +1002,7 @@ aarch64_addr_reg_parse (char **ccp, aarch64_reg_type reg_type, static const reg_entry * aarch64_reg_parse_32_64 (char **ccp, aarch64_opnd_qualifier_t *qualifier) { - return aarch64_addr_reg_parse (ccp, REG_TYPE_R_Z_SP, qualifier); + return aarch64_addr_reg_parse (ccp, REG_TYPE_R_ZR_SP, qualifier); } /* Parse the qualifier of a vector register or vector element of type @@ -838,7 +1025,7 @@ parse_vector_type_for_operand (aarch64_reg_type reg_type, gas_assert (*ptr == '.'); ptr++; - if (reg_type == REG_TYPE_ZN || reg_type == REG_TYPE_PN || !ISDIGIT (*ptr)) + if (reg_type != REG_TYPE_V || !ISDIGIT (*ptr)) { width = 0; goto elt_size; @@ -870,7 +1057,7 @@ parse_vector_type_for_operand (aarch64_reg_type reg_type, element_size = 64; break; case 'q': - if (reg_type == REG_TYPE_ZN || width == 1) + if (reg_type != REG_TYPE_V || width == 1) { type = NT_q; element_size = 128; @@ -898,6 +1085,7 @@ parse_vector_type_for_operand (aarch64_reg_type reg_type, parsed_type->type = type; parsed_type->width = width; + parsed_type->element_size = element_size; *str = ptr; @@ -936,67 +1124,135 @@ parse_predication_for_operand (struct vector_type_el *parsed_type, char **str) return true; } +/* Return true if CH is a valid suffix character for registers of + type TYPE. */ + +static bool +aarch64_valid_suffix_char_p (aarch64_reg_type type, char ch) +{ + switch (type) + { + case REG_TYPE_V: + case REG_TYPE_Z: + case REG_TYPE_ZA: + case REG_TYPE_ZAT: + case REG_TYPE_ZATH: + case REG_TYPE_ZATV: + return ch == '.'; + + case REG_TYPE_P: + case REG_TYPE_PN: + return ch == '.' || ch == '/'; + + default: + return false; + } +} + +/* Parse an index expression at *STR, storing it in *IMM on success. */ + +static bool +parse_index_expression (char **str, int64_t *imm) +{ + expressionS exp; + + aarch64_get_expression (&exp, str, GE_NO_PREFIX, REJECT_ABSENT); + if (exp.X_op != O_constant) + { + first_error (_("constant expression required")); + return false; + } + *imm = exp.X_add_number; + return true; +} + /* Parse a register of the type TYPE. - Return PARSE_FAIL if the string pointed by *CCP is not a valid register + Return null if the string pointed to by *CCP is not a valid register name or the parsed register is not of TYPE. - Otherwise return the register number, and optionally fill in the actual - type of the register in *RTYPE when multiple alternatives were given, and - return the register shape and element index information in *TYPEINFO. + Otherwise return the register, and optionally return the register + shape and element index information in *TYPEINFO. - IN_REG_LIST should be set with TRUE if the caller is parsing a register - list. */ + FLAGS includes PTR_IN_REGLIST if the caller is parsing a register list. -static int -parse_typed_reg (char **ccp, aarch64_reg_type type, aarch64_reg_type *rtype, - struct vector_type_el *typeinfo, bool in_reg_list) + FLAGS includes PTR_FULL_REG if the function should ignore any potential + register index. + + FLAGS includes PTR_GOOD_MATCH if we are sufficiently far into parsing + an operand that we can be confident that it is a good match. */ + +#define PTR_IN_REGLIST (1U << 0) +#define PTR_FULL_REG (1U << 1) +#define PTR_GOOD_MATCH (1U << 2) + +static const reg_entry * +parse_typed_reg (char **ccp, aarch64_reg_type type, + struct vector_type_el *typeinfo, unsigned int flags) { char *str = *ccp; + bool isalpha = ISALPHA (*str); const reg_entry *reg = parse_reg (&str); struct vector_type_el atype; struct vector_type_el parsetype; bool is_typed_vecreg = false; + unsigned int err_flags = (flags & PTR_IN_REGLIST) ? SEF_IN_REGLIST : 0; atype.defined = 0; atype.type = NT_invtype; atype.width = -1; + atype.element_size = 0; atype.index = 0; if (reg == NULL) { if (typeinfo) *typeinfo = atype; - set_default_error (); - return PARSE_FAIL; + if (!isalpha && (flags & PTR_IN_REGLIST)) + set_fatal_syntax_error (_("syntax error in register list")); + else if (flags & PTR_GOOD_MATCH) + set_fatal_syntax_error (NULL); + else + set_expected_reg_error (type, reg, err_flags); + return NULL; } if (! aarch64_check_reg_type (reg, type)) { DEBUG_TRACE ("reg type check failed"); - set_default_error (); - return PARSE_FAIL; + if (flags & PTR_GOOD_MATCH) + set_fatal_syntax_error (NULL); + else + set_expected_reg_error (type, reg, err_flags); + return NULL; } type = reg->type; - if ((type == REG_TYPE_VN || type == REG_TYPE_ZN || type == REG_TYPE_PN) - && (*str == '.' || (type == REG_TYPE_PN && *str == '/'))) + if (aarch64_valid_suffix_char_p (reg->type, *str)) { if (*str == '.') { if (!parse_vector_type_for_operand (type, &parsetype, &str)) - return PARSE_FAIL; + return NULL; + if ((reg->type == REG_TYPE_ZAT + || reg->type == REG_TYPE_ZATH + || reg->type == REG_TYPE_ZATV) + && reg->number * 8 >= parsetype.element_size) + { + set_syntax_error (_("ZA tile number out of range")); + return NULL; + } } else { if (!parse_predication_for_operand (&parsetype, &str)) - return PARSE_FAIL; + return NULL; } /* Register if of the form Vn.[bhsdq]. */ is_typed_vecreg = true; - if (type == REG_TYPE_ZN || type == REG_TYPE_PN) + if (type != REG_TYPE_V) { /* The width is always variable; we don't allow an integer width to be specified. */ @@ -1016,48 +1272,41 @@ parse_typed_reg (char **ccp, aarch64_reg_type type, aarch64_reg_type *rtype, atype.width = parsetype.width; } - if (skip_past_char (&str, '[')) + if (!(flags & PTR_FULL_REG) && skip_past_char (&str, '[')) { - expressionS exp; - /* Reject Sn[index] syntax. */ - if (!is_typed_vecreg) + if (reg->type != REG_TYPE_Z + && reg->type != REG_TYPE_PN + && reg->type != REG_TYPE_ZT0 + && !is_typed_vecreg) { first_error (_("this type of register can't be indexed")); - return PARSE_FAIL; + return NULL; } - if (in_reg_list) + if (flags & PTR_IN_REGLIST) { first_error (_("index not allowed inside register list")); - return PARSE_FAIL; + return NULL; } atype.defined |= NTA_HASINDEX; - aarch64_get_expression (&exp, &str, GE_NO_PREFIX, REJECT_ABSENT, - NORMAL_RESOLUTION); - - if (exp.X_op != O_constant) - { - first_error (_("constant expression required")); - return PARSE_FAIL; - } + if (!parse_index_expression (&str, &atype.index)) + return NULL; if (! skip_past_char (&str, ']')) - return PARSE_FAIL; - - atype.index = exp.X_add_number; + return NULL; } - else if (!in_reg_list && (atype.defined & NTA_HASINDEX) != 0) + else if (!(flags & PTR_IN_REGLIST) && (atype.defined & NTA_HASINDEX) != 0) { /* Indexed vector register expected. */ first_error (_("indexed vector register expected")); - return PARSE_FAIL; + return NULL; } /* A vector reg Vn should be typed or indexed. */ - if (type == REG_TYPE_VN && atype.defined == 0) + if (type == REG_TYPE_V && atype.defined == 0) { first_error (_("invalid use of vector register")); } @@ -1065,53 +1314,43 @@ parse_typed_reg (char **ccp, aarch64_reg_type type, aarch64_reg_type *rtype, if (typeinfo) *typeinfo = atype; - if (rtype) - *rtype = type; - *ccp = str; - return reg->number; + return reg; } /* Parse register. - Return the register number on success; return PARSE_FAIL otherwise. - - If RTYPE is not NULL, return in *RTYPE the (possibly restricted) type of - the register (e.g. NEON double or quad reg when either has been requested). + Return the register on success; return null otherwise. If this is a NEON vector register with additional type information, fill in the struct pointed to by VECTYPE (if non-NULL). - This parser does not handle register list. */ + This parser does not handle register lists. */ -static int +static const reg_entry * aarch64_reg_parse (char **ccp, aarch64_reg_type type, - aarch64_reg_type *rtype, struct vector_type_el *vectype) + struct vector_type_el *vectype) { - struct vector_type_el atype; - char *str = *ccp; - int reg = parse_typed_reg (&str, type, rtype, &atype, - /*in_reg_list= */ false); - - if (reg == PARSE_FAIL) - return PARSE_FAIL; - - if (vectype) - *vectype = atype; - - *ccp = str; - - return reg; + return parse_typed_reg (ccp, type, vectype, 0); } static inline bool eq_vector_type_el (struct vector_type_el e1, struct vector_type_el e2) { - return - e1.type == e2.type - && e1.defined == e2.defined - && e1.width == e2.width && e1.index == e2.index; + return (e1.type == e2.type + && e1.defined == e2.defined + && e1.width == e2.width + && e1.element_size == e2.element_size + && e1.index == e2.index); +} + +/* Return the register number mask for registers of type REG_TYPE. */ + +static inline int +reg_type_mask (aarch64_reg_type reg_type) +{ + return reg_type == REG_TYPE_P ? 15 : 31; } /* This function parses a list of vector registers of type TYPE. @@ -1142,16 +1381,16 @@ parse_vector_reg_list (char **ccp, aarch64_reg_type type, char *str = *ccp; int nb_regs; struct vector_type_el typeinfo, typeinfo_first; - int val, val_range; + uint32_t val, val_range, mask; int in_range; int ret_val; - int i; bool error = false; bool expect_index = false; + unsigned int ptr_flags = PTR_IN_REGLIST; if (*str != '{') { - set_syntax_error (_("expecting {")); + set_expected_reglist_error (type, parse_reg (&str)); return PARSE_FAIL; } str++; @@ -1160,11 +1399,13 @@ parse_vector_reg_list (char **ccp, aarch64_reg_type type, typeinfo_first.defined = 0; typeinfo_first.type = NT_invtype; typeinfo_first.width = -1; + typeinfo_first.element_size = 0; typeinfo_first.index = 0; ret_val = 0; - val = -1; - val_range = -1; + val = -1u; + val_range = -1u; in_range = 0; + mask = reg_type_mask (type); do { if (in_range) @@ -1172,16 +1413,17 @@ parse_vector_reg_list (char **ccp, aarch64_reg_type type, str++; /* skip over '-' */ val_range = val; } - val = parse_typed_reg (&str, type, NULL, &typeinfo, - /*in_reg_list= */ true); - if (val == PARSE_FAIL) + const reg_entry *reg = parse_typed_reg (&str, type, &typeinfo, + ptr_flags); + if (!reg) { set_first_syntax_error (_("invalid vector register in list")); error = true; continue; } + val = reg->number; /* reject [bhsd]n */ - if (type == REG_TYPE_VN && typeinfo.defined == 0) + if (type == REG_TYPE_V && typeinfo.defined == 0) { set_first_syntax_error (_("invalid scalar register in list")); error = true; @@ -1193,13 +1435,13 @@ parse_vector_reg_list (char **ccp, aarch64_reg_type type, if (in_range) { - if (val < val_range) + if (val == val_range) { set_first_syntax_error (_("invalid range in vector register list")); error = true; } - val_range++; + val_range = (val_range + 1) & mask; } else { @@ -1214,12 +1456,16 @@ parse_vector_reg_list (char **ccp, aarch64_reg_type type, } } if (! error) - for (i = val_range; i <= val; i++) + for (;;) { - ret_val |= i << (5 * nb_regs); + ret_val |= val_range << ((5 * nb_regs) & 31); nb_regs++; + if (val_range == val) + break; + val_range = (val_range + 1) & mask; } in_range = 0; + ptr_flags |= PTR_GOOD_MATCH; } while (skip_past_comma (&str) || (in_range = 1, *str == '-')); @@ -1237,19 +1483,10 @@ parse_vector_reg_list (char **ccp, aarch64_reg_type type, { if (skip_past_char (&str, '[')) { - expressionS exp; - - aarch64_get_expression (&exp, &str, GE_NO_PREFIX, REJECT_ABSENT, - NORMAL_RESOLUTION); - if (exp.X_op != O_constant) - { - set_first_syntax_error (_("constant expression required.")); - error = true; - } + if (!parse_index_expression (&str, &typeinfo_first.index)) + error = true; if (! skip_past_char (&str, ']')) error = true; - else - typeinfo_first.index = exp.X_add_number; } else { @@ -1412,11 +1649,7 @@ s_unreq (int a ATTRIBUTE_UNUSED) char saved_char; name = input_line_pointer; - - while (*input_line_pointer != 0 - && *input_line_pointer != ' ' && *input_line_pointer != '\n') - ++input_line_pointer; - + input_line_pointer = find_end_of_line (input_line_pointer, flag_m68k_mri); saved_char = *input_line_pointer; *input_line_pointer = 0; @@ -1475,7 +1708,7 @@ s_unreq (int a ATTRIBUTE_UNUSED) /* Directives: Instruction set selection. */ -#ifdef OBJ_ELF +#if defined OBJ_ELF || defined OBJ_COFF /* This code is to handle mapping symbols as defined in the ARM AArch64 ELF spec. (See "Mapping symbols", section 4.5.4, ARM AAELF64 version 0.05). Note that previously, $a and $t has type STT_FUNC (BSF_OBJECT flag), @@ -1890,10 +2123,9 @@ s_ltorg (int ignored ATTRIBUTE_UNUSED) } } -#ifdef OBJ_ELF +#if defined(OBJ_ELF) || defined(OBJ_COFF) /* Forward declarations for functions below, in the MD interface section. */ -static fixS *fix_new_aarch64 (fragS *, int, short, expressionS *, int, int); static struct reloc_table_entry * find_reloc_table_entry (char **); /* Directives: Data. */ @@ -1901,7 +2133,7 @@ static struct reloc_table_entry * find_reloc_table_entry (char **); implemented properly. */ static void -s_aarch64_elf_cons (int nbytes) +s_aarch64_cons (int nbytes) { expressionS exp; @@ -1951,6 +2183,12 @@ s_aarch64_elf_cons (int nbytes) input_line_pointer--; demand_empty_rest_of_line (); } +#endif + +#ifdef OBJ_ELF +/* Forward declarations for functions below, in the MD interface + section. */ + static fixS *fix_new_aarch64 (fragS *, int, short, expressionS *, int, int); /* Mark symbol that it follows a variant PCS convention. */ @@ -2093,6 +2331,57 @@ s_tlsdescldr (int ignored ATTRIBUTE_UNUSED) } #endif /* OBJ_ELF */ +#ifdef TE_PE +static void +s_secrel (int dummy ATTRIBUTE_UNUSED) +{ + expressionS exp; + + do + { + expression (&exp); + if (exp.X_op == O_symbol) + exp.X_op = O_secrel; + + emit_expr (&exp, 4); + } + while (*input_line_pointer++ == ','); + + input_line_pointer--; + demand_empty_rest_of_line (); +} + +void +tc_pe_dwarf2_emit_offset (symbolS *symbol, unsigned int size) +{ + expressionS exp; + + exp.X_op = O_secrel; + exp.X_add_symbol = symbol; + exp.X_add_number = 0; + emit_expr (&exp, size); +} + +static void +s_secidx (int dummy ATTRIBUTE_UNUSED) +{ + expressionS exp; + + do + { + expression (&exp); + if (exp.X_op == O_symbol) + exp.X_op = O_secidx; + + emit_expr (&exp, 2); + } + while (*input_line_pointer++ == ','); + + input_line_pointer--; + demand_empty_rest_of_line (); +} +#endif /* TE_PE */ + static void s_aarch64_arch (int); static void s_aarch64_cpu (int); static void s_aarch64_arch_extension (int); @@ -2120,11 +2409,17 @@ const pseudo_typeS md_pseudo_table[] = { {"tlsdescadd", s_tlsdescadd, 0}, {"tlsdesccall", s_tlsdesccall, 0}, {"tlsdescldr", s_tlsdescldr, 0}, - {"word", s_aarch64_elf_cons, 4}, - {"long", s_aarch64_elf_cons, 4}, - {"xword", s_aarch64_elf_cons, 8}, - {"dword", s_aarch64_elf_cons, 8}, {"variant_pcs", s_variant_pcs, 0}, +#endif +#if defined(OBJ_ELF) || defined(OBJ_COFF) + {"word", s_aarch64_cons, 4}, + {"long", s_aarch64_cons, 4}, + {"xword", s_aarch64_cons, 8}, + {"dword", s_aarch64_cons, 8}, +#endif +#ifdef TE_PE + {"secrel32", s_secrel, 0}, + {"secidx", s_secidx, 0}, #endif {"float16", float_cons, 'h'}, {"bfloat16", float_cons, 'b'}, @@ -2147,18 +2442,18 @@ const pseudo_typeS md_pseudo_table[] = { static bool reg_name_p (char *str, aarch64_reg_type reg_type) { - int reg; + const reg_entry *reg; /* Prevent the diagnostics state from being spoiled. */ if (error_p ()) return false; - reg = aarch64_reg_parse (&str, reg_type, NULL, NULL); + reg = aarch64_reg_parse (&str, reg_type, NULL); /* Clear the parsing error that may be set by the reg parser. */ clear_error (); - if (reg == PARSE_FAIL) + if (!reg) return false; skip_whitespace (str); @@ -2187,8 +2482,7 @@ parse_immediate_expression (char **str, expressionS *exp, return false; } - aarch64_get_expression (exp, str, GE_OPT_PREFIX, REJECT_ABSENT, - NORMAL_RESOLUTION); + aarch64_get_expression (exp, str, GE_OPT_PREFIX, REJECT_ABSENT); if (exp->X_op == O_absent) { @@ -2422,8 +2716,7 @@ parse_big_immediate (char **str, int64_t *imm, aarch64_reg_type reg_type) return false; } - aarch64_get_expression (&inst.reloc.exp, &ptr, GE_OPT_PREFIX, REJECT_ABSENT, - NORMAL_RESOLUTION); + aarch64_get_expression (&inst.reloc.exp, &ptr, GE_OPT_PREFIX, REJECT_ABSENT); if (inst.reloc.exp.X_op == O_constant) *imm = inst.reloc.exp.X_add_number; @@ -3320,8 +3613,7 @@ parse_shift (char **str, aarch64_opnd_info *operand, enum parse_shift_mode mode) p++; exp_has_prefix = 1; } - (void) aarch64_get_expression (&exp, &p, GE_NO_PREFIX, ALLOW_ABSENT, - NORMAL_RESOLUTION); + aarch64_get_expression (&exp, &p, GE_NO_PREFIX, ALLOW_ABSENT); } if (kind == AARCH64_MOD_MUL_VL) /* For consistency, give MUL VL the same shift amount as an implicit @@ -3385,7 +3677,7 @@ parse_shifter_operand_imm (char **str, aarch64_opnd_info *operand, /* Accept an immediate expression. */ if (! aarch64_get_expression (&inst.reloc.exp, &p, GE_OPT_PREFIX, - REJECT_ABSENT, NORMAL_RESOLUTION)) + REJECT_ABSENT)) return false; /* Accept optional LSL for arithmetic immediate values. */ @@ -3437,9 +3729,9 @@ parse_shifter_operand (char **str, aarch64_opnd_info *operand, return false; } - if (!aarch64_check_reg_type (reg, REG_TYPE_R_Z)) + if (!aarch64_check_reg_type (reg, REG_TYPE_R_ZR)) { - set_syntax_error (_(get_reg_expected_msg (REG_TYPE_R_Z))); + set_expected_reg_error (REG_TYPE_R_ZR, reg, 0); return false; } @@ -3509,8 +3801,7 @@ parse_shifter_operand_reloc (char **str, aarch64_opnd_info *operand, /* Next, we parse the expression. */ if (! aarch64_get_expression (&inst.reloc.exp, str, GE_NO_PREFIX, - REJECT_ABSENT, - aarch64_force_reloc (entry->add_type) == 1)) + REJECT_ABSENT)) return false; /* Record the relocation type (use the ADD variant here). */ @@ -3656,8 +3947,7 @@ parse_address_main (char **str, aarch64_opnd_info *operand, } /* #:: */ - if (! aarch64_get_expression (exp, &p, GE_NO_PREFIX, REJECT_ABSENT, - aarch64_force_reloc (ty) == 1)) + if (! aarch64_get_expression (exp, &p, GE_NO_PREFIX, REJECT_ABSENT)) { set_syntax_error (_("invalid relocation expression")); return false; @@ -3673,8 +3963,7 @@ parse_address_main (char **str, aarch64_opnd_info *operand, /* =immediate; need to generate the literal in the literal pool. */ inst.gen_lit_pool = 1; - if (!aarch64_get_expression (exp, &p, GE_NO_PREFIX, REJECT_ABSENT, - NORMAL_RESOLUTION)) + if (!aarch64_get_expression (exp, &p, GE_NO_PREFIX, REJECT_ABSENT)) { set_syntax_error (_("invalid address")); return false; @@ -3687,10 +3976,18 @@ parse_address_main (char **str, aarch64_opnd_info *operand, /* [ */ + bool alpha_base_p = ISALPHA (*p); reg = aarch64_addr_reg_parse (&p, base_type, base_qualifier); if (!reg || !aarch64_check_reg_type (reg, base_type)) { - set_syntax_error (_(get_reg_expected_msg (base_type))); + if (reg + && aarch64_check_reg_type (reg, REG_TYPE_R_SP) + && *base_qualifier == AARCH64_OPND_QLF_W) + set_syntax_error (_("expected a 64-bit base register")); + else if (alpha_base_p) + set_syntax_error (_("invalid base register")); + else + set_syntax_error (_("expected a base register")); return false; } operand->addr.base_regno = reg->number; @@ -3706,7 +4003,7 @@ parse_address_main (char **str, aarch64_opnd_info *operand, { if (!aarch64_check_reg_type (reg, offset_type)) { - set_syntax_error (_(get_reg_expected_msg (offset_type))); + set_syntax_error (_("invalid offset register")); return false; } @@ -3780,8 +4077,7 @@ parse_address_main (char **str, aarch64_opnd_info *operand, /* We now have the group relocation table entry corresponding to the name in the assembler source. Next, we parse the expression. */ - if (! aarch64_get_expression (exp, &p, GE_NO_PREFIX, REJECT_ABSENT, - aarch64_force_reloc (entry->ldst_type) == 1)) + if (! aarch64_get_expression (exp, &p, GE_NO_PREFIX, REJECT_ABSENT)) { set_syntax_error (_("invalid relocation expression")); return false; @@ -3794,8 +4090,7 @@ parse_address_main (char **str, aarch64_opnd_info *operand, } else { - if (! aarch64_get_expression (exp, &p, GE_OPT_PREFIX, REJECT_ABSENT, - NORMAL_RESOLUTION)) + if (! aarch64_get_expression (exp, &p, GE_OPT_PREFIX, REJECT_ABSENT)) { set_syntax_error (_("invalid expression in the address")); return false; @@ -3844,15 +4139,14 @@ parse_address_main (char **str, aarch64_opnd_info *operand, /* [Xn],Xm */ if (!aarch64_check_reg_type (reg, REG_TYPE_R_64)) { - set_syntax_error (_(get_reg_expected_msg (REG_TYPE_R_64))); + set_syntax_error (_("invalid offset register")); return false; } operand->addr.offset.regno = reg->number; operand->addr.offset.is_reg = 1; } - else if (! aarch64_get_expression (exp, &p, GE_OPT_PREFIX, REJECT_ABSENT, - NORMAL_RESOLUTION)) + else if (! aarch64_get_expression (exp, &p, GE_OPT_PREFIX, REJECT_ABSENT)) { /* [Xn],#expr */ set_syntax_error (_("invalid expression in the address")); @@ -3911,7 +4205,7 @@ parse_address (char **str, aarch64_opnd_info *operand) { aarch64_opnd_qualifier_t base_qualifier, offset_qualifier; return parse_address_main (str, operand, &base_qualifier, &offset_qualifier, - REG_TYPE_R64_SP, REG_TYPE_R_Z, SHIFTED_NONE); + REG_TYPE_R64_SP, REG_TYPE_R_ZR, SHIFTED_NONE); } /* Parse an address in which SVE vector registers and MUL VL are allowed. @@ -3935,7 +4229,7 @@ parse_x0_to_x30 (char **str, aarch64_opnd_info *operand) const reg_entry *reg = parse_reg (str); if (!reg || !aarch64_check_reg_type (reg, REG_TYPE_R_64)) { - set_syntax_error (_(get_reg_expected_msg (REG_TYPE_R_64))); + set_expected_reg_error (REG_TYPE_R_64, reg, 0); return false; } operand->reg.regno = reg->number; @@ -3980,8 +4274,7 @@ parse_half (char **str, int *internal_fixup_p) else *internal_fixup_p = 1; - if (! aarch64_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX, REJECT_ABSENT, - aarch64_force_reloc (inst.reloc.type) == 1)) + if (! aarch64_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX, REJECT_ABSENT)) return false; *str = p; @@ -4023,8 +4316,7 @@ parse_adrp (char **str) inst.reloc.type = BFD_RELOC_AARCH64_ADR_HI21_PCREL; inst.reloc.pc_rel = 1; - if (! aarch64_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX, REJECT_ABSENT, - aarch64_force_reloc (inst.reloc.type) == 1)) + if (! aarch64_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX, REJECT_ABSENT)) return false; *str = p; return true; @@ -4034,8 +4326,10 @@ parse_adrp (char **str) /* Parse a symbolic operand such as "pow2" at *STR. ARRAY is an array of SIZE tokens in which index I gives the token for field value I, - or is null if field value I is invalid. REG_TYPE says which register - names should be treated as registers rather than as symbolic immediates. + or is null if field value I is invalid. If the symbolic operand + can also be given as a 0-based integer, REG_TYPE says which register + names should be treated as registers rather than as symbolic immediates + while parsing that integer. REG_TYPE is REG_TYPE_MAX otherwise. Return true on success, moving *STR past the operand and storing the field value in *VAL. */ @@ -4063,6 +4357,9 @@ parse_enum_string (char **str, int64_t *val, const char *const *array, return true; } + if (reg_type == REG_TYPE_MAX) + return false; + if (!parse_immediate_expression (&p, &exp, reg_type)) return false; @@ -4203,108 +4500,29 @@ parse_bti_operand (char **str, REG_TYPE.QUALIFIER Side effect: Update STR with current parse position of success. -*/ + + FLAGS is as for parse_typed_reg. */ static const reg_entry * parse_reg_with_qual (char **str, aarch64_reg_type reg_type, - aarch64_opnd_qualifier_t *qualifier) -{ - char *q; - - reg_entry *reg = parse_reg (str); - if (reg != NULL && reg->type == reg_type) - { - if (!skip_past_char (str, '.')) - { - set_syntax_error (_("missing ZA tile element size separator")); - return NULL; - } - - q = *str; - switch (TOLOWER (*q)) - { - case 'b': - *qualifier = AARCH64_OPND_QLF_S_B; - break; - case 'h': - *qualifier = AARCH64_OPND_QLF_S_H; - break; - case 's': - *qualifier = AARCH64_OPND_QLF_S_S; - break; - case 'd': - *qualifier = AARCH64_OPND_QLF_S_D; - break; - case 'q': - *qualifier = AARCH64_OPND_QLF_S_Q; - break; - default: - return NULL; - } - q++; - - *str = q; - return reg; - } - - return NULL; -} - -/* Parse SME ZA tile encoded in assembler symbol. - Function return tile QUALIFIER on success. - - Tiles are in example format: za[0-9]\.[bhsd] - - Function returns register number or PARSE_FAIL. -*/ -static int -parse_sme_zada_operand (char **str, aarch64_opnd_qualifier_t *qualifier) + aarch64_opnd_qualifier_t *qualifier, unsigned int flags) { - int regno; - const reg_entry *reg = parse_reg_with_qual (str, REG_TYPE_ZA, qualifier); - - if (reg == NULL) - return PARSE_FAIL; - regno = reg->number; + struct vector_type_el vectype; + const reg_entry *reg = parse_typed_reg (str, reg_type, &vectype, + PTR_FULL_REG | flags); + if (!reg) + return NULL; - switch (*qualifier) + if (vectype.type == NT_invtype) + *qualifier = AARCH64_OPND_QLF_NIL; + else { - case AARCH64_OPND_QLF_S_B: - if (regno != 0x00) - { - set_syntax_error (_("invalid ZA tile register number, expected za0")); - return PARSE_FAIL; - } - break; - case AARCH64_OPND_QLF_S_H: - if (regno > 0x01) - { - set_syntax_error (_("invalid ZA tile register number, expected za0-za1")); - return PARSE_FAIL; - } - break; - case AARCH64_OPND_QLF_S_S: - if (regno > 0x03) - { - /* For the 32-bit variant: is the name of the ZA tile ZA0-ZA3. */ - set_syntax_error (_("invalid ZA tile register number, expected za0-za3")); - return PARSE_FAIL; - } - break; - case AARCH64_OPND_QLF_S_D: - if (regno > 0x07) - { - /* For the 64-bit variant: is the name of the ZA tile ZA0-ZA7 */ - set_syntax_error (_("invalid ZA tile register number, expected za0-za7")); - return PARSE_FAIL; - } - break; - default: - set_syntax_error (_("invalid ZA tile element size, allowed b, h, s and d")); - return PARSE_FAIL; + *qualifier = vectype_to_qualifier (&vectype); + if (*qualifier == AARCH64_OPND_QLF_NIL) + return NULL; } - return regno; + return reg; } /* Parse STR for unsigned, immediate (1-2 digits) in format: @@ -4325,22 +4543,15 @@ parse_sme_immediate (char **str, int64_t *imm) return true; } -/* Parse index with vector select register and immediate: +/* Parse index with selection register and immediate offset: [, ] [, #] - where is in W12-W15 range and # is optional for immediate. - Function performs extra check for mandatory immediate value if REQUIRE_IMM - is set to true. + Return true on success, populating OPND with the parsed index. */ - On success function returns TRUE and populated VECTOR_SELECT_REGISTER and - IMM output. -*/ static bool -parse_sme_za_hv_tiles_operand_index (char **str, - int *vector_select_register, - int64_t *imm) +parse_sme_za_index (char **str, struct aarch64_indexed_za *opnd) { const reg_entry *reg; @@ -4350,28 +4561,70 @@ parse_sme_za_hv_tiles_operand_index (char **str, return false; } - /* Vector select register W12-W15 encoded in the 2-bit Rv field. */ + /* The selection register, encoded in the 2-bit Rv field. */ reg = parse_reg (str); - if (reg == NULL || reg->type != REG_TYPE_R_32 - || reg->number < 12 || reg->number > 15) + if (reg == NULL || reg->type != REG_TYPE_R_32) { - set_syntax_error (_("expected vector select register W12-W15")); + set_syntax_error (_("expected a 32-bit selection register")); return false; } - *vector_select_register = reg->number; + opnd->index.regno = reg->number; - if (!skip_past_char (str, ',')) /* Optional index offset immediate. */ + if (!skip_past_char (str, ',')) { - set_syntax_error (_("expected ','")); + set_syntax_error (_("missing immediate offset")); return false; } - if (!parse_sme_immediate (str, imm)) + if (!parse_sme_immediate (str, &opnd->index.imm)) { - set_syntax_error (_("index offset immediate expected")); + set_syntax_error (_("expected a constant immediate offset")); return false; } + if (skip_past_char (str, ':')) + { + int64_t end; + if (!parse_sme_immediate (str, &end)) + { + set_syntax_error (_("expected a constant immediate offset")); + return false; + } + if (end < opnd->index.imm) + { + set_syntax_error (_("the last offset is less than the" + " first offset")); + return false; + } + if (end == opnd->index.imm) + { + set_syntax_error (_("the last offset is equal to the" + " first offset")); + return false; + } + opnd->index.countm1 = (uint64_t) end - opnd->index.imm; + } + + opnd->group_size = 0; + if (skip_past_char (str, ',')) + { + if (strncasecmp (*str, "vgx2", 4) == 0 && !ISALPHA ((*str)[4])) + { + *str += 4; + opnd->group_size = 2; + } + else if (strncasecmp (*str, "vgx4", 4) == 0 && !ISALPHA ((*str)[4])) + { + *str += 4; + opnd->group_size = 4; + } + else + { + set_syntax_error (_("invalid vector group size")); + return false; + } + } + if (!skip_past_char (str, ']')) { set_syntax_error (_("expected ']'")); @@ -4381,133 +4634,63 @@ parse_sme_za_hv_tiles_operand_index (char **str, return true; } -/* Parse SME ZA horizontal or vertical vector access to tiles. - Function extracts from STR to SLICE_INDICATOR horizontal (0) or - vertical (1) ZA tile vector orientation. VECTOR_SELECT_REGISTER - contains select register and corresponding optional IMMEDIATE. - In addition QUALIFIER is extracted. - - Field format examples: - - ZA0.B[, #] - .H[, #] - .S[, #] - .D[, #] - .Q[, #] +/* Parse a register of type REG_TYPE that might have an element type + qualifier and that is indexed by two values: a 32-bit register, + followed by an immediate. The ranges of the register and the + immediate vary by opcode and are checked in libopcodes. - Function returns register number or PARSE_FAIL. -*/ -static int -parse_sme_za_hv_tiles_operand (char **str, - enum sme_hv_slice *slice_indicator, - int *vector_select_register, - int *imm, - aarch64_opnd_qualifier_t *qualifier) -{ - char *qh, *qv; - int regno; - int regno_limit; - int64_t imm_limit; - int64_t imm_value; - const reg_entry *reg; - - qh = qv = *str; - if ((reg = parse_reg_with_qual (&qh, REG_TYPE_ZAH, qualifier)) != NULL) - { - *slice_indicator = HV_horizontal; - *str = qh; - } - else if ((reg = parse_reg_with_qual (&qv, REG_TYPE_ZAV, qualifier)) != NULL) - { - *slice_indicator = HV_vertical; - *str = qv; - } - else - return PARSE_FAIL; - regno = reg->number; - - switch (*qualifier) - { - case AARCH64_OPND_QLF_S_B: - regno_limit = 0; - imm_limit = 15; - break; - case AARCH64_OPND_QLF_S_H: - regno_limit = 1; - imm_limit = 7; - break; - case AARCH64_OPND_QLF_S_S: - regno_limit = 3; - imm_limit = 3; - break; - case AARCH64_OPND_QLF_S_D: - regno_limit = 7; - imm_limit = 1; - break; - case AARCH64_OPND_QLF_S_Q: - regno_limit = 15; - imm_limit = 0; - break; - default: - set_syntax_error (_("invalid ZA tile element size, allowed b, h, s, d and q")); - return PARSE_FAIL; - } + Return true on success, populating OPND with information about + the operand and setting QUALIFIER to the register qualifier. - /* Check if destination register ZA tile vector is in range for given - instruction variant. */ - if (regno < 0 || regno > regno_limit) - { - set_syntax_error (_("ZA tile vector out of range")); - return PARSE_FAIL; - } + Field format examples: - if (!parse_sme_za_hv_tiles_operand_index (str, vector_select_register, - &imm_value)) - return PARSE_FAIL; + .[< #] + ZA[, #] + .[, #] - /* Check if optional index offset is in the range for instruction - variant. */ - if (imm_value < 0 || imm_value > imm_limit) - { - set_syntax_error (_("index offset out of range")); - return PARSE_FAIL; - } + FLAGS is as for parse_typed_reg. */ + +static bool +parse_dual_indexed_reg (char **str, aarch64_reg_type reg_type, + struct aarch64_indexed_za *opnd, + aarch64_opnd_qualifier_t *qualifier, + unsigned int flags) +{ + const reg_entry *reg = parse_reg_with_qual (str, reg_type, qualifier, flags); + if (!reg) + return false; - *imm = imm_value; + opnd->v = aarch64_check_reg_type (reg, REG_TYPE_ZATV); + opnd->regno = reg->number; - return regno; + return parse_sme_za_index (str, opnd); } +/* Like parse_sme_za_hv_tiles_operand, but expect braces around the + operand. */ -static int +static bool parse_sme_za_hv_tiles_operand_with_braces (char **str, - enum sme_hv_slice *slice_indicator, - int *vector_select_register, - int *imm, + struct aarch64_indexed_za *opnd, aarch64_opnd_qualifier_t *qualifier) { - int regno; - if (!skip_past_char (str, '{')) { - set_syntax_error (_("expected '{'")); - return PARSE_FAIL; + set_expected_reglist_error (REG_TYPE_ZATHV, parse_reg (str)); + return false; } - regno = parse_sme_za_hv_tiles_operand (str, slice_indicator, - vector_select_register, imm, - qualifier); - - if (regno == PARSE_FAIL) - return PARSE_FAIL; + if (!parse_dual_indexed_reg (str, REG_TYPE_ZATHV, opnd, qualifier, + PTR_IN_REGLIST)) + return false; if (!skip_past_char (str, '}')) { set_syntax_error (_("expected '}'")); - return PARSE_FAIL; + return false; } - return regno; + return true; } /* Parse list of up to eight 64-bit element tile names separated by commas in @@ -4525,46 +4708,59 @@ parse_sme_zero_mask(char **str) char *q; int mask; aarch64_opnd_qualifier_t qualifier; + unsigned int ptr_flags = PTR_IN_REGLIST; mask = 0x00; q = *str; do { - const reg_entry *reg = parse_reg_with_qual (&q, REG_TYPE_ZA, &qualifier); - if (reg) - { + const reg_entry *reg = parse_reg_with_qual (&q, REG_TYPE_ZA_ZAT, + &qualifier, ptr_flags); + if (!reg) + return PARSE_FAIL; + + if (reg->type == REG_TYPE_ZA) + { + if (qualifier != AARCH64_OPND_QLF_NIL) + { + set_syntax_error ("ZA should not have a size suffix"); + return PARSE_FAIL; + } + /* { ZA } is assembled as all-ones immediate. */ + mask = 0xff; + } + else + { int regno = reg->number; - if (qualifier == AARCH64_OPND_QLF_S_B && regno == 0) + if (qualifier == AARCH64_OPND_QLF_S_B) { /* { ZA0.B } is assembled as all-ones immediate. */ mask = 0xff; } - else if (qualifier == AARCH64_OPND_QLF_S_H && regno < 2) + else if (qualifier == AARCH64_OPND_QLF_S_H) mask |= 0x55 << regno; - else if (qualifier == AARCH64_OPND_QLF_S_S && regno < 4) + else if (qualifier == AARCH64_OPND_QLF_S_S) mask |= 0x11 << regno; - else if (qualifier == AARCH64_OPND_QLF_S_D && regno < 8) + else if (qualifier == AARCH64_OPND_QLF_S_D) mask |= 0x01 << regno; + else if (qualifier == AARCH64_OPND_QLF_S_Q) + { + set_syntax_error (_("ZA tile masks do not operate at .Q" + " granularity")); + return PARSE_FAIL; + } + else if (qualifier == AARCH64_OPND_QLF_NIL) + { + set_syntax_error (_("missing ZA tile size")); + return PARSE_FAIL; + } else { - set_syntax_error (_("wrong ZA tile element format")); + set_syntax_error (_("invalid ZA tile")); return PARSE_FAIL; } - continue; - } - else if (strncasecmp (q, "za", 2) == 0 - && !ISALNUM (q[2])) - { - /* { ZA } is assembled as all-ones immediate. */ - mask = 0xff; - q += 2; - continue; - } - else - { - set_syntax_error (_("wrong ZA tile element format")); - return PARSE_FAIL; } + ptr_flags |= PTR_GOOD_MATCH; } while (skip_past_char (&q, ',')); @@ -4608,45 +4804,6 @@ parse_sme_list_of_64bit_tiles (char **str) return regno; } -/* Parse ZA array operand used in e.g. STR and LDR instruction. - Operand format: - - ZA[, ] - ZA[, #] - - Function returns or PARSE_FAIL. -*/ -static int -parse_sme_za_array (char **str, int *imm) -{ - char *p, *q; - int regno; - int64_t imm_value; - - p = q = *str; - while (ISALPHA (*q)) - q++; - - if ((q - p != 2) || strncasecmp ("za", p, q - p) != 0) - { - set_syntax_error (_("expected ZA array")); - return PARSE_FAIL; - } - - if (! parse_sme_za_hv_tiles_operand_index (&q, ®no, &imm_value)) - return PARSE_FAIL; - - if (imm_value < 0 || imm_value > 15) - { - set_syntax_error (_("offset out of range")); - return PARSE_FAIL; - } - - *imm = imm_value; - *str = q; - return regno; -} - /* Parse streaming mode operand for SMSTART and SMSTOP. {SM | ZA} @@ -4673,65 +4830,6 @@ parse_sme_sm_za (char **str) return TOLOWER (p[0]); } -/* Parse the name of the source scalable predicate register, the index base - register W12-W15 and the element index. Function performs element index - limit checks as well as qualifier type checks. - - .[, ] - .[, #] - - On success function sets to INDEX_BASE_REG, to QUALIFIER and - to IMM. - Function returns , or PARSE_FAIL. -*/ -static int -parse_sme_pred_reg_with_index(char **str, - int *index_base_reg, - int *imm, - aarch64_opnd_qualifier_t *qualifier) -{ - int regno; - int64_t imm_limit; - int64_t imm_value; - const reg_entry *reg = parse_reg_with_qual (str, REG_TYPE_PN, qualifier); - - if (reg == NULL) - return PARSE_FAIL; - regno = reg->number; - - switch (*qualifier) - { - case AARCH64_OPND_QLF_S_B: - imm_limit = 15; - break; - case AARCH64_OPND_QLF_S_H: - imm_limit = 7; - break; - case AARCH64_OPND_QLF_S_S: - imm_limit = 3; - break; - case AARCH64_OPND_QLF_S_D: - imm_limit = 1; - break; - default: - set_syntax_error (_("wrong predicate register element size, allowed b, h, s and d")); - return PARSE_FAIL; - } - - if (! parse_sme_za_hv_tiles_operand_index (str, index_base_reg, &imm_value)) - return PARSE_FAIL; - - if (imm_value < 0 || imm_value > imm_limit) - { - set_syntax_error (_("element index out of range for given variant")); - return PARSE_FAIL; - } - - *imm = imm_value; - - return regno; -} - /* Parse a system register or a PSTATE field name for an MSR/MRS instruction. Returns the encoding for the option, or PARSE_FAIL. @@ -4791,7 +4889,8 @@ parse_sys_reg (char **str, htab_t sys_regs, "name '%s'"), buf); if (!pstatefield_p && !aarch64_sys_ins_reg_supported_p (cpu_variant, o->name, - o->value, o->flags, o->features)) + o->value, o->flags, + &o->features)) as_bad (_("selected processor does not support system register " "name '%s'"), buf); if (aarch64_sys_reg_deprecated_p (o->flags)) @@ -4850,23 +4949,20 @@ parse_sys_ins_reg (char **str, htab_t sys_ins_regs) } while (0) #define po_reg_or_fail(regtype) do { \ - val = aarch64_reg_parse (&str, regtype, &rtype, NULL); \ - if (val == PARSE_FAIL) \ - { \ - set_default_error (); \ - goto failure; \ - } \ + reg = aarch64_reg_parse (&str, regtype, NULL); \ + if (!reg) \ + goto failure; \ } while (0) -#define po_int_reg_or_fail(reg_type) do { \ - reg = aarch64_reg_parse_32_64 (&str, &qualifier); \ +#define po_int_fp_reg_or_fail(reg_type) do { \ + reg = parse_reg (&str); \ if (!reg || !aarch64_check_reg_type (reg, reg_type)) \ { \ - set_default_error (); \ + set_expected_reg_error (reg_type, reg, 0); \ goto failure; \ } \ info->reg.regno = reg->number; \ - info->qualifier = qualifier; \ + info->qualifier = inherent_reg_qualifier (reg); \ } while (0) #define po_imm_nc_or_fail() do { \ @@ -4891,11 +4987,31 @@ parse_sys_ins_reg (char **str, htab_t sys_ins_regs) goto failure; \ } while (0) +#define po_strict_enum_or_fail(array) do { \ + if (!parse_enum_string (&str, &val, array, \ + ARRAY_SIZE (array), REG_TYPE_MAX)) \ + goto failure; \ + } while (0) + #define po_misc_or_fail(expr) do { \ if (!expr) \ goto failure; \ } while (0) +/* A primitive log calculator. */ + +static inline unsigned int +get_log2 (unsigned int n) +{ + unsigned int count = 0; + while (n > 1) + { + n >>= 1; + count += 1; + } + return count; +} + /* encode the 12-bit imm field of Add/sub immediate */ static inline uint32_t encode_addsub_imm (uint32_t imm) @@ -5033,10 +5149,15 @@ const char* operand_mismatch_kind_names[] = "AARCH64_OPDE_SYNTAX_ERROR", "AARCH64_OPDE_FATAL_SYNTAX_ERROR", "AARCH64_OPDE_INVALID_VARIANT", + "AARCH64_OPDE_INVALID_VG_SIZE", + "AARCH64_OPDE_REG_LIST_LENGTH", + "AARCH64_OPDE_REG_LIST_STRIDE", + "AARCH64_OPDE_UNTIED_IMMS", + "AARCH64_OPDE_UNTIED_OPERAND", "AARCH64_OPDE_OUT_OF_RANGE", "AARCH64_OPDE_UNALIGNED", - "AARCH64_OPDE_REG_LIST", "AARCH64_OPDE_OTHER_ERROR", + "AARCH64_OPDE_INVALID_REGNO", }; #endif /* DEBUG_AARCH64 */ @@ -5057,10 +5178,13 @@ operand_error_higher_severity_p (enum aarch64_operand_error_kind lhs, gas_assert (AARCH64_OPDE_SYNTAX_ERROR > AARCH64_OPDE_EXPECTED_A_AFTER_B); gas_assert (AARCH64_OPDE_FATAL_SYNTAX_ERROR > AARCH64_OPDE_SYNTAX_ERROR); gas_assert (AARCH64_OPDE_INVALID_VARIANT > AARCH64_OPDE_FATAL_SYNTAX_ERROR); - gas_assert (AARCH64_OPDE_OUT_OF_RANGE > AARCH64_OPDE_INVALID_VARIANT); + gas_assert (AARCH64_OPDE_INVALID_VG_SIZE > AARCH64_OPDE_INVALID_VARIANT); + gas_assert (AARCH64_OPDE_REG_LIST_LENGTH > AARCH64_OPDE_INVALID_VG_SIZE); + gas_assert (AARCH64_OPDE_REG_LIST_STRIDE > AARCH64_OPDE_REG_LIST_LENGTH); + gas_assert (AARCH64_OPDE_OUT_OF_RANGE > AARCH64_OPDE_REG_LIST_STRIDE); gas_assert (AARCH64_OPDE_UNALIGNED > AARCH64_OPDE_OUT_OF_RANGE); - gas_assert (AARCH64_OPDE_REG_LIST > AARCH64_OPDE_UNALIGNED); - gas_assert (AARCH64_OPDE_OTHER_ERROR > AARCH64_OPDE_REG_LIST); + gas_assert (AARCH64_OPDE_OTHER_ERROR > AARCH64_OPDE_REG_LIST_STRIDE); + gas_assert (AARCH64_OPDE_INVALID_REGNO > AARCH64_OPDE_OTHER_ERROR); return lhs > rhs; } @@ -5347,6 +5471,41 @@ assign_qualifier_sequence (aarch64_inst *instr, instr->operands[i].qualifier = *qualifiers; } +/* Callback used by aarch64_print_operand to apply STYLE to the + disassembler output created from FMT and ARGS. The STYLER object holds + any required state. Must return a pointer to a string (created from FMT + and ARGS) that will continue to be valid until the complete disassembled + instruction has been printed. + + We don't currently add any styling to the output of the disassembler as + used within assembler error messages, and so STYLE is ignored here. A + new string is allocated on the obstack help within STYLER and returned + to the caller. */ + +static const char *aarch64_apply_style + (struct aarch64_styler *styler, + enum disassembler_style style ATTRIBUTE_UNUSED, + const char *fmt, va_list args) +{ + int res; + char *ptr; + struct obstack *stack = (struct obstack *) styler->state; + va_list ap; + + /* Calculate the required space. */ + va_copy (ap, args); + res = vsnprintf (NULL, 0, fmt, ap); + va_end (ap); + gas_assert (res >= 0); + + /* Allocate space on the obstack and format the result. */ + ptr = (char *) obstack_alloc (stack, res + 1); + res = vsnprintf (ptr, (res + 1), fmt, args); + gas_assert (res >= 0); + + return ptr; +} + /* Print operands for the diagnosis purpose. */ static void @@ -5354,6 +5513,12 @@ print_operands (char *buf, const aarch64_opcode *opcode, const aarch64_opnd_info *opnds) { int i; + struct aarch64_styler styler; + struct obstack content; + obstack_init (&content); + + styler.apply_style = aarch64_apply_style; + styler.state = (void *) &content; for (i = 0; i < AARCH64_MAX_OPND_NUM; ++i) { @@ -5371,7 +5536,7 @@ print_operands (char *buf, const aarch64_opcode *opcode, /* Generate the operand string in STR. */ aarch64_print_operand (str, sizeof (str), 0, opcode, opnds, i, NULL, NULL, - NULL, cmt, sizeof (cmt), cpu_variant); + NULL, cmt, sizeof (cmt), cpu_variant, &styler); /* Delimiter. */ if (str[0] != '\0') @@ -5389,6 +5554,8 @@ print_operands (char *buf, const aarch64_opcode *opcode, strcat (buf, cmt); } } + + obstack_free (&content, NULL); } /* Send to stderr a string as information. */ @@ -5415,6 +5582,64 @@ output_info (const char *format, ...) (void) putc ('\n', stderr); } +/* See if the AARCH64_OPDE_SYNTAX_ERROR error described by DETAIL + relates to registers or register lists. If so, return a string that + reports the error against "operand %d", otherwise return null. */ + +static const char * +get_reg_error_message (const aarch64_operand_error *detail) +{ + /* Handle the case where we found a register that was expected + to be in a register list outside of a register list. */ + if ((detail->data[1].i & detail->data[2].i) != 0 + && (detail->data[1].i & SEF_IN_REGLIST) == 0) + return _("missing braces at operand %d"); + + /* If some opcodes expected a register, and we found a register, + complain about the difference. */ + if (detail->data[2].i) + { + unsigned int expected = (detail->data[1].i & SEF_IN_REGLIST + ? detail->data[1].i & ~SEF_IN_REGLIST + : detail->data[0].i & ~SEF_DEFAULT_ERROR); + const char *msg = get_reg_expected_msg (expected, detail->data[2].i); + if (!msg) + msg = N_("unexpected register type at operand %d"); + return msg; + } + + /* Handle the case where we got to the point of trying to parse a + register within a register list, but didn't find a known register. */ + if (detail->data[1].i & SEF_IN_REGLIST) + { + unsigned int expected = detail->data[1].i & ~SEF_IN_REGLIST; + const char *msg = get_reg_expected_msg (expected, 0); + if (!msg) + msg = _("invalid register list at operand %d"); + return msg; + } + + /* Punt if register-related problems weren't the only errors. */ + if (detail->data[0].i & SEF_DEFAULT_ERROR) + return NULL; + + /* Handle the case where the only acceptable things are registers. */ + if (detail->data[1].i == 0) + { + const char *msg = get_reg_expected_msg (detail->data[0].i, 0); + if (!msg) + msg = _("expected a register at operand %d"); + return msg; + } + + /* Handle the case where the only acceptable things are register lists, + and there was no opening '{'. */ + if (detail->data[0].i == 0) + return _("expected '{' at operand %d"); + + return _("expected a register or register list at operand %d"); +} + /* Output one operand error record. */ static void @@ -5428,6 +5653,7 @@ output_operand_error_record (const operand_error_record *record, char *str) typedef void (*handler_t)(const char *format, ...); handler_t handler = detail->non_fatal ? as_warn : as_bad; + const char *msg = detail->error; switch (detail->kind) { @@ -5448,18 +5674,31 @@ output_operand_error_record (const operand_error_record *record, char *str) break; case AARCH64_OPDE_SYNTAX_ERROR: + if (!msg && idx >= 0) + { + msg = get_reg_error_message (detail); + if (msg) + { + char *full_msg = xasprintf (msg, idx + 1); + handler (_("%s -- `%s'"), full_msg, str); + free (full_msg); + break; + } + } + /* Fall through. */ + case AARCH64_OPDE_RECOVERABLE: case AARCH64_OPDE_FATAL_SYNTAX_ERROR: case AARCH64_OPDE_OTHER_ERROR: /* Use the prepared error message if there is, otherwise use the operand description string to describe the error. */ - if (detail->error != NULL) + if (msg != NULL) { if (idx < 0) - handler (_("%s -- `%s'"), detail->error, str); + handler (_("%s -- `%s'"), msg, str); else handler (_("%s at operand %d -- `%s'"), - detail->error, idx + 1, str); + msg, idx + 1, str); } else { @@ -5577,26 +5816,59 @@ output_operand_error_record (const operand_error_record *record, char *str) detail->index + 1, str); break; + case AARCH64_OPDE_INVALID_REGNO: + handler (_("%s%d-%s%d expected at operand %d -- `%s'"), + detail->data[0].s, detail->data[1].i, + detail->data[0].s, detail->data[2].i, idx + 1, str); + break; + case AARCH64_OPDE_OUT_OF_RANGE: if (detail->data[0].i != detail->data[1].i) handler (_("%s out of range %d to %d at operand %d -- `%s'"), - detail->error ? detail->error : _("immediate value"), + msg ? msg : _("immediate value"), detail->data[0].i, detail->data[1].i, idx + 1, str); else handler (_("%s must be %d at operand %d -- `%s'"), - detail->error ? detail->error : _("immediate value"), + msg ? msg : _("immediate value"), detail->data[0].i, idx + 1, str); break; - case AARCH64_OPDE_REG_LIST: - if (detail->data[0].i == 1) - handler (_("invalid number of registers in the list; " - "only 1 register is expected at operand %d -- `%s'"), + case AARCH64_OPDE_INVALID_VG_SIZE: + if (detail->data[0].i == 0) + handler (_("unexpected vector group size at operand %d -- `%s'"), idx + 1, str); else - handler (_("invalid number of registers in the list; " - "%d registers are expected at operand %d -- `%s'"), - detail->data[0].i, idx + 1, str); + handler (_("operand %d must have a vector group size of %d -- `%s'"), + idx + 1, detail->data[0].i, str); + break; + + case AARCH64_OPDE_REG_LIST_LENGTH: + if (detail->data[0].i == (1 << 1)) + handler (_("expected a single-register list at operand %d -- `%s'"), + idx + 1, str); + else if ((detail->data[0].i & -detail->data[0].i) == detail->data[0].i) + handler (_("expected a list of %d registers at operand %d -- `%s'"), + get_log2 (detail->data[0].i), idx + 1, str); + else if (detail->data[0].i == 0x14) + handler (_("expected a list of %d or %d registers at" + " operand %d -- `%s'"), + 2, 4, idx + 1, str); + else + handler (_("invalid number of registers in the list" + " at operand %d -- `%s'"), idx + 1, str); + break; + + case AARCH64_OPDE_REG_LIST_STRIDE: + if (detail->data[0].i == (1 << 1)) + handler (_("the register list must have a stride of %d" + " at operand %d -- `%s'"), 1, idx + 1, str); + else if (detail->data[0].i == 0x12 || detail->data[0].i == 0x102) + handler (_("the register list must have a stride of %d or %d" + " at operand %d -- `%s`"), 1, + detail->data[0].i == 0x12 ? 4 : 8, idx + 1, str); + else + handler (_("invalid register stride at operand %d -- `%s'"), + idx + 1, str); break; case AARCH64_OPDE_UNALIGNED: @@ -5611,6 +5883,44 @@ output_operand_error_record (const operand_error_record *record, char *str) } } +/* Return true if the presence of error A against an instruction means + that error B should not be reported. This is only used as a first pass, + to pick the kind of error that we should report. */ + +static bool +better_error_p (operand_error_record *a, operand_error_record *b) +{ + /* For errors reported during parsing, prefer errors that relate to + later operands, since that implies that the earlier operands were + syntactically valid. + + For example, if we see a register R instead of an immediate in + operand N, we'll report that as a recoverable "immediate operand + required" error. This is because there is often another opcode + entry that accepts a register operand N, and any errors about R + should be reported against the register forms of the instruction. + But if no such register form exists, the recoverable error should + still win over a syntax error against operand N-1. + + For these purposes, count an error reported at the end of the + assembly string as equivalent to an error reported against the + final operand. This means that opcode entries that expect more + operands win over "unexpected characters following instruction". */ + if (a->detail.kind <= AARCH64_OPDE_FATAL_SYNTAX_ERROR + && b->detail.kind <= AARCH64_OPDE_FATAL_SYNTAX_ERROR) + { + int a_index = (a->detail.index < 0 + ? aarch64_num_of_operands (a->opcode) - 1 + : a->detail.index); + int b_index = (b->detail.index < 0 + ? aarch64_num_of_operands (b->opcode) - 1 + : b->detail.index); + if (a_index != b_index) + return a_index > b_index; + } + return operand_error_higher_severity_p (a->detail.kind, b->detail.kind); +} + /* Process and output the error message about the operand mismatching. When this function is called, the operand error information had @@ -5626,12 +5936,10 @@ output_operand_error_record (const operand_error_record *record, char *str) static void output_operand_error_report (char *str, bool non_fatal_only) { - int largest_error_pos; - const char *msg = NULL; enum aarch64_operand_error_kind kind; operand_error_record *curr; operand_error_record *head = operand_error_report.head; - operand_error_record *record = NULL; + operand_error_record *record; /* No error to report. */ if (head == NULL) @@ -5655,20 +5963,38 @@ output_operand_error_report (char *str, bool non_fatal_only) /* Find the error kind of the highest severity. */ DEBUG_TRACE ("multiple opcode entries with error kind"); - kind = AARCH64_OPDE_NIL; + record = NULL; for (curr = head; curr != NULL; curr = curr->next) { gas_assert (curr->detail.kind != AARCH64_OPDE_NIL); - DEBUG_TRACE ("\t%s", operand_mismatch_kind_names[curr->detail.kind]); - if (operand_error_higher_severity_p (curr->detail.kind, kind) - && (!non_fatal_only || (non_fatal_only && curr->detail.non_fatal))) - kind = curr->detail.kind; + if (curr->detail.kind == AARCH64_OPDE_SYNTAX_ERROR) + { + DEBUG_TRACE ("\t%s [%x, %x, %x]", + operand_mismatch_kind_names[curr->detail.kind], + curr->detail.data[0].i, curr->detail.data[1].i, + curr->detail.data[2].i); + } + else if (curr->detail.kind == AARCH64_OPDE_REG_LIST_LENGTH + || curr->detail.kind == AARCH64_OPDE_REG_LIST_STRIDE) + { + DEBUG_TRACE ("\t%s [%x]", + operand_mismatch_kind_names[curr->detail.kind], + curr->detail.data[0].i); + } + else + { + DEBUG_TRACE ("\t%s", operand_mismatch_kind_names[curr->detail.kind]); + } + if ((!non_fatal_only || curr->detail.non_fatal) + && (!record || better_error_p (curr, record))) + record = curr; } + kind = (record ? record->detail.kind : AARCH64_OPDE_NIL); gas_assert (kind != AARCH64_OPDE_NIL || non_fatal_only); /* Pick up one of errors of KIND to report. */ - largest_error_pos = -2; /* Index can be -1 which means unknown index. */ + record = NULL; for (curr = head; curr != NULL; curr = curr->next) { /* If we don't want to print non-fatal errors then don't consider them @@ -5680,13 +6006,35 @@ output_operand_error_report (char *str, bool non_fatal_only) mismatching operand index. In the case of multiple errors with the equally highest operand index, pick up the first one or the first one with non-NULL error message. */ - if (curr->detail.index > largest_error_pos - || (curr->detail.index == largest_error_pos && msg == NULL - && curr->detail.error != NULL)) + if (!record || curr->detail.index > record->detail.index) + record = curr; + else if (curr->detail.index == record->detail.index + && !record->detail.error) { - largest_error_pos = curr->detail.index; - record = curr; - msg = record->detail.error; + if (curr->detail.error) + record = curr; + else if (kind == AARCH64_OPDE_SYNTAX_ERROR) + { + record->detail.data[0].i |= curr->detail.data[0].i; + record->detail.data[1].i |= curr->detail.data[1].i; + record->detail.data[2].i |= curr->detail.data[2].i; + DEBUG_TRACE ("\t--> %s [%x, %x, %x]", + operand_mismatch_kind_names[kind], + curr->detail.data[0].i, curr->detail.data[1].i, + curr->detail.data[2].i); + } + else if (kind == AARCH64_OPDE_REG_LIST_LENGTH + || kind == AARCH64_OPDE_REG_LIST_STRIDE) + { + record->detail.data[0].i |= curr->detail.data[0].i; + DEBUG_TRACE ("\t--> %s [%x]", + operand_mismatch_kind_names[kind], + curr->detail.data[0].i); + } + /* Pick the variant with the cloest match. */ + else if (kind == AARCH64_OPDE_INVALID_VARIANT + && record->detail.data[0].i > curr->detail.data[0].i) + record = curr; } } @@ -5701,9 +6049,9 @@ output_operand_error_report (char *str, bool non_fatal_only) if (non_fatal_only && !record) return; - gas_assert (largest_error_pos != -2 && record != NULL); + gas_assert (record); DEBUG_TRACE ("Pick up error kind %s to report", - operand_mismatch_kind_names[record->detail.kind]); + operand_mismatch_kind_names[kind]); /* Output. */ output_operand_error_record (record, str); @@ -5796,113 +6144,38 @@ opcode_lookup (char *base, char *dot, char *end) int len; if (dot == end) - return 0; - - inst.cond = COND_ALWAYS; - - /* Handle a possible condition. */ - if (dot) - { - cond = str_hash_find_n (aarch64_cond_hsh, dot + 1, end - dot - 1); - if (!cond) - return 0; - inst.cond = cond->value; - len = dot - base; - } - else - len = end - base; - - if (inst.cond == COND_ALWAYS) - { - /* Look for unaffixed mnemonic. */ - return lookup_mnemonic (base, len); - } - else if (len <= 13) - { - /* append ".c" to mnemonic if conditional */ - memcpy (condname, base, len); - memcpy (condname + len, ".c", 2); - base = condname; - len += 2; - return lookup_mnemonic (base, len); - } - - return NULL; -} - -/* Internal helper routine converting a vector_type_el structure *VECTYPE - to a corresponding operand qualifier. */ - -static inline aarch64_opnd_qualifier_t -vectype_to_qualifier (const struct vector_type_el *vectype) -{ - /* Element size in bytes indexed by vector_el_type. */ - const unsigned char ele_size[5] - = {1, 2, 4, 8, 16}; - const unsigned int ele_base [5] = - { - AARCH64_OPND_QLF_V_4B, - AARCH64_OPND_QLF_V_2H, - AARCH64_OPND_QLF_V_2S, - AARCH64_OPND_QLF_V_1D, - AARCH64_OPND_QLF_V_1Q - }; - - if (!vectype->defined || vectype->type == NT_invtype) - goto vectype_conversion_fail; - - if (vectype->type == NT_zero) - return AARCH64_OPND_QLF_P_Z; - if (vectype->type == NT_merge) - return AARCH64_OPND_QLF_P_M; - - gas_assert (vectype->type >= NT_b && vectype->type <= NT_q); - - if (vectype->defined & (NTA_HASINDEX | NTA_HASVARWIDTH)) - { - /* Special case S_4B. */ - if (vectype->type == NT_b && vectype->width == 4) - return AARCH64_OPND_QLF_S_4B; - - /* Special case S_2H. */ - if (vectype->type == NT_h && vectype->width == 2) - return AARCH64_OPND_QLF_S_2H; + return 0; - /* Vector element register. */ - return AARCH64_OPND_QLF_S_B + vectype->type; + inst.cond = COND_ALWAYS; + + /* Handle a possible condition. */ + if (dot) + { + cond = str_hash_find_n (aarch64_cond_hsh, dot + 1, end - dot - 1); + if (!cond) + return 0; + inst.cond = cond->value; + len = dot - base; } else - { - /* Vector register. */ - int reg_size = ele_size[vectype->type] * vectype->width; - unsigned offset; - unsigned shift; - if (reg_size != 16 && reg_size != 8 && reg_size != 4) - goto vectype_conversion_fail; - - /* The conversion is by calculating the offset from the base operand - qualifier for the vector type. The operand qualifiers are regular - enough that the offset can established by shifting the vector width by - a vector-type dependent amount. */ - shift = 0; - if (vectype->type == NT_b) - shift = 3; - else if (vectype->type == NT_h || vectype->type == NT_s) - shift = 2; - else if (vectype->type >= NT_d) - shift = 1; - else - gas_assert (0); + len = end - base; - offset = ele_base [vectype->type] + (vectype->width >> shift); - gas_assert (AARCH64_OPND_QLF_V_4B <= offset - && offset <= AARCH64_OPND_QLF_V_1Q); - return offset; + if (inst.cond == COND_ALWAYS) + { + /* Look for unaffixed mnemonic. */ + return lookup_mnemonic (base, len); + } + else if (len <= 13) + { + /* append ".c" to mnemonic if conditional */ + memcpy (condname, base, len); + memcpy (condname + len, ".c", 2); + base = condname; + len += 2; + return lookup_mnemonic (base, len); } - vectype_conversion_fail: - first_error (_("bad vector arrangement type")); - return AARCH64_OPND_QLF_NIL; + return NULL; } /* Process an optional operand that is found omitted from the assembly line. @@ -6106,22 +6379,6 @@ process_movw_reloc_info (void) return true; } -/* A primitive log calculator. */ - -static inline unsigned int -get_logsz (unsigned int size) -{ - const unsigned char ls[16] = - {0, 1, -1, 2, -1, -1, -1, 3, -1, -1, -1, -1, -1, -1, -1, 4}; - if (size > 16) - { - gas_assert (0); - return -1; - } - gas_assert (ls[size - 1] != (unsigned char)-1); - return ls[size - 1]; -} - /* Determine and return the real reloc type code for an instruction with the pseudo reloc type code BFD_RELOC_AARCH64_LDST_LO12. */ @@ -6186,7 +6443,7 @@ ldst_lo12_determine_real_reloc_type (void) 1, opd0_qlf, 0); gas_assert (opd1_qlf != AARCH64_OPND_QLF_NIL); - logsz = get_logsz (aarch64_get_qualifier_esize (opd1_qlf)); + logsz = get_log2 (aarch64_get_qualifier_esize (opd1_qlf)); if (inst.reloc.type == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12 || inst.reloc.type == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12_NC @@ -6210,34 +6467,43 @@ ldst_lo12_determine_real_reloc_type (void) return reloc_ldst_lo12[inst.reloc.type - BFD_RELOC_AARCH64_LDST_LO12][logsz]; } -/* Check whether a register list REGINFO is valid. The registers must be - numbered in increasing order (modulo 32), in increments of one or two. +/* Check whether a register list REGINFO is valid. The registers have type + REG_TYPE and must be numbered in increasing order (modulo the register + bank size). They must have a consistent stride. - If ACCEPT_ALTERNATE is non-zero, the register numbers should be in - increments of two. - - Return FALSE if such a register list is invalid, otherwise return TRUE. */ + Return true if the list is valid, describing it in LIST if so. */ static bool -reg_list_valid_p (uint32_t reginfo, int accept_alternate) +reg_list_valid_p (uint32_t reginfo, struct aarch64_reglist *list, + aarch64_reg_type reg_type) { - uint32_t i, nb_regs, prev_regno, incr; + uint32_t i, nb_regs, prev_regno, incr, mask; + mask = reg_type_mask (reg_type); nb_regs = 1 + (reginfo & 0x3); reginfo >>= 2; prev_regno = reginfo & 0x1f; - incr = accept_alternate ? 2 : 1; + incr = 1; + + list->first_regno = prev_regno; + list->num_regs = nb_regs; for (i = 1; i < nb_regs; ++i) { - uint32_t curr_regno; + uint32_t curr_regno, curr_incr; reginfo >>= 5; curr_regno = reginfo & 0x1f; - if (curr_regno != ((prev_regno + incr) & 0x1f)) + curr_incr = (curr_regno - prev_regno) & mask; + if (curr_incr == 0) + return false; + else if (i == 1) + incr = curr_incr; + else if (curr_incr != incr) return false; prev_regno = curr_regno; } + list->stride = incr; return true; } @@ -6257,17 +6523,19 @@ parse_operands (char *str, const aarch64_opcode *opcode) clear_error (); skip_whitespace (str); - if (AARCH64_CPU_HAS_FEATURE (AARCH64_FEATURE_SVE, *opcode->avariant)) - imm_reg_type = REG_TYPE_R_Z_SP_BHSDQ_VZP; + if (AARCH64_CPU_HAS_FEATURE (*opcode->avariant, SME2)) + imm_reg_type = REG_TYPE_R_ZR_SP_BHSDQ_VZP_PN; + else if (AARCH64_CPU_HAS_FEATURE (*opcode->avariant, SVE) + || AARCH64_CPU_HAS_FEATURE (*opcode->avariant, SVE2)) + imm_reg_type = REG_TYPE_R_ZR_SP_BHSDQ_VZP; else - imm_reg_type = REG_TYPE_R_Z_BHSDQ_V; + imm_reg_type = REG_TYPE_R_ZR_BHSDQ_V; for (i = 0; operands[i] != AARCH64_OPND_NIL; i++) { int64_t val; const reg_entry *reg; int comma_skipped_p = 0; - aarch64_reg_type rtype; struct vector_type_el vectype; aarch64_opnd_qualifier_t qualifier, base_qualifier, offset_qualifier; aarch64_opnd_info *info = &inst.base.operands[i]; @@ -6308,7 +6576,7 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_Rt_SYS: case AARCH64_OPND_PAIRREG: case AARCH64_OPND_SVE_Rm: - po_int_reg_or_fail (REG_TYPE_R_Z); + po_int_fp_reg_or_fail (REG_TYPE_R_ZR); /* In LS64 load/store instructions Rt register number must be even and <=22. */ @@ -6331,7 +6599,7 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_Rt_SP: case AARCH64_OPND_SVE_Rn_SP: case AARCH64_OPND_Rm_SP: - po_int_reg_or_fail (REG_TYPE_R_SP); + po_int_fp_reg_or_fail (REG_TYPE_R_SP); break; case AARCH64_OPND_Rm_EXT: @@ -6366,16 +6634,7 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_SVE_Vd: case AARCH64_OPND_SVE_Vm: case AARCH64_OPND_SVE_Vn: - val = aarch64_reg_parse (&str, REG_TYPE_BHSDQ, &rtype, NULL); - if (val == PARSE_FAIL) - { - first_error (_(get_reg_expected_msg (REG_TYPE_BHSDQ))); - goto failure; - } - gas_assert (rtype >= REG_TYPE_FP_B && rtype <= REG_TYPE_FP_Q); - - info->reg.regno = val; - info->qualifier = AARCH64_OPND_QLF_S_B + (rtype - REG_TYPE_FP_B); + po_int_fp_reg_or_fail (REG_TYPE_BHSDQ); break; case AARCH64_OPND_SVE_Pd: @@ -6387,7 +6646,7 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_SVE_Pn: case AARCH64_OPND_SVE_Pt: case AARCH64_OPND_SME_Pm: - reg_type = REG_TYPE_PN; + reg_type = REG_TYPE_P; goto vector_reg; case AARCH64_OPND_SVE_Za_5: @@ -6397,28 +6656,38 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_SVE_Zm_16: case AARCH64_OPND_SVE_Zn: case AARCH64_OPND_SVE_Zt: - reg_type = REG_TYPE_ZN; + case AARCH64_OPND_SME_Zm: + reg_type = REG_TYPE_Z; + goto vector_reg; + + case AARCH64_OPND_SVE_PNd: + case AARCH64_OPND_SVE_PNg4_10: + case AARCH64_OPND_SVE_PNn: + case AARCH64_OPND_SVE_PNt: + case AARCH64_OPND_SME_PNd3: + case AARCH64_OPND_SME_PNg3: + case AARCH64_OPND_SME_PNn: + reg_type = REG_TYPE_PN; goto vector_reg; case AARCH64_OPND_Va: case AARCH64_OPND_Vd: case AARCH64_OPND_Vn: case AARCH64_OPND_Vm: - reg_type = REG_TYPE_VN; + reg_type = REG_TYPE_V; vector_reg: - val = aarch64_reg_parse (&str, reg_type, NULL, &vectype); - if (val == PARSE_FAIL) - { - first_error (_(get_reg_expected_msg (reg_type))); - goto failure; - } + reg = aarch64_reg_parse (&str, reg_type, &vectype); + if (!reg) + goto failure; if (vectype.defined & NTA_HASINDEX) goto failure; - info->reg.regno = val; - if ((reg_type == REG_TYPE_PN || reg_type == REG_TYPE_ZN) + info->reg.regno = reg->number; + if ((reg_type == REG_TYPE_P + || reg_type == REG_TYPE_PN + || reg_type == REG_TYPE_Z) && vectype.type == NT_invtype) - /* Unqualified Pn and Zn registers are allowed in certain + /* Unqualified P and Z registers are allowed in certain contexts. Rely on F_STRICT qualifier checking to catch invalid uses. */ info->qualifier = AARCH64_OPND_QLF_NIL; @@ -6432,19 +6701,16 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_VdD1: case AARCH64_OPND_VnD1: - val = aarch64_reg_parse (&str, REG_TYPE_VN, NULL, &vectype); - if (val == PARSE_FAIL) - { - set_first_syntax_error (_(get_reg_expected_msg (REG_TYPE_VN))); - goto failure; - } + reg = aarch64_reg_parse (&str, REG_TYPE_V, &vectype); + if (!reg) + goto failure; if (vectype.type != NT_d || vectype.index != 1) { set_fatal_syntax_error (_("the top half of a 128-bit FP/SIMD register is expected")); goto failure; } - info->reg.regno = val; + info->reg.regno = reg->number; /* N.B: VdD1 and VnD1 are treated as an fp or advsimd scalar register here; it is correct for the purpose of encoding/decoding since only the register number is explicitly encoded in the related @@ -6454,11 +6720,25 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_SVE_Zm3_INDEX: case AARCH64_OPND_SVE_Zm3_22_INDEX: + case AARCH64_OPND_SVE_Zm3_19_INDEX: case AARCH64_OPND_SVE_Zm3_11_INDEX: case AARCH64_OPND_SVE_Zm4_11_INDEX: case AARCH64_OPND_SVE_Zm4_INDEX: case AARCH64_OPND_SVE_Zn_INDEX: - reg_type = REG_TYPE_ZN; + case AARCH64_OPND_SME_Zm_INDEX1: + case AARCH64_OPND_SME_Zm_INDEX2: + case AARCH64_OPND_SME_Zm_INDEX3_1: + case AARCH64_OPND_SME_Zm_INDEX3_2: + case AARCH64_OPND_SME_Zm_INDEX3_10: + case AARCH64_OPND_SME_Zm_INDEX4_1: + case AARCH64_OPND_SME_Zm_INDEX4_10: + case AARCH64_OPND_SME_Zn_INDEX1_16: + case AARCH64_OPND_SME_Zn_INDEX2_15: + case AARCH64_OPND_SME_Zn_INDEX2_16: + case AARCH64_OPND_SME_Zn_INDEX3_14: + case AARCH64_OPND_SME_Zn_INDEX3_15: + case AARCH64_OPND_SME_Zn_INDEX4_14: + reg_type = REG_TYPE_Z; goto vector_reg_index; case AARCH64_OPND_Ed: @@ -6466,47 +6746,64 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_Em: case AARCH64_OPND_Em16: case AARCH64_OPND_SM3_IMM2: - reg_type = REG_TYPE_VN; + reg_type = REG_TYPE_V; vector_reg_index: - val = aarch64_reg_parse (&str, reg_type, NULL, &vectype); - if (val == PARSE_FAIL) + reg = aarch64_reg_parse (&str, reg_type, &vectype); + if (!reg) + goto failure; + if (!(vectype.defined & NTA_HASINDEX)) + goto failure; + + if (reg->type == REG_TYPE_Z && vectype.type == NT_invtype) + /* Unqualified Zn[index] is allowed in LUTI2 instructions. */ + info->qualifier = AARCH64_OPND_QLF_NIL; + else { - first_error (_(get_reg_expected_msg (reg_type))); - goto failure; + if (vectype.type == NT_invtype) + goto failure; + info->qualifier = vectype_to_qualifier (&vectype); + if (info->qualifier == AARCH64_OPND_QLF_NIL) + goto failure; } - if (vectype.type == NT_invtype || !(vectype.defined & NTA_HASINDEX)) - goto failure; - info->reglane.regno = val; + info->reglane.regno = reg->number; info->reglane.index = vectype.index; - info->qualifier = vectype_to_qualifier (&vectype); - if (info->qualifier == AARCH64_OPND_QLF_NIL) - goto failure; break; case AARCH64_OPND_SVE_ZnxN: case AARCH64_OPND_SVE_ZtxN: - reg_type = REG_TYPE_ZN; + case AARCH64_OPND_SME_Zdnx2: + case AARCH64_OPND_SME_Zdnx4: + case AARCH64_OPND_SME_Zmx2: + case AARCH64_OPND_SME_Zmx4: + case AARCH64_OPND_SME_Znx2: + case AARCH64_OPND_SME_Znx4: + case AARCH64_OPND_SME_Ztx2_STRIDED: + case AARCH64_OPND_SME_Ztx4_STRIDED: + reg_type = REG_TYPE_Z; + goto vector_reg_list; + + case AARCH64_OPND_SME_Pdx2: + case AARCH64_OPND_SME_PdxN: + reg_type = REG_TYPE_P; goto vector_reg_list; case AARCH64_OPND_LVn: case AARCH64_OPND_LVt: case AARCH64_OPND_LVt_AL: case AARCH64_OPND_LEt: - reg_type = REG_TYPE_VN; + reg_type = REG_TYPE_V; vector_reg_list: - if (reg_type == REG_TYPE_ZN + if (reg_type == REG_TYPE_Z && get_opcode_dependent_value (opcode) == 1 && *str != '{') { - val = aarch64_reg_parse (&str, reg_type, NULL, &vectype); - if (val == PARSE_FAIL) - { - first_error (_(get_reg_expected_msg (reg_type))); - goto failure; - } - info->reglist.first_regno = val; + reg = aarch64_reg_parse (&str, reg_type, &vectype); + if (!reg) + goto failure; + info->reglist.first_regno = reg->number; info->reglist.num_regs = 1; + info->reglist.stride = 1; } else { @@ -6514,21 +6811,18 @@ parse_operands (char *str, const aarch64_opcode *opcode) if (val == PARSE_FAIL) goto failure; - if (! reg_list_valid_p (val, /* accept_alternate */ 0)) + if (! reg_list_valid_p (val, &info->reglist, reg_type)) { set_fatal_syntax_error (_("invalid register list")); goto failure; } - if (vectype.width != 0 && *str != ',') + if ((int) vectype.width > 0 && *str != ',') { set_fatal_syntax_error (_("expected element type rather than vector type")); goto failure; } - - info->reglist.first_regno = (val >> 2) & 0x1f; - info->reglist.num_regs = (val & 0x3) + 1; } if (operands[i] == AARCH64_OPND_LEt) { @@ -6543,7 +6837,7 @@ parse_operands (char *str, const aarch64_opcode *opcode) goto failure; if (!(vectype.defined & NTA_HASTYPE)) { - if (reg_type == REG_TYPE_ZN) + if (reg_type == REG_TYPE_Z || reg_type == REG_TYPE_P) set_fatal_syntax_error (_("missing type suffix")); goto failure; } @@ -6596,6 +6890,8 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_SVE_SHLIMM_PRED: case AARCH64_OPND_SVE_SHLIMM_UNPRED: case AARCH64_OPND_SVE_SHLIMM_UNPRED_22: + case AARCH64_OPND_SME_SHRIMM4: + case AARCH64_OPND_SME_SHRIMM5: case AARCH64_OPND_SVE_SHRIMM_PRED: case AARCH64_OPND_SVE_SHRIMM_UNPRED: case AARCH64_OPND_SVE_SHRIMM_UNPRED_22: @@ -6613,6 +6909,8 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_SVE_IMM_ROT1: case AARCH64_OPND_SVE_IMM_ROT2: case AARCH64_OPND_SVE_IMM_ROT3: + case AARCH64_OPND_CSSC_SIMM8: + case AARCH64_OPND_CSSC_UIMM8: po_imm_nc_or_fail (); info->imm.value = val; break; @@ -6701,13 +6999,12 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_IMM_MOV: { char *saved = str; - if (reg_name_p (str, REG_TYPE_R_Z_SP) || - reg_name_p (str, REG_TYPE_VN)) + if (reg_name_p (str, REG_TYPE_R_ZR_SP) + || reg_name_p (str, REG_TYPE_V)) goto failure; str = saved; po_misc_or_fail (aarch64_get_expression (&inst.reloc.exp, &str, - GE_OPT_PREFIX, REJECT_ABSENT, - NORMAL_RESOLUTION)); + GE_OPT_PREFIX, REJECT_ABSENT)); /* The MOV immediate alias will be fixed up by fix_mov_imm_insn later. fix_mov_imm_insn will try to determine a machine instruction (MOVZ, MOVN or ORR) for it and will issue an error @@ -7157,23 +7454,11 @@ parse_operands (char *str, const aarch64_opcode *opcode) break; case AARCH64_OPND_SME_PnT_Wm_imm: - /* .[, #] */ - { - int index_base_reg; - int imm; - val = parse_sme_pred_reg_with_index (&str, - &index_base_reg, - &imm, - &qualifier); - if (val == PARSE_FAIL) - goto failure; - - info->za_tile_vector.regno = val; - info->za_tile_vector.index.regno = index_base_reg; - info->za_tile_vector.index.imm = imm; - info->qualifier = qualifier; - break; - } + if (!parse_dual_indexed_reg (&str, REG_TYPE_P, + &info->indexed_za, &qualifier, 0)) + goto failure; + info->qualifier = qualifier; + break; case AARCH64_OPND_SVE_ADDR_RI_S4x16: case AARCH64_OPND_SVE_ADDR_RI_S4x32: @@ -7476,12 +7761,68 @@ parse_operands (char *str, const aarch64_opcode *opcode) inst.base.operands[i].prfop = aarch64_prfops + val; break; + case AARCH64_OPND_RPRFMOP: + po_enum_or_fail (aarch64_rprfmop_array); + info->imm.value = val; + break; + case AARCH64_OPND_BARRIER_PSB: val = parse_barrier_psb (&str, &(info->hint_option)); if (val == PARSE_FAIL) goto failure; break; + case AARCH64_OPND_SME_ZT0: + po_reg_or_fail (REG_TYPE_ZT0); + break; + + case AARCH64_OPND_SME_ZT0_INDEX: + reg = aarch64_reg_parse (&str, REG_TYPE_ZT0, &vectype); + if (!reg || vectype.type != NT_invtype) + goto failure; + if (!(vectype.defined & NTA_HASINDEX)) + { + set_syntax_error (_("missing register index")); + goto failure; + } + info->imm.value = vectype.index; + break; + + case AARCH64_OPND_SME_ZT0_LIST: + if (*str != '{') + { + set_expected_reglist_error (REG_TYPE_ZT0, parse_reg (&str)); + goto failure; + } + str++; + if (!parse_typed_reg (&str, REG_TYPE_ZT0, &vectype, PTR_IN_REGLIST)) + goto failure; + if (*str != '}') + { + set_syntax_error (_("expected '}' after ZT0")); + goto failure; + } + str++; + break; + + case AARCH64_OPND_SME_PNn3_INDEX1: + case AARCH64_OPND_SME_PNn3_INDEX2: + reg = aarch64_reg_parse (&str, REG_TYPE_PN, &vectype); + if (!reg) + goto failure; + if (!(vectype.defined & NTA_HASINDEX)) + { + set_syntax_error (_("missing register index")); + goto failure; + } + info->reglane.regno = reg->number; + info->reglane.index = vectype.index; + if (vectype.type == NT_invtype) + info->qualifier = AARCH64_OPND_QLF_NIL; + else + info->qualifier = vectype_to_qualifier (&vectype); + break; + case AARCH64_OPND_BTI_TARGET: val = parse_bti_operand (&str, &(info->hint_option)); if (val == PARSE_FAIL) @@ -7490,59 +7831,53 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_SME_ZAda_2b: case AARCH64_OPND_SME_ZAda_3b: - val = parse_sme_zada_operand (&str, &qualifier); - if (val == PARSE_FAIL) + reg = parse_reg_with_qual (&str, REG_TYPE_ZAT, &qualifier, 0); + if (!reg) goto failure; - info->reg.regno = val; + info->reg.regno = reg->number; info->qualifier = qualifier; break; case AARCH64_OPND_SME_ZA_HV_idx_src: + case AARCH64_OPND_SME_ZA_HV_idx_srcxN: case AARCH64_OPND_SME_ZA_HV_idx_dest: + case AARCH64_OPND_SME_ZA_HV_idx_destxN: case AARCH64_OPND_SME_ZA_HV_idx_ldstr: - { - enum sme_hv_slice slice_indicator; - int vector_select_register; - int imm; - - if (operands[i] == AARCH64_OPND_SME_ZA_HV_idx_ldstr) - val = parse_sme_za_hv_tiles_operand_with_braces (&str, - &slice_indicator, - &vector_select_register, - &imm, - &qualifier); - else - val = parse_sme_za_hv_tiles_operand (&str, &slice_indicator, - &vector_select_register, - &imm, - &qualifier); - if (val == PARSE_FAIL) - goto failure; - info->za_tile_vector.regno = val; - info->za_tile_vector.index.regno = vector_select_register; - info->za_tile_vector.index.imm = imm; - info->za_tile_vector.v = slice_indicator; - info->qualifier = qualifier; - break; - } + if (operands[i] == AARCH64_OPND_SME_ZA_HV_idx_ldstr + ? !parse_sme_za_hv_tiles_operand_with_braces (&str, + &info->indexed_za, + &qualifier) + : !parse_dual_indexed_reg (&str, REG_TYPE_ZATHV, + &info->indexed_za, &qualifier, 0)) + goto failure; + info->qualifier = qualifier; + break; - case AARCH64_OPND_SME_list_of_64bit_tiles: - val = parse_sme_list_of_64bit_tiles (&str); - if (val == PARSE_FAIL) - goto failure; - info->imm.value = val; - break; + case AARCH64_OPND_SME_list_of_64bit_tiles: + val = parse_sme_list_of_64bit_tiles (&str); + if (val == PARSE_FAIL) + goto failure; + info->imm.value = val; + break; - case AARCH64_OPND_SME_ZA_array: - { - int imm; - val = parse_sme_za_array (&str, &imm); - if (val == PARSE_FAIL) - goto failure; - info->za_tile_vector.index.regno = val; - info->za_tile_vector.index.imm = imm; - break; - } + case AARCH64_OPND_SME_ZA_array_off1x4: + case AARCH64_OPND_SME_ZA_array_off2x2: + case AARCH64_OPND_SME_ZA_array_off2x4: + case AARCH64_OPND_SME_ZA_array_off3_0: + case AARCH64_OPND_SME_ZA_array_off3_5: + case AARCH64_OPND_SME_ZA_array_off3x2: + case AARCH64_OPND_SME_ZA_array_off4: + if (!parse_dual_indexed_reg (&str, REG_TYPE_ZA, + &info->indexed_za, &qualifier, 0)) + goto failure; + info->qualifier = qualifier; + break; + + case AARCH64_OPND_SME_VLxN_10: + case AARCH64_OPND_SME_VLxN_13: + po_strict_enum_or_fail (aarch64_sme_vlxn_array); + info->imm.value = val; + break; case AARCH64_OPND_MOPS_ADDR_Rd: case AARCH64_OPND_MOPS_ADDR_Rs: @@ -7637,15 +7972,15 @@ parse_operands (char *str, const aarch64_opcode *opcode) if (error_p ()) { + inst.parsing_error.index = i; DEBUG_TRACE ("parsing FAIL: %s - %s", - operand_mismatch_kind_names[get_error_kind ()], - get_error_message ()); + operand_mismatch_kind_names[inst.parsing_error.kind], + inst.parsing_error.error); /* Record the operand error properly; this is useful when there are multiple instruction templates for a mnemonic name, so that later on, we can select the error that most closely describes the problem. */ - record_operand_error (opcode, i, get_error_kind (), - get_error_message ()); + record_operand_error_info (opcode, &inst.parsing_error); return false; } else @@ -8026,8 +8361,7 @@ md_assemble (char *str) && do_encode (inst_base->opcode, &inst.base, &inst_base->value)) { /* Check that this instruction is supported for this CPU. */ - if (!opcode->avariant - || !AARCH64_CPU_HAS_ALL_FEATURES (cpu_variant, *opcode->avariant)) + if (!aarch64_cpu_supports_inst_p (cpu_variant, inst_base)) { as_bad (_("selected processor does not support `%s'"), str); return; @@ -8156,8 +8490,8 @@ static const reg_entry reg_names[] = { REGDEF (wsp, 31, SP_32), REGDEF (WSP, 31, SP_32), REGDEF (sp, 31, SP_64), REGDEF (SP, 31, SP_64), - REGDEF (wzr, 31, Z_32), REGDEF (WZR, 31, Z_32), - REGDEF (xzr, 31, Z_64), REGDEF (XZR, 31, Z_64), + REGDEF (wzr, 31, ZR_32), REGDEF (WZR, 31, ZR_32), + REGDEF (xzr, 31, ZR_64), REGDEF (XZR, 31, ZR_64), /* Floating-point single precision registers. */ REGSET (s, FP_S), REGSET (S, FP_S), @@ -8175,22 +8509,32 @@ static const reg_entry reg_names[] = { REGSET (q, FP_Q), REGSET (Q, FP_Q), /* FP/SIMD registers. */ - REGSET (v, VN), REGSET (V, VN), + REGSET (v, V), REGSET (V, V), /* SVE vector registers. */ - REGSET (z, ZN), REGSET (Z, ZN), + REGSET (z, Z), REGSET (Z, Z), + + /* SVE predicate(-as-mask) registers. */ + REGSET16 (p, P), REGSET16 (P, P), - /* SVE predicate registers. */ - REGSET16 (p, PN), REGSET16 (P, PN), + /* SVE predicate-as-counter registers. */ + REGSET16 (pn, PN), REGSET16 (PN, PN), + + /* SME ZA. We model this as a register because it acts syntactically + like ZA0H, supporting qualifier suffixes and indexing. */ + REGDEF (za, 0, ZA), REGDEF (ZA, 0, ZA), /* SME ZA tile registers. */ - REGSET16 (za, ZA), REGSET16 (ZA, ZA), + REGSET16 (za, ZAT), REGSET16 (ZA, ZAT), /* SME ZA tile registers (horizontal slice). */ - REGSET16S (za, h, ZAH), REGSET16S (ZA, H, ZAH), + REGSET16S (za, h, ZATH), REGSET16S (ZA, H, ZATH), /* SME ZA tile registers (vertical slice). */ - REGSET16S (za, v, ZAV), REGSET16S (ZA, V, ZAV) + REGSET16S (za, v, ZATV), REGSET16S (ZA, V, ZATV), + + /* SME2 ZT0. */ + REGDEF (zt0, 0, ZT0), REGDEF (ZT0, 0, ZT0) }; #undef REGDEF @@ -8321,7 +8665,7 @@ aarch64_handle_align (fragS * fragP) fix = bytes & (noop_size - 1); if (fix) { -#ifdef OBJ_ELF +#if defined OBJ_ELF || defined OBJ_COFF insert_data_mapping_symbol (MAP_INSN, fragP->fr_fix, fragP, fix); #endif memset (p, 0, fix); @@ -8379,6 +8723,51 @@ aarch64_init_frag (fragS * fragP, int max_chars) break; } } + +/* Whether SFrame stack trace info is supported. */ + +bool +aarch64_support_sframe_p (void) +{ + /* At this time, SFrame is supported for aarch64 only. */ + return (aarch64_abi == AARCH64_ABI_LP64); +} + +/* Specify if RA tracking is needed. */ + +bool +aarch64_sframe_ra_tracking_p (void) +{ + return true; +} + +/* Specify the fixed offset to recover RA from CFA. + (useful only when RA tracking is not needed). */ + +offsetT +aarch64_sframe_cfa_ra_offset (void) +{ + return (offsetT) SFRAME_CFA_FIXED_RA_INVALID; +} + +/* Get the abi/arch indentifier for SFrame. */ + +unsigned char +aarch64_sframe_get_abi_arch (void) +{ + unsigned char sframe_abi_arch = 0; + + if (aarch64_support_sframe_p ()) + { + sframe_abi_arch = target_big_endian + ? SFRAME_ABI_AARCH64_ENDIAN_BIG + : SFRAME_ABI_AARCH64_ENDIAN_LITTLE; + } + + return sframe_abi_arch; +} + +#endif /* OBJ_ELF */ /* Initialize the DWARF-2 unwind information for this procedure. */ @@ -8387,7 +8776,6 @@ tc_aarch64_frame_initial_instructions (void) { cfi_add_CFA_def_cfa (REG_SP, 0); } -#endif /* OBJ_ELF */ /* Convert REGNAME to a DWARF-2 register number. */ @@ -8424,10 +8812,10 @@ tc_aarch64_regname_to_dw2regnum (char *regname) int aarch64_dwarf2_addr_size (void) { -#if defined (OBJ_MAYBE_ELF) || defined (OBJ_ELF) if (ilp32_p) return 4; -#endif + else if (llp64_p) + return 8; return bfd_arch_bits_per_address (stdoutput) / 8; } @@ -8834,7 +9222,8 @@ md_apply_fix (fixS * fixP, valueT * valP, segT seg) /* Note whether this will delete the relocation. */ - if (fixP->fx_addsy == 0 && !fixP->fx_pcrel) + if (fixP->fx_addsy == 0 && !fixP->fx_pcrel + && aarch64_force_reloc (fixP->fx_r_type) <= 0) fixP->fx_done = 1; /* Process the relocations. */ @@ -9183,6 +9572,11 @@ md_apply_fix (fixS * fixP, valueT * valP, segT seg) /* An error will already have been reported. */ break; + case BFD_RELOC_RVA: + case BFD_RELOC_32_SECREL: + case BFD_RELOC_16_SECIDX: + break; + default: as_bad_where (fixP->fx_file, fixP->fx_line, _("unexpected %s fixup"), @@ -9266,33 +9660,48 @@ cons_fix_new_aarch64 (fragS * frag, int where, int size, expressionS * exp) bfd_reloc_code_real_type type; int pcrel = 0; - /* Pick a reloc. - FIXME: @@ Should look at CPU word size. */ - switch (size) +#ifdef TE_PE + if (exp->X_op == O_secrel) { - case 1: - type = BFD_RELOC_8; - break; - case 2: - type = BFD_RELOC_16; - break; - case 4: - type = BFD_RELOC_32; - break; - case 8: - type = BFD_RELOC_64; - break; - default: - as_bad (_("cannot do %u-byte relocation"), size); - type = BFD_RELOC_UNUSED; - break; + exp->X_op = O_symbol; + type = BFD_RELOC_32_SECREL; + } + else if (exp->X_op == O_secidx) + { + exp->X_op = O_symbol; + type = BFD_RELOC_16_SECIDX; + } + else + { +#endif + /* Pick a reloc. + FIXME: @@ Should look at CPU word size. */ + switch (size) + { + case 1: + type = BFD_RELOC_8; + break; + case 2: + type = BFD_RELOC_16; + break; + case 4: + type = BFD_RELOC_32; + break; + case 8: + type = BFD_RELOC_64; + break; + default: + as_bad (_("cannot do %u-byte relocation"), size); + type = BFD_RELOC_UNUSED; + break; + } +#ifdef TE_PE } +#endif fix_new_exp (frag, where, (int) size, exp, pcrel, type); } -#ifdef OBJ_ELF - /* Implement md_after_parse_args. This is the earliest time we need to decide ABI. If no -mabi specified, the ABI will be decided by target triplet. */ @@ -9302,13 +9711,18 @@ aarch64_after_parse_args (void) if (aarch64_abi != AARCH64_ABI_NONE) return; +#ifdef OBJ_ELF /* DEFAULT_ARCH will have ":32" extension if it's configured for ILP32. */ if (strlen (default_arch) > 7 && strcmp (default_arch + 7, ":32") == 0) aarch64_abi = AARCH64_ABI_ILP32; else aarch64_abi = AARCH64_ABI_LP64; +#else + aarch64_abi = AARCH64_ABI_LLP64; +#endif } +#ifdef OBJ_ELF const char * elf64_aarch64_target_format (void) { @@ -9331,6 +9745,12 @@ aarch64elf_frob_symbol (symbolS * symp, int *puntp) { elf_frob_symbol (symp, puntp); } +#elif defined OBJ_COFF +const char * +coff_aarch64_target_format (void) +{ + return "pe-aarch64-little"; +} #endif /* MD interface: Finalization. */ @@ -9646,9 +10066,20 @@ md_begin (void) cpu_variant = *mcpu_cpu_opt; /* Record the CPU type. */ - mach = ilp32_p ? bfd_mach_aarch64_ilp32 : bfd_mach_aarch64; + if(ilp32_p) + mach = bfd_mach_aarch64_ilp32; + else if (llp64_p) + mach = bfd_mach_aarch64_llp64; + else + mach = bfd_mach_aarch64; bfd_set_arch_mach (stdoutput, TARGET_ARCH, mach); +#ifdef OBJ_ELF + /* FIXME - is there a better way to do it ? */ + aarch64_sframe_cfa_sp_reg = 31; + aarch64_sframe_cfa_fp_reg = 29; /* x29. */ + aarch64_sframe_cfa_ra_reg = 30; +#endif } /* Command line processing. */ @@ -9713,161 +10144,80 @@ struct aarch64_cpu_option_table /* This list should, at a minimum, contain all the cpu names recognized by GCC. */ static const struct aarch64_cpu_option_table aarch64_cpus[] = { - {"all", AARCH64_ANY, NULL}, - {"cortex-a34", AARCH64_FEATURE (AARCH64_ARCH_V8, - AARCH64_FEATURE_CRC), "Cortex-A34"}, - {"cortex-a35", AARCH64_FEATURE (AARCH64_ARCH_V8, - AARCH64_FEATURE_CRC), "Cortex-A35"}, - {"cortex-a53", AARCH64_FEATURE (AARCH64_ARCH_V8, - AARCH64_FEATURE_CRC), "Cortex-A53"}, - {"cortex-a57", AARCH64_FEATURE (AARCH64_ARCH_V8, - AARCH64_FEATURE_CRC), "Cortex-A57"}, - {"cortex-a72", AARCH64_FEATURE (AARCH64_ARCH_V8, - AARCH64_FEATURE_CRC), "Cortex-A72"}, - {"cortex-a73", AARCH64_FEATURE (AARCH64_ARCH_V8, - AARCH64_FEATURE_CRC), "Cortex-A73"}, - {"cortex-a55", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_RCPC | AARCH64_FEATURE_F16 | AARCH64_FEATURE_DOTPROD), + {"all", AARCH64_ALL_FEATURES, NULL}, + {"cortex-a34", AARCH64_CPU_FEATURES (V8A, 1, CRC), "Cortex-A34"}, + {"cortex-a35", AARCH64_CPU_FEATURES (V8A, 1, CRC), "Cortex-A35"}, + {"cortex-a53", AARCH64_CPU_FEATURES (V8A, 1, CRC), "Cortex-A53"}, + {"cortex-a57", AARCH64_CPU_FEATURES (V8A, 1, CRC), "Cortex-A57"}, + {"cortex-a72", AARCH64_CPU_FEATURES (V8A, 1, CRC), "Cortex-A72"}, + {"cortex-a73", AARCH64_CPU_FEATURES (V8A, 1, CRC), "Cortex-A73"}, + {"cortex-a55", AARCH64_CPU_FEATURES (V8_2A, 3, RCPC, F16, DOTPROD), "Cortex-A55"}, - {"cortex-a75", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_RCPC | AARCH64_FEATURE_F16 | AARCH64_FEATURE_DOTPROD), + {"cortex-a75", AARCH64_CPU_FEATURES (V8_2A, 3, RCPC, F16, DOTPROD), "Cortex-A75"}, - {"cortex-a76", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_RCPC | AARCH64_FEATURE_F16 | AARCH64_FEATURE_DOTPROD), + {"cortex-a76", AARCH64_CPU_FEATURES (V8_2A, 3, RCPC, F16, DOTPROD), "Cortex-A76"}, - {"cortex-a76ae", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_F16 | AARCH64_FEATURE_RCPC - | AARCH64_FEATURE_DOTPROD - | AARCH64_FEATURE_SSBS), - "Cortex-A76AE"}, - {"cortex-a77", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_F16 | AARCH64_FEATURE_RCPC - | AARCH64_FEATURE_DOTPROD - | AARCH64_FEATURE_SSBS), - "Cortex-A77"}, - {"cortex-a65", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_F16 | AARCH64_FEATURE_RCPC - | AARCH64_FEATURE_DOTPROD - | AARCH64_FEATURE_SSBS), - "Cortex-A65"}, - {"cortex-a65ae", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_F16 | AARCH64_FEATURE_RCPC - | AARCH64_FEATURE_DOTPROD - | AARCH64_FEATURE_SSBS), - "Cortex-A65AE"}, - {"cortex-a78", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_F16 - | AARCH64_FEATURE_RCPC - | AARCH64_FEATURE_DOTPROD - | AARCH64_FEATURE_SSBS - | AARCH64_FEATURE_PROFILE), - "Cortex-A78"}, - {"cortex-a78ae", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_F16 - | AARCH64_FEATURE_RCPC - | AARCH64_FEATURE_DOTPROD - | AARCH64_FEATURE_SSBS - | AARCH64_FEATURE_PROFILE), - "Cortex-A78AE"}, - {"cortex-a78c", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_DOTPROD - | AARCH64_FEATURE_F16 - | AARCH64_FEATURE_FLAGM - | AARCH64_FEATURE_PAC - | AARCH64_FEATURE_PROFILE - | AARCH64_FEATURE_RCPC - | AARCH64_FEATURE_SSBS), - "Cortex-A78C"}, - {"cortex-a510", AARCH64_FEATURE (AARCH64_ARCH_V9, - AARCH64_FEATURE_BFLOAT16 - | AARCH64_FEATURE_I8MM - | AARCH64_FEATURE_MEMTAG - | AARCH64_FEATURE_SVE2_BITPERM), - "Cortex-A510"}, - {"cortex-a710", AARCH64_FEATURE (AARCH64_ARCH_V9, - AARCH64_FEATURE_BFLOAT16 - | AARCH64_FEATURE_I8MM - | AARCH64_FEATURE_MEMTAG - | AARCH64_FEATURE_SVE2_BITPERM), - "Cortex-A710"}, - {"ares", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_RCPC | AARCH64_FEATURE_F16 - | AARCH64_FEATURE_DOTPROD - | AARCH64_FEATURE_PROFILE), - "Ares"}, - {"exynos-m1", AARCH64_FEATURE (AARCH64_ARCH_V8, - AARCH64_FEATURE_CRC | AARCH64_FEATURE_CRYPTO), - "Samsung Exynos M1"}, - {"falkor", AARCH64_FEATURE (AARCH64_ARCH_V8, - AARCH64_FEATURE_CRC | AARCH64_FEATURE_CRYPTO - | AARCH64_FEATURE_RDMA), - "Qualcomm Falkor"}, - {"neoverse-e1", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_RCPC | AARCH64_FEATURE_F16 - | AARCH64_FEATURE_DOTPROD - | AARCH64_FEATURE_SSBS), - "Neoverse E1"}, - {"neoverse-n1", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_RCPC | AARCH64_FEATURE_F16 - | AARCH64_FEATURE_DOTPROD - | AARCH64_FEATURE_PROFILE), - "Neoverse N1"}, - {"neoverse-n2", AARCH64_FEATURE (AARCH64_ARCH_V8_5, - AARCH64_FEATURE_BFLOAT16 - | AARCH64_FEATURE_I8MM - | AARCH64_FEATURE_F16 - | AARCH64_FEATURE_SVE - | AARCH64_FEATURE_SVE2 - | AARCH64_FEATURE_SVE2_BITPERM - | AARCH64_FEATURE_MEMTAG - | AARCH64_FEATURE_RNG), - "Neoverse N2"}, - {"neoverse-v1", AARCH64_FEATURE (AARCH64_ARCH_V8_4, - AARCH64_FEATURE_PROFILE - | AARCH64_FEATURE_CVADP - | AARCH64_FEATURE_SVE - | AARCH64_FEATURE_SSBS - | AARCH64_FEATURE_RNG - | AARCH64_FEATURE_F16 - | AARCH64_FEATURE_BFLOAT16 - | AARCH64_FEATURE_I8MM), "Neoverse V1"}, - {"qdf24xx", AARCH64_FEATURE (AARCH64_ARCH_V8, - AARCH64_FEATURE_CRC | AARCH64_FEATURE_CRYPTO - | AARCH64_FEATURE_RDMA), - "Qualcomm QDF24XX"}, - {"saphira", AARCH64_FEATURE (AARCH64_ARCH_V8_4, - AARCH64_FEATURE_CRYPTO | AARCH64_FEATURE_PROFILE), - "Qualcomm Saphira"}, - {"thunderx", AARCH64_FEATURE (AARCH64_ARCH_V8, - AARCH64_FEATURE_CRC | AARCH64_FEATURE_CRYPTO), - "Cavium ThunderX"}, - {"vulcan", AARCH64_FEATURE (AARCH64_ARCH_V8_1, - AARCH64_FEATURE_CRYPTO), - "Broadcom Vulcan"}, + {"cortex-a76ae", AARCH64_CPU_FEATURES (V8_2A, 4, F16, RCPC, DOTPROD, + SSBS), "Cortex-A76AE"}, + {"cortex-a77", AARCH64_CPU_FEATURES (V8_2A, 4, F16, RCPC, DOTPROD, + SSBS), "Cortex-A77"}, + {"cortex-a65", AARCH64_CPU_FEATURES (V8_2A, 4, F16, RCPC, DOTPROD, + SSBS), "Cortex-A65"}, + {"cortex-a65ae", AARCH64_CPU_FEATURES (V8_2A, 4, F16, RCPC, DOTPROD, + SSBS), "Cortex-A65AE"}, + {"cortex-a78", AARCH64_CPU_FEATURES (V8_2A, 5, F16, RCPC, DOTPROD, + SSBS, PROFILE), "Cortex-A78"}, + {"cortex-a78ae", AARCH64_CPU_FEATURES (V8_2A, 5, F16, RCPC, DOTPROD, + SSBS, PROFILE), "Cortex-A78AE"}, + {"cortex-a78c", AARCH64_CPU_FEATURES (V8_2A, 7, DOTPROD, F16, FLAGM, + PAC, PROFILE, RCPC, SSBS), + "Cortex-A78C"}, + {"cortex-a510", AARCH64_CPU_FEATURES (V9A, 4, BFLOAT16, I8MM, MEMTAG, + SVE2_BITPERM), "Cortex-A510"}, + {"cortex-a520", AARCH64_CPU_FEATURES (V9_2A, 2, MEMTAG, SVE2_BITPERM), + "Cortex-A520"}, + {"cortex-a710", AARCH64_CPU_FEATURES (V9A, 4, BFLOAT16, I8MM, MEMTAG, + SVE2_BITPERM), "Cortex-A710"}, + {"cortex-a720", AARCH64_CPU_FEATURES (V9_2A, 3, MEMTAG, PROFILE, + SVE2_BITPERM), "Cortex-A720"}, + {"ares", AARCH64_CPU_FEATURES (V8_2A, 4, RCPC, F16, DOTPROD, + PROFILE), "Ares"}, + {"exynos-m1", AARCH64_CPU_FEATURES (V8A, 3, CRC, SHA2, AES), + "Samsung Exynos M1"}, + {"falkor", AARCH64_CPU_FEATURES (V8A, 4, CRC, SHA2, AES, RDMA), + "Qualcomm Falkor"}, + {"neoverse-e1", AARCH64_CPU_FEATURES (V8_2A, 4, RCPC, F16, DOTPROD, + SSBS), "Neoverse E1"}, + {"neoverse-n1", AARCH64_CPU_FEATURES (V8_2A, 4, RCPC, F16, DOTPROD, + PROFILE), "Neoverse N1"}, + {"neoverse-n2", AARCH64_CPU_FEATURES (V8_5A, 8, BFLOAT16, I8MM, F16, + SVE, SVE2, SVE2_BITPERM, MEMTAG, + RNG), "Neoverse N2"}, + {"neoverse-v1", AARCH64_CPU_FEATURES (V8_4A, 8, PROFILE, CVADP, SVE, + SSBS, RNG, F16, BFLOAT16, I8MM), + "Neoverse V1"}, + {"qdf24xx", AARCH64_CPU_FEATURES (V8A, 4, CRC, SHA2, AES, RDMA), + "Qualcomm QDF24XX"}, + {"saphira", AARCH64_CPU_FEATURES (V8_4A, 3, SHA2, AES, PROFILE), + "Qualcomm Saphira"}, + {"thunderx", AARCH64_CPU_FEATURES (V8A, 3, CRC, SHA2, AES), + "Cavium ThunderX"}, + {"vulcan", AARCH64_CPU_FEATURES (V8_1A, 2, SHA2, AES), + "Broadcom Vulcan"}, /* The 'xgene-1' name is an older name for 'xgene1', which was used in earlier releases and is superseded by 'xgene1' in all tools. */ - {"xgene-1", AARCH64_ARCH_V8, "APM X-Gene 1"}, - {"xgene1", AARCH64_ARCH_V8, "APM X-Gene 1"}, - {"xgene2", AARCH64_FEATURE (AARCH64_ARCH_V8, - AARCH64_FEATURE_CRC), "APM X-Gene 2"}, - {"cortex-r82", AARCH64_ARCH_V8_R, "Cortex-R82"}, - {"cortex-x1", AARCH64_FEATURE (AARCH64_ARCH_V8_2, - AARCH64_FEATURE_F16 - | AARCH64_FEATURE_RCPC - | AARCH64_FEATURE_DOTPROD - | AARCH64_FEATURE_SSBS - | AARCH64_FEATURE_PROFILE), - "Cortex-X1"}, - {"cortex-x2", AARCH64_FEATURE (AARCH64_ARCH_V9, - AARCH64_FEATURE_BFLOAT16 - | AARCH64_FEATURE_I8MM - | AARCH64_FEATURE_MEMTAG - | AARCH64_FEATURE_SVE2_BITPERM), - "Cortex-X2"}, - {"generic", AARCH64_ARCH_V8, NULL}, - - {NULL, AARCH64_ARCH_NONE, NULL} + {"xgene-1", AARCH64_ARCH_FEATURES (V8A), "APM X-Gene 1"}, + {"xgene1", AARCH64_ARCH_FEATURES (V8A), "APM X-Gene 1"}, + {"xgene2", AARCH64_CPU_FEATURES (V8A, 1, CRC), "APM X-Gene 2"}, + {"cortex-r82", AARCH64_ARCH_FEATURES (V8R), "Cortex-R82"}, + {"cortex-x1", AARCH64_CPU_FEATURES (V8_2A, 5, F16, RCPC, DOTPROD, + SSBS, PROFILE), "Cortex-X1"}, + {"cortex-x2", AARCH64_CPU_FEATURES (V9A, 4, BFLOAT16, I8MM, MEMTAG, + SVE2_BITPERM), "Cortex-X2"}, + {"generic", AARCH64_ARCH_FEATURES (V8A), NULL}, + + {NULL, AARCH64_NO_FEATURES, NULL} }; struct aarch64_arch_option_table @@ -9879,22 +10229,22 @@ struct aarch64_arch_option_table /* This list should, at a minimum, contain all the architecture names recognized by GCC. */ static const struct aarch64_arch_option_table aarch64_archs[] = { - {"all", AARCH64_ANY}, - {"armv8-a", AARCH64_ARCH_V8}, - {"armv8.1-a", AARCH64_ARCH_V8_1}, - {"armv8.2-a", AARCH64_ARCH_V8_2}, - {"armv8.3-a", AARCH64_ARCH_V8_3}, - {"armv8.4-a", AARCH64_ARCH_V8_4}, - {"armv8.5-a", AARCH64_ARCH_V8_5}, - {"armv8.6-a", AARCH64_ARCH_V8_6}, - {"armv8.7-a", AARCH64_ARCH_V8_7}, - {"armv8.8-a", AARCH64_ARCH_V8_8}, - {"armv8-r", AARCH64_ARCH_V8_R}, - {"armv9-a", AARCH64_ARCH_V9}, - {"armv9.1-a", AARCH64_ARCH_V9_1}, - {"armv9.2-a", AARCH64_ARCH_V9_2}, - {"armv9.3-a", AARCH64_ARCH_V9_3}, - {NULL, AARCH64_ARCH_NONE} + {"all", AARCH64_ALL_FEATURES}, + {"armv8-a", AARCH64_ARCH_FEATURES (V8A)}, + {"armv8.1-a", AARCH64_ARCH_FEATURES (V8_1A)}, + {"armv8.2-a", AARCH64_ARCH_FEATURES (V8_2A)}, + {"armv8.3-a", AARCH64_ARCH_FEATURES (V8_3A)}, + {"armv8.4-a", AARCH64_ARCH_FEATURES (V8_4A)}, + {"armv8.5-a", AARCH64_ARCH_FEATURES (V8_5A)}, + {"armv8.6-a", AARCH64_ARCH_FEATURES (V8_6A)}, + {"armv8.7-a", AARCH64_ARCH_FEATURES (V8_7A)}, + {"armv8.8-a", AARCH64_ARCH_FEATURES (V8_8A)}, + {"armv8-r", AARCH64_ARCH_FEATURES (V8R)}, + {"armv9-a", AARCH64_ARCH_FEATURES (V9A)}, + {"armv9.1-a", AARCH64_ARCH_FEATURES (V9_1A)}, + {"armv9.2-a", AARCH64_ARCH_FEATURES (V9_2A)}, + {"armv9.3-a", AARCH64_ARCH_FEATURES (V9_3A)}, + {NULL, AARCH64_NO_FEATURES} }; /* ISA extensions. */ @@ -9906,105 +10256,61 @@ struct aarch64_option_cpu_value_table }; static const struct aarch64_option_cpu_value_table aarch64_features[] = { - {"crc", AARCH64_FEATURE (AARCH64_FEATURE_CRC, 0), - AARCH64_ARCH_NONE}, - {"crypto", AARCH64_FEATURE (AARCH64_FEATURE_CRYPTO, 0), - AARCH64_FEATURE (AARCH64_FEATURE_SIMD, 0)}, - {"fp", AARCH64_FEATURE (AARCH64_FEATURE_FP, 0), - AARCH64_ARCH_NONE}, - {"lse", AARCH64_FEATURE (AARCH64_FEATURE_LSE, 0), - AARCH64_ARCH_NONE}, - {"simd", AARCH64_FEATURE (AARCH64_FEATURE_SIMD, 0), - AARCH64_FEATURE (AARCH64_FEATURE_FP, 0)}, - {"pan", AARCH64_FEATURE (AARCH64_FEATURE_PAN, 0), - AARCH64_ARCH_NONE}, - {"lor", AARCH64_FEATURE (AARCH64_FEATURE_LOR, 0), - AARCH64_ARCH_NONE}, - {"ras", AARCH64_FEATURE (AARCH64_FEATURE_RAS, 0), - AARCH64_ARCH_NONE}, - {"rdma", AARCH64_FEATURE (AARCH64_FEATURE_RDMA, 0), - AARCH64_FEATURE (AARCH64_FEATURE_SIMD, 0)}, - {"fp16", AARCH64_FEATURE (AARCH64_FEATURE_F16, 0), - AARCH64_FEATURE (AARCH64_FEATURE_FP, 0)}, - {"fp16fml", AARCH64_FEATURE (AARCH64_FEATURE_F16_FML, 0), - AARCH64_FEATURE (AARCH64_FEATURE_FP - | AARCH64_FEATURE_F16, 0)}, - {"profile", AARCH64_FEATURE (AARCH64_FEATURE_PROFILE, 0), - AARCH64_ARCH_NONE}, - {"sve", AARCH64_FEATURE (AARCH64_FEATURE_SVE, 0), - AARCH64_FEATURE (AARCH64_FEATURE_F16 - | AARCH64_FEATURE_SIMD - | AARCH64_FEATURE_COMPNUM, 0)}, - {"tme", AARCH64_FEATURE (AARCH64_FEATURE_TME, 0), - AARCH64_ARCH_NONE}, - {"compnum", AARCH64_FEATURE (AARCH64_FEATURE_COMPNUM, 0), - AARCH64_FEATURE (AARCH64_FEATURE_F16 - | AARCH64_FEATURE_SIMD, 0)}, - {"rcpc", AARCH64_FEATURE (AARCH64_FEATURE_RCPC, 0), - AARCH64_ARCH_NONE}, - {"dotprod", AARCH64_FEATURE (AARCH64_FEATURE_DOTPROD, 0), - AARCH64_ARCH_NONE}, - {"sha2", AARCH64_FEATURE (AARCH64_FEATURE_SHA2, 0), - AARCH64_ARCH_NONE}, - {"sb", AARCH64_FEATURE (AARCH64_FEATURE_SB, 0), - AARCH64_ARCH_NONE}, - {"predres", AARCH64_FEATURE (AARCH64_FEATURE_PREDRES, 0), - AARCH64_ARCH_NONE}, - {"aes", AARCH64_FEATURE (AARCH64_FEATURE_AES, 0), - AARCH64_ARCH_NONE}, - {"sm4", AARCH64_FEATURE (AARCH64_FEATURE_SM4, 0), - AARCH64_ARCH_NONE}, - {"sha3", AARCH64_FEATURE (AARCH64_FEATURE_SHA3, 0), - AARCH64_FEATURE (AARCH64_FEATURE_SHA2, 0)}, - {"rng", AARCH64_FEATURE (AARCH64_FEATURE_RNG, 0), - AARCH64_ARCH_NONE}, - {"ssbs", AARCH64_FEATURE (AARCH64_FEATURE_SSBS, 0), - AARCH64_ARCH_NONE}, - {"memtag", AARCH64_FEATURE (AARCH64_FEATURE_MEMTAG, 0), - AARCH64_ARCH_NONE}, - {"sve2", AARCH64_FEATURE (AARCH64_FEATURE_SVE2, 0), - AARCH64_FEATURE (AARCH64_FEATURE_SVE, 0)}, - {"sve2-sm4", AARCH64_FEATURE (AARCH64_FEATURE_SVE2_SM4, 0), - AARCH64_FEATURE (AARCH64_FEATURE_SVE2 - | AARCH64_FEATURE_SM4, 0)}, - {"sve2-aes", AARCH64_FEATURE (AARCH64_FEATURE_SVE2_AES, 0), - AARCH64_FEATURE (AARCH64_FEATURE_SVE2 - | AARCH64_FEATURE_AES, 0)}, - {"sve2-sha3", AARCH64_FEATURE (AARCH64_FEATURE_SVE2_SHA3, 0), - AARCH64_FEATURE (AARCH64_FEATURE_SVE2 - | AARCH64_FEATURE_SHA3, 0)}, - {"sve2-bitperm", AARCH64_FEATURE (AARCH64_FEATURE_SVE2_BITPERM, 0), - AARCH64_FEATURE (AARCH64_FEATURE_SVE2, 0)}, - {"sme", AARCH64_FEATURE (AARCH64_FEATURE_SME, 0), - AARCH64_FEATURE (AARCH64_FEATURE_SVE2 - | AARCH64_FEATURE_BFLOAT16, 0)}, - {"sme-f64", AARCH64_FEATURE (AARCH64_FEATURE_SME_F64, 0), - AARCH64_FEATURE (AARCH64_FEATURE_SME - | AARCH64_FEATURE_SVE2 - | AARCH64_FEATURE_BFLOAT16, 0)}, - {"sme-i64", AARCH64_FEATURE (AARCH64_FEATURE_SME_I64, 0), - AARCH64_FEATURE (AARCH64_FEATURE_SME - | AARCH64_FEATURE_SVE2 - | AARCH64_FEATURE_BFLOAT16, 0)}, - {"bf16", AARCH64_FEATURE (AARCH64_FEATURE_BFLOAT16, 0), - AARCH64_ARCH_NONE}, - {"i8mm", AARCH64_FEATURE (AARCH64_FEATURE_I8MM, 0), - AARCH64_ARCH_NONE}, - {"f32mm", AARCH64_FEATURE (AARCH64_FEATURE_F32MM, 0), - AARCH64_FEATURE (AARCH64_FEATURE_SVE, 0)}, - {"f64mm", AARCH64_FEATURE (AARCH64_FEATURE_F64MM, 0), - AARCH64_FEATURE (AARCH64_FEATURE_SVE, 0)}, - {"ls64", AARCH64_FEATURE (AARCH64_FEATURE_LS64, 0), - AARCH64_ARCH_NONE}, - {"flagm", AARCH64_FEATURE (AARCH64_FEATURE_FLAGM, 0), - AARCH64_ARCH_NONE}, - {"pauth", AARCH64_FEATURE (AARCH64_FEATURE_PAC, 0), - AARCH64_ARCH_NONE}, - {"mops", AARCH64_FEATURE (AARCH64_FEATURE_MOPS, 0), - AARCH64_ARCH_NONE}, - {"hbc", AARCH64_FEATURE (AARCH64_FEATURE_HBC, 0), - AARCH64_ARCH_NONE}, - {NULL, AARCH64_ARCH_NONE, AARCH64_ARCH_NONE}, + {"crc", AARCH64_FEATURE (CRC), AARCH64_NO_FEATURES}, + {"crypto", AARCH64_FEATURES (2, AES, SHA2), + AARCH64_FEATURE (SIMD)}, + {"fp", AARCH64_FEATURE (FP), AARCH64_NO_FEATURES}, + {"lse", AARCH64_FEATURE (LSE), AARCH64_NO_FEATURES}, + {"simd", AARCH64_FEATURE (SIMD), AARCH64_FEATURE (FP)}, + {"pan", AARCH64_FEATURE (PAN), AARCH64_NO_FEATURES}, + {"lor", AARCH64_FEATURE (LOR), AARCH64_NO_FEATURES}, + {"ras", AARCH64_FEATURE (RAS), AARCH64_NO_FEATURES}, + {"rdma", AARCH64_FEATURE (RDMA), AARCH64_FEATURE (SIMD)}, + {"fp16", AARCH64_FEATURE (F16), AARCH64_FEATURE (FP)}, + {"fp16fml", AARCH64_FEATURE (F16_FML), AARCH64_FEATURE (F16)}, + {"profile", AARCH64_FEATURE (PROFILE), AARCH64_NO_FEATURES}, + {"sve", AARCH64_FEATURE (SVE), AARCH64_FEATURE (COMPNUM)}, + {"tme", AARCH64_FEATURE (TME), AARCH64_NO_FEATURES}, + {"compnum", AARCH64_FEATURE (COMPNUM), + AARCH64_FEATURES (2, F16, SIMD)}, + {"rcpc", AARCH64_FEATURE (RCPC), AARCH64_NO_FEATURES}, + {"dotprod", AARCH64_FEATURE (DOTPROD), AARCH64_FEATURE (SIMD)}, + {"sha2", AARCH64_FEATURE (SHA2), AARCH64_FEATURE (FP)}, + {"sb", AARCH64_FEATURE (SB), AARCH64_NO_FEATURES}, + {"predres", AARCH64_FEATURE (PREDRES), AARCH64_NO_FEATURES}, + {"aes", AARCH64_FEATURE (AES), AARCH64_FEATURE (SIMD)}, + {"sm4", AARCH64_FEATURE (SM4), AARCH64_FEATURE (SIMD)}, + {"sha3", AARCH64_FEATURE (SHA3), AARCH64_FEATURE (SHA2)}, + {"rng", AARCH64_FEATURE (RNG), AARCH64_NO_FEATURES}, + {"ssbs", AARCH64_FEATURE (SSBS), AARCH64_NO_FEATURES}, + {"memtag", AARCH64_FEATURE (MEMTAG), AARCH64_NO_FEATURES}, + {"sve2", AARCH64_FEATURE (SVE2), AARCH64_FEATURE (SVE)}, + {"sve2-sm4", AARCH64_FEATURE (SVE2_SM4), + AARCH64_FEATURES (2, SVE2, SM4)}, + {"sve2-aes", AARCH64_FEATURE (SVE2_AES), + AARCH64_FEATURES (2, SVE2, AES)}, + {"sve2-sha3", AARCH64_FEATURE (SVE2_SHA3), + AARCH64_FEATURES (2, SVE2, SHA3)}, + {"sve2-bitperm", AARCH64_FEATURE (SVE2_BITPERM), + AARCH64_FEATURE (SVE2)}, + {"sme", AARCH64_FEATURE (SME), + AARCH64_FEATURES (2, SVE2, BFLOAT16)}, + {"sme-f64", AARCH64_FEATURE (SME_F64F64), AARCH64_FEATURE (SME)}, + {"sme-f64f64", AARCH64_FEATURE (SME_F64F64), AARCH64_FEATURE (SME)}, + {"sme-i64", AARCH64_FEATURE (SME_I16I64), AARCH64_FEATURE (SME)}, + {"sme-i16i64", AARCH64_FEATURE (SME_I16I64), AARCH64_FEATURE (SME)}, + {"sme2", AARCH64_FEATURE (SME2), AARCH64_FEATURE (SME)}, + {"bf16", AARCH64_FEATURE (BFLOAT16), AARCH64_FEATURE (FP)}, + {"i8mm", AARCH64_FEATURE (I8MM), AARCH64_FEATURE (SIMD)}, + {"f32mm", AARCH64_FEATURE (F32MM), AARCH64_FEATURE (SVE)}, + {"f64mm", AARCH64_FEATURE (F64MM), AARCH64_FEATURE (SVE)}, + {"ls64", AARCH64_FEATURE (LS64), AARCH64_NO_FEATURES}, + {"flagm", AARCH64_FEATURE (FLAGM), AARCH64_NO_FEATURES}, + {"pauth", AARCH64_FEATURE (PAC), AARCH64_NO_FEATURES}, + {"mops", AARCH64_FEATURE (MOPS), AARCH64_NO_FEATURES}, + {"hbc", AARCH64_FEATURE (HBC), AARCH64_NO_FEATURES}, + {"cssc", AARCH64_FEATURE (CSSC), AARCH64_NO_FEATURES}, + {NULL, AARCH64_NO_FEATURES, AARCH64_NO_FEATURES}, }; struct aarch64_long_option_table @@ -10020,14 +10326,15 @@ static aarch64_feature_set aarch64_feature_disable_set (aarch64_feature_set set) { const struct aarch64_option_cpu_value_table *opt; - aarch64_feature_set prev = 0; + aarch64_feature_set prev = AARCH64_NO_FEATURES; - while (prev != set) { - prev = set; - for (opt = aarch64_features; opt->name != NULL; opt++) - if (AARCH64_CPU_HAS_ANY_FEATURES (opt->require, set)) - AARCH64_MERGE_FEATURE_SETS (set, set, opt->value); - } + while (!AARCH64_CPU_HAS_ALL_FEATURES (prev, set)) + { + prev = set; + for (opt = aarch64_features; opt->name != NULL; opt++) + if (AARCH64_CPU_HAS_ANY_FEATURES (opt->require, set)) + AARCH64_MERGE_FEATURE_SETS (set, set, opt->value); + } return set; } @@ -10036,14 +10343,15 @@ static aarch64_feature_set aarch64_feature_enable_set (aarch64_feature_set set) { const struct aarch64_option_cpu_value_table *opt; - aarch64_feature_set prev = 0; + aarch64_feature_set prev = AARCH64_NO_FEATURES; - while (prev != set) { - prev = set; - for (opt = aarch64_features; opt->name != NULL; opt++) - if (AARCH64_CPU_HAS_FEATURE (set, opt->value)) - AARCH64_MERGE_FEATURE_SETS (set, set, opt->require); - } + while (!AARCH64_CPU_HAS_ALL_FEATURES (prev, set)) + { + prev = set; + for (opt = aarch64_features; opt->name != NULL; opt++) + if (AARCH64_CPU_HAS_ALL_FEATURES (set, opt->value)) + AARCH64_MERGE_FEATURE_SETS (set, set, opt->require); + } return set; } @@ -10125,7 +10433,7 @@ aarch64_parse_features (const char *str, const aarch64_feature_set **opt_p, else { set = aarch64_feature_disable_set (opt->value); - AARCH64_CLEAR_FEATURE (*ext_set, *ext_set, set); + AARCH64_CLEAR_FEATURES (*ext_set, *ext_set, set); } break; } @@ -10214,8 +10522,12 @@ struct aarch64_option_abi_value_table }; static const struct aarch64_option_abi_value_table aarch64_abis[] = { +#ifdef OBJ_ELF {"ilp32", AARCH64_ABI_ILP32}, {"lp64", AARCH64_ABI_LP64}, +#else + {"llp64", AARCH64_ABI_LLP64}, +#endif }; static int @@ -10241,10 +10553,8 @@ aarch64_parse_abi (const char *str) } static struct aarch64_long_option_table aarch64_long_opts[] = { -#ifdef OBJ_ELF {"mabi=", N_("\t specify for ABI "), aarch64_parse_abi, NULL}, -#endif /* OBJ_ELF */ {"mcpu=", N_("\t assemble for CPU "), aarch64_parse_cpu, NULL}, {"march=", N_("\t assemble for architecture "), @@ -10358,8 +10668,7 @@ s_aarch64_cpu (int ignored ATTRIBUTE_UNUSED) size_t optlen; name = input_line_pointer; - while (*input_line_pointer && !ISSPACE (*input_line_pointer)) - input_line_pointer++; + input_line_pointer = find_end_of_line (input_line_pointer, flag_m68k_mri); saved_char = *input_line_pointer; *input_line_pointer = 0; @@ -10404,8 +10713,7 @@ s_aarch64_arch (int ignored ATTRIBUTE_UNUSED) size_t optlen; name = input_line_pointer; - while (*input_line_pointer && !ISSPACE (*input_line_pointer)) - input_line_pointer++; + input_line_pointer = find_end_of_line (input_line_pointer, flag_m68k_mri); saved_char = *input_line_pointer; *input_line_pointer = 0; @@ -10444,10 +10752,9 @@ static void s_aarch64_arch_extension (int ignored ATTRIBUTE_UNUSED) { char saved_char; - char *ext = input_line_pointer;; + char *ext = input_line_pointer; - while (*input_line_pointer && !ISSPACE (*input_line_pointer)) - input_line_pointer++; + input_line_pointer = find_end_of_line (input_line_pointer, flag_m68k_mri); saved_char = *input_line_pointer; *input_line_pointer = 0;