/* tc-aarch64.c -- Assemble for the AArch64 ISA
- Copyright (C) 2009-2014 Free Software Foundation, Inc.
+ Copyright (C) 2009-2016 Free Software Foundation, Inc.
Contributed by ARM Ltd.
This file is part of GAS.
/* Constants for known architecture features. */
static const aarch64_feature_set cpu_default = CPU_DEFAULT;
-static const aarch64_feature_set aarch64_arch_any = AARCH64_ANY;
-static const aarch64_feature_set aarch64_arch_none = AARCH64_ARCH_NONE;
-
#ifdef OBJ_ELF
/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
static symbolS *GOT_symbol;
#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;
return inst.parsing_error.error;
}
-static inline void
-set_error_message (const char *error)
-{
- inst.parsing_error.error = error;
-}
-
static inline enum aarch64_operand_error_kind
get_error_kind (void)
{
return inst.parsing_error.kind;
}
-static inline void
-set_error_kind (enum aarch64_operand_error_kind kind)
-{
- inst.parsing_error.kind = kind;
-}
-
static inline void
set_error (enum aarch64_operand_error_kind kind, const char *error)
{
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. */
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: 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: 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) \
#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
#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[] =
{
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_R_Z:
+ msg = N_("integer or zero register expected");
+ break;
+ case REG_TYPE_R_SP:
+ msg = N_("integer or SP register expected");
+ break;
case REG_TYPE_R_Z_SP:
msg = N_("integer, zero or SP register expected");
break;
case REG_TYPE_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);
}
/* 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;
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:
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);
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. 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_reg_parse_32_64 (char **ccp, 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;
+
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.
+/* 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;
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"),
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;
}
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;
}
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.
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);
}
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
&& 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
(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;
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)
{
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;
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"));
}
name = xstrdup (str);
- new = xmalloc (sizeof (reg_entry));
+ new = XNEW (reg_entry);
new->name = name;
new->number = number;
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
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++)
insert_reg_alias (nbuf, old->number, old->type);
}
+ free (nbuf);
return TRUE;
}
{
enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
-#define TRANSITION(from, to) (mapstate == (from) && state == (to))
+ if (state == MAP_INSN)
+ /* AArch64 instructions require 4-byte alignment. When emitting
+ instructions into any section, record the appropriate section
+ alignment. */
+ record_alignment (now_seg, 2);
if (mapstate == state)
/* The mapping symbol has already been emitted.
There is nothing else to do. */
return;
- else if (TRANSITION (MAP_UNDEFINED, MAP_DATA))
- /* This case will be evaluated later in the next else. */
+
+#define TRANSITION(from, to) (mapstate == (from) && state == (to))
+ if (TRANSITION (MAP_UNDEFINED, MAP_DATA) && !subseg_text_p (now_seg))
+ /* Emit MAP_DATA within executable section in order. Otherwise, it will be
+ evaluated later in the next else. */
return;
else if (TRANSITION (MAP_UNDEFINED, MAP_INSN))
{
/* Only add the symbol if the offset is > 0:
- if we're at the first frag, check it's size > 0;
- if we're not at the first frag, then for sure
- the offset is > 0. */
+ if we're at the first frag, check it's size > 0;
+ if we're not at the first frag, then for sure
+ the offset is > 0. */
struct frag *const frag_first = seg_info (now_seg)->frchainP->frch_root;
const int add_symbol = (frag_now != frag_first)
|| (frag_now_fix () > 0);
if (add_symbol)
make_mapping_symbol (MAP_DATA, (valueT) 0, frag_first);
}
+#undef TRANSITION
mapping_state_2 (state, 0);
-#undef TRANSITION
}
/* Same as mapping_state, but MAX_CHARS bytes have already been
if (pool == NULL)
{
/* Create a new pool. */
- pool = xmalloc (sizeof (*pool));
+ pool = XNEW (literal_pool);
if (!pool)
return NULL;
{
/* 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);
}
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);
return;
}
- if (!need_pass_2)
+ /* Sections are assumed to start aligned. In executable section, there is no
+ MAP_DATA symbol pending. So we only align the address during
+ MAP_DATA --> MAP_INSN transition.
+ For other sections, this is not guaranteed. */
+ enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
+ if (!need_pass_2 && subseg_text_p (now_seg) && mapstate == MAP_DATA)
frag_align_code (2, 0);
+
#ifdef OBJ_ELF
mapping_state (MAP_INSN);
#endif
}
#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
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);
{"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},
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;
/* 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)
&& ((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;
/* 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)
}
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'. */
}
}
- 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"));
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;
{
const char *name;
int pc_rel;
+ bfd_reloc_code_real_type adr_type;
bfd_reloc_code_real_type adrp_type;
bfd_reloc_code_real_type movw_type;
bfd_reloc_code_real_type add_type;
bfd_reloc_code_real_type ldst_type;
+ bfd_reloc_code_real_type ld_literal_type;
};
static struct reloc_table_entry reloc_table[] = {
/* Low 12 bits of absolute address: ADD/i and LDR/STR */
{"lo12", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_ADD_LO12,
- BFD_RELOC_AARCH64_LDST_LO12},
+ BFD_RELOC_AARCH64_LDST_LO12,
+ 0},
/* Higher 21 bits of pc-relative page offset: ADRP */
{"pg_hi21", 1,
+ 0, /* adr_type */
BFD_RELOC_AARCH64_ADR_HI21_PCREL,
0,
0,
+ 0,
0},
/* Higher 21 bits of pc-relative page offset: ADRP, no check */
{"pg_hi21_nc", 1,
+ 0, /* adr_type */
BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL,
0,
0,
+ 0,
0},
/* Most significant bits 0-15 of unsigned address/value: MOVZ */
{"abs_g0", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G0,
0,
+ 0,
0},
/* Most significant bits 0-15 of signed address/value: MOVN/Z */
{"abs_g0_s", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G0_S,
0,
+ 0,
0},
/* Less significant bits 0-15 of address/value: MOVK, no check */
{"abs_g0_nc", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G0_NC,
0,
+ 0,
0},
/* Most significant bits 16-31 of unsigned address/value: MOVZ */
{"abs_g1", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G1,
0,
+ 0,
0},
/* Most significant bits 16-31 of signed address/value: MOVN/Z */
{"abs_g1_s", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G1_S,
0,
+ 0,
0},
/* Less significant bits 16-31 of address/value: MOVK, no check */
{"abs_g1_nc", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G1_NC,
0,
+ 0,
0},
/* Most significant bits 32-47 of unsigned address/value: MOVZ */
{"abs_g2", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G2,
0,
+ 0,
0},
/* Most significant bits 32-47 of signed address/value: MOVN/Z */
{"abs_g2_s", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G2_S,
0,
+ 0,
0},
/* Less significant bits 32-47 of address/value: MOVK, no check */
{"abs_g2_nc", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G2_NC,
0,
+ 0,
0},
/* Most significant bits 48-63 of signed/unsigned address/value: MOVZ */
{"abs_g3", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_MOVW_G3,
0,
+ 0,
0},
/* Get to the page containing GOT entry for a symbol. */
{"got", 1,
+ 0, /* adr_type */
BFD_RELOC_AARCH64_ADR_GOT_PAGE,
0,
0,
+ 0,
BFD_RELOC_AARCH64_GOT_LD_PREL19},
/* 12 bit offset into the page containing GOT entry for that symbol. */
{"got_lo12", 0,
+ 0, /* adr_type */
0,
0,
0,
- BFD_RELOC_AARCH64_LD_GOT_LO12_NC},
+ 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 */
+ 0,
+ 0,
+ 0,
+ 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 */
BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21,
0,
0,
+ 0,
0},
/* 12 bit offset into the page containing GOT TLS entry for a symbol */
{"tlsgd_lo12", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC,
+ 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 */
BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21,
0,
0,
- 0},
+ 0,
+ BFD_RELOC_AARCH64_TLSDESC_LD_PREL19},
/* 12 bit offset into the page containing GOT TLS entry for a symbol */
{"tlsdesc_lo12", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC,
- BFD_RELOC_AARCH64_TLSDESC_LD_LO12_NC},
+ BFD_RELOC_AARCH64_TLSDESC_LD_LO12_NC,
+ 0},
+
+ /* Get to the page containing GOT TLS entry for a symbol.
+ The same as GD, we allocate two consecutive GOT slots
+ for module index and module offset, the only difference
+ with GD is the module offset should be intialized to
+ zero without any outstanding runtime relocation. */
+ {"tlsldm", 0,
+ BFD_RELOC_AARCH64_TLSLD_ADR_PREL21, /* adr_type */
+ BFD_RELOC_AARCH64_TLSLD_ADR_PAGE21,
+ 0,
+ 0,
+ 0,
+ 0},
+
+ /* 12 bit offset into the page containing GOT TLS entry for a symbol */
+ {"tlsldm_lo12_nc", 0,
+ 0, /* adr_type */
+ 0,
+ 0,
+ BFD_RELOC_AARCH64_TLSLD_ADD_LO12_NC,
+ 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 */
BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21,
0,
0,
- 0},
+ 0,
+ BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_PREL19},
/* 12 bit offset into the page containing GOT TLS entry for a symbol */
{"gottprel_lo12", 0,
+ 0, /* adr_type */
0,
0,
0,
- BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_LO12_NC},
+ BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_LO12_NC,
+ 0},
/* Get tp offset for a symbol. */
{"tprel", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12,
+ 0,
0},
/* Get tp offset for a symbol. */
{"tprel_lo12", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12,
+ 0,
0},
/* Get tp offset for a symbol. */
{"tprel_hi12", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12,
+ 0,
0},
/* Get tp offset for a symbol. */
{"tprel_lo12_nc", 0,
+ 0, /* adr_type */
0,
0,
BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC,
+ 0,
0},
/* Most significant bits 32-47 of address/value: MOVZ. */
{"tprel_g2", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2,
0,
+ 0,
0},
/* Most significant bits 16-31 of address/value: MOVZ. */
{"tprel_g1", 0,
+ 0, /* adr_type */
0,
BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1,
0,
+ 0,
0},
/* Most significant bits 16-31 of address/value: MOVZ, no check. */
{"tprel_g1_nc", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC,
+ 0,
+ 0,
+ 0},
+
+ /* Most significant bits 0-15 of address/value: MOVZ. */
+ {"tprel_g0", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0,
+ 0,
+ 0,
+ 0},
+
+ /* Most significant bits 0-15 of address/value: MOVZ, no check. */
+ {"tprel_g0_nc", 0,
+ 0, /* adr_type */
+ 0,
+ BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC,
0,
- BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC,
0,
0},
- /* Most significant bits 0-15 of address/value: MOVZ. */
- {"tprel_g0", 0,
+ /* 15bit offset from got entry to base address of GOT table. */
+ {"gotpage_lo15", 0,
+ 0,
0,
- BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0,
0,
+ 0,
+ BFD_RELOC_AARCH64_LD64_GOTPAGE_LO15,
0},
- /* Most significant bits 0-15 of address/value: MOVZ, no check. */
- {"tprel_g0_nc", 0,
+ /* 14bit offset from got entry to base address of GOT table. */
+ {"gotpage_lo14", 0,
+ 0,
+ 0,
0,
- BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC,
0,
+ BFD_RELOC_AARCH64_LD32_GOTPAGE_LO14,
0},
};
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)
{
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))
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)
{
char *p = *str;
- int reg;
- int isreg32, isregzero;
+ const reg_entry *reg;
+ aarch64_opnd_qualifier_t base_qualifier;
+ aarch64_opnd_qualifier_t offset_qualifier;
expressionS *exp = &inst.reloc.exp;
if (! skip_past_char (&p, '['))
/* #:<reloc_op>:<symbol> */
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;
/* Try to parse a relocation modifier. Anything else is
return FALSE;
}
- if (entry->ldst_type == 0)
+ switch (operand->type)
+ {
+ case AARCH64_OPND_ADDR_PCREL21:
+ /* adr */
+ ty = entry->adr_type;
+ break;
+
+ default:
+ ty = entry->ld_literal_type;
+ break;
+ }
+
+ if (ty == 0)
{
set_syntax_error
(_("this relocation modifier is not allowed on this "
}
/* #:<reloc_op>:<expr> */
- /* Record the load/store relocation type. */
- inst.reloc.type = entry->ldst_type;
+ /* Record the relocation type. */
+ inst.reloc.type = ty;
inst.reloc.pc_rel = entry->pc_rel;
}
else
/* [ */
- /* Accept SP and reject ZR */
- reg = aarch64_reg_parse_32_64 (&p, 0, 1, &isreg32, &isregzero);
- if (reg == PARSE_FAIL || isreg32)
+ reg = aarch64_reg_parse_32_64 (&p, &base_qualifier);
+ if (!reg || !aarch64_check_reg_type (reg, REG_TYPE_R64_SP))
{
- set_syntax_error (_(get_reg_expected_msg (REG_TYPE_R_64)));
+ set_syntax_error (_(get_reg_expected_msg (REG_TYPE_R64_SP)));
return FALSE;
}
- operand->addr.base_regno = reg;
+ operand->addr.base_regno = reg->number;
/* [Xn */
if (skip_past_comma (&p))
/* [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_reg_parse_32_64 (&p, &offset_qualifier);
+ if (reg)
{
+ if (!aarch64_check_reg_type (reg, REG_TYPE_R_Z))
+ {
+ set_syntax_error (_(get_reg_expected_msg (REG_TYPE_R_Z)));
+ 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))
|| 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;
}
}
- else if (!isreg32)
+ else if (offset_qualifier == AARCH64_OPND_QLF_X)
{
set_syntax_error (_("invalid use of 64-bit register offset"));
return FALSE;
{
/* [Xn,#:<reloc_op>:<symbol> */
skip_past_char (&p, '#');
- if (reloc && skip_past_char (&p, ':'))
+ if (skip_past_char (&p, ':'))
{
struct reloc_table_entry *entry;
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))
return TRUE;
}
-/* Return TRUE on success; otherwise return FALSE. */
-static bfd_boolean
-parse_address (char **str, aarch64_opnd_info *operand,
- int accept_reg_post_index)
-{
- return parse_address_main (str, operand, 0, accept_reg_post_index);
-}
-
-/* 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_reloc (char **str, aarch64_opnd_info *operand)
+parse_address (char **str, aarch64_opnd_info *operand)
{
- return parse_address_main (str, operand, 1, 0);
+ return parse_address_main (str, operand);
}
/* Parse an operand for a MOVZ, MOVN or MOVK instruction.
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);
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;
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.
If IMPLE_DEFINED_P is non-zero, the function will also try to parse the
- implementation defined system register name S<op0>_<op1>_<Cn>_<Cm>_<op2>. */
+ implementation defined system register name S<op0>_<op1>_<Cn>_<Cm>_<op2>.
+
+ If PSTATEFIELD_P is non-zero, the function will parse the name as a PSTATE
+ field, otherwise as a system register.
+*/
static int
-parse_sys_reg (char **str, struct hash_control *sys_regs, int imple_defined_p)
+parse_sys_reg (char **str, struct hash_control *sys_regs,
+ int imple_defined_p, int pstatefield_p)
{
char *p, *q;
char buf[32];
}
else
{
+ if (pstatefield_p && !aarch64_pstatefield_supported_p (cpu_variant, o))
+ as_bad (_("selected processor does not support PSTATE field "
+ "name '%s'"), buf);
+ if (!pstatefield_p && !aarch64_sys_reg_supported_p (cpu_variant, o))
+ as_bad (_("selected processor does not support system register "
+ "name '%s'"), buf);
if (aarch64_sys_reg_deprecated_p (o))
as_warn (_("system register name '%s' is deprecated and may be "
-"removed in a future release"), buf);
+ "removed in a future release"), buf);
value = o->value;
}
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;
}
} \
} 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) \
{ \
/* 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
{
}
max_num_matched = 0;
- idx = -1;
+ idx = 0;
/* For each pattern. */
for (i = 0; i < AARCH64_MAX_QLF_SEQ_NUM; ++i, ++qualifiers_list)
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;
}
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
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')
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)
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;
/* 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);
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);
}
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'"),
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
/* 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:
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_G0_NC:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1:
- case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC:
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
set_syntax_error
(_("the specified relocation type is not allowed for MOVK"));
switch (inst.reloc.type)
{
case BFD_RELOC_AARCH64_MOVW_G0:
- case BFD_RELOC_AARCH64_MOVW_G0_S:
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;
break;
case BFD_RELOC_AARCH64_MOVW_G1:
- case BFD_RELOC_AARCH64_MOVW_G1_S:
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;
break;
case BFD_RELOC_AARCH64_MOVW_G2:
- case BFD_RELOC_AARCH64_MOVW_G2_S:
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)
{
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)
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
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;
aarch64_opnd_info *info = &inst.base.operands[i];
+ aarch64_reg_type reg_type;
DEBUG_TRACE ("parse operand %d", i);
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:
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:
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))
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))
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;
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))
{
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,
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);
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,
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"));
case AARCH64_OPND_ADDR_SIMPLE:
case AARCH64_OPND_SIMD_ADDR_SIMPLE:
- /* [<Xn|SP>{, #<simm>}] */
- 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;
+ {
+ /* [<Xn|SP>{, #<simm>}] */
+ 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:
/* [<Xn|SP>, <R><m>{, <extend> {<amount>}}] */
- 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
|| info->addr.writeback)
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,
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
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)
{
}
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:
/* [<Xn|SP>], <Xm|#<amount>> */
- 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"));
break;
case AARCH64_OPND_SYSREG:
- if ((val = parse_sys_reg (&str, aarch64_sys_regs_hsh, 1))
+ if ((val = parse_sys_reg (&str, aarch64_sys_regs_hsh, 1, 0))
== PARSE_FAIL)
{
set_syntax_error (_("unknown or missing system register name"));
break;
case AARCH64_OPND_PSTATEFIELD:
- if ((val = parse_sys_reg (&str, aarch64_pstatefield_hsh, 0))
+ if ((val = parse_sys_reg (&str, aarch64_pstatefield_hsh, 0, 1))
== PARSE_FAIL)
{
set_syntax_error (_("unknown or missing PSTATE field name"));
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]);
}
return TRUE;
}
+/* Check for loads and stores that will cause unpredictable behavior. */
+
+static void
+warn_unpredictable_ldst (aarch64_instruction *instr, char *str)
+{
+ aarch64_inst *base = &instr->base;
+ const aarch64_opcode *opcode = base->opcode;
+ const aarch64_opnd_info *opnds = base->operands;
+ switch (opcode->iclass)
+ {
+ case ldst_pos:
+ case ldst_imm9:
+ case ldst_unscaled:
+ case ldst_unpriv:
+ /* Loading/storing the base register is unpredictable if writeback. */
+ if ((aarch64_get_operand_class (opnds[0].type)
+ == AARCH64_OPND_CLASS_INT_REG)
+ && opnds[0].reg.regno == opnds[1].addr.base_regno
+ && opnds[1].addr.base_regno != REG_SP
+ && opnds[1].addr.writeback)
+ as_warn (_("unpredictable transfer with writeback -- `%s'"), str);
+ break;
+ case ldstpair_off:
+ case ldstnapair_offs:
+ case ldstpair_indexed:
+ /* Loading/storing the base register is unpredictable if writeback. */
+ if ((aarch64_get_operand_class (opnds[0].type)
+ == AARCH64_OPND_CLASS_INT_REG)
+ && (opnds[0].reg.regno == opnds[2].addr.base_regno
+ || opnds[1].reg.regno == opnds[2].addr.base_regno)
+ && opnds[2].addr.base_regno != REG_SP
+ && opnds[2].addr.writeback)
+ as_warn (_("unpredictable transfer with writeback -- `%s'"), str);
+ /* Load operations must load different registers. */
+ if ((opcode->opcode & (1 << 22))
+ && opnds[0].reg.regno == opnds[1].reg.regno)
+ as_warn (_("unpredictable load of register pair -- `%s'"), str);
+ break;
+ default:
+ break;
+ }
+}
+
/* A wrapper function to interface with libopcodes on encoding and
record the error message if there is any.
init_operand_error_report ();
+ /* Sections are assumed to start aligned. In executable section, there is no
+ MAP_DATA symbol pending. So we only align the address during
+ MAP_DATA --> MAP_INSN transition.
+ For other sections, this is not guaranteed. */
+ enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
+ if (!need_pass_2 && subseg_text_p (now_seg) && mapstate == MAP_DATA)
+ frag_align_code (2, 0);
+
saved_cond = inst.cond;
reset_aarch64_instruction (&inst);
inst.cond = saved_cond;
{
/* 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;
}
+ warn_unpredictable_ldst (&inst, str);
+
if (inst.reloc.type == BFD_RELOC_UNUSED
|| !inst.reloc.need_libopcodes_p)
output_inst (NULL);
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);
}
#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), \
/* 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
{
/* 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;
/* Record a mapping symbol for alignment frags. We will delete this
later if the alignment ends up empty. */
if (!fragP->tc_frag_data.recorded)
+ fragP->tc_frag_data.recorded = 1;
+
+ switch (fragP->fr_type)
{
- fragP->tc_frag_data.recorded = 1;
- 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_code:
- mapping_state_2 (MAP_INSN, max_chars);
- break;
- default:
- break;
- }
+ 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;
+ default:
+ break;
}
}
\f
}
break;
- case BFD_RELOC_AARCH64_JUMP26:
case BFD_RELOC_AARCH64_CALL26:
+ case BFD_RELOC_AARCH64_JUMP26:
if (fixP->fx_done || !seg->use_rela_p)
{
if (value & 3)
break;
case BFD_RELOC_AARCH64_MOVW_G0:
- case BFD_RELOC_AARCH64_MOVW_G0_S:
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_S:
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_S:
case BFD_RELOC_AARCH64_MOVW_G2_NC:
+ case BFD_RELOC_AARCH64_MOVW_G2_S:
scale = 32;
goto movw_common;
case BFD_RELOC_AARCH64_MOVW_G3:
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"));
case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC:
case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_TLSDESC_ADR_PREL21:
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_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:
gas_assert (seg->use_rela_p);
break;
- case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
- case BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL:
case BFD_RELOC_AARCH64_ADD_LO12:
- case BFD_RELOC_AARCH64_LDST8_LO12:
+ case BFD_RELOC_AARCH64_ADR_GOT_PAGE:
+ case BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL:
+ case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
+ case BFD_RELOC_AARCH64_GOT_LD_PREL19:
+ case BFD_RELOC_AARCH64_LD32_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_LD32_GOTPAGE_LO14:
+ case BFD_RELOC_AARCH64_LD64_GOTOFF_LO15:
+ case BFD_RELOC_AARCH64_LD64_GOTPAGE_LO15:
+ case BFD_RELOC_AARCH64_LD64_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_LDST128_LO12:
case BFD_RELOC_AARCH64_LDST16_LO12:
case BFD_RELOC_AARCH64_LDST32_LO12:
case BFD_RELOC_AARCH64_LDST64_LO12:
- case BFD_RELOC_AARCH64_LDST128_LO12:
- case BFD_RELOC_AARCH64_GOT_LD_PREL19:
- case BFD_RELOC_AARCH64_ADR_GOT_PAGE:
- case BFD_RELOC_AARCH64_LD64_GOT_LO12_NC:
- case BFD_RELOC_AARCH64_LD32_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_LDST8_LO12:
/* Should always be exported to object file, see
aarch64_force_relocation(). */
gas_assert (!fixP->fx_done);
break;
case BFD_RELOC_AARCH64_TLSDESC_ADD:
- case BFD_RELOC_AARCH64_TLSDESC_LDR:
case BFD_RELOC_AARCH64_TLSDESC_CALL:
+ case BFD_RELOC_AARCH64_TLSDESC_LDR:
break;
case BFD_RELOC_UNUSED:
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;
even if the symbol is extern or weak. */
return 0;
- case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_LO12_NC:
- case BFD_RELOC_AARCH64_TLSDESC_LD_LO12_NC:
case BFD_RELOC_AARCH64_LD_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSDESC_LD_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSIE_LD_GOTTPREL_LO12_NC:
/* Pseudo relocs that need to be fixed up according to
ilp32_p. */
return 0;
case BFD_RELOC_AARCH64_ADR_HI21_PCREL:
case BFD_RELOC_AARCH64_GOT_LD_PREL19:
case BFD_RELOC_AARCH64_LD32_GOT_LO12_NC:
+ case BFD_RELOC_AARCH64_LD32_GOTPAGE_LO14:
+ case BFD_RELOC_AARCH64_LD64_GOTOFF_LO15:
+ case BFD_RELOC_AARCH64_LD64_GOTPAGE_LO15:
case BFD_RELOC_AARCH64_LD64_GOT_LO12_NC:
case BFD_RELOC_AARCH64_LDST128_LO12:
case BFD_RELOC_AARCH64_LDST16_LO12:
case BFD_RELOC_AARCH64_LDST8_LO12:
case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC:
case BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE21:
+ case BFD_RELOC_AARCH64_TLSDESC_ADR_PREL21:
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:
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
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;
{
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;
}
|| (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 ();
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++)
(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;
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. */
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. */
recognized by GCC. */
static const struct aarch64_cpu_option_table aarch64_cpus[] = {
{"all", AARCH64_ANY, NULL},
- {"cortex-a53", AARCH64_FEATURE(AARCH64_ARCH_V8,
- AARCH64_FEATURE_CRC), "Cortex-A53"},
- {"cortex-a57", AARCH64_FEATURE(AARCH64_ARCH_V8,
- AARCH64_FEATURE_CRC), "Cortex-A57"},
- {"thunderx", AARCH64_ARCH_V8, "Cavium ThunderX"},
+ {"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. */
{"xgene-1", AARCH64_ARCH_V8, "APM X-Gene 1"},
{"xgene1", AARCH64_ARCH_V8, "APM X-Gene 1"},
- {"xgene2", AARCH64_FEATURE(AARCH64_ARCH_V8,
- AARCH64_FEATURE_CRC), "APM X-Gene 2"},
+ {"xgene2", AARCH64_FEATURE (AARCH64_ARCH_V8,
+ AARCH64_FEATURE_CRC), "APM X-Gene 2"},
{"generic", AARCH64_ARCH_V8, NULL},
{NULL, AARCH64_ARCH_NONE, NULL}
struct aarch64_arch_option_table
{
- char *name;
+ const char *name;
const aarch64_feature_set value;
};
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)},
- {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
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;
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)
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;
}
}
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)
}
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)
/* 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;
}
};
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;