X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gas%2Fconfig%2Ftc-aarch64.c;h=930b07a00903e06e308d2b761e289aa5861d8c1e;hb=98907a704908c5877d929c57b2ddb2e5f899d9a9;hp=a633b04fd84695d270b616bf41e4b2603fe33d45;hpb=a12fad50d28b9251893c6709ac4374773350c21a;p=binutils-gdb.git diff --git a/gas/config/tc-aarch64.c b/gas/config/tc-aarch64.c index a633b04fd84..930b07a0090 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-2015 Free Software Foundation, Inc. + Copyright (C) 2009-2016 Free Software Foundation, Inc. Contributed by ARM Ltd. This file is part of GAS. @@ -76,23 +76,26 @@ static enum aarch64_abi_type aarch64_abi = AARCH64_ABI_LP64; #define ilp32_p (aarch64_abi == AARCH64_ABI_ILP32) #endif -enum neon_el_type +enum vector_el_type { NT_invtype = -1, NT_b, NT_h, NT_s, NT_d, - NT_q + NT_q, + NT_zero, + NT_merge }; -/* Bits for DEFINED field in neon_type_el. */ -#define NTA_HASTYPE 1 -#define NTA_HASINDEX 2 +/* Bits for DEFINED field in vector_type_el. */ +#define NTA_HASTYPE 1 +#define NTA_HASINDEX 2 +#define NTA_HASVARWIDTH 4 -struct neon_type_el +struct vector_type_el { - enum neon_el_type type; + enum vector_el_type type; unsigned char defined; unsigned width; int64_t index; @@ -247,15 +250,6 @@ struct reloc_entry bfd_reloc_code_real_type reloc; }; -/* Structure for a hash table entry for a register. */ -typedef struct -{ - const char *name; - unsigned char number; - unsigned char type; - unsigned char builtin; -} reg_entry; - /* Macros to define the register types and masks for the purpose of parsing. */ @@ -274,16 +268,31 @@ typedef struct BASIC_REG_TYPE(FP_Q) /* q[0-31] */ \ BASIC_REG_TYPE(CN) /* c[0-7] */ \ BASIC_REG_TYPE(VN) /* v[0-31] */ \ - /* Typecheck: any 64-bit int reg (inc SP exc XZR) */ \ + BASIC_REG_TYPE(ZN) /* z[0-31] */ \ + BASIC_REG_TYPE(PN) /* p[0-15] */ \ + /* Typecheck: any 64-bit int reg (inc SP exc XZR). */ \ MULTI_REG_TYPE(R64_SP, REG_TYPE(R_64) | REG_TYPE(SP_64)) \ - /* Typecheck: any int (inc {W}SP inc [WX]ZR) */ \ + /* Typecheck: same, plus SVE registers. */ \ + MULTI_REG_TYPE(SVE_BASE, REG_TYPE(R_64) | REG_TYPE(SP_64) \ + | REG_TYPE(ZN)) \ + /* 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)) \ + /* 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)) \ + /* 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) \ | REG_TYPE(SP_32) | REG_TYPE(SP_64) \ | REG_TYPE(Z_32) | REG_TYPE(Z_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) */ \ + /* 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) \ | REG_TYPE(FP_B) | REG_TYPE(FP_H) \ @@ -301,7 +310,7 @@ typedef struct #define MULTI_REG_TYPE(T,V) BASIC_REG_TYPE(T) /* Register type enumerators. */ -typedef enum +typedef enum aarch64_reg_type_ { /* A list of REG_TYPE_*. */ AARCH64_REG_TYPES @@ -314,6 +323,15 @@ typedef enum #undef MULTI_REG_TYPE #define MULTI_REG_TYPE(T,V) V, +/* Structure for a hash table entry for a register. */ +typedef struct +{ + const char *name; + unsigned char number; + ENUM_BITFIELD (aarch64_reg_type_) type : 8; + unsigned char builtin; +} reg_entry; + /* Values indexed by aarch64_reg_type to assist the type checking. */ static const unsigned reg_type_masks[] = { @@ -344,6 +362,21 @@ get_reg_expected_msg (aarch64_reg_type reg_type) 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; @@ -378,6 +411,12 @@ get_reg_expected_msg (aarch64_reg_type reg_type) 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); } @@ -390,9 +429,6 @@ get_reg_expected_msg (aarch64_reg_type reg_type) /* Instructions take 4 bytes in the object file. */ #define INSN_SIZE 4 -/* Define some common error messages. */ -#define BAD_SP _("SP not allowed here") - static struct hash_control *aarch64_ops_hsh; static struct hash_control *aarch64_cond_hsh; static struct hash_control *aarch64_shift_hsh; @@ -406,6 +442,7 @@ static struct hash_control *aarch64_reg_hsh; static struct hash_control *aarch64_barrier_opt_hsh; static struct hash_control *aarch64_nzcv_hsh; static struct hash_control *aarch64_pldop_hsh; +static struct hash_control *aarch64_hint_opt_hsh; /* Stuff needed to resolve the label ambiguity As: @@ -572,7 +609,7 @@ my_get_expression (expressionS * ep, char **str, int prefix_mode, of LITTLENUMS emitted is stored in *SIZEP. An error message is returned, or NULL on OK. */ -char * +const char * md_atof (int type, char *litP, int *sizeP) { return ieee_md_atof (type, litP, sizeP, target_big_endian); @@ -670,93 +707,100 @@ parse_reg (char **ccp) static bfd_boolean aarch64_check_reg_type (const reg_entry *reg, aarch64_reg_type type) { - if (reg->type == type) - return TRUE; - - switch (type) - { - case REG_TYPE_R64_SP: /* 64-bit integer reg (inc SP exc XZR). */ - case REG_TYPE_R_Z_SP: /* Integer reg (inc {X}SP inc [WX]ZR). */ - case REG_TYPE_R_Z_BHSDQ_V: /* Any register apart from Cn. */ - case REG_TYPE_BHSDQ: /* Any [BHSDQ]P FP or SIMD scalar register. */ - case REG_TYPE_VN: /* Vector register. */ - gas_assert (reg->type < REG_TYPE_MAX && type < REG_TYPE_MAX); - return ((reg_type_masks[reg->type] & reg_type_masks[type]) - == reg_type_masks[reg->type]); - default: - as_fatal ("unhandled type %d", type); - abort (); - } + return (reg_type_masks[type] & (1 << reg->type)) != 0; } -/* Parse a register and return PARSE_FAIL if the register is not of type R_Z_SP. - Return the register number otherwise. *ISREG32 is set to one if the - register is 32-bit wide; *ISREGZERO is set to one if the register is - of type Z_32 or Z_64. +/* Try to parse a base or offset register. Allow SVE base and offset + registers if REG_TYPE includes SVE registers. Return the register + entry on success, setting *QUALIFIER to the register qualifier. + Return null otherwise. + Note that this function does not issue any diagnostics. */ -static int -aarch64_reg_parse_32_64 (char **ccp, int reject_sp, int reject_rz, - int *isreg32, int *isregzero) +static const reg_entry * +aarch64_addr_reg_parse (char **ccp, aarch64_reg_type reg_type, + aarch64_opnd_qualifier_t *qualifier) { char *str = *ccp; const reg_entry *reg = parse_reg (&str); if (reg == NULL) - return PARSE_FAIL; - - if (! aarch64_check_reg_type (reg, REG_TYPE_R_Z_SP)) - return PARSE_FAIL; + return NULL; switch (reg->type) { + case REG_TYPE_R_32: case REG_TYPE_SP_32: - case REG_TYPE_SP_64: - if (reject_sp) - return PARSE_FAIL; - *isreg32 = reg->type == REG_TYPE_SP_32; - *isregzero = 0; + case REG_TYPE_Z_32: + *qualifier = AARCH64_OPND_QLF_W; break; - case REG_TYPE_R_32: + case REG_TYPE_R_64: - *isreg32 = reg->type == REG_TYPE_R_32; - *isregzero = 0; - break; - case REG_TYPE_Z_32: + case REG_TYPE_SP_64: case REG_TYPE_Z_64: - if (reject_rz) - return PARSE_FAIL; - *isreg32 = reg->type == REG_TYPE_Z_32; - *isregzero = 1; + *qualifier = AARCH64_OPND_QLF_X; break; + + case REG_TYPE_ZN: + if ((reg_type_masks[reg_type] & (1 << REG_TYPE_ZN)) == 0 + || str[0] != '.') + return NULL; + switch (TOLOWER (str[1])) + { + case 's': + *qualifier = AARCH64_OPND_QLF_S_S; + break; + case 'd': + *qualifier = AARCH64_OPND_QLF_S_D; + break; + default: + return NULL; + } + str += 2; + break; + default: - return PARSE_FAIL; + return NULL; } *ccp = str; - return reg->number; + return reg; } -/* Parse the qualifier of a SIMD vector register or a SIMD vector element. - Fill in *PARSED_TYPE and return TRUE if the parsing succeeds; - otherwise return FALSE. +/* Try to parse a base or offset register. Return the register entry + on success, setting *QUALIFIER to the register qualifier. Return null + otherwise. + + Note that this function does not issue any diagnostics. */ + +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); +} + +/* Parse the qualifier of a vector register or vector element of type + REG_TYPE. Fill in *PARSED_TYPE and return TRUE if the parsing + succeeds; otherwise return FALSE. Accept only one occurrence of: - 8b 16b 4h 8h 2s 4s 1d 2d + 8b 16b 2h 4h 8h 2s 4s 1d 2d b h s d q */ static bfd_boolean -parse_neon_type_for_operand (struct neon_type_el *parsed_type, char **str) +parse_vector_type_for_operand (aarch64_reg_type reg_type, + struct vector_type_el *parsed_type, char **str) { char *ptr = *str; unsigned width; unsigned element_size; - enum neon_el_type type; + enum vector_el_type type; /* skip '.' */ + gas_assert (*ptr == '.'); ptr++; - if (!ISDIGIT (*ptr)) + if (reg_type == REG_TYPE_ZN || reg_type == REG_TYPE_PN || !ISDIGIT (*ptr)) { width = 0; goto elt_size; @@ -802,7 +846,8 @@ elt_size: first_error (_("missing element size")); return FALSE; } - if (width != 0 && width * element_size != 64 && width * element_size != 128) + if (width != 0 && width * element_size != 64 && width * element_size != 128 + && !(width == 2 && element_size == 16)) { first_error_fmt (_ ("invalid element size %d and vector size combination %c"), @@ -819,28 +864,35 @@ elt_size: return TRUE; } -/* Parse a single type, e.g. ".8b", leading period included. - Only applicable to Vn registers. +/* *STR contains an SVE zero/merge predication suffix. Parse it into + *PARSED_TYPE and point *STR at the end of the suffix. */ - Return TRUE on success; otherwise return FALSE. */ static bfd_boolean -parse_neon_operand_type (struct neon_type_el *vectype, char **ccp) +parse_predication_for_operand (struct vector_type_el *parsed_type, char **str) { - char *str = *ccp; + char *ptr = *str; - if (*str == '.') + /* Skip '/'. */ + gas_assert (*ptr == '/'); + ptr++; + switch (TOLOWER (*ptr)) { - if (! parse_neon_type_for_operand (vectype, &str)) - { - first_error (_("vector type expected")); - return FALSE; - } + case 'z': + parsed_type->type = NT_zero; + break; + case 'm': + parsed_type->type = NT_merge; + break; + default: + if (*ptr != '\0' && *ptr != ',') + first_error_fmt (_("unexpected character `%c' in predication type"), + *ptr); + else + first_error (_("missing predication type")); + return FALSE; } - else - return FALSE; - - *ccp = str; - + parsed_type->width = 0; + *str = ptr + 1; return TRUE; } @@ -858,12 +910,12 @@ parse_neon_operand_type (struct neon_type_el *vectype, char **ccp) static int parse_typed_reg (char **ccp, aarch64_reg_type type, aarch64_reg_type *rtype, - struct neon_type_el *typeinfo, bfd_boolean in_reg_list) + struct vector_type_el *typeinfo, bfd_boolean in_reg_list) { char *str = *ccp; const reg_entry *reg = parse_reg (&str); - struct neon_type_el atype; - struct neon_type_el parsetype; + struct vector_type_el atype; + struct vector_type_el parsetype; bfd_boolean is_typed_vecreg = FALSE; atype.defined = 0; @@ -887,13 +939,31 @@ parse_typed_reg (char **ccp, aarch64_reg_type type, aarch64_reg_type *rtype, } type = reg->type; - if (type == REG_TYPE_VN - && parse_neon_operand_type (&parsetype, &str)) + if ((type == REG_TYPE_VN || type == REG_TYPE_ZN || type == REG_TYPE_PN) + && (*str == '.' || (type == REG_TYPE_PN && *str == '/'))) { + if (*str == '.') + { + if (!parse_vector_type_for_operand (type, &parsetype, &str)) + return PARSE_FAIL; + } + else + { + if (!parse_predication_for_operand (&parsetype, &str)) + return PARSE_FAIL; + } + /* Register if of the form Vn.[bhsdq]. */ is_typed_vecreg = TRUE; - if (parsetype.width == 0) + if (type == REG_TYPE_ZN || type == REG_TYPE_PN) + { + /* The width is always variable; we don't allow an integer width + to be specified. */ + gas_assert (parsetype.width == 0); + atype.defined |= NTA_HASVARWIDTH | NTA_HASTYPE; + } + else if (parsetype.width == 0) /* Expect index. In the new scheme we cannot have Vn.[bhsdq] represent a scalar. Therefore any Vn.[bhsdq] should have an index following it. @@ -976,9 +1046,9 @@ parse_typed_reg (char **ccp, aarch64_reg_type type, aarch64_reg_type *rtype, static int aarch64_reg_parse (char **ccp, aarch64_reg_type type, - aarch64_reg_type *rtype, struct neon_type_el *vectype) + aarch64_reg_type *rtype, struct vector_type_el *vectype) { - struct neon_type_el atype; + struct vector_type_el atype; char *str = *ccp; int reg = parse_typed_reg (&str, type, rtype, &atype, /*in_reg_list= */ FALSE); @@ -995,7 +1065,7 @@ aarch64_reg_parse (char **ccp, aarch64_reg_type type, } static inline bfd_boolean -eq_neon_type_el (struct neon_type_el e1, struct neon_type_el e2) +eq_vector_type_el (struct vector_type_el e1, struct vector_type_el e2) { return e1.type == e2.type @@ -1003,8 +1073,9 @@ eq_neon_type_el (struct neon_type_el e1, struct neon_type_el e2) && e1.width == e2.width && e1.index == e2.index; } -/* This function parses the NEON register list. On success, it returns - the parsed register list information in the following encoded format: +/* This function parses a list of vector registers of type TYPE. + On success, it returns the parsed register list information in the + following encoded format: bit 18-22 | 13-17 | 7-11 | 2-6 | 0-1 4th regno | 3rd regno | 2nd regno | 1st regno | num_of_reg @@ -1024,11 +1095,12 @@ eq_neon_type_el (struct neon_type_el e1, struct neon_type_el e2) (by reg_list_valid_p). */ static int -parse_neon_reg_list (char **ccp, struct neon_type_el *vectype) +parse_vector_reg_list (char **ccp, aarch64_reg_type type, + struct vector_type_el *vectype) { char *str = *ccp; int nb_regs; - struct neon_type_el typeinfo, typeinfo_first; + struct vector_type_el typeinfo, typeinfo_first; int val, val_range; int in_range; int ret_val; @@ -1059,7 +1131,7 @@ parse_neon_reg_list (char **ccp, struct neon_type_el *vectype) str++; /* skip over '-' */ val_range = val; } - val = parse_typed_reg (&str, REG_TYPE_VN, NULL, &typeinfo, + val = parse_typed_reg (&str, type, NULL, &typeinfo, /*in_reg_list= */ TRUE); if (val == PARSE_FAIL) { @@ -1068,7 +1140,7 @@ parse_neon_reg_list (char **ccp, struct neon_type_el *vectype) continue; } /* reject [bhsd]n */ - if (typeinfo.defined == 0) + if (type == REG_TYPE_VN && typeinfo.defined == 0) { set_first_syntax_error (_("invalid scalar register in list")); error = TRUE; @@ -1093,7 +1165,7 @@ parse_neon_reg_list (char **ccp, struct neon_type_el *vectype) val_range = val; if (nb_regs == 0) typeinfo_first = typeinfo; - else if (! eq_neon_type_el (typeinfo_first, typeinfo)) + else if (! eq_vector_type_el (typeinfo_first, typeinfo)) { set_first_syntax_error (_("type mismatch in vector register list")); @@ -1185,7 +1257,7 @@ insert_reg_alias (char *str, int number, aarch64_reg_type type) } name = xstrdup (str); - new = xmalloc (sizeof (reg_entry)); + new = XNEW (reg_entry); new->name = name; new->number = number; @@ -1239,9 +1311,7 @@ create_register_alias (char *newname, char *p) nlen = strlen (newname); #endif - nbuf = alloca (nlen + 1); - memcpy (nbuf, newname, nlen); - nbuf[nlen] = '\0'; + nbuf = xmemdup0 (newname, nlen); /* Create aliases under the new name as stated; an all-lowercase version of the new name; and an all-uppercase version of the new @@ -1263,7 +1333,10 @@ create_register_alias (char *newname, char *p) the artificial FOO alias because it has already been created by the first .req. */ if (insert_reg_alias (nbuf, old->number, old->type) == NULL) - return TRUE; + { + free (nbuf); + return TRUE; + } } for (p = nbuf; *p; p++) @@ -1273,6 +1346,7 @@ create_register_alias (char *newname, char *p) insert_reg_alias (nbuf, old->number, old->type); } + free (nbuf); return TRUE; } @@ -1571,7 +1645,7 @@ find_or_make_literal_pool (int size) if (pool == NULL) { /* Create a new pool. */ - pool = xmalloc (sizeof (*pool)); + pool = XNEW (literal_pool); if (!pool) return NULL; @@ -1648,7 +1722,8 @@ add_to_lit_pool (expressionS *exp, int size) { /* PR 16688: Bignums are held in a single global array. We must copy and preserve that value now, before it is overwritten. */ - pool->literals[entry].bignum = xmalloc (CHARS_PER_LITTLENUM * exp->X_add_number); + pool->literals[entry].bignum = XNEWVEC (LITTLENUM_TYPE, + exp->X_add_number); memcpy (pool->literals[entry].bignum, generic_bignum, CHARS_PER_LITTLENUM * exp->X_add_number); } @@ -1731,13 +1806,13 @@ s_ltorg (int ignored ATTRIBUTE_UNUSED) if (pool == NULL || pool->symbol == NULL || pool->next_free_entry == 0) continue; - mapping_state (MAP_DATA); - /* Align pool as you have word accesses. Only make a frag if we have to. */ if (!need_pass_2) frag_align (align, 0, 0); + mapping_state (MAP_DATA); + record_alignment (now_seg, align); sprintf (sym_name, "$$lit_\002%x", pool->id); @@ -1892,6 +1967,21 @@ s_aarch64_inst (int ignored ATTRIBUTE_UNUSED) } #ifdef OBJ_ELF +/* Emit BFD_RELOC_AARCH64_TLSDESC_ADD on the next ADD instruction. */ + +static void +s_tlsdescadd (int ignored ATTRIBUTE_UNUSED) +{ + expressionS exp; + + expression (&exp); + frag_grow (4); + fix_new_aarch64 (frag_now, frag_more (0) - frag_now->fr_literal, 4, &exp, 0, + BFD_RELOC_AARCH64_TLSDESC_ADD); + + demand_empty_rest_of_line (); +} + /* Emit BFD_RELOC_AARCH64_TLSDESC_CALL on the next BLR instruction. */ static void @@ -1911,6 +2001,21 @@ s_tlsdesccall (int ignored ATTRIBUTE_UNUSED) demand_empty_rest_of_line (); } + +/* Emit BFD_RELOC_AARCH64_TLSDESC_LDR on the next LDR instruction. */ + +static void +s_tlsdescldr (int ignored ATTRIBUTE_UNUSED) +{ + expressionS exp; + + expression (&exp); + frag_grow (4); + fix_new_aarch64 (frag_now, frag_more (0) - frag_now->fr_literal, 4, &exp, 0, + BFD_RELOC_AARCH64_TLSDESC_LDR); + + demand_empty_rest_of_line (); +} #endif /* OBJ_ELF */ static void s_aarch64_arch (int); @@ -1936,7 +2041,9 @@ const pseudo_typeS md_pseudo_table[] = { {"arch_extension", s_aarch64_arch_extension, 0}, {"inst", s_aarch64_inst, 0}, #ifdef OBJ_ELF + {"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}, @@ -1988,14 +2095,14 @@ reg_name_p (char *str, aarch64_reg_type reg_type) To prevent the expression parser from pushing a register name into the symbol table as an undefined symbol, firstly a check is - done to find out whether STR is a valid register name followed - by a comma or the end of line. Return FALSE if STR is such a - string. */ + done to find out whether STR is a register of type REG_TYPE followed + by a comma or the end of line. Return FALSE if STR is such a string. */ static bfd_boolean -parse_immediate_expression (char **str, expressionS *exp) +parse_immediate_expression (char **str, expressionS *exp, + aarch64_reg_type reg_type) { - if (reg_name_p (*str, REG_TYPE_R_Z_BHSDQ_V)) + if (reg_name_p (*str, reg_type)) { set_recoverable_error (_("immediate operand required")); return FALSE; @@ -2014,16 +2121,17 @@ parse_immediate_expression (char **str, expressionS *exp) /* Constant immediate-value read function for use in insn parsing. STR points to the beginning of the immediate (with the optional - leading #); *VAL receives the value. + leading #); *VAL receives the value. REG_TYPE says which register + names should be treated as registers rather than as symbolic immediates. Return TRUE on success; otherwise return FALSE. */ static bfd_boolean -parse_constant_immediate (char **str, int64_t * val) +parse_constant_immediate (char **str, int64_t *val, aarch64_reg_type reg_type) { expressionS exp; - if (! parse_immediate_expression (str, &exp)) + if (! parse_immediate_expression (str, &exp, reg_type)) return FALSE; if (exp.X_op != O_constant) @@ -2076,68 +2184,67 @@ aarch64_imm_float_p (uint32_t imm) && ((imm & 0x7e000000) == pattern); /* bits 25 - 29 == ~ bit 30. */ } -/* Like aarch64_imm_float_p but for a double-precision floating-point value. - - Return TRUE if the value encoded in IMM can be expressed in the AArch64 - 8-bit signed floating-point format with 3-bit exponent and normalized 4 - bits of precision (i.e. can be used in an FMOV instruction); return the - equivalent single-precision encoding in *FPWORD. - - Otherwise return FALSE. */ +/* Return TRUE if the IEEE double value encoded in IMM can be expressed + as an IEEE float without any loss of precision. Store the value in + *FPWORD if so. */ static bfd_boolean -aarch64_double_precision_fmovable (uint64_t imm, uint32_t *fpword) +can_convert_double_to_float (uint64_t imm, uint32_t *fpword) { /* If a double-precision floating-point value has the following bit - pattern, it can be expressed in the AArch64 8-bit floating-point - format: + pattern, it can be expressed in a float: - 6 66655555555 554444444...21111111111 - 3 21098765432 109876543...098765432109876543210 - n Eeeeeeeeexx xxxx00000...000000000000000000000 + 6 66655555555 5544 44444444 33333333 33222222 22221111 111111 + 3 21098765432 1098 76543210 98765432 10987654 32109876 54321098 76543210 + n E~~~eeeeeee ssss ssssssss ssssssss SSS00000 00000000 00000000 00000000 - where n, e and each x are either 0 or 1 independently, with - E == ~ e. */ + -----------------------------> nEeeeeee esssssss ssssssss sssssSSS + if Eeee_eeee != 1111_1111 + + where n, e, s and S are either 0 or 1 independently and where ~ is the + inverse of E. */ uint32_t pattern; uint32_t high32 = imm >> 32; + uint32_t low32 = imm; - /* Lower 32 bits need to be 0s. */ - if ((imm & 0xffffffff) != 0) + /* Lower 29 bits need to be 0s. */ + if ((imm & 0x1fffffff) != 0) return FALSE; /* Prepare the pattern for 'Eeeeeeeee'. */ if (((high32 >> 30) & 0x1) == 0) - pattern = 0x3fc00000; + pattern = 0x38000000; else pattern = 0x40000000; - if ((high32 & 0xffff) == 0 /* bits 32 - 47 are 0. */ - && (high32 & 0x7fc00000) == pattern) /* bits 54 - 61 == ~ bit 62. */ - { - /* Convert to the single-precision encoding. - i.e. convert - n Eeeeeeeeexx xxxx00000...000000000000000000000 - to - n Eeeeeexx xxxx0000000000000000000. */ - *fpword = ((high32 & 0xfe000000) /* nEeeeee. */ - | (((high32 >> 16) & 0x3f) << 19)); /* xxxxxx. */ - return TRUE; - } - else + /* Check E~~~. */ + if ((high32 & 0x78000000) != pattern) return FALSE; + + /* Check Eeee_eeee != 1111_1111. */ + if ((high32 & 0x7ff00000) == 0x47f00000) + return FALSE; + + *fpword = ((high32 & 0xc0000000) /* 1 n bit and 1 E bit. */ + | ((high32 << 3) & 0x3ffffff8) /* 7 e and 20 s bits. */ + | (low32 >> 29)); /* 3 S bits. */ + return TRUE; } /* Parse a floating-point immediate. Return TRUE on success and return the value in *IMMED in the format of IEEE754 single-precision encoding. *CCP points to the start of the string; DP_P is TRUE when the immediate is expected to be in double-precision (N.B. this only matters when - hexadecimal representation is involved). + hexadecimal representation is involved). REG_TYPE says which register + names should be treated as registers rather than as symbolic immediates. - N.B. 0.0 is accepted by this function. */ + This routine accepts any IEEE float; it is up to the callers to reject + invalid ones. */ static bfd_boolean -parse_aarch64_imm_float (char **ccp, int *immed, bfd_boolean dp_p) +parse_aarch64_imm_float (char **ccp, int *immed, bfd_boolean dp_p, + aarch64_reg_type reg_type) { char *str = *ccp; char *fpnum; @@ -2157,12 +2264,12 @@ parse_aarch64_imm_float (char **ccp, int *immed, bfd_boolean dp_p) /* Support the hexadecimal representation of the IEEE754 encoding. Double-precision is expected when DP_P is TRUE, otherwise the representation should be in single-precision. */ - if (! parse_constant_immediate (&str, &val)) + if (! parse_constant_immediate (&str, &val, reg_type)) goto invalid_fp; if (dp_p) { - if (! aarch64_double_precision_fmovable (val, &fpword)) + if (!can_convert_double_to_float (val, &fpword)) goto invalid_fp; } else if ((uint64_t) val > 0xffffffff) @@ -2174,6 +2281,12 @@ parse_aarch64_imm_float (char **ccp, int *immed, bfd_boolean dp_p) } else { + if (reg_name_p (str, reg_type)) + { + set_recoverable_error (_("immediate operand required")); + return FALSE; + } + /* We must not accidentally parse an integer as a floating-point number. Make sure that the value we parse is not an integer by checking for special characters '.' or 'e'. */ @@ -2203,12 +2316,9 @@ parse_aarch64_imm_float (char **ccp, int *immed, bfd_boolean dp_p) } } - if (aarch64_imm_float_p (fpword) || (fpword & 0x7fffffff) == 0) - { - *immed = fpword; - *ccp = str; - return TRUE; - } + *immed = fpword; + *ccp = str; + return TRUE; invalid_fp: set_fatal_syntax_error (_("invalid floating-point constant")); @@ -2221,15 +2331,15 @@ invalid_fp: To prevent the expression parser from pushing a register name into the symbol table as an undefined symbol, a check is firstly done to find - out whether STR is a valid register name followed by a comma or the end - of line. Return FALSE if STR is such a register. */ + out whether STR is a register of type REG_TYPE followed by a comma or + the end of line. Return FALSE if STR is such a register. */ static bfd_boolean -parse_big_immediate (char **str, int64_t *imm) +parse_big_immediate (char **str, int64_t *imm, aarch64_reg_type reg_type) { char *ptr = *str; - if (reg_name_p (ptr, REG_TYPE_R_Z_BHSDQ_V)) + if (reg_name_p (ptr, reg_type)) { set_syntax_error (_("immediate operand required")); return FALSE; @@ -2455,6 +2565,24 @@ static struct reloc_table_entry reloc_table[] = { BFD_RELOC_AARCH64_LD_GOT_LO12_NC, 0}, + /* 0-15 bits of address/value: MOVk, no check. */ + {"gotoff_g0_nc", 0, + 0, /* adr_type */ + 0, + BFD_RELOC_AARCH64_MOVW_GOTOFF_G0_NC, + 0, + 0, + 0}, + + /* Most significant bits 16-31 of address/value: MOVZ. */ + {"gotoff_g1", 0, + 0, /* adr_type */ + 0, + BFD_RELOC_AARCH64_MOVW_GOTOFF_G1, + 0, + 0, + 0}, + /* 15 bit offset into the page containing GOT entry for that symbol. */ {"gotoff_lo15", 0, 0, /* adr_type */ @@ -2464,6 +2592,24 @@ static struct reloc_table_entry reloc_table[] = { BFD_RELOC_AARCH64_LD64_GOTOFF_LO15, 0}, + /* Get to the page containing GOT TLS entry for a symbol */ + {"gottprel_g0_nc", 0, + 0, /* adr_type */ + 0, + BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC, + 0, + 0, + 0}, + + /* Get to the page containing GOT TLS entry for a symbol */ + {"gottprel_g1", 0, + 0, /* adr_type */ + 0, + BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G1, + 0, + 0, + 0}, + /* Get to the page containing GOT TLS entry for a symbol */ {"tlsgd", 0, BFD_RELOC_AARCH64_TLSGD_ADR_PREL21, /* adr_type */ @@ -2482,6 +2628,24 @@ static struct reloc_table_entry reloc_table[] = { 0, 0}, + /* Lower 16 bits address/value: MOVk. */ + {"tlsgd_g0_nc", 0, + 0, /* adr_type */ + 0, + BFD_RELOC_AARCH64_TLSGD_MOVW_G0_NC, + 0, + 0, + 0}, + + /* Most significant bits 16-31 of address/value: MOVZ. */ + {"tlsgd_g1", 0, + 0, /* adr_type */ + 0, + BFD_RELOC_AARCH64_TLSGD_MOVW_G1, + 0, + 0, + 0}, + /* Get to the page containing GOT TLS entry for a symbol */ {"tlsdesc", 0, BFD_RELOC_AARCH64_TLSDESC_ADR_PREL21, /* adr_type */ @@ -2522,6 +2686,96 @@ static struct reloc_table_entry reloc_table[] = { 0, 0}, + /* 12 bit offset into the module TLS base address. */ + {"dtprel_lo12", 0, + 0, /* adr_type */ + 0, + 0, + BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_LO12, + BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12, + 0}, + + /* Same as dtprel_lo12, no overflow check. */ + {"dtprel_lo12_nc", 0, + 0, /* adr_type */ + 0, + 0, + BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_LO12_NC, + BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12_NC, + 0}, + + /* bits[23:12] of offset to the module TLS base address. */ + {"dtprel_hi12", 0, + 0, /* adr_type */ + 0, + 0, + BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_HI12, + 0, + 0}, + + /* bits[15:0] of offset to the module TLS base address. */ + {"dtprel_g0", 0, + 0, /* adr_type */ + 0, + BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0, + 0, + 0, + 0}, + + /* No overflow check version of BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0. */ + {"dtprel_g0_nc", 0, + 0, /* adr_type */ + 0, + BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0_NC, + 0, + 0, + 0}, + + /* bits[31:16] of offset to the module TLS base address. */ + {"dtprel_g1", 0, + 0, /* adr_type */ + 0, + BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1, + 0, + 0, + 0}, + + /* No overflow check version of BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1. */ + {"dtprel_g1_nc", 0, + 0, /* adr_type */ + 0, + BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1_NC, + 0, + 0, + 0}, + + /* bits[47:32] of offset to the module TLS base address. */ + {"dtprel_g2", 0, + 0, /* adr_type */ + 0, + BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G2, + 0, + 0, + 0}, + + /* Lower 16 bit offset into GOT entry for a symbol */ + {"tlsdesc_off_g0_nc", 0, + 0, /* adr_type */ + 0, + BFD_RELOC_AARCH64_TLSDESC_OFF_G0_NC, + 0, + 0, + 0}, + + /* Higher 16 bit offset into GOT entry for a symbol */ + {"tlsdesc_off_g1", 0, + 0, /* adr_type */ + 0, + BFD_RELOC_AARCH64_TLSDESC_OFF_G1, + 0, + 0, + 0}, + /* Get to the page containing GOT TLS entry for a symbol */ {"gottprel", 0, 0, /* adr_type */ @@ -2668,12 +2922,15 @@ find_reloc_table_entry (char **str) /* Mode argument to parse_shift and parser_shifter_operand. */ enum parse_shift_mode { + SHIFTED_NONE, /* no shifter allowed */ SHIFTED_ARITH_IMM, /* "rn{,lsl|lsr|asl|asr|uxt|sxt #n}" or "#imm{,lsl #n}" */ SHIFTED_LOGIC_IMM, /* "rn{,lsl|lsr|asl|asr|ror #n}" or "#imm" */ SHIFTED_LSL, /* bare "lsl #n" */ + SHIFTED_MUL, /* bare "mul #n" */ SHIFTED_LSL_MSL, /* "lsl|msl #n" */ + SHIFTED_MUL_VL, /* "mul vl" */ SHIFTED_REG_OFFSET /* [su]xtw|sxtx {#n} or lsl #n */ }; @@ -2714,6 +2971,14 @@ parse_shift (char **str, aarch64_opnd_info *operand, enum parse_shift_mode mode) return FALSE; } + if (kind == AARCH64_MOD_MUL + && mode != SHIFTED_MUL + && mode != SHIFTED_MUL_VL) + { + set_syntax_error (_("invalid use of 'MUL'")); + return FALSE; + } + switch (mode) { case SHIFTED_LOGIC_IMM: @@ -2740,6 +3005,30 @@ parse_shift (char **str, aarch64_opnd_info *operand, enum parse_shift_mode mode) } break; + case SHIFTED_MUL: + if (kind != AARCH64_MOD_MUL) + { + set_syntax_error (_("only 'MUL' is permitted")); + return FALSE; + } + break; + + case SHIFTED_MUL_VL: + /* "MUL VL" consists of two separate tokens. Require the first + token to be "MUL" and look for a following "VL". */ + if (kind == AARCH64_MOD_MUL) + { + skip_whitespace (p); + if (strncasecmp (p, "vl", 2) == 0 && !ISALPHA (p[2])) + { + p += 2; + kind = AARCH64_MOD_MUL_VL; + break; + } + } + set_syntax_error (_("only 'MUL VL' is permitted")); + return FALSE; + case SHIFTED_REG_OFFSET: if (kind != AARCH64_MOD_UXTW && kind != AARCH64_MOD_LSL && kind != AARCH64_MOD_SXTW && kind != AARCH64_MOD_SXTX) @@ -2767,7 +3056,7 @@ parse_shift (char **str, aarch64_opnd_info *operand, enum parse_shift_mode mode) /* Parse shift amount. */ exp_has_prefix = 0; - if (mode == SHIFTED_REG_OFFSET && *p == ']') + if ((mode == SHIFTED_REG_OFFSET && *p == ']') || kind == AARCH64_MOD_MUL_VL) exp.X_op = O_absent; else { @@ -2778,7 +3067,11 @@ parse_shift (char **str, aarch64_opnd_info *operand, enum parse_shift_mode mode) } my_get_expression (&exp, &p, GE_NO_PREFIX, 0); } - if (exp.X_op == O_absent) + if (kind == AARCH64_MOD_MUL_VL) + /* For consistency, give MUL VL the same shift amount as an implicit + MUL #1. */ + operand->shifter.amount = 1; + else if (exp.X_op == O_absent) { if (aarch64_extend_operator_p (kind) == FALSE || exp_has_prefix) { @@ -2792,7 +3085,11 @@ parse_shift (char **str, aarch64_opnd_info *operand, enum parse_shift_mode mode) set_syntax_error (_("constant shift amount required")); return FALSE; } - else if (exp.X_add_number < 0 || exp.X_add_number > 63) + /* For parsing purposes, MUL #n has no inherent range. The range + depends on the operand and will be checked by operand-specific + routines. */ + else if (kind != AARCH64_MOD_MUL + && (exp.X_add_number < 0 || exp.X_add_number > 63)) { set_fatal_syntax_error (_("shift amount out of range 0 to 63")); return FALSE; @@ -2869,13 +3166,13 @@ static bfd_boolean parse_shifter_operand (char **str, aarch64_opnd_info *operand, enum parse_shift_mode mode) { - int reg; - int isreg32, isregzero; + const reg_entry *reg; + aarch64_opnd_qualifier_t qualifier; enum aarch64_operand_class opd_class = aarch64_get_operand_class (operand->type); - if ((reg = - aarch64_reg_parse_32_64 (str, 0, 0, &isreg32, &isregzero)) != PARSE_FAIL) + reg = aarch64_reg_parse_32_64 (str, &qualifier); + if (reg) { if (opd_class == AARCH64_OPND_CLASS_IMMEDIATE) { @@ -2883,14 +3180,14 @@ parse_shifter_operand (char **str, aarch64_opnd_info *operand, return FALSE; } - if (!isregzero && reg == REG_SP) + if (!aarch64_check_reg_type (reg, REG_TYPE_R_Z)) { - set_syntax_error (BAD_SP); + set_syntax_error (_(get_reg_expected_msg (REG_TYPE_R_Z))); return FALSE; } - operand->reg.regno = reg; - operand->qualifier = isreg32 ? AARCH64_OPND_QLF_W : AARCH64_OPND_QLF_X; + operand->reg.regno = reg->number; + operand->qualifier = qualifier; /* Accept optional shift operation on register. */ if (! skip_past_comma (str)) @@ -2980,8 +3277,8 @@ parse_shifter_operand_reloc (char **str, aarch64_opnd_info *operand, The A64 instruction set has the following addressing modes: Offset - [base] // in SIMD ld/st structure - [base{,#0}] // in ld/st exclusive + [base] // in SIMD ld/st structure + [base{,#0}] // in ld/st exclusive [base{,#imm}] [base,Xm{,LSL #imm}] [base,Xm,SXTX {#imm}] @@ -2990,10 +3287,19 @@ parse_shifter_operand_reloc (char **str, aarch64_opnd_info *operand, [base,#imm]! Post-indexed [base],#imm - [base],Xm // in SIMD ld/st structure + [base],Xm // in SIMD ld/st structure PC-relative (literal) label - =immediate + SVE: + [base,#imm,MUL VL] + [base,Zm.D{,LSL #imm}] + [base,Zm.S,(S|U)XTW {#imm}] + [base,Zm.D,(S|U)XTW {#imm}] // ignores top 32 bits of Zm.D elements + [Zn.S,#imm] + [Zn.D,#imm] + [Zn.S,Zm.S{,LSL #imm}] // in ADR + [Zn.D,Zm.D{,LSL #imm}] // in ADR + [Zn.D,Zm.D,(S|U)XTW {#imm}] // in ADR (As a convenience, the notation "=immediate" is permitted in conjunction with the pc-relative literal load instructions to automatically place an @@ -3020,19 +3326,32 @@ parse_shifter_operand_reloc (char **str, aarch64_opnd_info *operand, .pcrel=1; .preind=1; .postind=0; .writeback=0 The shift/extension information, if any, will be stored in .shifter. + The base and offset qualifiers will be stored in *BASE_QUALIFIER and + *OFFSET_QUALIFIER respectively, with NIL being used if there's no + corresponding register. - It is the caller's responsibility to check for addressing modes not - supported by the instruction, and to set inst.reloc.type. */ + BASE_TYPE says which types of base register should be accepted and + OFFSET_TYPE says the same for offset registers. IMM_SHIFT_MODE + is the type of shifter that is allowed for immediate offsets, + or SHIFTED_NONE if none. + + In all other respects, it is the caller's responsibility to check + for addressing modes not supported by the instruction, and to set + inst.reloc.type. */ static bfd_boolean -parse_address_main (char **str, aarch64_opnd_info *operand, int reloc, - int accept_reg_post_index) +parse_address_main (char **str, aarch64_opnd_info *operand, + aarch64_opnd_qualifier_t *base_qualifier, + aarch64_opnd_qualifier_t *offset_qualifier, + aarch64_reg_type base_type, aarch64_reg_type offset_type, + enum parse_shift_mode imm_shift_mode) { char *p = *str; - int reg; - int isreg32, isregzero; + const reg_entry *reg; expressionS *exp = &inst.reloc.exp; + *base_qualifier = AARCH64_OPND_QLF_NIL; + *offset_qualifier = AARCH64_OPND_QLF_NIL; if (! skip_past_char (&p, '[')) { /* =immediate or label. */ @@ -3041,7 +3360,7 @@ parse_address_main (char **str, aarch64_opnd_info *operand, int reloc, /* #:: */ skip_past_char (&p, '#'); - if (reloc && skip_past_char (&p, ':')) + if (skip_past_char (&p, ':')) { bfd_reloc_code_real_type ty; struct reloc_table_entry *entry; @@ -3107,14 +3426,13 @@ parse_address_main (char **str, aarch64_opnd_info *operand, int reloc, /* [ */ - /* Accept SP and reject ZR */ - reg = aarch64_reg_parse_32_64 (&p, 0, 1, &isreg32, &isregzero); - if (reg == PARSE_FAIL || isreg32) + 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 (REG_TYPE_R_64))); + set_syntax_error (_(get_reg_expected_msg (base_type))); return FALSE; } - operand->addr.base_regno = reg; + operand->addr.base_regno = reg->number; /* [Xn */ if (skip_past_comma (&p)) @@ -3122,12 +3440,17 @@ parse_address_main (char **str, aarch64_opnd_info *operand, int reloc, /* [Xn, */ operand->addr.preind = 1; - /* Reject SP and accept ZR */ - reg = aarch64_reg_parse_32_64 (&p, 1, 0, &isreg32, &isregzero); - if (reg != PARSE_FAIL) + reg = aarch64_addr_reg_parse (&p, offset_type, offset_qualifier); + if (reg) { + if (!aarch64_check_reg_type (reg, offset_type)) + { + set_syntax_error (_(get_reg_expected_msg (offset_type))); + return FALSE; + } + /* [Xn,Rm */ - operand->addr.offset.regno = reg; + operand->addr.offset.regno = reg->number; operand->addr.offset.is_reg = 1; /* Shifted index. */ if (skip_past_comma (&p)) @@ -3146,13 +3469,19 @@ parse_address_main (char **str, aarch64_opnd_info *operand, int reloc, || operand->shifter.kind == AARCH64_MOD_LSL || operand->shifter.kind == AARCH64_MOD_SXTX) { - if (isreg32) + if (*offset_qualifier == AARCH64_OPND_QLF_W) { set_syntax_error (_("invalid use of 32-bit register offset")); return FALSE; } + if (aarch64_get_qualifier_esize (*base_qualifier) + != aarch64_get_qualifier_esize (*offset_qualifier)) + { + set_syntax_error (_("offset has different size from base")); + return FALSE; + } } - else if (!isreg32) + else if (*offset_qualifier == AARCH64_OPND_QLF_X) { set_syntax_error (_("invalid use of 64-bit register offset")); return FALSE; @@ -3162,7 +3491,7 @@ parse_address_main (char **str, aarch64_opnd_info *operand, int reloc, { /* [Xn,#:: */ skip_past_char (&p, '#'); - if (reloc && skip_past_char (&p, ':')) + if (skip_past_char (&p, ':')) { struct reloc_table_entry *entry; @@ -3197,12 +3526,19 @@ parse_address_main (char **str, aarch64_opnd_info *operand, int reloc, inst.reloc.type = entry->ldst_type; inst.reloc.pc_rel = entry->pc_rel; } - else if (! my_get_expression (exp, &p, GE_OPT_PREFIX, 1)) + else { - set_syntax_error (_("invalid expression in the address")); - return FALSE; + if (! my_get_expression (exp, &p, GE_OPT_PREFIX, 1)) + { + set_syntax_error (_("invalid expression in the address")); + return FALSE; + } + /* [Xn, */ + if (imm_shift_mode != SHIFTED_NONE && skip_past_comma (&p)) + /* [Xn,, */ + if (! parse_shift (&p, operand, imm_shift_mode)) + return FALSE; } - /* [Xn, */ } } @@ -3235,17 +3571,17 @@ parse_address_main (char **str, aarch64_opnd_info *operand, int reloc, return FALSE; } - if (accept_reg_post_index - && (reg = aarch64_reg_parse_32_64 (&p, 1, 1, &isreg32, - &isregzero)) != PARSE_FAIL) + reg = aarch64_reg_parse_32_64 (&p, offset_qualifier); + if (reg) { /* [Xn],Xm */ - if (isreg32) + if (!aarch64_check_reg_type (reg, REG_TYPE_R_64)) { - set_syntax_error (_("invalid 32-bit register offset")); + set_syntax_error (_(get_reg_expected_msg (REG_TYPE_R_64))); return FALSE; } - operand->addr.offset.regno = reg; + + operand->addr.offset.regno = reg->number; operand->addr.offset.is_reg = 1; } else if (! my_get_expression (exp, &p, GE_OPT_PREFIX, 1)) @@ -3275,19 +3611,27 @@ parse_address_main (char **str, aarch64_opnd_info *operand, int reloc, return TRUE; } -/* Return TRUE on success; otherwise return FALSE. */ +/* Parse a base AArch64 address (as opposed to an SVE one). Return TRUE + on success. */ static bfd_boolean -parse_address (char **str, aarch64_opnd_info *operand, - int accept_reg_post_index) +parse_address (char **str, aarch64_opnd_info *operand) { - return parse_address_main (str, operand, 0, accept_reg_post_index); + 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); } -/* Return TRUE on success; otherwise return FALSE. */ +/* Parse an address in which SVE vector registers and MUL VL are allowed. + The arguments have the same meaning as for parse_address_main. + Return TRUE on success. */ static bfd_boolean -parse_address_reloc (char **str, aarch64_opnd_info *operand) +parse_sve_address (char **str, aarch64_opnd_info *operand, + aarch64_opnd_qualifier_t *base_qualifier, + aarch64_opnd_qualifier_t *offset_qualifier) { - return parse_address_main (str, operand, 1, 0); + return parse_address_main (str, operand, base_qualifier, offset_qualifier, + REG_TYPE_SVE_BASE, REG_TYPE_SVE_OFFSET, + SHIFTED_MUL_VL); } /* Parse an operand for a MOVZ, MOVN or MOVK instruction. @@ -3295,10 +3639,8 @@ parse_address_reloc (char **str, aarch64_opnd_info *operand) static bfd_boolean parse_half (char **str, int *internal_fixup_p) { - char *p, *saved; - int dummy; + char *p = *str; - p = *str; skip_past_char (&p, '#'); gas_assert (internal_fixup_p); @@ -3328,12 +3670,6 @@ parse_half (char **str, int *internal_fixup_p) else *internal_fixup_p = 1; - /* Avoid parsing a register as a general symbol. */ - saved = p; - if (aarch64_reg_parse_32_64 (&p, 0, 0, &dummy, &dummy) != PARSE_FAIL) - return FALSE; - p = saved; - if (! my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX, 1)) return FALSE; @@ -3386,6 +3722,52 @@ parse_adrp (char **str) /* Miscellaneous. */ +/* 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. + + Return true on success, moving *STR past the operand and storing the + field value in *VAL. */ + +static int +parse_enum_string (char **str, int64_t *val, const char *const *array, + size_t size, aarch64_reg_type reg_type) +{ + expressionS exp; + char *p, *q; + size_t i; + + /* Match C-like tokens. */ + p = q = *str; + while (ISALNUM (*q)) + q++; + + for (i = 0; i < size; ++i) + if (array[i] + && strncasecmp (array[i], p, q - p) == 0 + && array[i][q - p] == 0) + { + *val = i; + *str = q; + return TRUE; + } + + if (!parse_immediate_expression (&p, &exp, reg_type)) + return FALSE; + + if (exp.X_op == O_constant + && (uint64_t) exp.X_add_number < size) + { + *val = exp.X_add_number; + *str = p; + return TRUE; + } + + /* Use the default error for this operand. */ + return FALSE; +} + /* Parse an option for a preload instruction. Returns the encoding for the option, or PARSE_FAIL. */ @@ -3428,6 +3810,41 @@ parse_barrier (char **str) return o->value; } +/* Parse an operand for a PSB barrier. Set *HINT_OPT to the hint-option record + return 0 if successful. Otherwise return PARSE_FAIL. */ + +static int +parse_barrier_psb (char **str, + const struct aarch64_name_value_pair ** hint_opt) +{ + char *p, *q; + const struct aarch64_name_value_pair *o; + + p = q = *str; + while (ISALPHA (*q)) + q++; + + o = hash_find_n (aarch64_hint_opt_hsh, p, q - p); + if (!o) + { + set_fatal_syntax_error + ( _("unknown or missing option to PSB")); + return PARSE_FAIL; + } + + if (o->value != 0x11) + { + /* PSB only accepts option name 'CSYNC'. */ + set_syntax_error + (_("the specified option is not accepted for PSB")); + return PARSE_FAIL; + } + + *str = q; + *hint_opt = o; + return 0; +} + /* Parse a system register or a PSTATE field name for an MSR/MRS instruction. Returns the encoding for the option, or PARSE_FAIL. @@ -3511,6 +3928,10 @@ parse_sys_ins_reg (char **str, struct hash_control *sys_ins_regs) if (!o) return NULL; + if (!aarch64_sys_ins_reg_supported_p (cpu_variant, o)) + as_bad (_("selected processor does not support system register " + "name '%s'"), buf); + *str = q; return o; } @@ -3529,28 +3950,24 @@ parse_sys_ins_reg (char **str, struct hash_control *sys_ins_regs) } \ } while (0) -#define po_int_reg_or_fail(reject_sp, reject_rz) do { \ - val = aarch64_reg_parse_32_64 (&str, reject_sp, reject_rz, \ - &isreg32, &isregzero); \ - if (val == PARSE_FAIL) \ +#define po_int_reg_or_fail(reg_type) do { \ + reg = aarch64_reg_parse_32_64 (&str, &qualifier); \ + if (!reg || !aarch64_check_reg_type (reg, reg_type)) \ { \ set_default_error (); \ goto failure; \ } \ - info->reg.regno = val; \ - if (isreg32) \ - info->qualifier = AARCH64_OPND_QLF_W; \ - else \ - info->qualifier = AARCH64_OPND_QLF_X; \ + info->reg.regno = reg->number; \ + info->qualifier = qualifier; \ } while (0) #define po_imm_nc_or_fail() do { \ - if (! parse_constant_immediate (&str, &val)) \ + if (! parse_constant_immediate (&str, &val, imm_reg_type)) \ goto failure; \ } while (0) #define po_imm_or_fail(min, max) do { \ - if (! parse_constant_immediate (&str, &val)) \ + if (! parse_constant_immediate (&str, &val, imm_reg_type)) \ goto failure; \ if (val < min || val > max) \ { \ @@ -3560,6 +3977,12 @@ parse_sys_ins_reg (char **str, struct hash_control *sys_ins_regs) } \ } while (0) +#define po_enum_or_fail(array) do { \ + if (!parse_enum_string (&str, &val, array, \ + ARRAY_SIZE (array), imm_reg_type)) \ + goto failure; \ + } while (0) + #define po_misc_or_fail(expr) do { \ if (!expr) \ goto failure; \ @@ -3838,9 +4261,7 @@ add_operand_error_record (const operand_error_record* new_record) /* Get one empty record. */ if (free_opnd_error_record_nodes == NULL) { - record = xmalloc (sizeof (operand_error_record)); - if (record == NULL) - abort (); + record = XNEW (operand_error_record); } else { @@ -3964,7 +4385,7 @@ find_best_match (const aarch64_inst *instr, } max_num_matched = 0; - idx = -1; + idx = 0; /* For each pattern. */ for (i = 0; i < AARCH64_MAX_QLF_SEQ_NUM; ++i, ++qualifiers_list) @@ -3976,9 +4397,6 @@ find_best_match (const aarch64_inst *instr, if (empty_qualifier_sequence_p (qualifiers) == TRUE) { DEBUG_TRACE_IF (i == 0, "empty list of qualifier sequence"); - if (i != 0 && idx == -1) - /* If nothing has been matched, return the 1st sequence. */ - idx = 0; break; } @@ -4021,8 +4439,7 @@ print_operands (char *buf, const aarch64_opcode *opcode, for (i = 0; i < AARCH64_MAX_OPND_NUM; ++i) { - const size_t size = 128; - char str[size]; + char str[128]; /* We regard the opcode operand info more, however we also look into the inst->operands to support the disassembling of the optional @@ -4034,7 +4451,7 @@ print_operands (char *buf, const aarch64_opcode *opcode, break; /* Generate the operand string in STR. */ - aarch64_print_operand (str, size, 0, opcode, opnds, i, NULL, NULL); + aarch64_print_operand (str, sizeof (str), 0, opcode, opnds, i, NULL, NULL); /* Delimiter. */ if (str[0] != '\0') @@ -4050,11 +4467,11 @@ print_operands (char *buf, const aarch64_opcode *opcode, static void output_info (const char *format, ...) { - char *file; + const char *file; unsigned int line; va_list args; - as_where (&file, &line); + file = as_where (&line); if (file) { if (line != 0) @@ -4140,8 +4557,7 @@ output_operand_error_record (const operand_error_record *record, char *str) size_t len = strlen (get_mnemonic_name (str)); int i, qlf_idx; bfd_boolean result; - const size_t size = 2048; - char buf[size]; + char buf[2048]; aarch64_inst *inst_base = &inst.base; const aarch64_opnd_qualifier_seq_t *qualifiers_list; @@ -4171,7 +4587,7 @@ output_operand_error_record (const operand_error_record *record, char *str) /* Print the hint. */ output_info (_(" did you mean this?")); - snprintf (buf, size, "\t%s", get_mnemonic_name (str)); + snprintf (buf, sizeof (buf), "\t%s", get_mnemonic_name (str)); print_operands (buf, opcode, inst_base->operands); output_info (_(" %s"), buf); @@ -4192,7 +4608,7 @@ output_operand_error_record (const operand_error_record *record, char *str) if (i != qlf_idx) { /* Mnemonics name. */ - snprintf (buf, size, "\t%s", get_mnemonic_name (str)); + snprintf (buf, sizeof (buf), "\t%s", get_mnemonic_name (str)); /* Assign the qualifiers. */ assign_qualifier_sequence (inst_base, *qualifiers_list); @@ -4206,6 +4622,11 @@ output_operand_error_record (const operand_error_record *record, char *str) } break; + case AARCH64_OPDE_UNTIED_OPERAND: + as_bad (_("operand %d must be the same register as operand 1 -- `%s'"), + detail->index + 1, str); + break; + case AARCH64_OPDE_OUT_OF_RANGE: if (detail->data[0] != detail->data[1]) as_bad (_("%s out of range %d to %d at operand %d -- `%s'"), @@ -4446,22 +4867,35 @@ opcode_lookup (char **str) return NULL; } -/* Internal helper routine converting a vector neon_type_el structure - *VECTYPE to a corresponding operand qualifier. */ +/* 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 neon_type_el *vectype) +vectype_to_qualifier (const struct vector_type_el *vectype) { - /* Element size in bytes indexed by neon_el_type. */ + /* 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_8B, + 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) + if (vectype->defined & (NTA_HASINDEX | NTA_HASVARWIDTH)) /* Vector element register. */ return AARCH64_OPND_QLF_S_B + vectype->type; else @@ -4469,14 +4903,28 @@ vectype_to_qualifier (const struct neon_type_el *vectype) /* Vector register. */ int reg_size = ele_size[vectype->type] * vectype->width; unsigned offset; - if (reg_size != 16 && reg_size != 8) + unsigned shift; + if (reg_size != 16 && reg_size != 8 && reg_size != 4) goto vectype_conversion_fail; - /* The conversion is calculated based on the relation of the order of - qualifiers to the vector element size and vector register size. */ - offset = (vectype->type == NT_q) - ? 8 : (vectype->type << 1) + (reg_size >> 4); - gas_assert (offset <= 8); - return AARCH64_OPND_QLF_V_8B + offset; + + /* 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 = 4; + 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_8B <= offset + && offset <= AARCH64_OPND_QLF_V_1Q); + return offset; } vectype_conversion_fail: @@ -4548,9 +4996,17 @@ process_omitted_operand (enum aarch64_opnd type, const aarch64_opcode *opcode, case AARCH64_OPND_WIDTH: case AARCH64_OPND_UIMM7: case AARCH64_OPND_NZCV: + case AARCH64_OPND_SVE_PATTERN: + case AARCH64_OPND_SVE_PRFOP: operand->imm.value = default_value; break; + case AARCH64_OPND_SVE_PATTERN_SCALED: + operand->imm.value = default_value; + operand->shifter.kind = AARCH64_MOD_MUL; + operand->shifter.amount = 1; + break; + case AARCH64_OPND_EXCEPTION: inst.reloc.type = BFD_RELOC_UNUSED; break; @@ -4580,6 +5036,7 @@ process_movw_reloc_info (void) case BFD_RELOC_AARCH64_MOVW_G0_S: case BFD_RELOC_AARCH64_MOVW_G1_S: case BFD_RELOC_AARCH64_MOVW_G2_S: + case BFD_RELOC_AARCH64_TLSGD_MOVW_G1: case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0: case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1: case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2: @@ -4595,6 +5052,12 @@ process_movw_reloc_info (void) case BFD_RELOC_AARCH64_MOVW_G0: case BFD_RELOC_AARCH64_MOVW_G0_NC: case BFD_RELOC_AARCH64_MOVW_G0_S: + case BFD_RELOC_AARCH64_MOVW_GOTOFF_G0_NC: + case BFD_RELOC_AARCH64_TLSDESC_OFF_G0_NC: + case BFD_RELOC_AARCH64_TLSGD_MOVW_G0_NC: + case BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0_NC: case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0: case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC: shift = 0; @@ -4602,6 +5065,12 @@ process_movw_reloc_info (void) case BFD_RELOC_AARCH64_MOVW_G1: case BFD_RELOC_AARCH64_MOVW_G1_NC: case BFD_RELOC_AARCH64_MOVW_G1_S: + case BFD_RELOC_AARCH64_MOVW_GOTOFF_G1: + case BFD_RELOC_AARCH64_TLSDESC_OFF_G1: + case BFD_RELOC_AARCH64_TLSGD_MOVW_G1: + case BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G1: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1_NC: case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1: case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC: shift = 16; @@ -4609,6 +5078,7 @@ process_movw_reloc_info (void) case BFD_RELOC_AARCH64_MOVW_G2: case BFD_RELOC_AARCH64_MOVW_G2_NC: case BFD_RELOC_AARCH64_MOVW_G2_S: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G2: case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2: if (is32) { @@ -4662,17 +5132,38 @@ get_logsz (unsigned int size) static inline bfd_reloc_code_real_type ldst_lo12_determine_real_reloc_type (void) { - int logsz; + unsigned logsz; enum aarch64_opnd_qualifier opd0_qlf = inst.base.operands[0].qualifier; enum aarch64_opnd_qualifier opd1_qlf = inst.base.operands[1].qualifier; - const bfd_reloc_code_real_type reloc_ldst_lo12[5] = { - BFD_RELOC_AARCH64_LDST8_LO12, BFD_RELOC_AARCH64_LDST16_LO12, - BFD_RELOC_AARCH64_LDST32_LO12, BFD_RELOC_AARCH64_LDST64_LO12, + const bfd_reloc_code_real_type reloc_ldst_lo12[3][5] = { + { + BFD_RELOC_AARCH64_LDST8_LO12, + BFD_RELOC_AARCH64_LDST16_LO12, + BFD_RELOC_AARCH64_LDST32_LO12, + BFD_RELOC_AARCH64_LDST64_LO12, BFD_RELOC_AARCH64_LDST128_LO12 + }, + { + BFD_RELOC_AARCH64_TLSLD_LDST8_DTPREL_LO12, + BFD_RELOC_AARCH64_TLSLD_LDST16_DTPREL_LO12, + BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12, + BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12, + BFD_RELOC_AARCH64_NONE + }, + { + BFD_RELOC_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC, + BFD_RELOC_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC, + BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC, + BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC, + BFD_RELOC_AARCH64_NONE + } }; - gas_assert (inst.reloc.type == BFD_RELOC_AARCH64_LDST_LO12); + gas_assert (inst.reloc.type == BFD_RELOC_AARCH64_LDST_LO12 + || inst.reloc.type == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12 + || (inst.reloc.type + == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12_NC)); gas_assert (inst.base.opcode->operands[1] == AARCH64_OPND_ADDR_UIMM12); if (opd1_qlf == AARCH64_OPND_QLF_NIL) @@ -4682,9 +5173,16 @@ ldst_lo12_determine_real_reloc_type (void) gas_assert (opd1_qlf != AARCH64_OPND_QLF_NIL); logsz = get_logsz (aarch64_get_qualifier_esize (opd1_qlf)); - gas_assert (logsz >= 0 && logsz <= 4); + if (inst.reloc.type == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12 + || inst.reloc.type == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12_NC) + gas_assert (logsz <= 3); + else + gas_assert (logsz <= 4); - return reloc_ldst_lo12[logsz]; + /* In reloc.c, these pseudo relocation types should be defined in similar + order as above reloc_ldst_lo12 array. Because the array index calcuation + below relies on this. */ + 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 @@ -4729,18 +5227,23 @@ parse_operands (char *str, const aarch64_opcode *opcode) int i; char *backtrack_pos = 0; const enum aarch64_opnd *operands = opcode->operands; + aarch64_reg_type imm_reg_type; clear_error (); skip_whitespace (str); + imm_reg_type = REG_TYPE_R_Z_BHSDQ_V; + for (i = 0; operands[i] != AARCH64_OPND_NIL; i++) { int64_t val; - int isreg32, isregzero; + const reg_entry *reg; int comma_skipped_p = 0; aarch64_reg_type rtype; - struct neon_type_el vectype; + struct vector_type_el vectype; + aarch64_opnd_qualifier_t qualifier, base_qualifier, offset_qualifier; aarch64_opnd_info *info = &inst.base.operands[i]; + aarch64_reg_type reg_type; DEBUG_TRACE ("parse operand %d", i); @@ -4775,12 +5278,12 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_Ra: case AARCH64_OPND_Rt_SYS: case AARCH64_OPND_PAIRREG: - po_int_reg_or_fail (1, 0); + po_int_reg_or_fail (REG_TYPE_R_Z); break; case AARCH64_OPND_Rd_SP: case AARCH64_OPND_Rn_SP: - po_int_reg_or_fail (0, 1); + po_int_reg_or_fail (REG_TYPE_R_SP); break; case AARCH64_OPND_Rm_EXT: @@ -4823,22 +5326,54 @@ parse_operands (char *str, const aarch64_opcode *opcode) info->qualifier = AARCH64_OPND_QLF_S_B + (rtype - REG_TYPE_FP_B); break; + case AARCH64_OPND_SVE_Pd: + case AARCH64_OPND_SVE_Pg3: + case AARCH64_OPND_SVE_Pg4_5: + case AARCH64_OPND_SVE_Pg4_10: + case AARCH64_OPND_SVE_Pg4_16: + case AARCH64_OPND_SVE_Pm: + case AARCH64_OPND_SVE_Pn: + case AARCH64_OPND_SVE_Pt: + reg_type = REG_TYPE_PN; + goto vector_reg; + + case AARCH64_OPND_SVE_Za_5: + case AARCH64_OPND_SVE_Za_16: + case AARCH64_OPND_SVE_Zd: + case AARCH64_OPND_SVE_Zm_5: + case AARCH64_OPND_SVE_Zm_16: + case AARCH64_OPND_SVE_Zn: + case AARCH64_OPND_SVE_Zt: + reg_type = REG_TYPE_ZN; + goto vector_reg; + case AARCH64_OPND_Vd: case AARCH64_OPND_Vn: case AARCH64_OPND_Vm: - val = aarch64_reg_parse (&str, REG_TYPE_VN, NULL, &vectype); + reg_type = REG_TYPE_VN; + vector_reg: + val = aarch64_reg_parse (&str, reg_type, NULL, &vectype); if (val == PARSE_FAIL) { - first_error (_(get_reg_expected_msg (REG_TYPE_VN))); + first_error (_(get_reg_expected_msg (reg_type))); goto failure; } if (vectype.defined & NTA_HASINDEX) goto failure; info->reg.regno = val; - info->qualifier = vectype_to_qualifier (&vectype); - if (info->qualifier == AARCH64_OPND_QLF_NIL) - goto failure; + if ((reg_type == REG_TYPE_PN || reg_type == REG_TYPE_ZN) + && vectype.type == NT_invtype) + /* Unqualified Pn and Zn registers are allowed in certain + contexts. Rely on F_STRICT qualifier checking to catch + invalid uses. */ + info->qualifier = AARCH64_OPND_QLF_NIL; + else + { + info->qualifier = vectype_to_qualifier (&vectype); + if (info->qualifier == AARCH64_OPND_QLF_NIL) + goto failure; + } break; case AARCH64_OPND_VdD1: @@ -4863,13 +5398,19 @@ parse_operands (char *str, const aarch64_opcode *opcode) info->qualifier = AARCH64_OPND_QLF_S_D; break; + case AARCH64_OPND_SVE_Zn_INDEX: + reg_type = REG_TYPE_ZN; + goto vector_reg_index; + case AARCH64_OPND_Ed: case AARCH64_OPND_En: case AARCH64_OPND_Em: - val = aarch64_reg_parse (&str, REG_TYPE_VN, NULL, &vectype); + reg_type = REG_TYPE_VN; + vector_reg_index: + val = aarch64_reg_parse (&str, reg_type, NULL, &vectype); if (val == PARSE_FAIL) { - first_error (_(get_reg_expected_msg (REG_TYPE_VN))); + first_error (_(get_reg_expected_msg (reg_type))); goto failure; } if (vectype.type == NT_invtype || !(vectype.defined & NTA_HASINDEX)) @@ -4882,19 +5423,43 @@ parse_operands (char *str, const aarch64_opcode *opcode) goto failure; break; + case AARCH64_OPND_SVE_ZnxN: + case AARCH64_OPND_SVE_ZtxN: + reg_type = REG_TYPE_ZN; + goto vector_reg_list; + case AARCH64_OPND_LVn: case AARCH64_OPND_LVt: case AARCH64_OPND_LVt_AL: case AARCH64_OPND_LEt: - if ((val = parse_neon_reg_list (&str, &vectype)) == PARSE_FAIL) - goto failure; - if (! reg_list_valid_p (val, /* accept_alternate */ 0)) + reg_type = REG_TYPE_VN; + vector_reg_list: + if (reg_type == REG_TYPE_ZN + && get_opcode_dependent_value (opcode) == 1 + && *str != '{') { - set_fatal_syntax_error (_("invalid register list")); - goto failure; + 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; + info->reglist.num_regs = 1; + } + else + { + val = parse_vector_reg_list (&str, reg_type, &vectype); + if (val == PARSE_FAIL) + goto failure; + if (! reg_list_valid_p (val, /* accept_alternate */ 0)) + { + set_fatal_syntax_error (_("invalid register list")); + goto failure; + } + info->reglist.first_regno = (val >> 2) & 0x1f; + info->reglist.num_regs = (val & 0x3) + 1; } - info->reglist.first_regno = (val >> 2) & 0x1f; - info->reglist.num_regs = (val & 0x3) + 1; if (operands[i] == AARCH64_OPND_LEt) { if (!(vectype.defined & NTA_HASINDEX)) @@ -4902,8 +5467,17 @@ parse_operands (char *str, const aarch64_opcode *opcode) info->reglist.has_index = 1; info->reglist.index = vectype.index; } - else if (!(vectype.defined & NTA_HASTYPE)) - goto failure; + else + { + if (vectype.defined & NTA_HASINDEX) + goto failure; + if (!(vectype.defined & NTA_HASTYPE)) + { + if (reg_type == REG_TYPE_ZN) + set_fatal_syntax_error (_("missing type suffix")); + goto failure; + } + } info->qualifier = vectype_to_qualifier (&vectype); if (info->qualifier == AARCH64_OPND_QLF_NIL) goto failure; @@ -4938,6 +5512,30 @@ parse_operands (char *str, const aarch64_opcode *opcode) info->imm.value = val; break; + case AARCH64_OPND_SVE_PATTERN: + po_enum_or_fail (aarch64_sve_pattern_array); + info->imm.value = val; + break; + + case AARCH64_OPND_SVE_PATTERN_SCALED: + po_enum_or_fail (aarch64_sve_pattern_array); + info->imm.value = val; + if (skip_past_comma (&str) + && !parse_shift (&str, info, SHIFTED_MUL)) + goto failure; + if (!info->shifter.operator_present) + { + gas_assert (info->shifter.kind == AARCH64_MOD_NONE); + info->shifter.kind = AARCH64_MOD_MUL; + info->shifter.amount = 1; + } + break; + + case AARCH64_OPND_SVE_PRFOP: + po_enum_or_fail (aarch64_sve_prfop_array); + info->imm.value = val; + break; + case AARCH64_OPND_UIMM7: po_imm_or_fail (0, 127); info->imm.value = val; @@ -4967,8 +5565,11 @@ parse_operands (char *str, const aarch64_opcode *opcode) bfd_boolean res1 = FALSE, res2 = FALSE; /* N.B. -0.0 will be rejected; although -0.0 shouldn't be rejected, it is probably not worth the effort to support it. */ - if (!(res1 = parse_aarch64_imm_float (&str, &qfloat, FALSE)) - && !(res2 = parse_constant_immediate (&str, &val))) + if (!(res1 = parse_aarch64_imm_float (&str, &qfloat, FALSE, + imm_reg_type)) + && (error_p () + || !(res2 = parse_constant_immediate (&str, &val, + imm_reg_type)))) goto failure; if ((res1 && qfloat == 0) || (res2 && val == 0)) { @@ -5001,7 +5602,7 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_SIMD_IMM: case AARCH64_OPND_SIMD_IMM_SFT: - if (! parse_big_immediate (&str, &val)) + if (! parse_big_immediate (&str, &val, imm_reg_type)) goto failure; assign_imm_if_const_or_fixup_later (&inst.reloc, info, /* addr_off_p */ 0, @@ -5032,11 +5633,12 @@ parse_operands (char *str, const aarch64_opcode *opcode) bfd_boolean dp_p = (aarch64_get_qualifier_esize (inst.base.operands[0].qualifier) == 8); - if (! parse_aarch64_imm_float (&str, &qfloat, dp_p)) - goto failure; - if (qfloat == 0) + if (!parse_aarch64_imm_float (&str, &qfloat, dp_p, imm_reg_type) + || !aarch64_imm_float_p (qfloat)) { - set_fatal_syntax_error (_("invalid floating-point constant")); + if (!error_p ()) + set_fatal_syntax_error (_("invalid floating-point" + " constant")); goto failure; } inst.base.operands[i].imm.value = encode_imm_float_bits (qfloat); @@ -5120,7 +5722,8 @@ parse_operands (char *str, const aarch64_opcode *opcode) break; case AARCH64_OPND_EXCEPTION: - po_misc_or_fail (parse_immediate_expression (&str, &inst.reloc.exp)); + po_misc_or_fail (parse_immediate_expression (&str, &inst.reloc.exp, + imm_reg_type)); assign_imm_if_const_or_fixup_later (&inst.reloc, info, /* addr_off_p */ 0, /* need_libopcodes_p */ 0, @@ -5169,7 +5772,7 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_ADDR_PCREL19: case AARCH64_OPND_ADDR_PCREL21: case AARCH64_OPND_ADDR_PCREL26: - po_misc_or_fail (parse_address_reloc (&str, info)); + po_misc_or_fail (parse_address (&str, info)); if (!info->addr.pcrel) { set_syntax_error (_("invalid pc-relative address")); @@ -5235,28 +5838,44 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_ADDR_SIMPLE: case AARCH64_OPND_SIMD_ADDR_SIMPLE: - /* [{, #}] */ - po_char_or_fail ('['); - po_reg_or_fail (REG_TYPE_R64_SP); - /* Accept optional ", #0". */ - if (operands[i] == AARCH64_OPND_ADDR_SIMPLE - && skip_past_char (&str, ',')) - { - skip_past_char (&str, '#'); - if (! skip_past_char (&str, '0')) - { - set_fatal_syntax_error - (_("the optional immediate offset can only be 0")); - goto failure; - } - } - po_char_or_fail (']'); - info->addr.base_regno = val; - break; + { + /* [{, #}] */ + char *start = str; + /* First use the normal address-parsing routines, to get + the usual syntax errors. */ + po_misc_or_fail (parse_address (&str, info)); + if (info->addr.pcrel || info->addr.offset.is_reg + || !info->addr.preind || info->addr.postind + || info->addr.writeback) + { + set_syntax_error (_("invalid addressing mode")); + goto failure; + } + + /* Then retry, matching the specific syntax of these addresses. */ + str = start; + po_char_or_fail ('['); + po_reg_or_fail (REG_TYPE_R64_SP); + /* Accept optional ", #0". */ + if (operands[i] == AARCH64_OPND_ADDR_SIMPLE + && skip_past_char (&str, ',')) + { + skip_past_char (&str, '#'); + if (! skip_past_char (&str, '0')) + { + set_fatal_syntax_error + (_("the optional immediate offset can only be 0")); + goto failure; + } + } + po_char_or_fail (']'); + break; + } case AARCH64_OPND_ADDR_REGOFF: /* [, {, {}}] */ - po_misc_or_fail (parse_address (&str, info, 0)); + po_misc_or_fail (parse_address (&str, info)); + regoff_addr: if (info->addr.pcrel || !info->addr.offset.is_reg || !info->addr.preind || info->addr.postind || info->addr.writeback) @@ -5275,13 +5894,18 @@ parse_operands (char *str, const aarch64_opcode *opcode) break; case AARCH64_OPND_ADDR_SIMM7: - po_misc_or_fail (parse_address (&str, info, 0)); + po_misc_or_fail (parse_address (&str, info)); if (info->addr.pcrel || info->addr.offset.is_reg || (!info->addr.preind && !info->addr.postind)) { set_syntax_error (_("invalid addressing mode")); goto failure; } + if (inst.reloc.type != BFD_RELOC_UNUSED) + { + set_syntax_error (_("relocation not allowed")); + goto failure; + } assign_imm_if_const_or_fixup_later (&inst.reloc, info, /* addr_off_p */ 1, /* need_libopcodes_p */ 1, @@ -5290,7 +5914,7 @@ parse_operands (char *str, const aarch64_opcode *opcode) case AARCH64_OPND_ADDR_SIMM9: case AARCH64_OPND_ADDR_SIMM9_2: - po_misc_or_fail (parse_address_reloc (&str, info)); + po_misc_or_fail (parse_address (&str, info)); if (info->addr.pcrel || info->addr.offset.is_reg || (!info->addr.preind && !info->addr.postind) || (operands[i] == AARCH64_OPND_ADDR_SIMM9_2 @@ -5311,7 +5935,7 @@ parse_operands (char *str, const aarch64_opcode *opcode) break; case AARCH64_OPND_ADDR_UIMM12: - po_misc_or_fail (parse_address_reloc (&str, info)); + po_misc_or_fail (parse_address (&str, info)); if (info->addr.pcrel || info->addr.offset.is_reg || !info->addr.preind || info->addr.writeback) { @@ -5320,14 +5944,18 @@ parse_operands (char *str, const aarch64_opcode *opcode) } if (inst.reloc.type == BFD_RELOC_UNUSED) aarch64_set_gas_internal_fixup (&inst.reloc, info, 1); - else if (inst.reloc.type == BFD_RELOC_AARCH64_LDST_LO12) + else if (inst.reloc.type == BFD_RELOC_AARCH64_LDST_LO12 + || (inst.reloc.type + == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12) + || (inst.reloc.type + == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12_NC)) inst.reloc.type = ldst_lo12_determine_real_reloc_type (); /* Leave qualifier to be determined by libopcodes. */ break; case AARCH64_OPND_SIMD_ADDR_POST: /* [], > */ - po_misc_or_fail (parse_address (&str, info, 1)); + po_misc_or_fail (parse_address (&str, info)); if (!info->addr.postind || !info->addr.writeback) { set_syntax_error (_("invalid addressing mode")); @@ -5347,6 +5975,130 @@ parse_operands (char *str, const aarch64_opcode *opcode) /* No qualifier. */ break; + case AARCH64_OPND_SVE_ADDR_RI_S4xVL: + case AARCH64_OPND_SVE_ADDR_RI_S4x2xVL: + case AARCH64_OPND_SVE_ADDR_RI_S4x3xVL: + case AARCH64_OPND_SVE_ADDR_RI_S4x4xVL: + case AARCH64_OPND_SVE_ADDR_RI_S6xVL: + case AARCH64_OPND_SVE_ADDR_RI_S9xVL: + case AARCH64_OPND_SVE_ADDR_RI_U6: + case AARCH64_OPND_SVE_ADDR_RI_U6x2: + case AARCH64_OPND_SVE_ADDR_RI_U6x4: + case AARCH64_OPND_SVE_ADDR_RI_U6x8: + /* [X{, #imm, MUL VL}] + [X{, #imm}] + but recognizing SVE registers. */ + po_misc_or_fail (parse_sve_address (&str, info, &base_qualifier, + &offset_qualifier)); + if (base_qualifier != AARCH64_OPND_QLF_X) + { + set_syntax_error (_("invalid addressing mode")); + goto failure; + } + sve_regimm: + if (info->addr.pcrel || info->addr.offset.is_reg + || !info->addr.preind || info->addr.writeback) + { + set_syntax_error (_("invalid addressing mode")); + goto failure; + } + if (inst.reloc.type != BFD_RELOC_UNUSED + || inst.reloc.exp.X_op != O_constant) + { + /* Make sure this has priority over + "invalid addressing mode". */ + set_fatal_syntax_error (_("constant offset required")); + goto failure; + } + info->addr.offset.imm = inst.reloc.exp.X_add_number; + break; + + case AARCH64_OPND_SVE_ADDR_RR: + case AARCH64_OPND_SVE_ADDR_RR_LSL1: + case AARCH64_OPND_SVE_ADDR_RR_LSL2: + case AARCH64_OPND_SVE_ADDR_RR_LSL3: + case AARCH64_OPND_SVE_ADDR_RX: + case AARCH64_OPND_SVE_ADDR_RX_LSL1: + case AARCH64_OPND_SVE_ADDR_RX_LSL2: + case AARCH64_OPND_SVE_ADDR_RX_LSL3: + /* [, {, lsl #}] + but recognizing SVE registers. */ + po_misc_or_fail (parse_sve_address (&str, info, &base_qualifier, + &offset_qualifier)); + if (base_qualifier != AARCH64_OPND_QLF_X + || offset_qualifier != AARCH64_OPND_QLF_X) + { + set_syntax_error (_("invalid addressing mode")); + goto failure; + } + goto regoff_addr; + + case AARCH64_OPND_SVE_ADDR_RZ: + case AARCH64_OPND_SVE_ADDR_RZ_LSL1: + case AARCH64_OPND_SVE_ADDR_RZ_LSL2: + case AARCH64_OPND_SVE_ADDR_RZ_LSL3: + case AARCH64_OPND_SVE_ADDR_RZ_XTW_14: + case AARCH64_OPND_SVE_ADDR_RZ_XTW_22: + case AARCH64_OPND_SVE_ADDR_RZ_XTW1_14: + case AARCH64_OPND_SVE_ADDR_RZ_XTW1_22: + case AARCH64_OPND_SVE_ADDR_RZ_XTW2_14: + case AARCH64_OPND_SVE_ADDR_RZ_XTW2_22: + case AARCH64_OPND_SVE_ADDR_RZ_XTW3_14: + case AARCH64_OPND_SVE_ADDR_RZ_XTW3_22: + /* [, Z.D{, LSL #}] + [, Z., {#}] */ + po_misc_or_fail (parse_sve_address (&str, info, &base_qualifier, + &offset_qualifier)); + if (base_qualifier != AARCH64_OPND_QLF_X + || (offset_qualifier != AARCH64_OPND_QLF_S_S + && offset_qualifier != AARCH64_OPND_QLF_S_D)) + { + set_syntax_error (_("invalid addressing mode")); + goto failure; + } + info->qualifier = offset_qualifier; + goto regoff_addr; + + case AARCH64_OPND_SVE_ADDR_ZI_U5: + case AARCH64_OPND_SVE_ADDR_ZI_U5x2: + case AARCH64_OPND_SVE_ADDR_ZI_U5x4: + case AARCH64_OPND_SVE_ADDR_ZI_U5x8: + /* [Z.{, #imm}] */ + po_misc_or_fail (parse_sve_address (&str, info, &base_qualifier, + &offset_qualifier)); + if (base_qualifier != AARCH64_OPND_QLF_S_S + && base_qualifier != AARCH64_OPND_QLF_S_D) + { + set_syntax_error (_("invalid addressing mode")); + goto failure; + } + info->qualifier = base_qualifier; + goto sve_regimm; + + case AARCH64_OPND_SVE_ADDR_ZZ_LSL: + case AARCH64_OPND_SVE_ADDR_ZZ_SXTW: + case AARCH64_OPND_SVE_ADDR_ZZ_UXTW: + /* [Z., Z.{, LSL #}] + [Z.D, Z.D, {#}] + + We don't reject: + + [Z.S, Z.S, {#}] + + here since we get better error messages by leaving it to + the qualifier checking routines. */ + po_misc_or_fail (parse_sve_address (&str, info, &base_qualifier, + &offset_qualifier)); + if ((base_qualifier != AARCH64_OPND_QLF_S_S + && base_qualifier != AARCH64_OPND_QLF_S_D) + || offset_qualifier != base_qualifier) + { + set_syntax_error (_("invalid addressing mode")); + goto failure; + } + info->qualifier = base_qualifier; + goto regoff_addr; + case AARCH64_OPND_SYSREG: if ((val = parse_sys_reg (&str, aarch64_sys_regs_hsh, 1, 0)) == PARSE_FAIL) @@ -5417,6 +6169,12 @@ sys_reg_ins: inst.base.operands[i].prfop = aarch64_prfops + val; break; + case AARCH64_OPND_BARRIER_PSB: + val = parse_barrier_psb (&str, &(info->hint_option)); + if (val == PARSE_FAIL) + goto failure; + break; + default: as_fatal (_("unhandled operand code %d"), operands[i]); } @@ -5798,7 +6556,7 @@ md_assemble (char *str) { /* Check that this instruction is supported for this CPU. */ if (!opcode->avariant - || !AARCH64_CPU_HAS_FEATURE (cpu_variant, *opcode->avariant)) + || !AARCH64_CPU_HAS_ALL_FEATURES (cpu_variant, *opcode->avariant)) { as_bad (_("selected processor does not support `%s'"), str); return; @@ -5815,8 +6573,7 @@ md_assemble (char *str) store the instruction information for the future fix-up. */ struct aarch64_inst *copy; gas_assert (inst.reloc.type != BFD_RELOC_UNUSED); - if ((copy = xmalloc (sizeof (struct aarch64_inst))) == NULL) - abort (); + copy = XNEW (struct aarch64_inst); memcpy (copy, &inst.base, sizeof (struct aarch64_inst)); output_inst (copy); } @@ -5884,11 +6641,13 @@ aarch64_canonicalize_symbol_name (char *name) #define REGDEF(s,n,t) { #s, n, REG_TYPE_##t, TRUE } #define REGNUM(p,n,t) REGDEF(p##n, n, t) -#define REGSET31(p,t) \ +#define REGSET16(p,t) \ REGNUM(p, 0,t), REGNUM(p, 1,t), REGNUM(p, 2,t), REGNUM(p, 3,t), \ REGNUM(p, 4,t), REGNUM(p, 5,t), REGNUM(p, 6,t), REGNUM(p, 7,t), \ REGNUM(p, 8,t), REGNUM(p, 9,t), REGNUM(p,10,t), REGNUM(p,11,t), \ - REGNUM(p,12,t), REGNUM(p,13,t), REGNUM(p,14,t), REGNUM(p,15,t), \ + REGNUM(p,12,t), REGNUM(p,13,t), REGNUM(p,14,t), REGNUM(p,15,t) +#define REGSET31(p,t) \ + REGSET16(p, t), \ REGNUM(p,16,t), REGNUM(p,17,t), REGNUM(p,18,t), REGNUM(p,19,t), \ REGNUM(p,20,t), REGNUM(p,21,t), REGNUM(p,22,t), REGNUM(p,23,t), \ REGNUM(p,24,t), REGNUM(p,25,t), REGNUM(p,26,t), REGNUM(p,27,t), \ @@ -5928,10 +6687,18 @@ static const reg_entry reg_names[] = { /* FP/SIMD registers. */ REGSET (v, VN), REGSET (V, VN), + + /* SVE vector registers. */ + REGSET (z, ZN), REGSET (Z, ZN), + + /* SVE predicate registers. */ + REGSET16 (p, PN), REGSET16 (P, PN) }; #undef REGDEF #undef REGNUM +#undef REGSET16 +#undef REGSET31 #undef REGSET #define N 1 @@ -6035,7 +6802,7 @@ aarch64_handle_align (fragS * fragP) { /* NOP = d503201f */ /* AArch64 instructions are always little-endian. */ - static char const aarch64_noop[4] = { 0x1f, 0x20, 0x03, 0xd5 }; + static unsigned char const aarch64_noop[4] = { 0x1f, 0x20, 0x03, 0xd5 }; int bytes, fix, noop_size; char *p; @@ -6092,11 +6859,15 @@ aarch64_init_frag (fragS * fragP, int max_chars) switch (fragP->fr_type) { - case rs_align: case rs_align_test: case rs_fill: mapping_state_2 (MAP_DATA, max_chars); break; + case rs_align: + /* PR 20364: We can get alignment frags in code sections, + so do not just assume that we should use the MAP_DATA state. */ + mapping_state_2 (subseg_text_p (now_seg) ? MAP_INSN : MAP_DATA, max_chars); + break; case rs_align_code: mapping_state_2 (MAP_INSN, max_chars); break; @@ -6680,12 +7451,30 @@ md_apply_fix (fixS * fixP, valueT * valP, segT seg) case BFD_RELOC_AARCH64_MOVW_G0: case BFD_RELOC_AARCH64_MOVW_G0_NC: case BFD_RELOC_AARCH64_MOVW_G0_S: + case BFD_RELOC_AARCH64_MOVW_GOTOFF_G0_NC: scale = 0; goto movw_common; case BFD_RELOC_AARCH64_MOVW_G1: case BFD_RELOC_AARCH64_MOVW_G1_NC: case BFD_RELOC_AARCH64_MOVW_G1_S: + case BFD_RELOC_AARCH64_MOVW_GOTOFF_G1: + scale = 16; + goto movw_common; + case BFD_RELOC_AARCH64_TLSDESC_OFF_G0_NC: + scale = 0; + S_SET_THREAD_LOCAL (fixP->fx_addsy); + /* Should always be exported to object file, see + aarch64_force_relocation(). */ + gas_assert (!fixP->fx_done); + gas_assert (seg->use_rela_p); + goto movw_common; + case BFD_RELOC_AARCH64_TLSDESC_OFF_G1: scale = 16; + S_SET_THREAD_LOCAL (fixP->fx_addsy); + /* Should always be exported to object file, see + aarch64_force_relocation(). */ + gas_assert (!fixP->fx_done); + gas_assert (seg->use_rela_p); goto movw_common; case BFD_RELOC_AARCH64_MOVW_G2: case BFD_RELOC_AARCH64_MOVW_G2_NC: @@ -6715,6 +7504,8 @@ md_apply_fix (fixS * fixP, valueT * valP, segT seg) case BFD_RELOC_AARCH64_MOVW_G1: case BFD_RELOC_AARCH64_MOVW_G2: case BFD_RELOC_AARCH64_MOVW_G3: + case BFD_RELOC_AARCH64_MOVW_GOTOFF_G1: + case BFD_RELOC_AARCH64_TLSDESC_OFF_G1: if (unsigned_overflow (value, scale + 16)) as_bad_where (fixP->fx_file, fixP->fx_line, _("unsigned value out of range")); @@ -6783,13 +7574,33 @@ md_apply_fix (fixS * fixP, valueT * valP, segT seg) case BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC: case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21: case BFD_RELOC_AARCH64_TLSGD_ADR_PREL21: + case BFD_RELOC_AARCH64_TLSGD_MOVW_G0_NC: + case BFD_RELOC_AARCH64_TLSGD_MOVW_G1: case BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case BFD_RELOC_AARCH64_TLSIE_LD32_GOTTPREL_LO12_NC: case BFD_RELOC_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19: + case BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC: + case BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G1: + case BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_HI12: + case BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_LO12: + case BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_LO12_NC: case BFD_RELOC_AARCH64_TLSLD_ADD_LO12_NC: case BFD_RELOC_AARCH64_TLSLD_ADR_PAGE21: case BFD_RELOC_AARCH64_TLSLD_ADR_PREL21: + case BFD_RELOC_AARCH64_TLSLD_LDST16_DTPREL_LO12: + case BFD_RELOC_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC: + case BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12: + case BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC: + case BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12: + case BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC: + case BFD_RELOC_AARCH64_TLSLD_LDST8_DTPREL_LO12: + case BFD_RELOC_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0_NC: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1_NC: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G2: case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12: case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12: case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC: @@ -6871,9 +7682,9 @@ tc_gen_reloc (asection * section, fixS * fixp) arelent *reloc; bfd_reloc_code_real_type code; - reloc = xmalloc (sizeof (arelent)); + reloc = XNEW (arelent); - reloc->sym_ptr_ptr = xmalloc (sizeof (asymbol *)); + reloc->sym_ptr_ptr = XNEW (asymbol *); *reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy); reloc->address = fixp->fx_frag->fr_address + fixp->fx_where; @@ -6992,16 +7803,38 @@ aarch64_force_relocation (struct fix *fixp) case BFD_RELOC_AARCH64_TLSDESC_LD32_LO12_NC: case BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC: case BFD_RELOC_AARCH64_TLSDESC_LD_PREL19: + case BFD_RELOC_AARCH64_TLSDESC_OFF_G0_NC: + case BFD_RELOC_AARCH64_TLSDESC_OFF_G1: case BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC: case BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21: case BFD_RELOC_AARCH64_TLSGD_ADR_PREL21: + case BFD_RELOC_AARCH64_TLSGD_MOVW_G0_NC: + case BFD_RELOC_AARCH64_TLSGD_MOVW_G1: case BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21: case BFD_RELOC_AARCH64_TLSIE_LD32_GOTTPREL_LO12_NC: case BFD_RELOC_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC: case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19: + case BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G0_NC: + case BFD_RELOC_AARCH64_TLSIE_MOVW_GOTTPREL_G1: + case BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_HI12: + case BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_LO12: + case BFD_RELOC_AARCH64_TLSLD_ADD_DTPREL_LO12_NC: case BFD_RELOC_AARCH64_TLSLD_ADD_LO12_NC: case BFD_RELOC_AARCH64_TLSLD_ADR_PAGE21: case BFD_RELOC_AARCH64_TLSLD_ADR_PREL21: + case BFD_RELOC_AARCH64_TLSLD_LDST16_DTPREL_LO12: + case BFD_RELOC_AARCH64_TLSLD_LDST16_DTPREL_LO12_NC: + case BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12: + case BFD_RELOC_AARCH64_TLSLD_LDST32_DTPREL_LO12_NC: + case BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12: + case BFD_RELOC_AARCH64_TLSLD_LDST64_DTPREL_LO12_NC: + case BFD_RELOC_AARCH64_TLSLD_LDST8_DTPREL_LO12: + case BFD_RELOC_AARCH64_TLSLD_LDST8_DTPREL_LO12_NC: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G0_NC: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G1_NC: + case BFD_RELOC_AARCH64_TLSLD_MOVW_DTPREL_G2: case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12: case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12: case BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC: @@ -7025,6 +7858,11 @@ aarch64_force_relocation (struct fix *fixp) const char * elf64_aarch64_target_format (void) { + if (strcmp (TARGET_OS, "cloudabi") == 0) + { + /* FIXME: What to do for ilp32_p ? */ + return target_big_endian ? "elf64-bigaarch64-cloudabi" : "elf64-littleaarch64-cloudabi"; + } if (target_big_endian) return ilp32_p ? "elf32-bigaarch64" : "elf64-bigaarch64"; else @@ -7156,7 +7994,7 @@ fill_instruction_hash_table (void) templates *templ, *new_templ; templ = hash_find (aarch64_ops_hsh, opcode->name); - new_templ = (templates *) xmalloc (sizeof (templates)); + new_templ = XNEW (templates); new_templ->opcode = opcode; new_templ->next = NULL; @@ -7187,8 +8025,7 @@ get_upper_str (const char *str) { char *ret; size_t len = strlen (str); - if ((ret = xmalloc (len + 1)) == NULL) - abort (); + ret = XNEWVEC (char, len + 1); convert_to_upper (ret, str, len); return ret; } @@ -7213,7 +8050,8 @@ md_begin (void) || (aarch64_reg_hsh = hash_new ()) == NULL || (aarch64_barrier_opt_hsh = hash_new ()) == NULL || (aarch64_nzcv_hsh = hash_new ()) == NULL - || (aarch64_pldop_hsh = hash_new ()) == NULL) + || (aarch64_pldop_hsh = hash_new ()) == NULL + || (aarch64_hint_opt_hsh = hash_new ()) == NULL) as_fatal (_("virtual memory exhausted")); fill_instruction_hash_table (); @@ -7227,24 +8065,24 @@ md_begin (void) aarch64_pstatefields[i].name, (void *) (aarch64_pstatefields + i)); - for (i = 0; aarch64_sys_regs_ic[i].template != NULL; i++) + for (i = 0; aarch64_sys_regs_ic[i].name != NULL; i++) checked_hash_insert (aarch64_sys_regs_ic_hsh, - aarch64_sys_regs_ic[i].template, + aarch64_sys_regs_ic[i].name, (void *) (aarch64_sys_regs_ic + i)); - for (i = 0; aarch64_sys_regs_dc[i].template != NULL; i++) + for (i = 0; aarch64_sys_regs_dc[i].name != NULL; i++) checked_hash_insert (aarch64_sys_regs_dc_hsh, - aarch64_sys_regs_dc[i].template, + aarch64_sys_regs_dc[i].name, (void *) (aarch64_sys_regs_dc + i)); - for (i = 0; aarch64_sys_regs_at[i].template != NULL; i++) + for (i = 0; aarch64_sys_regs_at[i].name != NULL; i++) checked_hash_insert (aarch64_sys_regs_at_hsh, - aarch64_sys_regs_at[i].template, + aarch64_sys_regs_at[i].name, (void *) (aarch64_sys_regs_at + i)); - for (i = 0; aarch64_sys_regs_tlbi[i].template != NULL; i++) + for (i = 0; aarch64_sys_regs_tlbi[i].name != NULL; i++) checked_hash_insert (aarch64_sys_regs_tlbi_hsh, - aarch64_sys_regs_tlbi[i].template, + aarch64_sys_regs_tlbi[i].name, (void *) (aarch64_sys_regs_tlbi + i)); for (i = 0; i < ARRAY_SIZE (reg_names); i++) @@ -7309,6 +8147,17 @@ md_begin (void) (void *) (aarch64_prfops + i)); } + for (i = 0; aarch64_hint_options[i].name != NULL; i++) + { + const char* name = aarch64_hint_options[i].name; + + checked_hash_insert (aarch64_hint_opt_hsh, name, + (void *) (aarch64_hint_options + i)); + /* Also hash the name in the upper case. */ + checked_hash_insert (aarch64_pldop_hsh, get_upper_str (name), + (void *) (aarch64_hint_options + i)); + } + /* Set the cpu variant based on the command-line options. */ if (!mcpu_cpu_opt) mcpu_cpu_opt = march_cpu_opt; @@ -7353,8 +8202,8 @@ size_t md_longopts_size = sizeof (md_longopts); struct aarch64_option_table { - char *option; /* Option name to match. */ - char *help; /* Help information. */ + const char *option; /* Option name to match. */ + const char *help; /* Help information. */ int *var; /* Variable to change. */ int value; /* What to change it to. */ char *deprecated; /* If non-null, print this message. */ @@ -7376,7 +8225,7 @@ static struct aarch64_option_table aarch64_opts[] = { struct aarch64_cpu_option_table { - char *name; + const char *name; const aarch64_feature_set value; /* The canonical name of the CPU, or NULL to use NAME converted to upper case. */ @@ -7387,18 +8236,28 @@ struct aarch64_cpu_option_table recognized by GCC. */ static const struct aarch64_cpu_option_table aarch64_cpus[] = { {"all", AARCH64_ANY, NULL}, + {"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"}, {"exynos-m1", AARCH64_FEATURE (AARCH64_ARCH_V8, AARCH64_FEATURE_CRC | AARCH64_FEATURE_CRYPTO), "Samsung Exynos M1"}, + {"qdf24xx", AARCH64_FEATURE (AARCH64_ARCH_V8, + AARCH64_FEATURE_CRC | AARCH64_FEATURE_CRYPTO), + "Qualcomm QDF24XX"}, {"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"}, /* The 'xgene-1' name is an older name for 'xgene1', which was used in earlier releases and is superseded by 'xgene1' in all tools. */ @@ -7413,7 +8272,7 @@ static const struct aarch64_cpu_option_table aarch64_cpus[] = { struct aarch64_arch_option_table { - char *name; + const char *name; const aarch64_feature_set value; }; @@ -7423,39 +8282,86 @@ 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}, {NULL, AARCH64_ARCH_NONE} }; /* ISA extensions. */ struct aarch64_option_cpu_value_table { - char *name; + const char *name; const aarch64_feature_set value; + const aarch64_feature_set require; /* Feature dependencies. */ }; static const struct aarch64_option_cpu_value_table aarch64_features[] = { - {"crc", AARCH64_FEATURE (AARCH64_FEATURE_CRC, 0)}, - {"crypto", AARCH64_FEATURE (AARCH64_FEATURE_CRYPTO, 0)}, - {"fp", AARCH64_FEATURE (AARCH64_FEATURE_FP, 0)}, - {"lse", AARCH64_FEATURE (AARCH64_FEATURE_LSE, 0)}, - {"simd", AARCH64_FEATURE (AARCH64_FEATURE_SIMD, 0)}, - {"pan", AARCH64_FEATURE (AARCH64_FEATURE_PAN, 0)}, - {"lor", AARCH64_FEATURE (AARCH64_FEATURE_LOR, 0)}, - {"rdma", AARCH64_FEATURE (AARCH64_FEATURE_SIMD - | AARCH64_FEATURE_RDMA, 0)}, - {NULL, AARCH64_ARCH_NONE} + {"crc", AARCH64_FEATURE (AARCH64_FEATURE_CRC, 0), + AARCH64_ARCH_NONE}, + {"crypto", AARCH64_FEATURE (AARCH64_FEATURE_CRYPTO, 0), + AARCH64_ARCH_NONE}, + {"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_ARCH_NONE}, + {"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)}, + {"profile", AARCH64_FEATURE (AARCH64_FEATURE_PROFILE, 0), + AARCH64_ARCH_NONE}, + {NULL, AARCH64_ARCH_NONE, AARCH64_ARCH_NONE}, }; struct aarch64_long_option_table { - char *option; /* Substring to match. */ - char *help; /* Help information. */ - int (*func) (char *subopt); /* Function to decode sub-option. */ + const char *option; /* Substring to match. */ + const char *help; /* Help information. */ + int (*func) (const char *subopt); /* Function to decode sub-option. */ char *deprecated; /* If non-null, print this message. */ }; +/* Transitive closure of features depending on set. */ +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; + + 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); + } + return set; +} + +/* Transitive closure of dependencies of set. */ +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; + + 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); + } + return set; +} + static int -aarch64_parse_features (char *str, const aarch64_feature_set **opt_p, +aarch64_parse_features (const char *str, const aarch64_feature_set **opt_p, bfd_boolean ext_only) { /* We insist on extensions being added before being removed. We achieve @@ -7463,7 +8369,7 @@ aarch64_parse_features (char *str, const aarch64_feature_set **opt_p, adding an extension (1) or removing it (0) and only allowing it to change in the order -1 -> 1 -> 0. */ int adding_value = -1; - aarch64_feature_set *ext_set = xmalloc (sizeof (aarch64_feature_set)); + aarch64_feature_set *ext_set = XNEW (aarch64_feature_set); /* Copy the feature set, so that we can modify it. */ *ext_set = **opt_p; @@ -7472,7 +8378,7 @@ aarch64_parse_features (char *str, const aarch64_feature_set **opt_p, while (str != NULL && *str != 0) { const struct aarch64_option_cpu_value_table *opt; - char *ext = NULL; + const char *ext = NULL; int optlen; if (!ext_only) @@ -7521,11 +8427,19 @@ aarch64_parse_features (char *str, const aarch64_feature_set **opt_p, for (opt = aarch64_features; opt->name != NULL; opt++) if (strncmp (opt->name, str, optlen) == 0) { + aarch64_feature_set set; + /* Add or remove the extension. */ if (adding_value) - AARCH64_MERGE_FEATURE_SETS (*ext_set, *ext_set, opt->value); + { + set = aarch64_feature_enable_set (opt->value); + AARCH64_MERGE_FEATURE_SETS (*ext_set, *ext_set, set); + } else - AARCH64_CLEAR_FEATURE (*ext_set, *ext_set, opt->value); + { + set = aarch64_feature_disable_set (opt->value); + AARCH64_CLEAR_FEATURE (*ext_set, *ext_set, set); + } break; } @@ -7542,10 +8456,10 @@ aarch64_parse_features (char *str, const aarch64_feature_set **opt_p, } static int -aarch64_parse_cpu (char *str) +aarch64_parse_cpu (const char *str) { const struct aarch64_cpu_option_table *opt; - char *ext = strchr (str, '+'); + const char *ext = strchr (str, '+'); size_t optlen; if (ext != NULL) @@ -7574,10 +8488,10 @@ aarch64_parse_cpu (char *str) } static int -aarch64_parse_arch (char *str) +aarch64_parse_arch (const char *str) { const struct aarch64_arch_option_table *opt; - char *ext = strchr (str, '+'); + const char *ext = strchr (str, '+'); size_t optlen; if (ext != NULL) @@ -7608,32 +8522,30 @@ aarch64_parse_arch (char *str) /* ABIs. */ struct aarch64_option_abi_value_table { - char *name; + const char *name; enum aarch64_abi_type value; }; static const struct aarch64_option_abi_value_table aarch64_abis[] = { {"ilp32", AARCH64_ABI_ILP32}, {"lp64", AARCH64_ABI_LP64}, - {NULL, 0} }; static int -aarch64_parse_abi (char *str) +aarch64_parse_abi (const char *str) { - const struct aarch64_option_abi_value_table *opt; - size_t optlen = strlen (str); + unsigned int i; - if (optlen == 0) + if (str[0] == '\0') { as_bad (_("missing abi name `%s'"), str); return 0; } - for (opt = aarch64_abis; opt->name != NULL; opt++) - if (strlen (opt->name) == optlen && strncmp (str, opt->name, optlen) == 0) + for (i = 0; i < ARRAY_SIZE (aarch64_abis); i++) + if (strcmp (str, aarch64_abis[i].name) == 0) { - aarch64_abi = opt->value; + aarch64_abi = aarch64_abis[i].value; return 1; } @@ -7654,7 +8566,7 @@ static struct aarch64_long_option_table aarch64_long_opts[] = { }; int -md_parse_option (int c, char *arg) +md_parse_option (int c, const char *arg) { struct aarch64_option_table *opt; struct aarch64_long_option_table *lopt;