/* tc-aarch64.c -- Assemble for the AArch64 ISA
Copyright 2009, 2010, 2011, 2012 Free Software Foundation, Inc.
Contributed by ARM Ltd.
This file is part of GAS.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3 of the license, or
(at your option) any later version.
GAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program; see the file COPYING3. If not,
see . */
#include "as.h"
#include
#include
#include "bfd_stdint.h"
#define NO_RELOC 0
#include "safe-ctype.h"
#include "subsegs.h"
#include "obstack.h"
#ifdef OBJ_ELF
#include "elf/aarch64.h"
#include "dw2gencfi.h"
#endif
#include "dwarf2dbg.h"
/* Types of processor to assemble for. */
#ifndef CPU_DEFAULT
#define CPU_DEFAULT AARCH64_ARCH_V8
#endif
#define streq(a, b) (strcmp (a, b) == 0)
static aarch64_feature_set cpu_variant;
/* Variables that we set while parsing command-line options. Once all
options have been read we re-process these values to set the real
assembly flags. */
static const aarch64_feature_set *mcpu_cpu_opt = NULL;
static const aarch64_feature_set *march_cpu_opt = NULL;
/* Constants for known architecture features. */
static const aarch64_feature_set cpu_default = CPU_DEFAULT;
static const aarch64_feature_set 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;
#endif
enum neon_el_type
{
NT_invtype = -1,
NT_b,
NT_h,
NT_s,
NT_d,
NT_q
};
/* Bits for DEFINED field in neon_type_el. */
#define NTA_HASTYPE 1
#define NTA_HASINDEX 2
struct neon_type_el
{
enum neon_el_type type;
unsigned char defined;
unsigned width;
int64_t index;
};
#define FIXUP_F_HAS_EXPLICIT_SHIFT 0x00000001
struct reloc
{
bfd_reloc_code_real_type type;
expressionS exp;
int pc_rel;
enum aarch64_opnd opnd;
uint32_t flags;
unsigned need_libopcodes_p : 1;
};
struct aarch64_instruction
{
/* libopcodes structure for instruction intermediate representation. */
aarch64_inst base;
/* Record assembly errors found during the parsing. */
struct
{
enum aarch64_operand_error_kind kind;
const char *error;
} parsing_error;
/* The condition that appears in the assembly line. */
int cond;
/* Relocation information (including the GAS internal fixup). */
struct reloc reloc;
/* Need to generate an immediate in the literal pool. */
unsigned gen_lit_pool : 1;
};
typedef struct aarch64_instruction aarch64_instruction;
static aarch64_instruction inst;
static bfd_boolean parse_operands (char *, const aarch64_opcode *);
static bfd_boolean programmer_friendly_fixup (aarch64_instruction *);
/* Diagnostics inline function utilites.
These are lightweight utlities which should only be called by parse_operands
and other parsers. GAS processes each assembly line by parsing it against
instruction template(s), in the case of multiple templates (for the same
mnemonic name), those templates are tried one by one until one succeeds or
all fail. An assembly line may fail a few templates before being
successfully parsed; an error saved here in most cases is not a user error
but an error indicating the current template is not the right template.
Therefore it is very important that errors can be saved at a low cost during
the parsing; we don't want to slow down the whole parsing by recording
non-user errors in detail.
Remember that the objective is to help GAS pick up the most approapriate
error message in the case of multiple templates, e.g. FMOV which has 8
templates. */
static inline void
clear_error (void)
{
inst.parsing_error.kind = AARCH64_OPDE_NIL;
inst.parsing_error.error = NULL;
}
static inline bfd_boolean
error_p (void)
{
return inst.parsing_error.kind != AARCH64_OPDE_NIL;
}
static inline const char *
get_error_message (void)
{
return inst.parsing_error.error;
}
static inline 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)
{
inst.parsing_error.kind = kind;
inst.parsing_error.error = error;
}
static inline void
set_recoverable_error (const char *error)
{
set_error (AARCH64_OPDE_RECOVERABLE, error);
}
/* Use the DESC field of the corresponding aarch64_operand entry to compose
the error message. */
static inline void
set_default_error (void)
{
set_error (AARCH64_OPDE_SYNTAX_ERROR, NULL);
}
static inline void
set_syntax_error (const char *error)
{
set_error (AARCH64_OPDE_SYNTAX_ERROR, error);
}
static inline void
set_first_syntax_error (const char *error)
{
if (! error_p ())
set_error (AARCH64_OPDE_SYNTAX_ERROR, error);
}
static inline void
set_fatal_syntax_error (const char *error)
{
set_error (AARCH64_OPDE_FATAL_SYNTAX_ERROR, error);
}
/* Number of littlenums required to hold an extended precision number. */
#define MAX_LITTLENUMS 6
/* Return value for certain parsers when the parsing fails; those parsers
return the information of the parsed result, e.g. register number, on
success. */
#define PARSE_FAIL -1
/* This is an invalid condition code that means no conditional field is
present. */
#define COND_ALWAYS 0x10
typedef struct
{
const char *template;
unsigned long value;
} asm_barrier_opt;
typedef struct
{
const char *template;
uint32_t value;
} asm_nzcv;
struct reloc_entry
{
char *name;
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. */
#undef AARCH64_REG_TYPES
#define AARCH64_REG_TYPES \
BASIC_REG_TYPE(R_32) /* w[0-30] */ \
BASIC_REG_TYPE(R_64) /* x[0-30] */ \
BASIC_REG_TYPE(SP_32) /* wsp */ \
BASIC_REG_TYPE(SP_64) /* sp */ \
BASIC_REG_TYPE(Z_32) /* wzr */ \
BASIC_REG_TYPE(Z_64) /* xzr */ \
BASIC_REG_TYPE(FP_B) /* b[0-31] *//* NOTE: keep FP_[BHSDQ] consecutive! */\
BASIC_REG_TYPE(FP_H) /* h[0-31] */ \
BASIC_REG_TYPE(FP_S) /* s[0-31] */ \
BASIC_REG_TYPE(FP_D) /* d[0-31] */ \
BASIC_REG_TYPE(FP_Q) /* q[0-31] */ \
BASIC_REG_TYPE(CN) /* c[0-7] */ \
BASIC_REG_TYPE(VN) /* v[0-31] */ \
/* 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) */ \
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) */ \
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) \
| REG_TYPE(FP_S) | REG_TYPE(FP_D) | REG_TYPE(FP_Q)) \
/* Any integer register; used for error messages only. */ \
MULTI_REG_TYPE(R_N, REG_TYPE(R_32) | REG_TYPE(R_64) \
| REG_TYPE(SP_32) | REG_TYPE(SP_64) \
| REG_TYPE(Z_32) | REG_TYPE(Z_64)) \
/* Pseudo type to mark the end of the enumerator sequence. */ \
BASIC_REG_TYPE(MAX)
#undef BASIC_REG_TYPE
#define BASIC_REG_TYPE(T) REG_TYPE_##T,
#undef MULTI_REG_TYPE
#define MULTI_REG_TYPE(T,V) BASIC_REG_TYPE(T)
/* Register type enumerators. */
typedef enum
{
/* A list of REG_TYPE_*. */
AARCH64_REG_TYPES
} aarch64_reg_type;
#undef BASIC_REG_TYPE
#define BASIC_REG_TYPE(T) 1 << REG_TYPE_##T,
#undef REG_TYPE
#define REG_TYPE(T) (1 << REG_TYPE_##T)
#undef MULTI_REG_TYPE
#define MULTI_REG_TYPE(T,V) V,
/* Values indexed by aarch64_reg_type to assist the type checking. */
static const unsigned reg_type_masks[] =
{
AARCH64_REG_TYPES
};
#undef BASIC_REG_TYPE
#undef REG_TYPE
#undef MULTI_REG_TYPE
#undef AARCH64_REG_TYPES
/* Diagnostics used when we don't get a register of the expected type.
Note: this has to synchronized with aarch64_reg_type definitions
above. */
static const char *
get_reg_expected_msg (aarch64_reg_type reg_type)
{
const char *msg;
switch (reg_type)
{
case REG_TYPE_R_32:
msg = N_("integer 32-bit register expected");
break;
case REG_TYPE_R_64:
msg = N_("integer 64-bit register expected");
break;
case REG_TYPE_R_N:
msg = N_("integer register expected");
break;
case REG_TYPE_R_Z_SP:
msg = N_("integer, zero or SP register expected");
break;
case REG_TYPE_FP_B:
msg = N_("8-bit SIMD scalar register expected");
break;
case REG_TYPE_FP_H:
msg = N_("16-bit SIMD scalar or floating-point half precision "
"register expected");
break;
case REG_TYPE_FP_S:
msg = N_("32-bit SIMD scalar or floating-point single precision "
"register expected");
break;
case REG_TYPE_FP_D:
msg = N_("64-bit SIMD scalar or floating-point double precision "
"register expected");
break;
case REG_TYPE_FP_Q:
msg = N_("128-bit SIMD scalar or floating-point quad precision "
"register expected");
break;
case REG_TYPE_CN:
msg = N_("C0 - C15 expected");
break;
case REG_TYPE_R_Z_BHSDQ_V:
msg = N_("register expected");
break;
case REG_TYPE_BHSDQ: /* any [BHSDQ]P FP */
msg = N_("SIMD scalar or floating-point register expected");
break;
case REG_TYPE_VN: /* any V reg */
msg = N_("vector register expected");
break;
default:
as_fatal (_("invalid register type %d"), reg_type);
}
return msg;
}
/* Some well known registers that we refer to directly elsewhere. */
#define REG_SP 31
/* 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_sys_regs_hsh;
static struct hash_control *aarch64_pstatefield_hsh;
static struct hash_control *aarch64_sys_regs_ic_hsh;
static struct hash_control *aarch64_sys_regs_dc_hsh;
static struct hash_control *aarch64_sys_regs_at_hsh;
static struct hash_control *aarch64_sys_regs_tlbi_hsh;
static struct hash_control *aarch64_reg_hsh;
static struct hash_control *aarch64_barrier_opt_hsh;
static struct hash_control *aarch64_nzcv_hsh;
static struct hash_control *aarch64_pldop_hsh;
/* Stuff needed to resolve the label ambiguity
As:
...
label:
may differ from:
...
label:
*/
static symbolS *last_label_seen;
/* Literal pool structure. Held on a per-section
and per-sub-section basis. */
#define MAX_LITERAL_POOL_SIZE 1024
typedef struct literal_pool
{
expressionS literals[MAX_LITERAL_POOL_SIZE];
unsigned int next_free_entry;
unsigned int id;
symbolS *symbol;
segT section;
subsegT sub_section;
int size;
struct literal_pool *next;
} literal_pool;
/* Pointer to a linked list of literal pools. */
static literal_pool *list_of_pools = NULL;
/* Pure syntax. */
/* This array holds the chars that always start a comment. If the
pre-processor is disabled, these aren't very useful. */
const char comment_chars[] = "";
/* This array holds the chars that only start a comment at the beginning of
a line. If the line seems to have the form '# 123 filename'
.line and .file directives will appear in the pre-processed output. */
/* Note that input_file.c hand checks for '#' at the beginning of the
first line of the input file. This is because the compiler outputs
#NO_APP at the beginning of its output. */
/* Also note that comments like this one will always work. */
const char line_comment_chars[] = "#";
const char line_separator_chars[] = ";";
/* Chars that can be used to separate mant
from exp in floating point numbers. */
const char EXP_CHARS[] = "eE";
/* Chars that mean this number is a floating point constant. */
/* As in 0f12.456 */
/* or 0d1.2345e12 */
const char FLT_CHARS[] = "rRsSfFdDxXeEpP";
/* Prefix character that indicates the start of an immediate value. */
#define is_immediate_prefix(C) ((C) == '#')
/* Separator character handling. */
#define skip_whitespace(str) do { if (*(str) == ' ') ++(str); } while (0)
static inline bfd_boolean
skip_past_char (char **str, char c)
{
if (**str == c)
{
(*str)++;
return TRUE;
}
else
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;
/* Third argument to my_get_expression. */
#define GE_NO_PREFIX 0
#define GE_OPT_PREFIX 1
/* 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. */
static bfd_boolean
my_get_expression (expressionS * ep, char **str, int prefix_mode,
int reject_absent)
{
char *save_in;
segT seg;
int prefix_present_p = 0;
switch (prefix_mode)
{
case GE_NO_PREFIX:
break;
case GE_OPT_PREFIX:
if (is_immediate_prefix (**str))
{
(*str)++;
prefix_present_p = 1;
}
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;
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 ())
set_fatal_syntax_error (_("bad expression"));
else
set_first_syntax_error (_("bad expression"));
return FALSE;
}
#ifdef OBJ_AOUT
if (seg != absolute_section
&& seg != text_section
&& seg != data_section
&& seg != bss_section && seg != undefined_section)
{
set_syntax_error (_("bad segment"));
*str = input_line_pointer;
input_line_pointer = save_in;
return FALSE;
}
#else
(void) seg;
#endif
*str = input_line_pointer;
input_line_pointer = save_in;
return TRUE;
}
/* Turn a string in input_line_pointer into a floating point constant
of type TYPE, and store the appropriate bytes in *LITP. The number
of LITTLENUMS emitted is stored in *SIZEP. An error message is
returned, or NULL on OK. */
char *
md_atof (int type, char *litP, int *sizeP)
{
return ieee_md_atof (type, litP, sizeP, target_big_endian);
}
/* We handle all bad expressions here, so that we can report the faulty
instruction in the error message. */
void
md_operand (expressionS * exp)
{
if (in_my_get_expression_p)
exp->X_op = O_illegal;
}
/* Immediate values. */
/* Errors may be set multiple times during parsing or bit encoding
(particularly in the Neon bits), but usually the earliest error which is set
will be the most meaningful. Avoid overwriting it with later (cascading)
errors by calling this function. */
static void
first_error (const char *error)
{
if (! error_p ())
set_syntax_error (error);
}
/* Similiar to first_error, but this function accepts formatted error
message. */
static void
first_error_fmt (const char *format, ...)
{
va_list args;
enum
{ size = 100 };
/* N.B. this single buffer will not cause error messages for different
instructions to pollute each other; this is because at the end of
processing of each assembly line, error message if any will be
collected by as_bad. */
static char buffer[size];
if (! error_p ())
{
int ret ATTRIBUTE_UNUSED;
va_start (args, format);
ret = vsnprintf (buffer, size, format, args);
know (ret <= size - 1 && ret >= 0);
va_end (args);
set_syntax_error (buffer);
}
}
/* Register parsing. */
/* Generic register parser which is called by other specialized
register parsers.
CCP points to what should be the beginning of a register name.
If it is indeed a valid register name, advance CCP over it and
return the reg_entry structure; otherwise return NULL.
It does not issue diagnostics. */
static reg_entry *
parse_reg (char **ccp)
{
char *start = *ccp;
char *p;
reg_entry *reg;
#ifdef REGISTER_PREFIX
if (*start != REGISTER_PREFIX)
return NULL;
start++;
#endif
p = start;
if (!ISALPHA (*p) || !is_name_beginner (*p))
return NULL;
do
p++;
while (ISALPHA (*p) || ISDIGIT (*p) || *p == '_');
reg = (reg_entry *) hash_find_n (aarch64_reg_hsh, start, p - start);
if (!reg)
return NULL;
*ccp = p;
return reg;
}
/* Return TRUE if REG->TYPE is a valid type of TYPE; otherwise
return FALSE. */
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 ();
}
}
/* 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.
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)
{
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;
switch (reg->type)
{
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;
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_Z_64:
if (reject_rz)
return PARSE_FAIL;
*isreg32 = reg->type == REG_TYPE_Z_32;
*isregzero = 1;
break;
default:
return PARSE_FAIL;
}
*ccp = str;
return reg->number;
}
/* 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.
Accept only one occurrence of:
8b 16b 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)
{
char *ptr = *str;
unsigned width;
unsigned element_size;
enum neon_el_type type;
/* skip '.' */
ptr++;
if (!ISDIGIT (*ptr))
{
width = 0;
goto elt_size;
}
width = strtoul (ptr, &ptr, 10);
if (width != 1 && width != 2 && width != 4 && width != 8 && width != 16)
{
first_error_fmt (_("bad size %d in vector width specifier"), width);
return FALSE;
}
elt_size:
switch (TOLOWER (*ptr))
{
case 'b':
type = NT_b;
element_size = 8;
break;
case 'h':
type = NT_h;
element_size = 16;
break;
case 's':
type = NT_s;
element_size = 32;
break;
case 'd':
type = NT_d;
element_size = 64;
break;
case 'q':
if (width == 1)
{
type = NT_q;
element_size = 128;
break;
}
/* fall through. */
default:
if (*ptr != '\0')
first_error_fmt (_("unexpected character `%c' in element size"), *ptr);
else
first_error (_("missing element size"));
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;
}
ptr++;
parsed_type->type = type;
parsed_type->width = width;
*str = ptr;
return TRUE;
}
/* Parse a single type, e.g. ".8b", leading period included.
Only applicable to Vn registers.
Return TRUE on success; otherwise return FALSE. */
static bfd_boolean
parse_neon_operand_type (struct neon_type_el *vectype, char **ccp)
{
char *str = *ccp;
if (*str == '.')
{
if (! parse_neon_type_for_operand (vectype, &str))
{
first_error (_("vector type expected"));
return FALSE;
}
}
else
return FALSE;
*ccp = str;
return TRUE;
}
/* Parse a register of the type TYPE.
Return PARSE_FAIL if the string pointed by *CCP is not a valid register
name or the parsed register is not of TYPE.
Otherwise return the register number, and optionally fill in the actual
type of the register in *RTYPE when multiple alternatives were given, and
return the register shape and element index information in *TYPEINFO.
IN_REG_LIST should be set with TRUE if the caller is parsing a register
list. */
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)
{
char *str = *ccp;
const reg_entry *reg = parse_reg (&str);
struct neon_type_el atype;
struct neon_type_el parsetype;
bfd_boolean is_typed_vecreg = FALSE;
atype.defined = 0;
atype.type = NT_invtype;
atype.width = -1;
atype.index = 0;
if (reg == NULL)
{
if (typeinfo)
*typeinfo = atype;
set_default_error ();
return PARSE_FAIL;
}
if (! aarch64_check_reg_type (reg, type))
{
DEBUG_TRACE ("reg type check failed");
set_default_error ();
return PARSE_FAIL;
}
type = reg->type;
if (type == REG_TYPE_VN
&& parse_neon_operand_type (&parsetype, &str))
{
/* Register if of the form Vn.[bhsdq]. */
is_typed_vecreg = TRUE;
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.
Except in reglists ofcourse. */
atype.defined |= NTA_HASINDEX;
else
atype.defined |= NTA_HASTYPE;
atype.type = parsetype.type;
atype.width = parsetype.width;
}
if (skip_past_char (&str, '['))
{
expressionS exp;
/* Reject Sn[index] syntax. */
if (!is_typed_vecreg)
{
first_error (_("this type of register can't be indexed"));
return PARSE_FAIL;
}
if (in_reg_list == TRUE)
{
first_error (_("index not allowed inside register list"));
return PARSE_FAIL;
}
atype.defined |= NTA_HASINDEX;
my_get_expression (&exp, &str, GE_NO_PREFIX, 1);
if (exp.X_op != O_constant)
{
first_error (_("constant expression required"));
return PARSE_FAIL;
}
if (! skip_past_char (&str, ']'))
return PARSE_FAIL;
atype.index = exp.X_add_number;
}
else if (!in_reg_list && (atype.defined & NTA_HASINDEX) != 0)
{
/* Indexed vector register expected. */
first_error (_("indexed vector register expected"));
return PARSE_FAIL;
}
/* A vector reg Vn should be typed or indexed. */
if (type == REG_TYPE_VN && atype.defined == 0)
{
first_error (_("invalid use of vector register"));
}
if (typeinfo)
*typeinfo = atype;
if (rtype)
*rtype = type;
*ccp = str;
return reg->number;
}
/* Parse register.
Return the register number on success; return PARSE_FAIL otherwise.
If RTYPE is not NULL, return in *RTYPE the (possibly restricted) type of
the register (e.g. NEON double or quad reg when either has been requested).
If this is a NEON vector register with additional type information, fill
in the struct pointed to by VECTYPE (if non-NULL).
This parser does not handle register list. */
static int
aarch64_reg_parse (char **ccp, aarch64_reg_type type,
aarch64_reg_type *rtype, struct neon_type_el *vectype)
{
struct neon_type_el atype;
char *str = *ccp;
int reg = parse_typed_reg (&str, type, rtype, &atype,
/*in_reg_list= */ FALSE);
if (reg == PARSE_FAIL)
return PARSE_FAIL;
if (vectype)
*vectype = atype;
*ccp = str;
return reg;
}
static inline bfd_boolean
eq_neon_type_el (struct neon_type_el e1, struct neon_type_el e2)
{
return
e1.type == e2.type
&& e1.defined == e2.defined
&& 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:
bit 18-22 | 13-17 | 7-11 | 2-6 | 0-1
4th regno | 3rd regno | 2nd regno | 1st regno | num_of_reg
The information of the register shape and/or index is returned in
*VECTYPE.
It returns PARSE_FAIL if the register list is invalid.
The list contains one to four registers.
Each register can be one of:
.[]
.
All should be identical.
All should be identical.
There are restrictions on numbers which are checked later
(by reg_list_valid_p). */
static int
parse_neon_reg_list (char **ccp, struct neon_type_el *vectype)
{
char *str = *ccp;
int nb_regs;
struct neon_type_el typeinfo, typeinfo_first;
int val, val_range;
int in_range;
int ret_val;
int i;
bfd_boolean error = FALSE;
bfd_boolean expect_index = FALSE;
if (*str != '{')
{
set_syntax_error (_("expecting {"));
return PARSE_FAIL;
}
str++;
nb_regs = 0;
typeinfo_first.defined = 0;
typeinfo_first.type = NT_invtype;
typeinfo_first.width = -1;
typeinfo_first.index = 0;
ret_val = 0;
val = -1;
val_range = -1;
in_range = 0;
do
{
if (in_range)
{
str++; /* skip over '-' */
val_range = val;
}
val = parse_typed_reg (&str, REG_TYPE_VN, NULL, &typeinfo,
/*in_reg_list= */ TRUE);
if (val == PARSE_FAIL)
{
set_first_syntax_error (_("invalid vector register in list"));
error = TRUE;
continue;
}
/* reject [bhsd]n */
if (typeinfo.defined == 0)
{
set_first_syntax_error (_("invalid scalar register in list"));
error = TRUE;
continue;
}
if (typeinfo.defined & NTA_HASINDEX)
expect_index = TRUE;
if (in_range)
{
if (val < val_range)
{
set_first_syntax_error
(_("invalid range in vector register list"));
error = TRUE;
}
val_range++;
}
else
{
val_range = val;
if (nb_regs == 0)
typeinfo_first = typeinfo;
else if (! eq_neon_type_el (typeinfo_first, typeinfo))
{
set_first_syntax_error
(_("type mismatch in vector register list"));
error = TRUE;
}
}
if (! error)
for (i = val_range; i <= val; i++)
{
ret_val |= i << (5 * nb_regs);
nb_regs++;
}
in_range = 0;
}
while (skip_past_comma (&str) || (in_range = 1, *str == '-'));
skip_whitespace (str);
if (*str != '}')
{
set_first_syntax_error (_("end of vector register list not found"));
error = TRUE;
}
str++;
skip_whitespace (str);
if (expect_index)
{
if (skip_past_char (&str, '['))
{
expressionS exp;
my_get_expression (&exp, &str, GE_NO_PREFIX, 1);
if (exp.X_op != O_constant)
{
set_first_syntax_error (_("constant expression required."));
error = TRUE;
}
if (! skip_past_char (&str, ']'))
error = TRUE;
else
typeinfo_first.index = exp.X_add_number;
}
else
{
set_first_syntax_error (_("expected index"));
error = TRUE;
}
}
if (nb_regs > 4)
{
set_first_syntax_error (_("too many registers in vector register list"));
error = TRUE;
}
else if (nb_regs == 0)
{
set_first_syntax_error (_("empty vector register list"));
error = TRUE;
}
*ccp = str;
if (! error)
*vectype = typeinfo_first;
return error ? PARSE_FAIL : (ret_val << 2) | (nb_regs - 1);
}
/* Directives: register aliases. */
static reg_entry *
insert_reg_alias (char *str, int number, aarch64_reg_type type)
{
reg_entry *new;
const char *name;
if ((new = hash_find (aarch64_reg_hsh, str)) != 0)
{
if (new->builtin)
as_warn (_("ignoring attempt to redefine built-in register '%s'"),
str);
/* Only warn about a redefinition if it's not defined as the
same register. */
else if (new->number != number || new->type != type)
as_warn (_("ignoring redefinition of register alias '%s'"), str);
return NULL;
}
name = xstrdup (str);
new = xmalloc (sizeof (reg_entry));
new->name = name;
new->number = number;
new->type = type;
new->builtin = FALSE;
if (hash_insert (aarch64_reg_hsh, name, (void *) new))
abort ();
return new;
}
/* Look for the .req directive. This is of the form:
new_register_name .req existing_register_name
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
create_register_alias (char *newname, char *p)
{
const reg_entry *old;
char *oldname, *nbuf;
size_t nlen;
/* The input scrubber ensures that whitespace after the mnemonic is
collapsed to single spaces. */
oldname = p;
if (strncmp (oldname, " .req ", 6) != 0)
return FALSE;
oldname += 6;
if (*oldname == '\0')
return FALSE;
old = hash_find (aarch64_reg_hsh, oldname);
if (!old)
{
as_warn (_("unknown register '%s' -- .req ignored"), oldname);
return TRUE;
}
/* If TC_CASE_SENSITIVE is defined, then newname already points to
the desired alias name, and p points to its end. If not, then
the desired alias name is in the global original_case_string. */
#ifdef TC_CASE_SENSITIVE
nlen = p - newname;
#else
newname = original_case_string;
nlen = strlen (newname);
#endif
nbuf = alloca (nlen + 1);
memcpy (nbuf, newname, nlen);
nbuf[nlen] = '\0';
/* Create aliases under the new name as stated; an all-lowercase
version of the new name; and an all-uppercase version of the new
name. */
if (insert_reg_alias (nbuf, old->number, old->type) != NULL)
{
for (p = nbuf; *p; p++)
*p = TOUPPER (*p);
if (strncmp (nbuf, newname, nlen))
{
/* If this attempt to create an additional alias fails, do not bother
trying to create the all-lower case alias. We will fail and issue
a second, duplicate error message. This situation arises when the
programmer does something like:
foo .req r0
Foo .req r1
The second .req creates the "Foo" alias but then fails to create
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;
}
for (p = nbuf; *p; p++)
*p = TOLOWER (*p);
if (strncmp (nbuf, newname, nlen))
insert_reg_alias (nbuf, old->number, old->type);
}
return TRUE;
}
/* Should never be called, as .req goes between the alias and the
register name, not at the beginning of the line. */
static void
s_req (int a ATTRIBUTE_UNUSED)
{
as_bad (_("invalid syntax for .req directive"));
}
/* The .unreq directive deletes an alias which was previously defined
by .req. For example:
my_alias .req r11
.unreq my_alias */
static void
s_unreq (int a ATTRIBUTE_UNUSED)
{
char *name;
char saved_char;
name = input_line_pointer;
while (*input_line_pointer != 0
&& *input_line_pointer != ' ' && *input_line_pointer != '\n')
++input_line_pointer;
saved_char = *input_line_pointer;
*input_line_pointer = 0;
if (!*name)
as_bad (_("invalid syntax for .unreq directive"));
else
{
reg_entry *reg = hash_find (aarch64_reg_hsh, name);
if (!reg)
as_bad (_("unknown register alias '%s'"), name);
else if (reg->builtin)
as_warn (_("ignoring attempt to undefine built-in register '%s'"),
name);
else
{
char *p;
char *nbuf;
hash_delete (aarch64_reg_hsh, name, FALSE);
free ((char *) reg->name);
free (reg);
/* Also locate the all upper case and all lower case versions.
Do not complain if we cannot find one or the other as it
was probably deleted above. */
nbuf = strdup (name);
for (p = nbuf; *p; p++)
*p = TOUPPER (*p);
reg = hash_find (aarch64_reg_hsh, nbuf);
if (reg)
{
hash_delete (aarch64_reg_hsh, nbuf, FALSE);
free ((char *) reg->name);
free (reg);
}
for (p = nbuf; *p; p++)
*p = TOLOWER (*p);
reg = hash_find (aarch64_reg_hsh, nbuf);
if (reg)
{
hash_delete (aarch64_reg_hsh, nbuf, FALSE);
free ((char *) reg->name);
free (reg);
}
free (nbuf);
}
}
*input_line_pointer = saved_char;
demand_empty_rest_of_line ();
}
/* Directives: Instruction set selection. */
#ifdef OBJ_ELF
/* This code is to handle mapping symbols as defined in the ARM AArch64 ELF
spec. (See "Mapping symbols", section 4.5.4, ARM AAELF64 version 0.05).
Note that previously, $a and $t has type STT_FUNC (BSF_OBJECT flag),
and $d has type STT_OBJECT (BSF_OBJECT flag). Now all three are untyped. */
/* Create a new mapping symbol for the transition to STATE. */
static void
make_mapping_symbol (enum mstate state, valueT value, fragS * frag)
{
symbolS *symbolP;
const char *symname;
int type;
switch (state)
{
case MAP_DATA:
symname = "$d";
type = BSF_NO_FLAGS;
break;
case MAP_INSN:
symname = "$x";
type = BSF_NO_FLAGS;
break;
default:
abort ();
}
symbolP = symbol_new (symname, now_seg, value, frag);
symbol_get_bfdsym (symbolP)->flags |= type | BSF_LOCAL;
/* Save the mapping symbols for future reference. Also check that
we do not place two mapping symbols at the same offset within a
frag. We'll handle overlap between frags in
check_mapping_symbols.
If .fill or other data filling directive generates zero sized data,
the mapping symbol for the following code will have the same value
as the one generated for the data filling directive. In this case,
we replace the old symbol with the new one at the same address. */
if (value == 0)
{
if (frag->tc_frag_data.first_map != NULL)
{
know (S_GET_VALUE (frag->tc_frag_data.first_map) == 0);
symbol_remove (frag->tc_frag_data.first_map, &symbol_rootP,
&symbol_lastP);
}
frag->tc_frag_data.first_map = symbolP;
}
if (frag->tc_frag_data.last_map != NULL)
{
know (S_GET_VALUE (frag->tc_frag_data.last_map) <=
S_GET_VALUE (symbolP));
if (S_GET_VALUE (frag->tc_frag_data.last_map) == S_GET_VALUE (symbolP))
symbol_remove (frag->tc_frag_data.last_map, &symbol_rootP,
&symbol_lastP);
}
frag->tc_frag_data.last_map = symbolP;
}
/* We must sometimes convert a region marked as code to data during
code alignment, if an odd number of bytes have to be padded. The
code mapping symbol is pushed to an aligned address. */
static void
insert_data_mapping_symbol (enum mstate state,
valueT value, fragS * frag, offsetT bytes)
{
/* If there was already a mapping symbol, remove it. */
if (frag->tc_frag_data.last_map != NULL
&& S_GET_VALUE (frag->tc_frag_data.last_map) ==
frag->fr_address + value)
{
symbolS *symp = frag->tc_frag_data.last_map;
if (value == 0)
{
know (frag->tc_frag_data.first_map == symp);
frag->tc_frag_data.first_map = NULL;
}
frag->tc_frag_data.last_map = NULL;
symbol_remove (symp, &symbol_rootP, &symbol_lastP);
}
make_mapping_symbol (MAP_DATA, value, frag);
make_mapping_symbol (state, value + bytes, frag);
}
static void mapping_state_2 (enum mstate state, int max_chars);
/* Set the mapping state to STATE. Only call this when about to
emit some STATE bytes to the file. */
void
mapping_state (enum mstate state)
{
enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
#define TRANSITION(from, to) (mapstate == (from) && state == (to))
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. */
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. */
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);
}
mapping_state_2 (state, 0);
#undef TRANSITION
}
/* Same as mapping_state, but MAX_CHARS bytes have already been
allocated. Put the mapping symbol that far back. */
static void
mapping_state_2 (enum mstate state, int max_chars)
{
enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
if (!SEG_NORMAL (now_seg))
return;
if (mapstate == state)
/* The mapping symbol has already been emitted.
There is nothing else to do. */
return;
seg_info (now_seg)->tc_segment_info_data.mapstate = state;
make_mapping_symbol (state, (valueT) frag_now_fix () - max_chars, frag_now);
}
#else
#define mapping_state(x) /* nothing */
#define mapping_state_2(x, y) /* nothing */
#endif
/* Directives: sectioning and alignment. */
static void
s_bss (int ignore ATTRIBUTE_UNUSED)
{
/* We don't support putting frags in the BSS segment, we fake it by
marking in_bss, then looking at s_skip for clues. */
subseg_set (bss_section, 0);
demand_empty_rest_of_line ();
mapping_state (MAP_DATA);
}
static void
s_even (int ignore ATTRIBUTE_UNUSED)
{
/* Never make frag if expect extra pass. */
if (!need_pass_2)
frag_align (1, 0, 0);
record_alignment (now_seg, 1);
demand_empty_rest_of_line ();
}
/* Directives: Literal pools. */
static literal_pool *
find_literal_pool (int size)
{
literal_pool *pool;
for (pool = list_of_pools; pool != NULL; pool = pool->next)
{
if (pool->section == now_seg
&& pool->sub_section == now_subseg && pool->size == size)
break;
}
return pool;
}
static literal_pool *
find_or_make_literal_pool (int size)
{
/* Next literal pool ID number. */
static unsigned int latest_pool_num = 1;
literal_pool *pool;
pool = find_literal_pool (size);
if (pool == NULL)
{
/* Create a new pool. */
pool = xmalloc (sizeof (*pool));
if (!pool)
return NULL;
/* Currently we always put the literal pool in the current text
section. If we were generating "small" model code where we
knew that all code and initialised data was within 1MB then
we could output literals to mergeable, read-only data
sections. */
pool->next_free_entry = 0;
pool->section = now_seg;
pool->sub_section = now_subseg;
pool->size = size;
pool->next = list_of_pools;
pool->symbol = NULL;
/* Add it to the list. */
list_of_pools = pool;
}
/* New pools, and emptied pools, will have a NULL symbol. */
if (pool->symbol == NULL)
{
pool->symbol = symbol_create (FAKE_LABEL_NAME, undefined_section,
(valueT) 0, &zero_address_frag);
pool->id = latest_pool_num++;
}
/* Done. */
return pool;
}
/* Add the literal of size SIZE in *EXP to the relevant literal pool.
Return TRUE on success, otherwise return FALSE. */
static bfd_boolean
add_to_lit_pool (expressionS *exp, int size)
{
literal_pool *pool;
unsigned int entry;
pool = find_or_make_literal_pool (size);
/* Check if this literal value is already in the pool. */
for (entry = 0; entry < pool->next_free_entry; entry++)
{
if ((pool->literals[entry].X_op == exp->X_op)
&& (exp->X_op == O_constant)
&& (pool->literals[entry].X_add_number == exp->X_add_number)
&& (pool->literals[entry].X_unsigned == exp->X_unsigned))
break;
if ((pool->literals[entry].X_op == exp->X_op)
&& (exp->X_op == O_symbol)
&& (pool->literals[entry].X_add_number == exp->X_add_number)
&& (pool->literals[entry].X_add_symbol == exp->X_add_symbol)
&& (pool->literals[entry].X_op_symbol == exp->X_op_symbol))
break;
}
/* Do we need to create a new entry? */
if (entry == pool->next_free_entry)
{
if (entry >= MAX_LITERAL_POOL_SIZE)
{
set_syntax_error (_("literal pool overflow"));
return FALSE;
}
pool->literals[entry] = *exp;
pool->next_free_entry += 1;
}
exp->X_op = O_symbol;
exp->X_add_number = ((int) entry) * size;
exp->X_add_symbol = pool->symbol;
return TRUE;
}
/* Can't use symbol_new here, so have to create a symbol and then at
a later date assign it a value. Thats what these functions do. */
static void
symbol_locate (symbolS * symbolP,
const char *name,/* It is copied, the caller can modify. */
segT segment, /* Segment identifier (SEG_). */
valueT valu, /* Symbol value. */
fragS * frag) /* Associated fragment. */
{
unsigned int name_length;
char *preserved_copy_of_name;
name_length = strlen (name) + 1; /* +1 for \0. */
obstack_grow (¬es, name, name_length);
preserved_copy_of_name = obstack_finish (¬es);
#ifdef tc_canonicalize_symbol_name
preserved_copy_of_name =
tc_canonicalize_symbol_name (preserved_copy_of_name);
#endif
S_SET_NAME (symbolP, preserved_copy_of_name);
S_SET_SEGMENT (symbolP, segment);
S_SET_VALUE (symbolP, valu);
symbol_clear_list_pointers (symbolP);
symbol_set_frag (symbolP, frag);
/* Link to end of symbol chain. */
{
extern int symbol_table_frozen;
if (symbol_table_frozen)
abort ();
}
symbol_append (symbolP, symbol_lastP, &symbol_rootP, &symbol_lastP);
obj_symbol_new_hook (symbolP);
#ifdef tc_symbol_new_hook
tc_symbol_new_hook (symbolP);
#endif
#ifdef DEBUG_SYMS
verify_symbol_chain (symbol_rootP, symbol_lastP);
#endif /* DEBUG_SYMS */
}
static void
s_ltorg (int ignored ATTRIBUTE_UNUSED)
{
unsigned int entry;
literal_pool *pool;
char sym_name[20];
int align;
for (align = 2; align <= 4; align++)
{
int size = 1 << align;
pool = find_literal_pool (size);
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);
record_alignment (now_seg, align);
sprintf (sym_name, "$$lit_\002%x", pool->id);
symbol_locate (pool->symbol, sym_name, now_seg,
(valueT) frag_now_fix (), frag_now);
symbol_table_insert (pool->symbol);
for (entry = 0; entry < pool->next_free_entry; entry++)
/* First output the expression in the instruction to the pool. */
emit_expr (&(pool->literals[entry]), size); /* .word|.xword */
/* Mark the pool as empty. */
pool->next_free_entry = 0;
pool->symbol = NULL;
}
}
#ifdef OBJ_ELF
/* Forward declarations for functions below, in the MD interface
section. */
static fixS *fix_new_aarch64 (fragS *, int, short, expressionS *, int, int);
static struct reloc_table_entry * find_reloc_table_entry (char **);
/* Directives: Data. */
/* N.B. the support for relocation suffix in this directive needs to be
implemented properly. */
static void
s_aarch64_elf_cons (int nbytes)
{
expressionS exp;
#ifdef md_flush_pending_output
md_flush_pending_output ();
#endif
if (is_it_end_of_statement ())
{
demand_empty_rest_of_line ();
return;
}
#ifdef md_cons_align
md_cons_align (nbytes);
#endif
mapping_state (MAP_DATA);
do
{
struct reloc_table_entry *reloc;
expression (&exp);
if (exp.X_op != O_symbol)
emit_expr (&exp, (unsigned int) nbytes);
else
{
skip_past_char (&input_line_pointer, '#');
if (skip_past_char (&input_line_pointer, ':'))
{
reloc = find_reloc_table_entry (&input_line_pointer);
if (reloc == NULL)
as_bad (_("unrecognized relocation suffix"));
else
as_bad (_("unimplemented relocation suffix"));
ignore_rest_of_line ();
return;
}
else
emit_expr (&exp, (unsigned int) nbytes);
}
}
while (*input_line_pointer++ == ',');
/* Put terminator back into stream. */
input_line_pointer--;
demand_empty_rest_of_line ();
}
#endif /* OBJ_ELF */
/* Output a 32-bit word, but mark as an instruction. */
static void
s_aarch64_inst (int ignored ATTRIBUTE_UNUSED)
{
expressionS exp;
#ifdef md_flush_pending_output
md_flush_pending_output ();
#endif
if (is_it_end_of_statement ())
{
demand_empty_rest_of_line ();
return;
}
if (!need_pass_2)
frag_align_code (2, 0);
#ifdef OBJ_ELF
mapping_state (MAP_INSN);
#endif
do
{
expression (&exp);
if (exp.X_op != O_constant)
{
as_bad (_("constant expression required"));
ignore_rest_of_line ();
return;
}
if (target_big_endian)
{
unsigned int val = exp.X_add_number;
exp.X_add_number = SWAP_32 (val);
}
emit_expr (&exp, 4);
}
while (*input_line_pointer++ == ',');
/* Put terminator back into stream. */
input_line_pointer--;
demand_empty_rest_of_line ();
}
#ifdef OBJ_ELF
/* Emit BFD_RELOC_AARCH64_TLSDESC_CALL on the next BLR instruction. */
static void
s_tlsdesccall (int ignored ATTRIBUTE_UNUSED)
{
expressionS exp;
/* Since we're just labelling the code, there's no need to define a
mapping symbol. */
expression (&exp);
/* Make sure there is enough room in this frag for the following
blr. This trick only works if the blr follows immediately after
the .tlsdesc directive. */
frag_grow (4);
fix_new_aarch64 (frag_now, frag_more (0) - frag_now->fr_literal, 4, &exp, 0,
BFD_RELOC_AARCH64_TLSDESC_CALL);
demand_empty_rest_of_line ();
}
#endif /* OBJ_ELF */
static void s_aarch64_arch (int);
static void s_aarch64_cpu (int);
/* This table describes all the machine specific pseudo-ops the assembler
has to support. The fields are:
pseudo-op name without dot
function to call to execute this pseudo-op
Integer arg to pass to the function. */
const pseudo_typeS md_pseudo_table[] = {
/* Never called because '.req' does not start a line. */
{"req", s_req, 0},
{"unreq", s_unreq, 0},
{"bss", s_bss, 0},
{"even", s_even, 0},
{"ltorg", s_ltorg, 0},
{"pool", s_ltorg, 0},
{"cpu", s_aarch64_cpu, 0},
{"arch", s_aarch64_arch, 0},
{"inst", s_aarch64_inst, 0},
#ifdef OBJ_ELF
{"tlsdesccall", s_tlsdesccall, 0},
{"word", s_aarch64_elf_cons, 4},
{"long", s_aarch64_elf_cons, 4},
{"xword", s_aarch64_elf_cons, 8},
{"dword", s_aarch64_elf_cons, 8},
#endif
{0, 0, 0}
};
/* Check whether STR points to a register name followed by a comma or the
end of line; REG_TYPE indicates which register types are checked
against. Return TRUE if STR is such a register name; otherwise return
FALSE. The function does not intend to produce any diagnostics, but since
the register parser aarch64_reg_parse, which is called by this function,
does produce diagnostics, we call clear_error to clear any diagnostics
that may be generated by aarch64_reg_parse.
Also, the function returns FALSE directly if there is any user error
present at the function entry. This prevents the existing diagnostics
state from being spoiled.
The function currently serves parse_constant_immediate and
parse_big_immediate only. */
static bfd_boolean
reg_name_p (char *str, aarch64_reg_type reg_type)
{
int reg;
/* Prevent the diagnostics state from being spoiled. */
if (error_p ())
return FALSE;
reg = aarch64_reg_parse (&str, reg_type, NULL, NULL);
/* Clear the parsing error that may be set by the reg parser. */
clear_error ();
if (reg == PARSE_FAIL)
return FALSE;
skip_whitespace (str);
if (*str == ',' || is_end_of_line[(unsigned int) *str])
return TRUE;
return FALSE;
}
/* Parser functions used exclusively in instruction operands. */
/* Parse an immediate expression which may not be constant.
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. */
static bfd_boolean
parse_immediate_expression (char **str, expressionS *exp)
{
if (reg_name_p (*str, REG_TYPE_R_Z_BHSDQ_V))
{
set_recoverable_error (_("immediate operand required"));
return FALSE;
}
my_get_expression (exp, str, GE_OPT_PREFIX, 1);
if (exp->X_op == O_absent)
{
set_fatal_syntax_error (_("missing immediate expression"));
return FALSE;
}
return TRUE;
}
/* 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.
Return TRUE on success; otherwise return FALSE. */
static bfd_boolean
parse_constant_immediate (char **str, int64_t * val)
{
expressionS exp;
if (! parse_immediate_expression (str, &exp))
return FALSE;
if (exp.X_op != O_constant)
{
set_syntax_error (_("constant expression required"));
return FALSE;
}
*val = exp.X_add_number;
return TRUE;
}
static uint32_t
encode_imm_float_bits (uint32_t imm)
{
return ((imm >> 19) & 0x7f) /* b[25:19] -> b[6:0] */
| ((imm >> (31 - 7)) & 0x80); /* b[31] -> b[7] */
}
/* Return TRUE if IMM is a valid floating-point immediate; return FALSE
otherwise. */
static bfd_boolean
aarch64_imm_float_p (uint32_t imm)
{
/* 3 32222222 2221111111111
1 09876543 21098765432109876543210
n Eeeeeexx xxxx0000000000000000000 */
uint32_t e;
e = (imm >> 30) & 0x1;
if (e == 0)
e = 0x3e000000;
else
e = 0x40000000;
return (imm & 0x7ffff) == 0 /* lower 19 bits are 0 */
&& ((imm & 0x7e000000) == e); /* bits 25-29 = ~ bit 30 */
}
/* Note: this accepts the floating-point 0 constant. */
static bfd_boolean
parse_aarch64_imm_float (char **ccp, int *immed)
{
char *str = *ccp;
char *fpnum;
LITTLENUM_TYPE words[MAX_LITTLENUMS];
int found_fpchar = 0;
skip_past_char (&str, '#');
/* 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'.
FIXME: This is a hack that is not very efficient, but doing better is
tricky because type information isn't in a very usable state at parse
time. */
fpnum = str;
skip_whitespace (fpnum);
if (strncmp (fpnum, "0x", 2) == 0)
return FALSE;
else
{
for (; *fpnum != '\0' && *fpnum != ' ' && *fpnum != '\n'; fpnum++)
if (*fpnum == '.' || *fpnum == 'e' || *fpnum == 'E')
{
found_fpchar = 1;
break;
}
if (!found_fpchar)
return FALSE;
}
if ((str = atof_ieee (str, 's', words)) != NULL)
{
unsigned fpword = 0;
int i;
/* Our FP word must be 32 bits (single-precision FP). */
for (i = 0; i < 32 / LITTLENUM_NUMBER_OF_BITS; i++)
{
fpword <<= LITTLENUM_NUMBER_OF_BITS;
fpword |= words[i];
}
if (aarch64_imm_float_p (fpword) || (fpword & 0x7fffffff) == 0)
*immed = fpword;
else
goto invalid_fp;
*ccp = str;
return TRUE;
}
invalid_fp:
set_fatal_syntax_error (_("invalid floating-point constant"));
return FALSE;
}
/* Less-generic immediate-value read function with the possibility of loading
a big (64-bit) immediate, as required by AdvSIMD Modified immediate
instructions.
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. */
static bfd_boolean
parse_big_immediate (char **str, int64_t *imm)
{
char *ptr = *str;
if (reg_name_p (ptr, REG_TYPE_R_Z_BHSDQ_V))
{
set_syntax_error (_("immediate operand required"));
return FALSE;
}
my_get_expression (&inst.reloc.exp, &ptr, GE_OPT_PREFIX, 1);
if (inst.reloc.exp.X_op == O_constant)
*imm = inst.reloc.exp.X_add_number;
*str = ptr;
return TRUE;
}
/* Set operand IDX of the *INSTR that needs a GAS internal fixup.
if NEED_LIBOPCODES is non-zero, the fixup will need
assistance from the libopcodes. */
static inline void
aarch64_set_gas_internal_fixup (struct reloc *reloc,
const aarch64_opnd_info *operand,
int need_libopcodes_p)
{
reloc->type = BFD_RELOC_AARCH64_GAS_INTERNAL_FIXUP;
reloc->opnd = operand->type;
if (need_libopcodes_p)
reloc->need_libopcodes_p = 1;
};
/* Return TRUE if the instruction needs to be fixed up later internally by
the GAS; otherwise return FALSE. */
static inline bfd_boolean
aarch64_gas_internal_fixup_p (void)
{
return inst.reloc.type == BFD_RELOC_AARCH64_GAS_INTERNAL_FIXUP;
}
/* Assign the immediate value to the relavant field in *OPERAND if
RELOC->EXP is a constant expression; otherwise, flag that *OPERAND
needs an internal fixup in a later stage.
ADDR_OFF_P determines whether it is the field ADDR.OFFSET.IMM or
IMM.VALUE that may get assigned with the constant. */
static inline void
assign_imm_if_const_or_fixup_later (struct reloc *reloc,
aarch64_opnd_info *operand,
int addr_off_p,
int need_libopcodes_p,
int skip_p)
{
if (reloc->exp.X_op == O_constant)
{
if (addr_off_p)
operand->addr.offset.imm = reloc->exp.X_add_number;
else
operand->imm.value = reloc->exp.X_add_number;
reloc->type = BFD_RELOC_UNUSED;
}
else
{
aarch64_set_gas_internal_fixup (reloc, operand, need_libopcodes_p);
/* Tell libopcodes to ignore this operand or not. This is helpful
when one of the operands needs to be fixed up later but we need
libopcodes to check the other operands. */
operand->skip = skip_p;
}
}
/* Relocation modifiers. Each entry in the table contains the textual
name for the relocation which may be placed before a symbol used as
a load/store offset, or add immediate. It must be surrounded by a
leading and trailing colon, for example:
ldr x0, [x1, #:rello:varsym]
add x0, x1, #:rello:varsym */
struct reloc_table_entry
{
const char *name;
int pc_rel;
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;
};
static struct reloc_table_entry reloc_table[] = {
/* Low 12 bits of absolute address: ADD/i and LDR/STR */
{"lo12", 0,
0,
0,
BFD_RELOC_AARCH64_ADD_LO12,
BFD_RELOC_AARCH64_LDST_LO12},
/* Higher 21 bits of pc-relative page offset: ADRP */
{"pg_hi21", 1,
BFD_RELOC_AARCH64_ADR_HI21_PCREL,
0,
0,
0},
/* Higher 21 bits of pc-relative page offset: ADRP, no check */
{"pg_hi21_nc", 1,
BFD_RELOC_AARCH64_ADR_HI21_NC_PCREL,
0,
0,
0},
/* Most significant bits 0-15 of unsigned address/value: MOVZ */
{"abs_g0", 0,
0,
BFD_RELOC_AARCH64_MOVW_G0,
0,
0},
/* Most significant bits 0-15 of signed address/value: MOVN/Z */
{"abs_g0_s", 0,
0,
BFD_RELOC_AARCH64_MOVW_G0_S,
0,
0},
/* Less significant bits 0-15 of address/value: MOVK, no check */
{"abs_g0_nc", 0,
0,
BFD_RELOC_AARCH64_MOVW_G0_NC,
0,
0},
/* Most significant bits 16-31 of unsigned address/value: MOVZ */
{"abs_g1", 0,
0,
BFD_RELOC_AARCH64_MOVW_G1,
0,
0},
/* Most significant bits 16-31 of signed address/value: MOVN/Z */
{"abs_g1_s", 0,
0,
BFD_RELOC_AARCH64_MOVW_G1_S,
0,
0},
/* Less significant bits 16-31 of address/value: MOVK, no check */
{"abs_g1_nc", 0,
0,
BFD_RELOC_AARCH64_MOVW_G1_NC,
0,
0},
/* Most significant bits 32-47 of unsigned address/value: MOVZ */
{"abs_g2", 0,
0,
BFD_RELOC_AARCH64_MOVW_G2,
0,
0},
/* Most significant bits 32-47 of signed address/value: MOVN/Z */
{"abs_g2_s", 0,
0,
BFD_RELOC_AARCH64_MOVW_G2_S,
0,
0},
/* Less significant bits 32-47 of address/value: MOVK, no check */
{"abs_g2_nc", 0,
0,
BFD_RELOC_AARCH64_MOVW_G2_NC,
0,
0},
/* Most significant bits 48-63 of signed/unsigned address/value: MOVZ */
{"abs_g3", 0,
0,
BFD_RELOC_AARCH64_MOVW_G3,
0,
0},
/* Get to the GOT entry for a symbol. */
{"got_prel19", 0,
0,
0,
0,
BFD_RELOC_AARCH64_GOT_LD_PREL19},
/* Get to the page containing GOT entry for a symbol. */
{"got", 1,
BFD_RELOC_AARCH64_ADR_GOT_PAGE,
0,
0,
0},
/* 12 bit offset into the page containing GOT entry for that symbol. */
{"got_lo12", 0,
0,
0,
0,
BFD_RELOC_AARCH64_LD64_GOT_LO12_NC},
/* Get to the page containing GOT TLS entry for a symbol */
{"tlsgd", 0,
BFD_RELOC_AARCH64_TLSGD_ADR_PAGE21,
0,
0,
0},
/* 12 bit offset into the page containing GOT TLS entry for a symbol */
{"tlsgd_lo12", 0,
0,
0,
BFD_RELOC_AARCH64_TLSGD_ADD_LO12_NC,
0},
/* Get to the page containing GOT TLS entry for a symbol */
{"tlsdesc", 0,
BFD_RELOC_AARCH64_TLSDESC_ADR_PAGE,
0,
0,
0},
/* 12 bit offset into the page containing GOT TLS entry for a symbol */
{"tlsdesc_lo12", 0,
0,
0,
BFD_RELOC_AARCH64_TLSDESC_ADD_LO12_NC,
BFD_RELOC_AARCH64_TLSDESC_LD64_LO12_NC},
/* Get to the page containing GOT TLS entry for a symbol */
{"gottprel", 0,
BFD_RELOC_AARCH64_TLSIE_ADR_GOTTPREL_PAGE21,
0,
0,
0},
/* 12 bit offset into the page containing GOT TLS entry for a symbol */
{"gottprel_lo12", 0,
0,
0,
0,
BFD_RELOC_AARCH64_TLSIE_LD64_GOTTPREL_LO12_NC},
/* Get tp offset for a symbol. */
{"tprel", 0,
0,
0,
BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12,
0},
/* Get tp offset for a symbol. */
{"tprel_lo12", 0,
0,
0,
BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12,
0},
/* Get tp offset for a symbol. */
{"tprel_hi12", 0,
0,
0,
BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_HI12,
0},
/* Get tp offset for a symbol. */
{"tprel_lo12_nc", 0,
0,
0,
BFD_RELOC_AARCH64_TLSLE_ADD_TPREL_LO12_NC,
0},
/* Most significant bits 32-47 of address/value: MOVZ. */
{"tprel_g2", 0,
0,
BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G2,
0,
0},
/* Most significant bits 16-31 of address/value: MOVZ. */
{"tprel_g1", 0,
0,
BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1,
0,
0},
/* Most significant bits 16-31 of address/value: MOVZ, no check. */
{"tprel_g1_nc", 0,
0,
BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G1_NC,
0,
0},
/* Most significant bits 0-15 of address/value: MOVZ. */
{"tprel_g0", 0,
0,
BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0,
0,
0},
/* Most significant bits 0-15 of address/value: MOVZ, no check. */
{"tprel_g0_nc", 0,
0,
BFD_RELOC_AARCH64_TLSLE_MOVW_TPREL_G0_NC,
0,
0},
};
/* Given the address of a pointer pointing to the textual name of a
relocation as may appear in assembler source, attempt to find its
details in reloc_table. The pointer will be updated to the character
after the trailing colon. On failure, NULL will be returned;
otherwise return the reloc_table_entry. */
static struct reloc_table_entry *
find_reloc_table_entry (char **str)
{
unsigned int i;
for (i = 0; i < ARRAY_SIZE (reloc_table); i++)
{
int length = strlen (reloc_table[i].name);
if (strncasecmp (reloc_table[i].name, *str, length) == 0
&& (*str)[length] == ':')
{
*str += (length + 1);
return &reloc_table[i];
}
}
return NULL;
}
/* Mode argument to parse_shift and parser_shifter_operand. */
enum parse_shift_mode
{
SHIFTED_ARITH_IMM, /* "rn{,lsl|lsr|asl|asr|uxt|sxt #n}" or
"#imm{,lsl #n}" */
SHIFTED_LOGIC_IMM, /* "rn{,lsl|lsr|asl|asr|ror #n}" or
"#imm" */
SHIFTED_LSL, /* bare "lsl #n" */
SHIFTED_LSL_MSL, /* "lsl|msl #n" */
SHIFTED_REG_OFFSET /* [su]xtw|sxtx {#n} or lsl #n */
};
/* Parse a operator on an AArch64 data processing instruction.
Return TRUE on success; otherwise return FALSE. */
static bfd_boolean
parse_shift (char **str, aarch64_opnd_info *operand, enum parse_shift_mode mode)
{
const struct aarch64_name_value_pair *shift_op;
enum aarch64_modifier_kind kind;
expressionS exp;
int exp_has_prefix;
char *s = *str;
char *p = s;
for (p = *str; ISALPHA (*p); p++)
;
if (p == *str)
{
set_syntax_error (_("shift expression expected"));
return FALSE;
}
shift_op = hash_find_n (aarch64_shift_hsh, *str, p - *str);
if (shift_op == NULL)
{
set_syntax_error (_("shift operator expected"));
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;
}
switch (mode)
{
case SHIFTED_LOGIC_IMM:
if (aarch64_extend_operator_p (kind) == TRUE)
{
set_syntax_error (_("extending shift is not permitted"));
return FALSE;
}
break;
case SHIFTED_ARITH_IMM:
if (kind == AARCH64_MOD_ROR)
{
set_syntax_error (_("'ROR' shift is not permitted"));
return FALSE;
}
break;
case SHIFTED_LSL:
if (kind != AARCH64_MOD_LSL)
{
set_syntax_error (_("only 'LSL' shift is permitted"));
return FALSE;
}
break;
case SHIFTED_REG_OFFSET:
if (kind != AARCH64_MOD_UXTW && kind != AARCH64_MOD_LSL
&& kind != AARCH64_MOD_SXTW && kind != AARCH64_MOD_SXTX)
{
set_fatal_syntax_error
(_("invalid shift for the register offset addressing mode"));
return FALSE;
}
break;
case SHIFTED_LSL_MSL:
if (kind != AARCH64_MOD_LSL && kind != AARCH64_MOD_MSL)
{
set_syntax_error (_("invalid shift operator"));
return FALSE;
}
break;
default:
abort ();
}
/* Whitespace can appear here if the next thing is a bare digit. */
skip_whitespace (p);
/* Parse shift amount. */
exp_has_prefix = 0;
if (mode == SHIFTED_REG_OFFSET && *p == ']')
exp.X_op = O_absent;
else
{
if (is_immediate_prefix (*p))
{
p++;
exp_has_prefix = 1;
}
my_get_expression (&exp, &p, GE_NO_PREFIX, 0);
}
if (exp.X_op == O_absent)
{
if (aarch64_extend_operator_p (kind) == FALSE || exp_has_prefix)
{
set_syntax_error (_("missing shift amount"));
return FALSE;
}
operand->shifter.amount = 0;
}
else if (exp.X_op != O_constant)
{
set_syntax_error (_("constant shift amount required"));
return FALSE;
}
else if (exp.X_add_number < 0 || exp.X_add_number > 63)
{
set_fatal_syntax_error (_("shift amount out of range 0 to 63"));
return FALSE;
}
else
{
operand->shifter.amount = exp.X_add_number;
operand->shifter.amount_present = 1;
}
operand->shifter.operator_present = 1;
operand->shifter.kind = kind;
*str = p;
return TRUE;
}
/* Parse a for a data processing instruction:
#
#, LSL #imm
Validation of immediate operands is deferred to md_apply_fix.
Return TRUE on success; otherwise return FALSE. */
static bfd_boolean
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;
p = *str;
/* Accept an immediate expression. */
if (! my_get_expression (&inst.reloc.exp, &p, GE_OPT_PREFIX, 1))
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;
/* 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;
}
*str = p;
return TRUE;
}
/* Parse a for a data processing instruction:
,
#
#, LSL #imm
where is handled by parse_shift above, and the last two
cases are handled by the function above.
Validation of immediate operands is deferred to md_apply_fix.
Return TRUE on success; otherwise return FALSE. */
static bfd_boolean
parse_shifter_operand (char **str, aarch64_opnd_info *operand,
enum parse_shift_mode mode)
{
int reg;
int isreg32, isregzero;
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)
{
if (opd_class == AARCH64_OPND_CLASS_IMMEDIATE)
{
set_syntax_error (_("unexpected register in the immediate operand"));
return FALSE;
}
if (!isregzero && reg == REG_SP)
{
set_syntax_error (BAD_SP);
return FALSE;
}
operand->reg.regno = reg;
operand->qualifier = isreg32 ? AARCH64_OPND_QLF_W : AARCH64_OPND_QLF_X;
/* Accept optional shift operation on register. */
if (! skip_past_comma (str))
return TRUE;
if (! parse_shift (str, operand, mode))
return FALSE;
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;
}
/* We have a shifted immediate variable. */
return parse_shifter_operand_imm (str, operand, mode);
}
/* Return TRUE on success; return FALSE otherwise. */
static bfd_boolean
parse_shifter_operand_reloc (char **str, aarch64_opnd_info *operand,
enum parse_shift_mode mode)
{
char *p = *str;
/* Determine if we have the sequence of characters #: or just :
coming next. If we do, then we check for a :rello: relocation
modifier. If we don't, punt the whole lot to
parse_shifter_operand. */
if ((p[0] == '#' && p[1] == ':') || p[0] == ':')
{
struct reloc_table_entry *entry;
if (p[0] == '#')
p += 2;
else
p++;
*str = p;
/* Try to parse a relocation. Anything else is an error. */
if (!(entry = find_reloc_table_entry (str)))
{
set_syntax_error (_("unknown relocation modifier"));
return FALSE;
}
if (entry->add_type == 0)
{
set_syntax_error
(_("this relocation modifier is not allowed on this instruction"));
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;
/* 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;
/* Otherwise, we have a shifted reloc modifier, so rewind to
recover the variable name and continue parsing for the shifter. */
*str = p;
return parse_shifter_operand_imm (str, operand, mode);
}
return parse_shifter_operand (str, operand, mode);
}
/* Parse all forms of an address expression. Information is written
to *OPERAND and/or inst.reloc.
The A64 instruction set has the following addressing modes:
Offset
[base] // in SIMD ld/st structure
[base{,#0}] // in ld/st exclusive
[base{,#imm}]
[base,Xm{,LSL #imm}]
[base,Xm,SXTX {#imm}]
[base,Wm,(S|U)XTW {#imm}]
Pre-indexed
[base,#imm]!
Post-indexed
[base],#imm
[base],Xm // in SIMD ld/st structure
PC-relative (literal)
label
=immediate
(As a convenience, the notation "=immediate" is permitted in conjunction
with the pc-relative literal load instructions to automatically place an
immediate value or symbolic address in a nearby literal pool and generate
a hidden label which references it.)
Upon a successful parsing, the address structure in *OPERAND will be
filled in the following way:
.base_regno =
.offset.is_reg // 1 if the offset is a register
.offset.imm =
.offset.regno =
For different addressing modes defined in the A64 ISA:
Offset
.pcrel=0; .preind=1; .postind=0; .writeback=0
Pre-indexed
.pcrel=0; .preind=1; .postind=0; .writeback=1
Post-indexed
.pcrel=0; .preind=0; .postind=1; .writeback=1
PC-relative (literal)
.pcrel=1; .preind=1; .postind=0; .writeback=0
The shift/extension information, if any, will be stored in .shifter.
It is the caller's responsibility to check for addressing modes not
supported by the instruction, and to set inst.reloc.type. */
static bfd_boolean
parse_address_main (char **str, aarch64_opnd_info *operand, int reloc,
int accept_reg_post_index)
{
char *p = *str;
int reg;
int isreg32, isregzero;
expressionS *exp = &inst.reloc.exp;
if (! skip_past_char (&p, '['))
{
/* =immediate or label. */
operand->addr.pcrel = 1;
operand->addr.preind = 1;
/* #:: */
skip_past_char (&p, '#');
if (reloc && skip_past_char (&p, ':'))
{
struct reloc_table_entry *entry;
/* Try to parse a relocation modifier. Anything else is
an error. */
entry = find_reloc_table_entry (&p);
if (! entry)
{
set_syntax_error (_("unknown relocation modifier"));
return FALSE;
}
if (entry->ldst_type == 0)
{
set_syntax_error
(_("this relocation modifier is not allowed on this "
"instruction"));
return FALSE;
}
/* #:: */
if (! my_get_expression (exp, &p, GE_NO_PREFIX, 1))
{
set_syntax_error (_("invalid relocation expression"));
return FALSE;
}
/* #:: */
/* Record the load/store relocation type. */
inst.reloc.type = entry->ldst_type;
inst.reloc.pc_rel = entry->pc_rel;
}
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))
{
set_syntax_error (_("invalid address"));
return FALSE;
}
}
*str = p;
return TRUE;
}
/* [ */
/* Accept SP and reject ZR */
reg = aarch64_reg_parse_32_64 (&p, 0, 1, &isreg32, &isregzero);
if (reg == PARSE_FAIL || isreg32)
{
set_syntax_error (_(get_reg_expected_msg (REG_TYPE_R_64)));
return FALSE;
}
operand->addr.base_regno = reg;
/* [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)
{
/* [Xn,Rm */
operand->addr.offset.regno = reg;
operand->addr.offset.is_reg = 1;
/* Shifted index. */
if (skip_past_comma (&p))
{
/* [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;
}
/* We only accept:
[base,Xm{,LSL #imm}]
[base,Xm,SXTX {#imm}]
[base,Wm,(S|U)XTW {#imm}] */
if (operand->shifter.kind == AARCH64_MOD_NONE
|| operand->shifter.kind == AARCH64_MOD_LSL
|| operand->shifter.kind == AARCH64_MOD_SXTX)
{
if (isreg32)
{
set_syntax_error (_("invalid use of 32-bit register offset"));
return FALSE;
}
}
else if (!isreg32)
{
set_syntax_error (_("invalid use of 64-bit register offset"));
return FALSE;
}
}
else
{
/* [Xn,#:: */
skip_past_char (&p, '#');
if (reloc && skip_past_char (&p, ':'))
{
struct reloc_table_entry *entry;
/* Try to parse a relocation modifier. Anything else is
an error. */
if (!(entry = find_reloc_table_entry (&p)))
{
set_syntax_error (_("unknown relocation modifier"));
return FALSE;
}
if (entry->ldst_type == 0)
{
set_syntax_error
(_("this relocation modifier is not allowed on this "
"instruction"));
return FALSE;
}
/* [Xn,#:: */
/* 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))
{
set_syntax_error (_("invalid relocation expression"));
return FALSE;
}
/* [Xn,#:: */
/* Record the load/store relocation type. */
inst.reloc.type = entry->ldst_type;
inst.reloc.pc_rel = entry->pc_rel;
}
else if (! my_get_expression (exp, &p, GE_OPT_PREFIX, 1))
{
set_syntax_error (_("invalid expression in the address"));
return FALSE;
}
/* [Xn, */
}
}
if (! skip_past_char (&p, ']'))
{
set_syntax_error (_("']' expected"));
return FALSE;
}
if (skip_past_char (&p, '!'))
{
if (operand->addr.preind && operand->addr.offset.is_reg)
{
set_syntax_error (_("register offset not allowed in pre-indexed "
"addressing mode"));
return FALSE;
}
/* [Xn]! */
operand->addr.writeback = 1;
}
else if (skip_past_comma (&p))
{
/* [Xn], */
operand->addr.postind = 1;
operand->addr.writeback = 1;
if (operand->addr.preind)
{
set_syntax_error (_("cannot combine pre- and post-indexing"));
return FALSE;
}
if (accept_reg_post_index
&& (reg = aarch64_reg_parse_32_64 (&p, 1, 1, &isreg32,
&isregzero)) != PARSE_FAIL)
{
/* [Xn],Xm */
if (isreg32)
{
set_syntax_error (_("invalid 32-bit register offset"));
return FALSE;
}
operand->addr.offset.regno = reg;
operand->addr.offset.is_reg = 1;
}
else if (! my_get_expression (exp, &p, GE_OPT_PREFIX, 1))
{
/* [Xn],#expr */
set_syntax_error (_("invalid expression in the address"));
return FALSE;
}
}
/* If at this point neither .preind nor .postind is set, we have a
bare [Rn]{!}; reject [Rn]! but accept [Rn] as a shorthand for [Rn,#0]. */
if (operand->addr.preind == 0 && operand->addr.postind == 0)
{
if (operand->addr.writeback)
{
/* Reject [Rn]! */
set_syntax_error (_("missing offset in the pre-indexed address"));
return FALSE;
}
operand->addr.preind = 1;
inst.reloc.exp.X_op = O_constant;
inst.reloc.exp.X_add_number = 0;
}
*str = p;
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. */
static bfd_boolean
parse_address_reloc (char **str, aarch64_opnd_info *operand)
{
return parse_address_main (str, operand, 1, 0);
}
/* Parse an operand for a MOVZ, MOVN or MOVK instruction.
Return TRUE on success; otherwise return FALSE. */
static bfd_boolean
parse_half (char **str, int *internal_fixup_p)
{
char *p, *saved;
int dummy;
p = *str;
skip_past_char (&p, '#');
gas_assert (internal_fixup_p);
*internal_fixup_p = 0;
if (*p == ':')
{
struct reloc_table_entry *entry;
/* 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;
}
if (entry->movw_type == 0)
{
set_syntax_error
(_("this relocation modifier is not allowed on this instruction"));
return FALSE;
}
inst.reloc.type = entry->movw_type;
}
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;
*str = p;
return TRUE;
}
/* Parse an operand for an ADRP instruction:
ADRP ,