/* tc-aarch64.c -- Assemble for the AArch64 ISA
- Copyright (C) 2009-2020 Free Software Foundation, Inc.
+ Copyright (C) 2009-2021 Free Software Foundation, Inc.
Contributed by ARM Ltd.
This file is part of GAS.
#include "as.h"
#include <limits.h>
#include <stdarg.h>
-#include "bfd_stdint.h"
+#include <stdint.h>
#define NO_RELOC 0
#include "safe-ctype.h"
#include "subsegs.h"
static aarch64_instruction inst;
-static bfd_boolean parse_operands (char *, const aarch64_opcode *);
-static bfd_boolean programmer_friendly_fixup (aarch64_instruction *);
+static bool parse_operands (char *, const aarch64_opcode *);
+static bool programmer_friendly_fixup (aarch64_instruction *);
#ifdef OBJ_ELF
# define now_instr_sequence seg_info \
inst.parsing_error.error = NULL;
}
-static inline bfd_boolean
+static inline bool
error_p (void)
{
return inst.parsing_error.kind != AARCH64_OPDE_NIL;
present. */
#define COND_ALWAYS 0x10
-typedef struct
-{
- const char *template;
- unsigned long value;
-} asm_barrier_opt;
-
typedef struct
{
const char *template;
#define skip_whitespace(str) do { if (*(str) == ' ') ++(str); } while (0)
-static inline bfd_boolean
+static inline bool
skip_past_char (char **str, char c)
{
if (**str == c)
{
(*str)++;
- return TRUE;
+ return true;
}
else
- return FALSE;
+ return false;
}
#define skip_past_comma(str) skip_past_char (str, ',')
/* Arithmetic expressions (possibly involving symbols). */
-static bfd_boolean in_my_get_expression_p = FALSE;
+static bool in_aarch64_get_expression = false;
+
+/* Third argument to aarch64_get_expression. */
+#define GE_NO_PREFIX false
+#define GE_OPT_PREFIX true
-/* Third argument to my_get_expression. */
-#define GE_NO_PREFIX 0
-#define GE_OPT_PREFIX 1
+/* Fourth argument to aarch64_get_expression. */
+#define ALLOW_ABSENT false
+#define REJECT_ABSENT true
+
+/* Fifth argument to aarch64_get_expression. */
+#define NORMAL_RESOLUTION false
/* Return TRUE if the string pointed by *STR is successfully parsed
as an valid expression; *EP will be filled with the information of
- such an expression. Otherwise return FALSE. */
+ such an expression. Otherwise return FALSE.
+
+ If ALLOW_IMMEDIATE_PREFIX is true then skip a '#' at the start.
+ If REJECT_ABSENT is true then trat missing expressions as an error.
+ If DEFER_RESOLUTION is true, then do not resolve expressions against
+ constant symbols. Necessary if the expression is part of a fixup
+ that uses a reloc that must be emitted. */
-static bfd_boolean
-my_get_expression (expressionS * ep, char **str, int prefix_mode,
- int reject_absent)
+static bool
+aarch64_get_expression (expressionS * ep,
+ char ** str,
+ bool allow_immediate_prefix,
+ bool reject_absent,
+ bool defer_resolution)
{
char *save_in;
segT seg;
- int prefix_present_p = 0;
+ bool prefix_present = false;
- switch (prefix_mode)
+ if (allow_immediate_prefix)
{
- case GE_NO_PREFIX:
- break;
- case GE_OPT_PREFIX:
if (is_immediate_prefix (**str))
{
(*str)++;
- prefix_present_p = 1;
+ prefix_present = true;
}
- break;
- default:
- abort ();
}
memset (ep, 0, sizeof (expressionS));
save_in = input_line_pointer;
input_line_pointer = *str;
- in_my_get_expression_p = TRUE;
- seg = expression (ep);
- in_my_get_expression_p = FALSE;
+ in_aarch64_get_expression = true;
+ if (defer_resolution)
+ seg = deferred_expression (ep);
+ else
+ seg = expression (ep);
+ in_aarch64_get_expression = false;
if (ep->X_op == O_illegal || (reject_absent && ep->X_op == O_absent))
{
/* We found a bad expression in md_operand(). */
*str = input_line_pointer;
input_line_pointer = save_in;
- if (prefix_present_p && ! error_p ())
+ if (prefix_present && ! error_p ())
set_fatal_syntax_error (_("bad expression"));
else
set_first_syntax_error (_("bad expression"));
- return FALSE;
+ return false;
}
#ifdef OBJ_AOUT
if (seg != absolute_section
&& seg != text_section
&& seg != data_section
- && seg != bss_section && seg != undefined_section)
+ && seg != bss_section
+ && seg != undefined_section)
{
set_syntax_error (_("bad segment"));
*str = input_line_pointer;
input_line_pointer = save_in;
- return FALSE;
+ return false;
}
#else
(void) seg;
*str = input_line_pointer;
input_line_pointer = save_in;
- return TRUE;
+ return true;
}
/* Turn a string in input_line_pointer into a floating point constant
void
md_operand (expressionS * exp)
{
- if (in_my_get_expression_p)
+ if (in_aarch64_get_expression)
exp->X_op = O_illegal;
}
/* Return TRUE if REG->TYPE is a valid type of TYPE; otherwise
return FALSE. */
-static bfd_boolean
+static bool
aarch64_check_reg_type (const reg_entry *reg, aarch64_reg_type type)
{
return (reg_type_masks[type] & (1 << reg->type)) != 0;
Accept only one occurrence of:
4b 8b 16b 2h 4h 8h 2s 4s 1d 2d
b h s d q */
-static bfd_boolean
+static bool
parse_vector_type_for_operand (aarch64_reg_type reg_type,
struct vector_type_el *parsed_type, char **str)
{
if (width != 1 && width != 2 && width != 4 && width != 8 && width != 16)
{
first_error_fmt (_("bad size %d in vector width specifier"), width);
- return FALSE;
+ return false;
}
elt_size:
first_error_fmt (_("unexpected character `%c' in element size"), *ptr);
else
first_error (_("missing element size"));
- return FALSE;
+ return false;
}
if (width != 0 && width * element_size != 64
&& width * element_size != 128
first_error_fmt (_
("invalid element size %d and vector size combination %c"),
width, *ptr);
- return FALSE;
+ return false;
}
ptr++;
*str = ptr;
- return TRUE;
+ return true;
}
/* *STR contains an SVE zero/merge predication suffix. Parse it into
*PARSED_TYPE and point *STR at the end of the suffix. */
-static bfd_boolean
+static bool
parse_predication_for_operand (struct vector_type_el *parsed_type, char **str)
{
char *ptr = *str;
*ptr);
else
first_error (_("missing predication type"));
- return FALSE;
+ return false;
}
parsed_type->width = 0;
*str = ptr + 1;
- return TRUE;
+ return true;
}
/* Parse a register of the type TYPE.
static int
parse_typed_reg (char **ccp, aarch64_reg_type type, aarch64_reg_type *rtype,
- struct vector_type_el *typeinfo, bfd_boolean in_reg_list)
+ struct vector_type_el *typeinfo, bool in_reg_list)
{
char *str = *ccp;
const reg_entry *reg = parse_reg (&str);
struct vector_type_el atype;
struct vector_type_el parsetype;
- bfd_boolean is_typed_vecreg = FALSE;
+ bool is_typed_vecreg = false;
atype.defined = 0;
atype.type = NT_invtype;
}
/* Register if of the form Vn.[bhsdq]. */
- is_typed_vecreg = TRUE;
+ is_typed_vecreg = true;
if (type == REG_TYPE_ZN || type == REG_TYPE_PN)
{
atype.defined |= NTA_HASINDEX;
- my_get_expression (&exp, &str, GE_NO_PREFIX, 1);
+ aarch64_get_expression (&exp, &str, GE_NO_PREFIX, REJECT_ABSENT,
+ NORMAL_RESOLUTION);
if (exp.X_op != O_constant)
{
struct vector_type_el atype;
char *str = *ccp;
int reg = parse_typed_reg (&str, type, rtype, &atype,
- /*in_reg_list= */ FALSE);
+ /*in_reg_list= */ false);
if (reg == PARSE_FAIL)
return PARSE_FAIL;
return reg;
}
-static inline bfd_boolean
+static inline bool
eq_vector_type_el (struct vector_type_el e1, struct vector_type_el e2)
{
return
int in_range;
int ret_val;
int i;
- bfd_boolean error = FALSE;
- bfd_boolean expect_index = FALSE;
+ bool error = false;
+ bool expect_index = false;
if (*str != '{')
{
val_range = val;
}
val = parse_typed_reg (&str, type, NULL, &typeinfo,
- /*in_reg_list= */ TRUE);
+ /*in_reg_list= */ true);
if (val == PARSE_FAIL)
{
set_first_syntax_error (_("invalid vector register in list"));
- error = TRUE;
+ error = true;
continue;
}
/* reject [bhsd]n */
if (type == REG_TYPE_VN && typeinfo.defined == 0)
{
set_first_syntax_error (_("invalid scalar register in list"));
- error = TRUE;
+ error = true;
continue;
}
if (typeinfo.defined & NTA_HASINDEX)
- expect_index = TRUE;
+ expect_index = true;
if (in_range)
{
{
set_first_syntax_error
(_("invalid range in vector register list"));
- error = TRUE;
+ error = true;
}
val_range++;
}
{
set_first_syntax_error
(_("type mismatch in vector register list"));
- error = TRUE;
+ error = true;
}
}
if (! error)
if (*str != '}')
{
set_first_syntax_error (_("end of vector register list not found"));
- error = TRUE;
+ error = true;
}
str++;
{
expressionS exp;
- my_get_expression (&exp, &str, GE_NO_PREFIX, 1);
+ aarch64_get_expression (&exp, &str, GE_NO_PREFIX, REJECT_ABSENT,
+ NORMAL_RESOLUTION);
if (exp.X_op != O_constant)
{
set_first_syntax_error (_("constant expression required."));
- error = TRUE;
+ error = true;
}
if (! skip_past_char (&str, ']'))
- error = TRUE;
+ error = true;
else
typeinfo_first.index = exp.X_add_number;
}
else
{
set_first_syntax_error (_("expected index"));
- error = TRUE;
+ error = true;
}
}
if (nb_regs > 4)
{
set_first_syntax_error (_("too many registers in vector register list"));
- error = TRUE;
+ error = true;
}
else if (nb_regs == 0)
{
set_first_syntax_error (_("empty vector register list"));
- error = TRUE;
+ error = true;
}
*ccp = str;
new->name = name;
new->number = number;
new->type = type;
- new->builtin = FALSE;
+ new->builtin = false;
str_hash_insert (aarch64_reg_hsh, name, new, 0);
If we find one, or if it looks sufficiently like one that we want to
handle any error here, return TRUE. Otherwise return FALSE. */
-static bfd_boolean
+static bool
create_register_alias (char *newname, char *p)
{
const reg_entry *old;
/* The input scrubber ensures that whitespace after the mnemonic is
collapsed to single spaces. */
oldname = p;
- if (strncmp (oldname, " .req ", 6) != 0)
- return FALSE;
+ if (!startswith (oldname, " .req "))
+ return false;
oldname += 6;
if (*oldname == '\0')
- return FALSE;
+ return false;
old = str_hash_find (aarch64_reg_hsh, oldname);
if (!old)
{
as_warn (_("unknown register '%s' -- .req ignored"), oldname);
- return TRUE;
+ return true;
}
/* If TC_CASE_SENSITIVE is defined, then newname already points to
if (insert_reg_alias (nbuf, old->number, old->type) == NULL)
{
free (nbuf);
- return TRUE;
+ return true;
}
}
}
free (nbuf);
- return TRUE;
+ return true;
}
/* Should never be called, as .req goes between the alias and the
/* Add the literal of size SIZE in *EXP to the relevant literal pool.
Return TRUE on success, otherwise return FALSE. */
-static bfd_boolean
+static bool
add_to_lit_pool (expressionS *exp, int size)
{
literal_pool *pool;
if (entry >= MAX_LITERAL_POOL_SIZE)
{
set_syntax_error (_("literal pool overflow"));
- return FALSE;
+ return false;
}
pool->literals[entry].exp = *exp;
exp->X_add_number = ((int) entry) * size;
exp->X_add_symbol = pool->symbol;
- return TRUE;
+ return true;
}
/* Can't use symbol_new here, so have to create a symbol and then at
state from being spoiled.
The function currently serves parse_constant_immediate and
parse_big_immediate only. */
-static bfd_boolean
+static bool
reg_name_p (char *str, aarch64_reg_type reg_type)
{
int reg;
/* Prevent the diagnostics state from being spoiled. */
if (error_p ())
- return FALSE;
+ return false;
reg = aarch64_reg_parse (&str, reg_type, NULL, NULL);
clear_error ();
if (reg == PARSE_FAIL)
- return FALSE;
+ return false;
skip_whitespace (str);
if (*str == ',' || is_end_of_line[(unsigned char) *str])
- return TRUE;
+ return true;
- return FALSE;
+ return false;
}
/* Parser functions used exclusively in instruction operands. */
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
+static bool
parse_immediate_expression (char **str, expressionS *exp,
aarch64_reg_type reg_type)
{
if (reg_name_p (*str, reg_type))
{
set_recoverable_error (_("immediate operand required"));
- return FALSE;
+ return false;
}
- my_get_expression (exp, str, GE_OPT_PREFIX, 1);
+ aarch64_get_expression (exp, str, GE_OPT_PREFIX, REJECT_ABSENT,
+ NORMAL_RESOLUTION);
if (exp->X_op == O_absent)
{
set_fatal_syntax_error (_("missing immediate expression"));
- return FALSE;
+ return false;
}
- return TRUE;
+ return true;
}
/* Constant immediate-value read function for use in insn parsing.
Return TRUE on success; otherwise return FALSE. */
-static bfd_boolean
+static bool
parse_constant_immediate (char **str, int64_t *val, aarch64_reg_type reg_type)
{
expressionS exp;
if (! parse_immediate_expression (str, &exp, reg_type))
- return FALSE;
+ return false;
if (exp.X_op != O_constant)
{
set_syntax_error (_("constant expression required"));
- return FALSE;
+ return false;
}
*val = exp.X_add_number;
- return TRUE;
+ return true;
}
static uint32_t
(+/-) n / 16 * power (2, r)
where n and r are integers such that 16 <= n <=31 and -3 <= r <= 4. */
-static bfd_boolean
+static bool
aarch64_imm_float_p (uint32_t imm)
{
/* If a single-precision floating-point value has the following bit
as an IEEE float without any loss of precision. Store the value in
*FPWORD if so. */
-static bfd_boolean
+static bool
can_convert_double_to_float (uint64_t imm, uint32_t *fpword)
{
/* If a double-precision floating-point value has the following bit
/* Lower 29 bits need to be 0s. */
if ((imm & 0x1fffffff) != 0)
- return FALSE;
+ return false;
/* Prepare the pattern for 'Eeeeeeeee'. */
if (((high32 >> 30) & 0x1) == 0)
/* Check E~~~. */
if ((high32 & 0x78000000) != pattern)
- return FALSE;
+ return false;
/* Check Eeee_eeee != 1111_1111. */
if ((high32 & 0x7ff00000) == 0x47f00000)
- return FALSE;
+ 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;
+ return true;
}
/* Return true if we should treat OPERAND as a double-precision
floating-point operand rather than a single-precision one. */
-static bfd_boolean
+static bool
double_precision_operand_p (const aarch64_opnd_info *operand)
{
/* Check for unsuffixed SVE registers, which are allowed
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,
+static bool
+parse_aarch64_imm_float (char **ccp, int *immed, bool dp_p,
aarch64_reg_type reg_type)
{
char *str = *ccp;
LITTLENUM_TYPE words[MAX_LITTLENUMS];
int64_t val = 0;
unsigned fpword = 0;
- bfd_boolean hex_p = FALSE;
+ bool hex_p = false;
skip_past_char (&str, '#');
fpnum = str;
skip_whitespace (fpnum);
- if (strncmp (fpnum, "0x", 2) == 0)
+ if (startswith (fpnum, "0x"))
{
/* Support the hexadecimal representation of the IEEE754 encoding.
Double-precision is expected when DP_P is TRUE, otherwise the
else
fpword = val;
- hex_p = TRUE;
+ hex_p = true;
}
else if (reg_name_p (str, reg_type))
{
set_recoverable_error (_("immediate operand required"));
- return FALSE;
+ return false;
}
if (! hex_p)
*immed = fpword;
*ccp = str;
- return TRUE;
+ return true;
invalid_fp:
set_fatal_syntax_error (_("invalid floating-point constant"));
- return FALSE;
+ return false;
}
/* Less-generic immediate-value read function with the possibility of loading
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
+static bool
parse_big_immediate (char **str, int64_t *imm, aarch64_reg_type reg_type)
{
char *ptr = *str;
if (reg_name_p (ptr, reg_type))
{
set_syntax_error (_("immediate operand required"));
- return FALSE;
+ return false;
}
- my_get_expression (&inst.reloc.exp, &ptr, GE_OPT_PREFIX, 1);
+ aarch64_get_expression (&inst.reloc.exp, &ptr, GE_OPT_PREFIX, REJECT_ABSENT,
+ NORMAL_RESOLUTION);
if (inst.reloc.exp.X_op == O_constant)
*imm = inst.reloc.exp.X_add_number;
*str = ptr;
- return TRUE;
+ return true;
}
/* Set operand IDX of the *INSTR that needs a GAS internal fixup.
/* Return TRUE if the instruction needs to be fixed up later internally by
the GAS; otherwise return FALSE. */
-static inline bfd_boolean
+static inline bool
aarch64_gas_internal_fixup_p (void)
{
return inst.reloc.type == BFD_RELOC_AARCH64_GAS_INTERNAL_FIXUP;
bfd_reloc_code_real_type ld_literal_type;
};
-static struct reloc_table_entry reloc_table[] = {
+static struct reloc_table_entry reloc_table[] =
+{
/* Low 12 bits of absolute address: ADD/i and LDR/STR */
{"lo12", 0,
0, /* adr_type */
return NULL;
}
+/* Returns 0 if the relocation should never be forced,
+ 1 if the relocation must be forced, and -1 if either
+ result is OK. */
+
+static signed int
+aarch64_force_reloc (unsigned int type)
+{
+ switch (type)
+ {
+ case BFD_RELOC_AARCH64_GAS_INTERNAL_FIXUP:
+ /* Perform these "immediate" internal relocations
+ even if the symbol is extern or weak. */
+ return 0;
+
+ 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_ADD_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_LDST8_LO12:
+ case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12:
+ 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:
+ 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_LDST16_TPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLE_LDST32_TPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLE_LDST64_TPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
+ case BFD_RELOC_AARCH64_TLSLE_LDST8_TPREL_LO12:
+ case BFD_RELOC_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
+ 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:
+ 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:
+ /* Always leave these relocations for the linker. */
+ return 1;
+
+ default:
+ return -1;
+ }
+}
+
+int
+aarch64_force_relocation (struct fix *fixp)
+{
+ int res = aarch64_force_reloc (fixp->fx_r_type);
+
+ if (res == -1)
+ return generic_force_reloc (fixp);
+ return res;
+}
+
/* Mode argument to parse_shift and parser_shifter_operand. */
enum parse_shift_mode
{
/* Parse a <shift> operator on an AArch64 data processing instruction.
Return TRUE on success; otherwise return FALSE. */
-static bfd_boolean
+static bool
parse_shift (char **str, aarch64_opnd_info *operand, enum parse_shift_mode mode)
{
const struct aarch64_name_value_pair *shift_op;
if (p == *str)
{
set_syntax_error (_("shift expression expected"));
- return FALSE;
+ return false;
}
shift_op = str_hash_find_n (aarch64_shift_hsh, *str, p - *str);
if (shift_op == NULL)
{
set_syntax_error (_("shift operator expected"));
- return FALSE;
+ return false;
}
kind = aarch64_get_operand_modifier (shift_op);
if (kind == AARCH64_MOD_MSL && mode != SHIFTED_LSL_MSL)
{
set_syntax_error (_("invalid use of 'MSL'"));
- return FALSE;
+ return false;
}
if (kind == AARCH64_MOD_MUL
&& mode != SHIFTED_MUL_VL)
{
set_syntax_error (_("invalid use of 'MUL'"));
- return FALSE;
+ return false;
}
switch (mode)
if (aarch64_extend_operator_p (kind))
{
set_syntax_error (_("extending shift is not permitted"));
- return FALSE;
+ return false;
}
break;
if (kind == AARCH64_MOD_ROR)
{
set_syntax_error (_("'ROR' shift is not permitted"));
- return FALSE;
+ return false;
}
break;
if (kind != AARCH64_MOD_LSL)
{
set_syntax_error (_("only 'LSL' shift is permitted"));
- return FALSE;
+ return false;
}
break;
if (kind != AARCH64_MOD_MUL)
{
set_syntax_error (_("only 'MUL' is permitted"));
- return FALSE;
+ return false;
}
break;
}
}
set_syntax_error (_("only 'MUL VL' is permitted"));
- return FALSE;
+ return false;
case SHIFTED_REG_OFFSET:
if (kind != AARCH64_MOD_UXTW && kind != AARCH64_MOD_LSL
{
set_fatal_syntax_error
(_("invalid shift for the register offset addressing mode"));
- return FALSE;
+ return false;
}
break;
if (kind != AARCH64_MOD_LSL && kind != AARCH64_MOD_MSL)
{
set_syntax_error (_("invalid shift operator"));
- return FALSE;
+ return false;
}
break;
p++;
exp_has_prefix = 1;
}
- my_get_expression (&exp, &p, GE_NO_PREFIX, 0);
+ (void) aarch64_get_expression (&exp, &p, GE_NO_PREFIX, ALLOW_ABSENT,
+ NORMAL_RESOLUTION);
}
if (kind == AARCH64_MOD_MUL_VL)
/* For consistency, give MUL VL the same shift amount as an implicit
if (!aarch64_extend_operator_p (kind) || exp_has_prefix)
{
set_syntax_error (_("missing shift amount"));
- return FALSE;
+ return false;
}
operand->shifter.amount = 0;
}
else if (exp.X_op != O_constant)
{
set_syntax_error (_("constant shift amount required"));
- return FALSE;
+ return false;
}
/* For parsing purposes, MUL #n has no inherent range. The range
depends on the operand and will be checked by operand-specific
&& (exp.X_add_number < 0 || exp.X_add_number > 63))
{
set_fatal_syntax_error (_("shift amount out of range 0 to 63"));
- return FALSE;
+ return false;
}
else
{
operand->shifter.kind = kind;
*str = p;
- return TRUE;
+ return true;
}
/* Parse a <shifter_operand> for a data processing instruction:
Return TRUE on success; otherwise return FALSE. */
-static bfd_boolean
+static bool
parse_shifter_operand_imm (char **str, aarch64_opnd_info *operand,
enum parse_shift_mode mode)
{
char *p;
if (mode != SHIFTED_ARITH_IMM && mode != SHIFTED_LOGIC_IMM)
- return FALSE;
+ return false;
p = *str;
/* Accept an immediate expression. */
- if (! my_get_expression (&inst.reloc.exp, &p, GE_OPT_PREFIX, 1))
- return FALSE;
+ if (! aarch64_get_expression (&inst.reloc.exp, &p, GE_OPT_PREFIX,
+ REJECT_ABSENT, NORMAL_RESOLUTION))
+ return false;
/* Accept optional LSL for arithmetic immediate values. */
if (mode == SHIFTED_ARITH_IMM && skip_past_comma (&p))
if (! parse_shift (&p, operand, SHIFTED_LSL))
- return FALSE;
+ return false;
/* Not accept any shifter for logical immediate values. */
if (mode == SHIFTED_LOGIC_IMM && skip_past_comma (&p)
&& parse_shift (&p, operand, mode))
{
set_syntax_error (_("unexpected shift operator"));
- return FALSE;
+ return false;
}
*str = p;
- return TRUE;
+ return true;
}
/* Parse a <shifter_operand> for a data processing instruction:
Return TRUE on success; otherwise return FALSE. */
-static bfd_boolean
+static bool
parse_shifter_operand (char **str, aarch64_opnd_info *operand,
enum parse_shift_mode mode)
{
if (opd_class == AARCH64_OPND_CLASS_IMMEDIATE)
{
set_syntax_error (_("unexpected register in the immediate operand"));
- return FALSE;
+ return false;
}
if (!aarch64_check_reg_type (reg, REG_TYPE_R_Z))
{
set_syntax_error (_(get_reg_expected_msg (REG_TYPE_R_Z)));
- return FALSE;
+ return false;
}
operand->reg.regno = reg->number;
/* Accept optional shift operation on register. */
if (! skip_past_comma (str))
- return TRUE;
+ return true;
if (! parse_shift (str, operand, mode))
- return FALSE;
+ return false;
- return TRUE;
+ return true;
}
else if (opd_class == AARCH64_OPND_CLASS_MODIFIED_REG)
{
set_syntax_error
(_("integer register expected in the extended/shifted operand "
"register"));
- return FALSE;
+ return false;
}
/* We have a shifted immediate variable. */
/* Return TRUE on success; return FALSE otherwise. */
-static bfd_boolean
+static bool
parse_shifter_operand_reloc (char **str, aarch64_opnd_info *operand,
enum parse_shift_mode mode)
{
if (!(entry = find_reloc_table_entry (str)))
{
set_syntax_error (_("unknown relocation modifier"));
- return FALSE;
+ return false;
}
if (entry->add_type == 0)
{
set_syntax_error
(_("this relocation modifier is not allowed on this instruction"));
- return FALSE;
+ return false;
}
/* Save str before we decompose it. */
p = *str;
/* Next, we parse the expression. */
- if (! my_get_expression (&inst.reloc.exp, str, GE_NO_PREFIX, 1))
- return FALSE;
-
+ if (! aarch64_get_expression (&inst.reloc.exp, str, GE_NO_PREFIX,
+ REJECT_ABSENT,
+ aarch64_force_reloc (entry->add_type) == 1))
+ return false;
+
/* Record the relocation type (use the ADD variant here). */
inst.reloc.type = entry->add_type;
inst.reloc.pc_rel = entry->pc_rel;
/* If str is empty, we've reached the end, stop here. */
if (**str == '\0')
- return TRUE;
+ return true;
/* Otherwise, we have a shifted reloc modifier, so rewind to
recover the variable name and continue parsing for the shifter. */
for addressing modes not supported by the instruction, and to set
inst.reloc.type. */
-static bfd_boolean
+static bool
parse_address_main (char **str, aarch64_opnd_info *operand,
aarch64_opnd_qualifier_t *base_qualifier,
aarch64_opnd_qualifier_t *offset_qualifier,
if (! entry)
{
set_syntax_error (_("unknown relocation modifier"));
- return FALSE;
+ return false;
}
switch (operand->type)
set_syntax_error
(_("this relocation modifier is not allowed on this "
"instruction"));
- return FALSE;
+ return false;
}
/* #:<reloc_op>: */
- if (! my_get_expression (exp, &p, GE_NO_PREFIX, 1))
+ if (! aarch64_get_expression (exp, &p, GE_NO_PREFIX, REJECT_ABSENT,
+ aarch64_force_reloc (entry->add_type) == 1))
{
set_syntax_error (_("invalid relocation expression"));
- return FALSE;
+ return false;
}
-
/* #:<reloc_op>:<expr> */
/* Record the relocation type. */
inst.reloc.type = ty;
}
else
{
-
if (skip_past_char (&p, '='))
/* =immediate; need to generate the literal in the literal pool. */
inst.gen_lit_pool = 1;
- if (!my_get_expression (exp, &p, GE_NO_PREFIX, 1))
+ if (!aarch64_get_expression (exp, &p, GE_NO_PREFIX, REJECT_ABSENT,
+ NORMAL_RESOLUTION))
{
set_syntax_error (_("invalid address"));
- return FALSE;
+ return false;
}
}
*str = p;
- return TRUE;
+ return true;
}
/* [ */
if (!reg || !aarch64_check_reg_type (reg, base_type))
{
set_syntax_error (_(get_reg_expected_msg (base_type)));
- return FALSE;
+ return false;
}
operand->addr.base_regno = reg->number;
if (!aarch64_check_reg_type (reg, offset_type))
{
set_syntax_error (_(get_reg_expected_msg (offset_type)));
- return FALSE;
+ return false;
}
/* [Xn,Rm */
if (! parse_shift (&p, operand, SHIFTED_REG_OFFSET))
/* Use the diagnostics set in parse_shift, so not set new
error message here. */
- return FALSE;
+ return false;
}
/* We only accept:
[base,Xm] # For vector plus scalar SVE2 indexing.
if (*offset_qualifier == AARCH64_OPND_QLF_W)
{
set_syntax_error (_("invalid use of 32-bit register offset"));
- return FALSE;
+ return false;
}
if (aarch64_get_qualifier_esize (*base_qualifier)
!= aarch64_get_qualifier_esize (*offset_qualifier)
|| *offset_qualifier != AARCH64_OPND_QLF_X))
{
set_syntax_error (_("offset has different size from base"));
- return FALSE;
+ return false;
}
}
else if (*offset_qualifier == AARCH64_OPND_QLF_X)
{
set_syntax_error (_("invalid use of 64-bit register offset"));
- return FALSE;
+ return false;
}
}
else
if (!(entry = find_reloc_table_entry (&p)))
{
set_syntax_error (_("unknown relocation modifier"));
- return FALSE;
+ return false;
}
if (entry->ldst_type == 0)
set_syntax_error
(_("this relocation modifier is not allowed on this "
"instruction"));
- return FALSE;
+ return false;
}
/* [Xn,#:<reloc_op>: */
/* We now have the group relocation table entry corresponding to
the name in the assembler source. Next, we parse the
expression. */
- if (! my_get_expression (exp, &p, GE_NO_PREFIX, 1))
+ if (! aarch64_get_expression (exp, &p, GE_NO_PREFIX, REJECT_ABSENT,
+ aarch64_force_reloc (entry->add_type) == 1))
{
set_syntax_error (_("invalid relocation expression"));
- return FALSE;
+ return false;
}
/* [Xn,#:<reloc_op>:<expr> */
}
else
{
- if (! my_get_expression (exp, &p, GE_OPT_PREFIX, 1))
+ if (! aarch64_get_expression (exp, &p, GE_OPT_PREFIX, REJECT_ABSENT,
+ NORMAL_RESOLUTION))
{
set_syntax_error (_("invalid expression in the address"));
- return FALSE;
+ return false;
}
/* [Xn,<expr> */
if (imm_shift_mode != SHIFTED_NONE && skip_past_comma (&p))
/* [Xn,<expr>,<shifter> */
if (! parse_shift (&p, operand, imm_shift_mode))
- return FALSE;
+ return false;
}
}
}
if (! skip_past_char (&p, ']'))
{
set_syntax_error (_("']' expected"));
- return FALSE;
+ return false;
}
if (skip_past_char (&p, '!'))
{
set_syntax_error (_("register offset not allowed in pre-indexed "
"addressing mode"));
- return FALSE;
+ return false;
}
/* [Xn]! */
operand->addr.writeback = 1;
if (operand->addr.preind)
{
set_syntax_error (_("cannot combine pre- and post-indexing"));
- return FALSE;
+ return false;
}
reg = aarch64_reg_parse_32_64 (&p, offset_qualifier);
if (!aarch64_check_reg_type (reg, REG_TYPE_R_64))
{
set_syntax_error (_(get_reg_expected_msg (REG_TYPE_R_64)));
- return FALSE;
+ return false;
}
operand->addr.offset.regno = reg->number;
operand->addr.offset.is_reg = 1;
}
- else if (! my_get_expression (exp, &p, GE_OPT_PREFIX, 1))
+ else if (! aarch64_get_expression (exp, &p, GE_OPT_PREFIX, REJECT_ABSENT,
+ NORMAL_RESOLUTION))
{
/* [Xn],#expr */
set_syntax_error (_("invalid expression in the address"));
- return FALSE;
+ return false;
}
}
{
/* Reject [Rn]! */
set_syntax_error (_("missing offset in the pre-indexed address"));
- return FALSE;
+ return false;
}
}
else
}
*str = p;
- return TRUE;
+ return true;
}
/* Parse a base AArch64 address (as opposed to an SVE one). Return TRUE
on success. */
-static bfd_boolean
+static bool
parse_address (char **str, aarch64_opnd_info *operand)
{
aarch64_opnd_qualifier_t base_qualifier, offset_qualifier;
/* 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
+static bool
parse_sve_address (char **str, aarch64_opnd_info *operand,
aarch64_opnd_qualifier_t *base_qualifier,
aarch64_opnd_qualifier_t *offset_qualifier)
/* Parse an operand for a MOVZ, MOVN or MOVK instruction.
Return TRUE on success; otherwise return FALSE. */
-static bfd_boolean
+static bool
parse_half (char **str, int *internal_fixup_p)
{
char *p = *str;
/* Try to parse a relocation. Anything else is an error. */
++p;
+
if (!(entry = find_reloc_table_entry (&p)))
{
set_syntax_error (_("unknown relocation modifier"));
- return FALSE;
+ return false;
}
if (entry->movw_type == 0)
{
set_syntax_error
(_("this relocation modifier is not allowed on this instruction"));
- return FALSE;
+ return false;
}
inst.reloc.type = entry->movw_type;
else
*internal_fixup_p = 1;
- if (! my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX, 1))
- return FALSE;
+ if (! aarch64_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX, REJECT_ABSENT,
+ aarch64_force_reloc (inst.reloc.type) == 1))
+ return false;
*str = p;
- return TRUE;
+ return true;
}
/* Parse an operand for an ADRP instruction:
ADRP <Xd>, <label>
Return TRUE on success; otherwise return FALSE. */
-static bfd_boolean
+static bool
parse_adrp (char **str)
{
char *p;
if (!(entry = find_reloc_table_entry (&p)))
{
set_syntax_error (_("unknown relocation modifier"));
- return FALSE;
+ return false;
}
if (entry->adrp_type == 0)
{
set_syntax_error
(_("this relocation modifier is not allowed on this instruction"));
- return FALSE;
+ return false;
}
inst.reloc.type = entry->adrp_type;
inst.reloc.type = BFD_RELOC_AARCH64_ADR_HI21_PCREL;
inst.reloc.pc_rel = 1;
-
- if (! my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX, 1))
- return FALSE;
-
+ if (! aarch64_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX, REJECT_ABSENT,
+ aarch64_force_reloc (inst.reloc.type) == 1))
+ return false;
*str = p;
- return TRUE;
+ return true;
}
/* Miscellaneous. */
{
*val = i;
*str = q;
- return TRUE;
+ return true;
}
if (!parse_immediate_expression (&p, &exp, reg_type))
- return FALSE;
+ return false;
if (exp.X_op == O_constant
&& (uint64_t) exp.X_add_number < size)
{
*val = exp.X_add_number;
*str = p;
- return TRUE;
+ return true;
}
/* Use the default error for this operand. */
- return FALSE;
+ return false;
}
/* Parse an option for a preload instruction. Returns the encoding for the
parse_barrier (char **str)
{
char *p, *q;
- const asm_barrier_opt *o;
+ const struct aarch64_name_value_pair *o;
p = q = *str;
while (ISALPHA (*q))
line, only the error of the highest severity will be picked up for
issuing the diagnostics. */
-static inline bfd_boolean
+static inline bool
operand_error_higher_severity_p (enum aarch64_operand_error_kind lhs,
enum aarch64_operand_error_kind rhs)
{
/* Return TRUE if some operand error has been recorded during the
parsing of the current assembly line using the opcode *OPCODE;
otherwise return FALSE. */
-static inline bfd_boolean
+static inline bool
opcode_has_operand_error_p (const aarch64_opcode *opcode)
{
operand_error_record *record = operand_error_report.head;
info.index = idx;
info.kind = kind;
info.error = error;
- info.non_fatal = FALSE;
+ info.non_fatal = false;
record_operand_error_info (opcode, &info);
}
info.data[0] = extra_data[0];
info.data[1] = extra_data[1];
info.data[2] = extra_data[2];
- info.non_fatal = FALSE;
+ info.non_fatal = false;
record_operand_error_info (opcode, &info);
}
which is still not right. */
size_t len = strlen (get_mnemonic_name (str));
int i, qlf_idx;
- bfd_boolean result;
+ bool result;
char buf[2048];
aarch64_inst *inst_base = &inst.base;
const aarch64_opnd_qualifier_seq_t *qualifiers_list;
print due to the different instruction templates. */
static void
-output_operand_error_report (char *str, bfd_boolean non_fatal_only)
+output_operand_error_report (char *str, bool non_fatal_only)
{
int largest_error_pos;
const char *msg = NULL;
case AARCH64_OPND_Rm:
case AARCH64_OPND_Rt:
case AARCH64_OPND_Rt2:
+ case AARCH64_OPND_Rt_LS64:
case AARCH64_OPND_Rt_SP:
case AARCH64_OPND_Rs:
case AARCH64_OPND_Ra:
/* Process the relocation type for move wide instructions.
Return TRUE on success; otherwise return FALSE. */
-static bfd_boolean
+static bool
process_movw_reloc_info (void)
{
int is32;
case BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2:
set_syntax_error
(_("the specified relocation type is not allowed for MOVK"));
- return FALSE;
+ return false;
default:
break;
}
set_fatal_syntax_error
(_("the specified relocation type is not allowed for 32-bit "
"register"));
- return FALSE;
+ return false;
}
shift = 32;
break;
set_fatal_syntax_error
(_("the specified relocation type is not allowed for 32-bit "
"register"));
- return FALSE;
+ return false;
}
shift = 48;
break;
are supported in GAS. */
gas_assert (aarch64_gas_internal_fixup_p ());
/* The shift amount should have already been set by the parser. */
- return TRUE;
+ return true;
}
inst.base.operands[1].shifter.amount = shift;
- return TRUE;
+ return true;
}
/* A primitive log calculator. */
static inline bfd_reloc_code_real_type
ldst_lo12_determine_real_reloc_type (void)
{
- unsigned logsz;
+ unsigned logsz, max_logsz;
enum aarch64_opnd_qualifier opd0_qlf = inst.base.operands[0].qualifier;
enum aarch64_opnd_qualifier opd1_qlf = inst.base.operands[1].qualifier;
gas_assert (opd1_qlf != AARCH64_OPND_QLF_NIL);
logsz = get_logsz (aarch64_get_qualifier_esize (opd1_qlf));
+
if (inst.reloc.type == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12
|| inst.reloc.type == BFD_RELOC_AARCH64_TLSLD_LDST_DTPREL_LO12_NC
|| inst.reloc.type == BFD_RELOC_AARCH64_TLSLE_LDST_TPREL_LO12
|| inst.reloc.type == BFD_RELOC_AARCH64_TLSLE_LDST_TPREL_LO12_NC)
- gas_assert (logsz <= 3);
+ max_logsz = 3;
else
- gas_assert (logsz <= 4);
+ max_logsz = 4;
+
+ if (logsz > max_logsz)
+ {
+ /* SEE PR 27904 for an example of this. */
+ set_fatal_syntax_error
+ (_("relocation qualifier does not match instruction size"));
+ return BFD_RELOC_AARCH64_NONE;
+ }
/* In reloc.c, these pseudo relocation types should be defined in similar
order as above reloc_ldst_lo12 array. Because the array index calculation
Return FALSE if such a register list is invalid, otherwise return TRUE. */
-static bfd_boolean
+static bool
reg_list_valid_p (uint32_t reginfo, int accept_alternate)
{
uint32_t i, nb_regs, prev_regno, incr;
reginfo >>= 5;
curr_regno = reginfo & 0x1f;
if (curr_regno != ((prev_regno + incr) & 0x1f))
- return FALSE;
+ return false;
prev_regno = curr_regno;
}
- return TRUE;
+ return true;
}
/* Generic instruction operand parser. This does no encoding and no
structure. Returns TRUE or FALSE depending on whether the
specified grammar matched. */
-static bfd_boolean
+static bool
parse_operands (char *str, const aarch64_opcode *opcode)
{
int i;
case AARCH64_OPND_Rt2:
case AARCH64_OPND_Rs:
case AARCH64_OPND_Ra:
+ case AARCH64_OPND_Rt_LS64:
case AARCH64_OPND_Rt_SYS:
case AARCH64_OPND_PAIRREG:
case AARCH64_OPND_SVE_Rm:
po_int_reg_or_fail (REG_TYPE_R_Z);
+
+ /* In LS64 load/store instructions Rt register number must be even
+ and <=22. */
+ if (operands[i] == AARCH64_OPND_Rt_LS64)
+ {
+ /* We've already checked if this is valid register.
+ This will check if register number (Rt) is not undefined for LS64
+ instructions:
+ if Rt<4:3> == '11' || Rt<0> == '1' then UNDEFINED. */
+ if ((info->reg.regno & 0x18) == 0x18 || (info->reg.regno & 0x01) == 0x01)
+ {
+ set_syntax_error (_("invalid Rt register number in 64-byte load/store"));
+ goto failure;
+ }
+ }
break;
case AARCH64_OPND_Rd_SP:
case AARCH64_OPND_FPIMM0:
{
int qfloat;
- bfd_boolean res1 = FALSE, res2 = FALSE;
+ bool 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,
+ if (!(res1 = parse_aarch64_imm_float (&str, &qfloat, false,
imm_reg_type))
&& (error_p ()
|| !(res2 = parse_constant_immediate (&str, &val,
reg_name_p (str, REG_TYPE_VN))
goto failure;
str = saved;
- po_misc_or_fail (my_get_expression (&inst.reloc.exp, &str,
- GE_OPT_PREFIX, 1));
+ po_misc_or_fail (aarch64_get_expression (&inst.reloc.exp, &str,
+ GE_OPT_PREFIX, REJECT_ABSENT,
+ NORMAL_RESOLUTION));
/* The MOV immediate alias will be fixed up by fix_mov_imm_insn
later. fix_mov_imm_insn will try to determine a machine
instruction (MOVZ, MOVN or ORR) for it and will issue an error
case AARCH64_OPND_SVE_FPIMM8:
{
int qfloat;
- bfd_boolean dp_p;
+ bool dp_p;
dp_p = double_precision_operand_p (&inst.base.operands[0]);
if (!parse_aarch64_imm_float (&str, &qfloat, dp_p, imm_reg_type)
case AARCH64_OPND_SVE_I1_ZERO_ONE:
{
int qfloat;
- bfd_boolean dp_p;
+ bool dp_p;
dp_p = double_precision_operand_p (&inst.base.operands[0]);
if (!parse_aarch64_imm_float (&str, &qfloat, dp_p, imm_reg_type))
backtrack_pos = 0;
goto failure;
}
+ if (val != PARSE_FAIL
+ && operands[i] == AARCH64_OPND_BARRIER)
+ {
+ /* Regular barriers accept options CRm (C0-C15).
+ DSB nXS barrier variant accepts values > 15. */
+ if (val < 0 || val > 15)
+ {
+ set_syntax_error (_("the specified option is not accepted in DSB"));
+ goto failure;
+ }
+ }
/* This is an extension to accept a 0..15 immediate. */
if (val == PARSE_FAIL)
po_imm_or_fail (0, 15);
info->barrier = aarch64_barrier_options + val;
break;
+ case AARCH64_OPND_BARRIER_DSB_NXS:
+ val = parse_barrier (&str);
+ if (val != PARSE_FAIL)
+ {
+ /* DSB nXS barrier variant accept only <option>nXS qualifiers. */
+ if (!(val == 16 || val == 20 || val == 24 || val == 28))
+ {
+ set_syntax_error (_("the specified option is not accepted in DSB"));
+ /* Turn off backtrack as this optional operand is present. */
+ backtrack_pos = 0;
+ goto failure;
+ }
+ }
+ else
+ {
+ /* DSB nXS barrier variant accept 5-bit unsigned immediate, with
+ possible values 16, 20, 24 or 28 , encoded as val<3:2>. */
+ if (! parse_constant_immediate (&str, &val, imm_reg_type))
+ goto failure;
+ if (!(val == 16 || val == 20 || val == 24 || val == 28))
+ {
+ set_syntax_error (_("immediate value must be 16, 20, 24, 28"));
+ goto failure;
+ }
+ }
+ /* Option index is encoded as 2-bit value in val<3:2>. */
+ val = (val >> 2) - 4;
+ info->barrier = aarch64_barrier_dsb_nxs_options + val;
+ break;
+
case AARCH64_OPND_PRFOP:
val = parse_pldop (&str);
/* This is an extension to accept a 0..31 immediate. */
the problem. */
record_operand_error (opcode, i, get_error_kind (),
get_error_message ());
- return FALSE;
+ return false;
}
else
{
DEBUG_TRACE ("parsing SUCCESS");
- return TRUE;
+ return true;
}
}
the preferred architectural syntax.
Return FALSE if there is any failure; otherwise return TRUE. */
-static bfd_boolean
+static bool
programmer_friendly_fixup (aarch64_instruction *instr)
{
aarch64_inst *base = &instr->base;
{
record_operand_out_of_range_error (opcode, 1, _("immediate value"),
0, 31);
- return FALSE;
+ return false;
}
operands[0].qualifier = AARCH64_OPND_QLF_X;
}
record_operand_error (opcode, 1,
AARCH64_OPDE_FATAL_SYNTAX_ERROR,
_("constant expression expected"));
- return FALSE;
+ return false;
}
if (! add_to_lit_pool (&instr->reloc.exp, size))
{
record_operand_error (opcode, 1,
AARCH64_OPDE_OTHER_ERROR,
_("literal pool insertion failed"));
- return FALSE;
+ return false;
}
}
break;
}
DEBUG_TRACE ("exit with SUCCESS");
- return TRUE;
+ return true;
}
/* Check for loads and stores that will cause unpredictable behavior. */
break;
case ldstexcl:
- /* It is unpredictable if the destination and status registers are the
- same. */
if ((aarch64_get_operand_class (opnds[0].type)
== AARCH64_OPND_CLASS_INT_REG)
&& (aarch64_get_operand_class (opnds[1].type)
- == AARCH64_OPND_CLASS_INT_REG)
- && (opnds[0].reg.regno == opnds[1].reg.regno
- || opnds[0].reg.regno == opnds[2].reg.regno))
- as_warn (_("unpredictable: identical transfer and status registers"
- " --`%s'"),
- str);
+ == AARCH64_OPND_CLASS_INT_REG))
+ {
+ if ((opcode->opcode & (1 << 22)))
+ {
+ /* It is unpredictable if load-exclusive pair with Rt == Rt2. */
+ if ((opcode->opcode & (1 << 21))
+ && opnds[0].reg.regno == opnds[1].reg.regno)
+ as_warn (_("unpredictable load of register pair -- `%s'"), str);
+ }
+ else
+ {
+ /* Store-Exclusive is unpredictable if Rt == Rs. */
+ if (opnds[0].reg.regno == opnds[1].reg.regno)
+ as_warn
+ (_("unpredictable: identical transfer and status registers"
+ " --`%s'"),str);
+
+ if (opnds[0].reg.regno == opnds[2].reg.regno)
+ {
+ if (!(opcode->opcode & (1 << 21)))
+ /* Store-Exclusive is unpredictable if Rn == Rs. */
+ as_warn
+ (_("unpredictable: identical base and status registers"
+ " --`%s'"),str);
+ else
+ /* Store-Exclusive pair is unpredictable if Rt2 == Rs. */
+ as_warn
+ (_("unpredictable: "
+ "identical transfer and status registers"
+ " --`%s'"),str);
+ }
+ /* Store-Exclusive pair is unpredictable if Rn == Rs. */
+ if ((opcode->opcode & (1 << 21))
+ && opnds[0].reg.regno == opnds[3].reg.regno
+ && opnds[3].reg.regno != REG_SP)
+ as_warn (_("unpredictable: identical base and status registers"
+ " --`%s'"),str);
+ }
+ }
break;
default:
Return TRUE on success; otherwise return FALSE. */
-static bfd_boolean
+static bool
do_encode (const aarch64_opcode *opcode, aarch64_inst *instr,
aarch64_insn *code)
{
error_info.kind = AARCH64_OPDE_NIL;
if (aarch64_opcode_encode (opcode, instr, code, NULL, &error_info, insn_sequence)
&& !error_info.non_fatal)
- return TRUE;
+ return true;
gas_assert (error_info.kind != AARCH64_OPDE_NIL);
record_operand_error_info (opcode, &error_info);
}
/* Issue non-fatal messages if any. */
- output_operand_error_report (str, TRUE);
+ output_operand_error_report (str, true);
return;
}
while (template != NULL);
/* Issue the error messages if any. */
- output_operand_error_report (str, FALSE);
+ output_operand_error_report (str, false);
}
/* Various frobbings of labels and their addresses. */
int
aarch64_data_in_code (void)
{
- if (!strncmp (input_line_pointer + 1, "data:", 5))
+ if (startswith (input_line_pointer + 1, "data:"))
{
*input_line_pointer = '/';
input_line_pointer += 5;
should appear in both upper and lowercase variants. Some registers
also have mixed-case names. */
-#define REGDEF(s,n,t) { #s, n, REG_TYPE_##t, TRUE }
-#define REGDEF_ALIAS(s, n, t) { #s, n, REG_TYPE_##t, FALSE}
+#define REGDEF(s,n,t) { #s, n, REG_TYPE_##t, true }
+#define REGDEF_ALIAS(s, n, t) { #s, n, REG_TYPE_##t, false}
#define REGNUM(p,n,t) REGDEF(p##n, n, t)
#define REGSET16(p,t) \
REGNUM(p, 0,t), REGNUM(p, 1,t), REGNUM(p, 2,t), REGNUM(p, 3,t), \
range expressible by a unsigned number with the indicated number of
BITS. */
-static bfd_boolean
+static bool
unsigned_overflow (valueT value, unsigned bits)
{
valueT lim;
if (bits >= sizeof (valueT) * 8)
- return FALSE;
+ return false;
lim = (valueT) 1 << bits;
return (value >= lim);
}
range expressible by an signed number with the indicated number of
BITS. */
-static bfd_boolean
+static bool
signed_overflow (offsetT value, unsigned bits)
{
offsetT lim;
if (bits >= sizeof (offsetT) * 8)
- return FALSE;
+ return false;
lim = (offsetT) 1 << (bits - 1);
return (value < -lim || value >= lim);
}
in response to the standard LDR/STR mnemonics when the immediate offset is
unambiguous, i.e. when it is negative or unaligned. */
-static bfd_boolean
+static bool
try_to_encode_as_unscaled_ldst (aarch64_inst *instr)
{
int idx;
}
if (new_op == OP_NIL)
- return FALSE;
+ return false;
new_opcode = aarch64_get_opcode (new_op);
gas_assert (new_opcode != NULL);
if (!aarch64_opcode_encode (instr->opcode, instr, &instr->value, NULL, NULL,
insn_sequence))
- return FALSE;
+ return false;
- return TRUE;
+ return true;
}
/* Called by fix_insn to fix a MOV immediate alias instruction.
fix_new_exp (frag, where, (int) size, exp, pcrel, type);
}
-int
-aarch64_force_relocation (struct fix *fixp)
-{
- switch (fixp->fx_r_type)
- {
- case BFD_RELOC_AARCH64_GAS_INTERNAL_FIXUP:
- /* Perform these "immediate" internal relocations
- even if the symbol is extern or weak. */
- return 0;
-
- 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_ADD_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_LDST8_LO12:
- case BFD_RELOC_AARCH64_TLSDESC_ADD_LO12:
- 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:
- 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_LDST16_TPREL_LO12:
- case BFD_RELOC_AARCH64_TLSLE_LDST16_TPREL_LO12_NC:
- case BFD_RELOC_AARCH64_TLSLE_LDST32_TPREL_LO12:
- case BFD_RELOC_AARCH64_TLSLE_LDST32_TPREL_LO12_NC:
- case BFD_RELOC_AARCH64_TLSLE_LDST64_TPREL_LO12:
- case BFD_RELOC_AARCH64_TLSLE_LDST64_TPREL_LO12_NC:
- case BFD_RELOC_AARCH64_TLSLE_LDST8_TPREL_LO12:
- case BFD_RELOC_AARCH64_TLSLE_LDST8_TPREL_LO12_NC:
- 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:
- 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:
- /* Always leave these relocations for the linker. */
- return 1;
-
- default:
- break;
- }
-
- return generic_force_reloc (fixp);
-}
-
#ifdef OBJ_ELF
/* Implement md_after_parse_args. This is the earliest time we need to decide
(void *) (aarch64_barrier_options + i));
}
+ for (i = 0; i < ARRAY_SIZE (aarch64_barrier_dsb_nxs_options); i++)
+ {
+ const char *name = aarch64_barrier_dsb_nxs_options[i].name;
+ checked_hash_insert (aarch64_barrier_opt_hsh, name,
+ (void *) (aarch64_barrier_dsb_nxs_options + i));
+ /* Also hash the name in the upper case. */
+ checked_hash_insert (aarch64_barrier_opt_hsh, get_upper_str (name),
+ (void *) (aarch64_barrier_dsb_nxs_options + i));
+ }
+
for (i = 0; i < ARRAY_SIZE (aarch64_prfops); i++)
{
const char* name = aarch64_prfops[i].name;
| AARCH64_FEATURE_DOTPROD
| AARCH64_FEATURE_SSBS),
"Cortex-A65AE"},
+ {"cortex-a78", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+ AARCH64_FEATURE_F16
+ | AARCH64_FEATURE_RCPC
+ | AARCH64_FEATURE_DOTPROD
+ | AARCH64_FEATURE_SSBS
+ | AARCH64_FEATURE_PROFILE),
+ "Cortex-A78"},
+ {"cortex-a78ae", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+ AARCH64_FEATURE_F16
+ | AARCH64_FEATURE_RCPC
+ | AARCH64_FEATURE_DOTPROD
+ | AARCH64_FEATURE_SSBS
+ | AARCH64_FEATURE_PROFILE),
+ "Cortex-A78AE"},
+ {"cortex-a78c", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
+ AARCH64_FEATURE_DOTPROD
+ | AARCH64_FEATURE_F16
+ | AARCH64_FEATURE_FLAGM
+ | AARCH64_FEATURE_PAC
+ | AARCH64_FEATURE_PROFILE
+ | AARCH64_FEATURE_RCPC
+ | AARCH64_FEATURE_SSBS),
+ "Cortex-A78C"},
{"ares", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
AARCH64_FEATURE_RCPC | AARCH64_FEATURE_F16
| AARCH64_FEATURE_DOTPROD
| AARCH64_FEATURE_DOTPROD
| AARCH64_FEATURE_PROFILE),
"Neoverse N1"},
+ {"neoverse-n2", AARCH64_FEATURE (AARCH64_ARCH_V8_5,
+ AARCH64_FEATURE_BFLOAT16
+ | AARCH64_FEATURE_I8MM
+ | AARCH64_FEATURE_F16
+ | AARCH64_FEATURE_SVE
+ | AARCH64_FEATURE_SVE2
+ | AARCH64_FEATURE_SVE2_BITPERM
+ | AARCH64_FEATURE_MEMTAG
+ | AARCH64_FEATURE_RNG),
+ "Neoverse N2"},
{"neoverse-v1", AARCH64_FEATURE (AARCH64_ARCH_V8_4,
AARCH64_FEATURE_PROFILE
| AARCH64_FEATURE_CVADP
AARCH64_FEATURE_CRC), "APM X-Gene 2"},
{"cortex-r82", AARCH64_ARCH_V8_R, "Cortex-R82"},
{"cortex-x1", AARCH64_FEATURE (AARCH64_ARCH_V8_2,
- AARCH64_FEATURE_DOTPROD | AARCH64_FEATURE_PROFILE),
- "Cortex-X1"},
+ AARCH64_FEATURE_F16
+ | AARCH64_FEATURE_RCPC
+ | AARCH64_FEATURE_DOTPROD
+ | AARCH64_FEATURE_SSBS
+ | AARCH64_FEATURE_PROFILE),
+ "Cortex-X1"},
{"generic", AARCH64_ARCH_V8, NULL},
{NULL, AARCH64_ARCH_NONE, NULL}
{"armv8.4-a", AARCH64_ARCH_V8_4},
{"armv8.5-a", AARCH64_ARCH_V8_5},
{"armv8.6-a", AARCH64_ARCH_V8_6},
+ {"armv8.7-a", AARCH64_ARCH_V8_7},
{"armv8-r", AARCH64_ARCH_V8_R},
{NULL, AARCH64_ARCH_NONE}
};
AARCH64_FEATURE (AARCH64_FEATURE_SVE, 0)},
{"f64mm", AARCH64_FEATURE (AARCH64_FEATURE_F64MM, 0),
AARCH64_FEATURE (AARCH64_FEATURE_SVE, 0)},
+ {"ls64", AARCH64_FEATURE (AARCH64_FEATURE_LS64, 0),
+ AARCH64_ARCH_NONE},
+ {"flagm", AARCH64_FEATURE (AARCH64_FEATURE_FLAGM, 0),
+ AARCH64_ARCH_NONE},
+ {"pauth", AARCH64_FEATURE (AARCH64_FEATURE_PAC, 0),
+ AARCH64_ARCH_NONE},
{NULL, AARCH64_ARCH_NONE, AARCH64_ARCH_NONE},
};
static int
aarch64_parse_features (const char *str, const aarch64_feature_set **opt_p,
- bfd_boolean ext_only)
+ bool ext_only)
{
/* We insist on extensions being added before being removed. We achieve
this by using the ADDING_VALUE variable to indicate whether we are
else
optlen = strlen (str);
- if (optlen >= 2 && strncmp (str, "no", 2) == 0)
+ if (optlen >= 2 && startswith (str, "no"))
{
if (adding_value != 0)
adding_value = 0;
{
as_bad (_("must specify extensions to add before specifying "
"those to remove"));
- return FALSE;
+ return false;
}
}
{
mcpu_cpu_opt = &opt->value;
if (ext != NULL)
- return aarch64_parse_features (ext, &mcpu_cpu_opt, FALSE);
+ return aarch64_parse_features (ext, &mcpu_cpu_opt, false);
return 1;
}
{
march_cpu_opt = &opt->value;
if (ext != NULL)
- return aarch64_parse_features (ext, &march_cpu_opt, FALSE);
+ return aarch64_parse_features (ext, &march_cpu_opt, false);
return 1;
}
/* These options are expected to have an argument. */
if (c == lopt->option[0]
&& arg != NULL
- && strncmp (arg, lopt->option + 1,
- strlen (lopt->option + 1)) == 0)
+ && startswith (arg, lopt->option + 1))
{
/* If the option is deprecated, tell the user. */
if (lopt->deprecated != NULL)
{
mcpu_cpu_opt = &opt->value;
if (ext != NULL)
- if (!aarch64_parse_features (ext, &mcpu_cpu_opt, FALSE))
+ if (!aarch64_parse_features (ext, &mcpu_cpu_opt, false))
return;
cpu_variant = *mcpu_cpu_opt;
{
mcpu_cpu_opt = &opt->value;
if (ext != NULL)
- if (!aarch64_parse_features (ext, &mcpu_cpu_opt, FALSE))
+ if (!aarch64_parse_features (ext, &mcpu_cpu_opt, false))
return;
cpu_variant = *mcpu_cpu_opt;
saved_char = *input_line_pointer;
*input_line_pointer = 0;
- if (!aarch64_parse_features (ext, &mcpu_cpu_opt, TRUE))
+ if (!aarch64_parse_features (ext, &mcpu_cpu_opt, true))
return;
cpu_variant = *mcpu_cpu_opt;