/* tc-arm.c -- Assemble for the ARM
- Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
- 2004, 2005, 2006, 2007, 2008, 2009, 2010, 2011
- Free Software Foundation, Inc.
+ Copyright 1994-2013 Free Software Foundation, Inc.
Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
Modified by David Taylor (dtaylor@armltd.co.uk)
Cirrus coprocessor mods by Aldy Hernandez (aldyh@redhat.com)
#include "safe-ctype.h"
#include "subsegs.h"
#include "obstack.h"
-
+#include "libiberty.h"
#include "opcode/arm.h"
#ifdef OBJ_ELF
/* Types of processor to assemble for. */
#ifndef CPU_DEFAULT
/* The code that was here used to select a default CPU depending on compiler
- pre-defines which were only present when doing native builds, thus
+ pre-defines which were only present when doing native builds, thus
changing gas' default behaviour depending upon the build host.
If you have a target that requires a default CPU option then the you
static const arm_feature_set arm_ext_v7a = ARM_FEATURE (ARM_EXT_V7A, 0);
static const arm_feature_set arm_ext_v7r = ARM_FEATURE (ARM_EXT_V7R, 0);
static const arm_feature_set arm_ext_v7m = ARM_FEATURE (ARM_EXT_V7M, 0);
+static const arm_feature_set arm_ext_v8 = ARM_FEATURE (ARM_EXT_V8, 0);
static const arm_feature_set arm_ext_m =
ARM_FEATURE (ARM_EXT_V6M | ARM_EXT_OS | ARM_EXT_V7M, 0);
static const arm_feature_set arm_ext_mp = ARM_FEATURE (ARM_EXT_MP, 0);
static const arm_feature_set fpu_vfp_fp16 = ARM_FEATURE (0, FPU_VFP_EXT_FP16);
static const arm_feature_set fpu_neon_ext_fma = ARM_FEATURE (0, FPU_NEON_EXT_FMA);
static const arm_feature_set fpu_vfp_ext_fma = ARM_FEATURE (0, FPU_VFP_EXT_FMA);
+static const arm_feature_set fpu_vfp_ext_armv8 =
+ ARM_FEATURE (0, FPU_VFP_EXT_ARMV8);
+static const arm_feature_set fpu_neon_ext_armv8 =
+ ARM_FEATURE (0, FPU_NEON_EXT_ARMV8);
+static const arm_feature_set fpu_crypto_ext_armv8 =
+ ARM_FEATURE (0, FPU_CRYPTO_EXT_ARMV8);
+static const arm_feature_set crc_ext_armv8 =
+ ARM_FEATURE (0, CRC_EXT_ARMV8);
static int mfloat_abi_opt = -1;
/* Record user cpu selection for object attributes. */
static bfd_boolean unified_syntax = FALSE;
+/* An immediate operand can start with #, and ld*, st*, pld operands
+ can contain [ and ]. We need to tell APP not to elide whitespace
+ before a [, which can appear as the first operand for pld.
+ Likewise, a { can appear as the first operand for push, pop, vld*, etc. */
+const char arm_symbol_chars[] = "#[]{}";
+
enum neon_el_type
{
NT_invtype,
INSIDE_IT_INSN,
INSIDE_IT_LAST_INSN,
IF_INSIDE_IT_LAST_INSN, /* Either outside or inside;
- if inside, should be the last one. */
+ if inside, should be the last one. */
NEUTRAL_IT_INSN, /* This could be either inside or outside,
- i.e. BKPT and NOP. */
+ i.e. BKPT and NOP. */
IT_INSN /* The IT insn has been parsed. */
};
+/* The maximum number of operands we need. */
+#define ARM_IT_MAX_OPERANDS 6
+
struct arm_it
{
const char * error;
unsigned negative : 1; /* Index register was negated. */
unsigned shifted : 1; /* Shift applied to operation. */
unsigned shift_kind : 3; /* Shift operation (enum shift_kind). */
- } operands[6];
+ } operands[ARM_IT_MAX_OPERANDS];
};
static struct arm_it inst;
struct asm_barrier_opt
{
- const char * template_name;
- unsigned long value;
+ const char * template_name;
+ unsigned long value;
+ const arm_feature_set arch;
};
/* The bit that distinguishes CPSR and SPSR. */
};
/* Some well known registers that we refer to directly elsewhere. */
+#define REG_R12 12
#define REG_SP 13
#define REG_LR 14
#define REG_PC 15
#define T2_OPCODE_MASK 0xfe1fffff
#define T2_DATA_OP_SHIFT 21
+#define A_COND_MASK 0xf0000000
+#define A_PUSH_POP_OP_MASK 0x0fff0000
+
+/* Opcodes for pushing/poping registers to/from the stack. */
+#define A1_OPCODE_PUSH 0x092d0000
+#define A2_OPCODE_PUSH 0x052d0004
+#define A2_OPCODE_POP 0x049d0004
+
/* Codes to distinguish the arithmetic instructions. */
#define OPCODE_AND 0
#define OPCODE_EOR 1
_("cannot use register index with PC-relative addressing")
#define BAD_PC_WRITEBACK \
_("cannot use writeback with PC-relative addressing")
+#define BAD_RANGE _("branch out of range")
+#define UNPRED_REG(R) _("using " R " results in unpredictable behaviour")
static struct hash_control * arm_ops_hsh;
static struct hash_control * arm_cond_hsh;
symbolS * symbol;
segT section;
subsegT sub_section;
+#ifdef OBJ_ELF
+ struct dwarf2_line_info locs [MAX_LITERAL_POOL_SIZE];
+#endif
struct literal_pool * next;
} literal_pool;
{ \
inst.it_insn_type = type; \
if (handle_it_state () == FAIL) \
- return; \
+ return; \
} \
while (0)
{ \
inst.it_insn_type = type; \
if (handle_it_state () == FAIL) \
- return failret; \
+ return failret; \
} \
while(0)
do \
{ \
if (inst.cond == COND_ALWAYS) \
- set_it_insn_type (IF_INSIDE_IT_LAST_INSN); \
+ set_it_insn_type (IF_INSIDE_IT_LAST_INSN); \
else \
- set_it_insn_type (INSIDE_IT_LAST_INSN); \
+ set_it_insn_type (INSIDE_IT_LAST_INSN); \
} \
while (0)
static inline int
skip_past_char (char ** str, char c)
{
+ /* PR gas/14987: Allow for whitespace before the expected character. */
+ skip_whitespace (*str);
+
if (**str == c)
{
(*str)++;
/* In unified syntax, all prefixes are optional. */
if (unified_syntax)
prefix_mode = (prefix_mode == GE_OPT_PREFIX_BIG) ? prefix_mode
- : GE_OPT_PREFIX;
+ : GE_OPT_PREFIX;
switch (prefix_mode)
{
in instructions, which is where this routine is always called. */
if (prefix_mode != GE_OPT_PREFIX_BIG
&& (ep->X_op == O_big
- || (ep->X_add_symbol
+ || (ep->X_add_symbol
&& (walk_no_bignums (ep->X_add_symbol)
- || (ep->X_op_symbol
+ || (ep->X_op_symbol
&& walk_no_bignums (ep->X_op_symbol))))))
{
inst.error = _("invalid constant");
char *p;
struct reg_entry *reg;
+ skip_whitespace (start);
+
#ifdef REGISTER_PREFIX
if (*start != REGISTER_PREFIX)
return NULL;
static int
arm_reg_alt_syntax (char **ccp, char *start, struct reg_entry *reg,
- enum arm_reg_type type)
+ enum arm_reg_type type)
{
/* Alternative syntaxes are accepted for a few register classes. */
switch (type)
case 'p': thistype = NT_poly; break;
case 's': thistype = NT_signed; break;
case 'u': thistype = NT_unsigned; break;
- case 'd':
- thistype = NT_float;
- thissize = 64;
- ptr++;
- goto done;
+ case 'd':
+ thistype = NT_float;
+ thissize = 64;
+ ptr++;
+ goto done;
default:
as_bad (_("unexpected character `%c' in type specifier"), *ptr);
return FAIL;
thissize = strtoul (ptr, &ptr, 10);
if (thissize != 8 && thissize != 16 && thissize != 32
- && thissize != 64)
- {
- as_bad (_("bad size %d in type specifier"), thissize);
+ && thissize != 64)
+ {
+ as_bad (_("bad size %d in type specifier"), thissize);
return FAIL;
}
}
done:
if (type)
- {
- type->el[type->elems].type = thistype;
+ {
+ type->el[type->elems].type = thistype;
type->el[type->elems].size = thissize;
type->elems++;
}
if (*str == '.')
{
if (parse_neon_type (&optype, &str) == SUCCESS)
- {
- if (optype.elems == 1)
- *vectype = optype.el[0];
- else
- {
- first_error (_("only one type should be specified for operand"));
- return FAIL;
- }
- }
+ {
+ if (optype.elems == 1)
+ *vectype = optype.el[0];
+ else
+ {
+ first_error (_("only one type should be specified for operand"));
+ return FAIL;
+ }
+ }
else
- {
- first_error (_("vector type expected"));
- return FAIL;
- }
+ {
+ first_error (_("vector type expected"));
+ return FAIL;
+ }
}
else
return FAIL;
static int
parse_typed_reg_or_scalar (char **ccp, enum arm_reg_type type,
- enum arm_reg_type *rtype,
- struct neon_typed_alias *typeinfo)
+ enum arm_reg_type *rtype,
+ struct neon_typed_alias *typeinfo)
{
char *str = *ccp;
struct reg_entry *reg = arm_reg_parse_multi (&str);
{
int altreg = arm_reg_alt_syntax (&str, *ccp, reg, type);
if (altreg != FAIL)
- *ccp = str;
+ *ccp = str;
if (typeinfo)
- *typeinfo = atype;
+ *typeinfo = atype;
return altreg;
}
if ((type == REG_TYPE_NDQ
&& (reg->type == REG_TYPE_NQ || reg->type == REG_TYPE_VFD))
|| (type == REG_TYPE_VFSD
- && (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD))
+ && (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD))
|| (type == REG_TYPE_NSDQ
- && (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD
- || reg->type == REG_TYPE_NQ))
+ && (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD
+ || reg->type == REG_TYPE_NQ))
|| (type == REG_TYPE_MMXWC
&& (reg->type == REG_TYPE_MMXWCG)))
type = (enum arm_reg_type) reg->type;
if (parse_neon_operand_type (&parsetype, &str) == SUCCESS)
{
if ((atype.defined & NTA_HASTYPE) != 0)
- {
- first_error (_("can't redefine type for operand"));
- return FAIL;
- }
+ {
+ first_error (_("can't redefine type for operand"));
+ return FAIL;
+ }
atype.defined |= NTA_HASTYPE;
atype.eltype = parsetype;
}
if (skip_past_char (&str, '[') == SUCCESS)
{
if (type != REG_TYPE_VFD)
- {
- first_error (_("only D registers may be indexed"));
- return FAIL;
- }
+ {
+ first_error (_("only D registers may be indexed"));
+ return FAIL;
+ }
if ((atype.defined & NTA_HASINDEX) != 0)
- {
- first_error (_("can't change index for operand"));
- return FAIL;
- }
+ {
+ first_error (_("can't change index for operand"));
+ return FAIL;
+ }
atype.defined |= NTA_HASINDEX;
if (skip_past_char (&str, ']') == SUCCESS)
- atype.index = NEON_ALL_LANES;
+ atype.index = NEON_ALL_LANES;
else
- {
- expressionS exp;
+ {
+ expressionS exp;
- my_get_expression (&exp, &str, GE_NO_PREFIX);
+ my_get_expression (&exp, &str, GE_NO_PREFIX);
- if (exp.X_op != O_constant)
- {
- first_error (_("constant expression required"));
- return FAIL;
- }
+ if (exp.X_op != O_constant)
+ {
+ first_error (_("constant expression required"));
+ return FAIL;
+ }
- if (skip_past_char (&str, ']') == FAIL)
- return FAIL;
+ if (skip_past_char (&str, ']') == FAIL)
+ return FAIL;
- atype.index = exp.X_add_number;
- }
+ atype.index = exp.X_add_number;
+ }
}
if (typeinfo)
static int
arm_typed_reg_parse (char **ccp, enum arm_reg_type type,
- enum arm_reg_type *rtype, struct neon_type_el *vectype)
+ enum arm_reg_type *rtype, struct neon_type_el *vectype)
{
struct neon_typed_alias atype;
char *str = *ccp;
/* We come back here if we get ranges concatenated by '+' or '|'. */
do
{
+ skip_whitespace (str);
+
another_range = 0;
if (*str == '{')
|| (in_range = 1, *str++ == '-'));
str--;
- if (*str++ != '}')
+ if (skip_past_char (&str, '}') == FAIL)
{
first_error (_("missing `}'"));
return FAIL;
If REGLIST_NEON_D is used, several syntax enhancements are enabled:
- Q registers can be used to specify pairs of D registers
- { } can be omitted from around a singleton register list
- FIXME: This is not implemented, as it would require backtracking in
- some cases, e.g.:
- vtbl.8 d3,d4,d5
- This could be done (the meaning isn't really ambiguous), but doesn't
- fit in well with the current parsing framework.
+ FIXME: This is not implemented, as it would require backtracking in
+ some cases, e.g.:
+ vtbl.8 d3,d4,d5
+ This could be done (the meaning isn't really ambiguous), but doesn't
+ fit in well with the current parsing framework.
- 32 D registers may be used (also true for VFPv3).
FIXME: Types are ignored in these register lists, which is probably a
bug. */
unsigned long mask = 0;
int i;
- if (*str != '{')
+ if (skip_past_char (&str, '{') == FAIL)
{
inst.error = _("expecting {");
return FAIL;
}
- str++;
-
switch (etype)
{
case REGLIST_VFP_S:
{
/* VFPv3 allows 32 D registers, except for the VFPv3-D16 variant. */
if (ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_d32))
- {
- max_regs = 32;
- if (thumb_mode)
- ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used,
- fpu_vfp_ext_d32);
- else
- ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used,
- fpu_vfp_ext_d32);
- }
+ {
+ max_regs = 32;
+ if (thumb_mode)
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used,
+ fpu_vfp_ext_d32);
+ else
+ ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used,
+ fpu_vfp_ext_d32);
+ }
else
- max_regs = 16;
+ max_regs = 16;
}
base_reg = max_regs;
}
if (new_base >= max_regs)
- {
- first_error (_("register out of range in list"));
- return FAIL;
- }
+ {
+ first_error (_("register out of range in list"));
+ return FAIL;
+ }
/* Note: a value of 2 * n is returned for the register Q<n>. */
if (regtype == REG_TYPE_NQ)
- {
- setmask = 3;
- addregs = 2;
- }
+ {
+ setmask = 3;
+ addregs = 2;
+ }
if (new_base < base_reg)
base_reg = new_base;
str++;
if ((high_range = arm_typed_reg_parse (&str, regtype, NULL, NULL))
- == FAIL)
+ == FAIL)
{
inst.error = gettext (reg_expected_msgs[regtype]);
return FAIL;
}
- if (high_range >= max_regs)
- {
- first_error (_("register out of range in list"));
- return FAIL;
- }
+ if (high_range >= max_regs)
+ {
+ first_error (_("register out of range in list"));
+ return FAIL;
+ }
- if (regtype == REG_TYPE_NQ)
- high_range = high_range + 1;
+ if (regtype == REG_TYPE_NQ)
+ high_range = high_range + 1;
if (high_range <= new_base)
{
if ((a->defined & NTA_HASTYPE) != 0
&& (a->eltype.type != b->eltype.type
- || a->eltype.size != b->eltype.size))
+ || a->eltype.size != b->eltype.size))
return FALSE;
if ((a->defined & NTA_HASINDEX) != 0
static int
parse_neon_el_struct_list (char **str, unsigned *pbase,
- struct neon_type_el *eltype)
+ struct neon_type_el *eltype)
{
char *ptr = *str;
int base_reg = -1;
int getreg = parse_typed_reg_or_scalar (&ptr, rtype, &rtype, &atype);
if (getreg == FAIL)
- {
- first_error (_(reg_expected_msgs[rtype]));
- return FAIL;
- }
+ {
+ first_error (_(reg_expected_msgs[rtype]));
+ return FAIL;
+ }
if (base_reg == -1)
- {
- base_reg = getreg;
- if (rtype == REG_TYPE_NQ)
- {
- reg_incr = 1;
- }
- firsttype = atype;
- }
+ {
+ base_reg = getreg;
+ if (rtype == REG_TYPE_NQ)
+ {
+ reg_incr = 1;
+ }
+ firsttype = atype;
+ }
else if (reg_incr == -1)
- {
- reg_incr = getreg - base_reg;
- if (reg_incr < 1 || reg_incr > 2)
- {
- first_error (_(incr_error));
- return FAIL;
- }
- }
+ {
+ reg_incr = getreg - base_reg;
+ if (reg_incr < 1 || reg_incr > 2)
+ {
+ first_error (_(incr_error));
+ return FAIL;
+ }
+ }
else if (getreg != base_reg + reg_incr * count)
- {
- first_error (_(incr_error));
- return FAIL;
- }
+ {
+ first_error (_(incr_error));
+ return FAIL;
+ }
if (! neon_alias_types_same (&atype, &firsttype))
- {
- first_error (_(type_error));
- return FAIL;
- }
+ {
+ first_error (_(type_error));
+ return FAIL;
+ }
/* Handle Dn-Dm or Qn-Qm syntax. Can only be used with non-indexed list
- modes. */
+ modes. */
if (ptr[0] == '-')
- {
- struct neon_typed_alias htype;
- int hireg, dregs = (rtype == REG_TYPE_NQ) ? 2 : 1;
- if (lane == -1)
- lane = NEON_INTERLEAVE_LANES;
- else if (lane != NEON_INTERLEAVE_LANES)
- {
- first_error (_(type_error));
- return FAIL;
- }
- if (reg_incr == -1)
- reg_incr = 1;
- else if (reg_incr != 1)
- {
- first_error (_("don't use Rn-Rm syntax with non-unit stride"));
- return FAIL;
- }
- ptr++;
- hireg = parse_typed_reg_or_scalar (&ptr, rtype, NULL, &htype);
- if (hireg == FAIL)
- {
- first_error (_(reg_expected_msgs[rtype]));
- return FAIL;
- }
- if (! neon_alias_types_same (&htype, &firsttype))
- {
- first_error (_(type_error));
- return FAIL;
- }
- count += hireg + dregs - getreg;
- continue;
- }
+ {
+ struct neon_typed_alias htype;
+ int hireg, dregs = (rtype == REG_TYPE_NQ) ? 2 : 1;
+ if (lane == -1)
+ lane = NEON_INTERLEAVE_LANES;
+ else if (lane != NEON_INTERLEAVE_LANES)
+ {
+ first_error (_(type_error));
+ return FAIL;
+ }
+ if (reg_incr == -1)
+ reg_incr = 1;
+ else if (reg_incr != 1)
+ {
+ first_error (_("don't use Rn-Rm syntax with non-unit stride"));
+ return FAIL;
+ }
+ ptr++;
+ hireg = parse_typed_reg_or_scalar (&ptr, rtype, NULL, &htype);
+ if (hireg == FAIL)
+ {
+ first_error (_(reg_expected_msgs[rtype]));
+ return FAIL;
+ }
+ if (! neon_alias_types_same (&htype, &firsttype))
+ {
+ first_error (_(type_error));
+ return FAIL;
+ }
+ count += hireg + dregs - getreg;
+ continue;
+ }
/* If we're using Q registers, we can't use [] or [n] syntax. */
if (rtype == REG_TYPE_NQ)
- {
- count += 2;
- continue;
- }
+ {
+ count += 2;
+ continue;
+ }
if ((atype.defined & NTA_HASINDEX) != 0)
- {
- if (lane == -1)
- lane = atype.index;
- else if (lane != atype.index)
- {
- first_error (_(type_error));
- return FAIL;
- }
- }
+ {
+ if (lane == -1)
+ lane = atype.index;
+ else if (lane != atype.index)
+ {
+ first_error (_(type_error));
+ return FAIL;
+ }
+ }
else if (lane == -1)
- lane = NEON_INTERLEAVE_LANES;
+ lane = NEON_INTERLEAVE_LANES;
else if (lane != NEON_INTERLEAVE_LANES)
- {
- first_error (_(type_error));
- return FAIL;
- }
+ {
+ first_error (_(type_error));
+ return FAIL;
+ }
count++;
}
while ((count != 1 || leading_brace) && skip_past_comma (&ptr) != FAIL);
arm_reloc_hsh contains no entries, so this function can only
succeed if there is no () after the word. Returns -1 on error,
BFD_RELOC_UNUSED if there wasn't any suffix. */
+
static int
parse_reloc (char **str)
{
static void
insert_neon_reg_alias (char *str, int number, int type,
- struct neon_typed_alias *atype)
+ struct neon_typed_alias *atype)
{
struct reg_entry *reg = insert_reg_alias (str, number, type);
if (atype)
{
reg->neon = (struct neon_typed_alias *)
- xmalloc (sizeof (struct neon_typed_alias));
+ xmalloc (sizeof (struct neon_typed_alias));
*reg->neon = *atype;
}
}
/* Try parsing as an integer. */
my_get_expression (&exp, &p, GE_NO_PREFIX);
if (exp.X_op != O_constant)
- {
- as_bad (_("expression must be constant"));
- return FALSE;
- }
+ {
+ as_bad (_("expression must be constant"));
+ return FALSE;
+ }
basereg = &mybasereg;
basereg->number = (basetype == REG_TYPE_NQ) ? exp.X_add_number * 2
- : exp.X_add_number;
+ : exp.X_add_number;
basereg->neon = 0;
}
{
/* We got a type. */
if (typeinfo.defined & NTA_HASTYPE)
- {
- as_bad (_("can't redefine the type of a register alias"));
- return FALSE;
- }
+ {
+ as_bad (_("can't redefine the type of a register alias"));
+ return FALSE;
+ }
typeinfo.defined |= NTA_HASTYPE;
if (ntype.elems != 1)
- {
- as_bad (_("you must specify a single type only"));
- return FALSE;
- }
+ {
+ as_bad (_("you must specify a single type only"));
+ return FALSE;
+ }
typeinfo.eltype = ntype.el[0];
}
/* We got a scalar index. */
if (typeinfo.defined & NTA_HASINDEX)
- {
- as_bad (_("can't redefine the index of a scalar alias"));
- return FALSE;
- }
+ {
+ as_bad (_("can't redefine the index of a scalar alias"));
+ return FALSE;
+ }
my_get_expression (&exp, &p, GE_NO_PREFIX);
if (exp.X_op != O_constant)
- {
- as_bad (_("scalar index must be constant"));
- return FALSE;
- }
+ {
+ as_bad (_("scalar index must be constant"));
+ return FALSE;
+ }
typeinfo.defined |= NTA_HASINDEX;
typeinfo.index = exp.X_add_number;
if (skip_past_char (&p, ']') == FAIL)
- {
- as_bad (_("expecting ]"));
- return FALSE;
- }
+ {
+ as_bad (_("expecting ]"));
+ return FALSE;
+ }
}
/* If TC_CASE_SENSITIVE is defined, then newname already points to
namebuf[namelen] = '\0';
insert_neon_reg_alias (namebuf, basereg->number, basetype,
- typeinfo.defined != 0 ? &typeinfo : NULL);
+ typeinfo.defined != 0 ? &typeinfo : NULL);
/* Insert name in all uppercase. */
for (p = namebuf; *p; p++)
if (strncmp (namebuf, newname, namelen))
insert_neon_reg_alias (namebuf, basereg->number, basetype,
- typeinfo.defined != 0 ? &typeinfo : NULL);
+ typeinfo.defined != 0 ? &typeinfo : NULL);
/* Insert name in all lowercase. */
for (p = namebuf; *p; p++)
if (strncmp (namebuf, newname, namelen))
insert_neon_reg_alias (namebuf, basereg->number, basetype,
- typeinfo.defined != 0 ? &typeinfo : NULL);
+ typeinfo.defined != 0 ? &typeinfo : NULL);
return TRUE;
}
else
{
struct reg_entry *reg = (struct reg_entry *) hash_find (arm_reg_hsh,
- name);
+ name);
if (!reg)
as_bad (_("unknown register alias '%s'"), name);
hash_delete (arm_reg_hsh, name, FALSE);
free ((char *) reg->name);
- if (reg->neon)
- free (reg->neon);
+ if (reg->neon)
+ free (reg->neon);
free (reg);
/* Also locate the all upper case and all lower case versions.
/* The mapping symbol has already been emitted.
There is nothing else to do. */
return;
- else if (TRANSITION (MAP_UNDEFINED, MAP_DATA))
+
+ if (state == MAP_ARM || state == MAP_THUMB)
+ /* PR gas/12931
+ All ARM instructions require 4-byte alignment.
+ (Almost) all Thumb instructions require 2-byte alignment.
+
+ When emitting instructions into any section, mark the section
+ appropriately.
+
+ Some Thumb instructions are alignment-sensitive modulo 4 bytes,
+ but themselves require 2-byte alignment; this applies to some
+ PC- relative forms. However, these cases will invovle implicit
+ literal pool generation or an explicit .align >=2, both of
+ which will cause the section to me marked with sufficient
+ alignment. Thus, we don't handle those cases here. */
+ record_alignment (now_seg, state == MAP_ARM ? 2 : 1);
+
+ if (TRANSITION (MAP_UNDEFINED, MAP_DATA))
/* This case will be evaluated later in the next else. */
return;
else if (TRANSITION (MAP_UNDEFINED, MAP_ARM)
- || TRANSITION (MAP_UNDEFINED, MAP_THUMB))
+ || TRANSITION (MAP_UNDEFINED, MAP_THUMB))
{
/* Only add the symbol if the offset is > 0:
- if we're at the first frag, check it's size > 0;
- if we're not at the first frag, then for sure
- the offset is > 0. */
+ if we're at the first frag, check it's size > 0;
+ if we're not at the first frag, then for sure
+ the offset is > 0. */
struct frag * const frag_first = seg_info (now_seg)->frchainP->frch_root;
const int add_symbol = (frag_now != frag_first) || (frag_now_fix () > 0);
if (add_symbol)
- make_mapping_symbol (MAP_DATA, (valueT) 0, frag_first);
+ make_mapping_symbol (MAP_DATA, (valueT) 0, frag_first);
}
mapping_state_2 (state, 0);
}
pool->literals[entry] = inst.reloc.exp;
+#ifdef OBJ_ELF
+ /* PR ld/12974: Record the location of the first source line to reference
+ this entry in the literal pool. If it turns out during linking that the
+ symbol does not exist we will be able to give an accurate line number for
+ the (first use of the) missing reference. */
+ if (debug_type == DEBUG_DWARF2)
+ dwarf2_where (pool->locs + entry);
+#endif
pool->next_free_entry += 1;
}
#endif
for (entry = 0; entry < pool->next_free_entry; entry ++)
- /* First output the expression in the instruction to the pool. */
- emit_expr (&(pool->literals[entry]), 4); /* .word */
+ {
+#ifdef OBJ_ELF
+ if (debug_type == DEBUG_DWARF2)
+ dwarf2_gen_line_info (frag_now_fix (), pool->locs + entry);
+#endif
+ /* First output the expression in the instruction to the pool. */
+ emit_expr (&(pool->literals[entry]), 4); /* .word */
+ }
/* Mark the pool as empty. */
pool->next_free_entry = 0;
else
{
reloc_howto_type *howto = (reloc_howto_type *)
- bfd_reloc_type_lookup (stdoutput,
- (bfd_reloc_code_real_type) reloc);
+ bfd_reloc_type_lookup (stdoutput,
+ (bfd_reloc_code_real_type) reloc);
int size = bfd_get_reloc_size (howto);
if (reloc == BFD_RELOC_ARM_PLT32)
record_alignment (now_seg, 2);
ptr = frag_more (8);
+ memset (ptr, 0, 8);
where = frag_now_fix () - 8;
/* Self relative offset of the function start. */
}
while (skip_past_comma (&input_line_pointer) != FAIL);
- if (*input_line_pointer == '}')
- input_line_pointer++;
+ skip_past_char (&input_line_pointer, '}');
demand_empty_rest_of_line ();
if (*input_line_pointer == '{')
input_line_pointer++;
+ skip_whitespace (input_line_pointer);
+
do
{
reg = arm_reg_parse (&input_line_pointer, REG_TYPE_MMXWCG);
}
while (skip_past_comma (&input_line_pointer) != FAIL);
- if (*input_line_pointer == '}')
- input_line_pointer++;
+ skip_past_char (&input_line_pointer, '}');
demand_empty_rest_of_line ();
case REG_TYPE_RN: s_arm_unwind_save_core (); return;
case REG_TYPE_VFD:
if (arch_v6)
- s_arm_unwind_save_vfp_armv6 ();
+ s_arm_unwind_save_vfp_armv6 ();
else
- s_arm_unwind_save_vfp ();
+ s_arm_unwind_save_vfp ();
return;
case REG_TYPE_MMXWR: s_arm_unwind_save_mmxwr (); return;
case REG_TYPE_MMXWCG: s_arm_unwind_save_mmxwcg (); return;
static void
s_arm_eabi_attribute (int ignored ATTRIBUTE_UNUSED)
{
- int tag = s_vendor_attribute (OBJ_ATTR_PROC);
+ int tag = obj_elf_vendor_attribute (OBJ_ATTR_PROC);
if (tag < NUM_KNOWN_OBJ_ATTRIBUTES)
attributes_set_explicitly[tag] = 1;
32-bit X_add_number, though. */
if ((exp.X_add_number & ~(offsetT)(0xffffffffU)) != 0)
{
- /* X >> 32 is illegal if sizeof (exp.X_add_number) == 4. */
+ /* X >> 32 is illegal if sizeof (exp.X_add_number) == 4. */
inst.operands[i].reg = ((exp.X_add_number >> 16) >> 16) & 0xffffffff;
inst.operands[i].regisimm = 1;
}
unsigned parts = 32 / LITTLENUM_NUMBER_OF_BITS, j, idx = 0;
/* Bignums have their least significant bits in
- generic_bignum[0]. Make sure we put 32 bits in imm and
- 32 bits in reg, in a (hopefully) portable way. */
+ generic_bignum[0]. Make sure we put 32 bits in imm and
+ 32 bits in reg, in a (hopefully) portable way. */
gas_assert (parts != 0);
/* Make sure that the number is not too big.
inst.operands[i].imm = 0;
for (j = 0; j < parts; j++, idx++)
- inst.operands[i].imm |= generic_bignum[idx]
- << (LITTLENUM_NUMBER_OF_BITS * j);
+ inst.operands[i].imm |= generic_bignum[idx]
+ << (LITTLENUM_NUMBER_OF_BITS * j);
inst.operands[i].reg = 0;
for (j = 0; j < parts; j++, idx++)
- inst.operands[i].reg |= generic_bignum[idx]
- << (LITTLENUM_NUMBER_OF_BITS * j);
+ inst.operands[i].reg |= generic_bignum[idx]
+ << (LITTLENUM_NUMBER_OF_BITS * j);
inst.operands[i].regisimm = 1;
}
else
else
{
for (; *fpnum != '\0' && *fpnum != ' ' && *fpnum != '\n'; fpnum++)
- if (*fpnum == '.' || *fpnum == 'e' || *fpnum == 'E')
- {
- found_fpchar = 1;
- break;
- }
+ if (*fpnum == '.' || *fpnum == 'e' || *fpnum == 'E')
+ {
+ found_fpchar = 1;
+ break;
+ }
if (!found_fpchar)
- return FAIL;
+ return FAIL;
}
if ((str = atof_ieee (str, 's', words)) != NULL)
/* 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];
- }
+ {
+ fpword <<= LITTLENUM_NUMBER_OF_BITS;
+ fpword |= words[i];
+ }
if (is_quarter_float (fpword) || (fpword & 0x7fffffff) == 0)
- *immed = fpword;
+ *immed = fpword;
else
- return FAIL;
+ return FAIL;
*ccp = str;
}
shift_name = (const struct asm_shift_name *) hash_find_n (arm_shift_hsh, *str,
- p - *str);
+ p - *str);
if (shift_name == NULL)
{
return FAIL;
}
- /* Convert to decoded value. md_apply_fix will put it back. */
- inst.reloc.exp.X_add_number
- = (((inst.reloc.exp.X_add_number << (32 - value))
- | (inst.reloc.exp.X_add_number >> value)) & 0xffffffff);
+ /* Encode as specified. */
+ inst.operands[i].imm = inst.reloc.exp.X_add_number | value << 7;
+ return SUCCESS;
}
inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
if (strncasecmp (group_reloc_table[i].name, *str, length) == 0
&& (*str)[length] == ':')
- {
- *out = &group_reloc_table[i];
- *str += (length + 1);
- return SUCCESS;
- }
+ {
+ *out = &group_reloc_table[i];
+ *str += (length + 1);
+ return SUCCESS;
+ }
}
return FAIL;
struct group_reloc_table_entry *entry;
if ((*str)[0] == '#')
- (*str) += 2;
+ (*str) += 2;
else
- (*str)++;
+ (*str)++;
/* Try to parse a group relocation. Anything else is an error. */
if (find_group_reloc_table_entry (str, &entry) == FAIL)
- {
- inst.error = _("unknown group relocation");
- return PARSE_OPERAND_FAIL_NO_BACKTRACK;
- }
+ {
+ inst.error = _("unknown group relocation");
+ return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+ }
/* We now have the group relocation table entry corresponding to
- the name in the assembler source. Next, we parse the expression. */
+ the name in the assembler source. Next, we parse the expression. */
if (my_get_expression (&inst.reloc.exp, str, GE_NO_PREFIX))
- return PARSE_OPERAND_FAIL_NO_BACKTRACK;
+ return PARSE_OPERAND_FAIL_NO_BACKTRACK;
/* Record the relocation type (always the ALU variant here). */
inst.reloc.type = (bfd_reloc_code_real_type) entry->alu_code;
}
else
return parse_shifter_operand (str, i) == SUCCESS
- ? PARSE_OPERAND_SUCCESS : PARSE_OPERAND_FAIL;
+ ? PARSE_OPERAND_SUCCESS : PARSE_OPERAND_FAIL;
/* Never reached. */
}
/* Parse a Neon alignment expression. Information is written to
inst.operands[i]. We assume the initial ':' has been skipped.
-
+
align .imm = align << 8, .immisalign=1, .preind=0 */
static parse_operand_result
parse_neon_alignment (char **str, int i)
static parse_operand_result
parse_address_main (char **str, int i, int group_relocations,
- group_reloc_type group_type)
+ group_reloc_type group_type)
{
char *p = *str;
int reg;
return PARSE_OPERAND_SUCCESS;
}
+ /* PR gas/14887: Allow for whitespace after the opening bracket. */
+ skip_whitespace (p);
+
if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
{
inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
code before we get to see it here. This may be subject to
change. */
parse_operand_result result = parse_neon_alignment (&p, i);
-
+
if (result != PARSE_OPERAND_SUCCESS)
return result;
}
{
struct group_reloc_table_entry *entry;
- /* Skip over the #: or : sequence. */
- if (*p == '#')
- p += 2;
- else
- p++;
+ /* Skip over the #: or : sequence. */
+ if (*p == '#')
+ p += 2;
+ else
+ p++;
/* Try to parse a group relocation. Anything else is an
- error. */
+ error. */
if (find_group_reloc_table_entry (&p, &entry) == FAIL)
{
inst.error = _("unknown group relocation");
/* We now have the group relocation table entry corresponding to
the name in the assembler source. Next, we parse the
- expression. */
+ expression. */
if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
return PARSE_OPERAND_FAIL_NO_BACKTRACK;
/* Record the relocation type. */
- switch (group_type)
- {
- case GROUP_LDR:
- inst.reloc.type = (bfd_reloc_code_real_type) entry->ldr_code;
- break;
+ switch (group_type)
+ {
+ case GROUP_LDR:
+ inst.reloc.type = (bfd_reloc_code_real_type) entry->ldr_code;
+ break;
- case GROUP_LDRS:
- inst.reloc.type = (bfd_reloc_code_real_type) entry->ldrs_code;
- break;
+ case GROUP_LDRS:
+ inst.reloc.type = (bfd_reloc_code_real_type) entry->ldrs_code;
+ break;
- case GROUP_LDC:
- inst.reloc.type = (bfd_reloc_code_real_type) entry->ldc_code;
- break;
+ case GROUP_LDC:
+ inst.reloc.type = (bfd_reloc_code_real_type) entry->ldc_code;
+ break;
- default:
- gas_assert (0);
- }
+ default:
+ gas_assert (0);
+ }
- if (inst.reloc.type == 0)
+ if (inst.reloc.type == 0)
{
inst.error = _("this group relocation is not allowed on this instruction");
return PARSE_OPERAND_FAIL_NO_BACKTRACK;
}
- }
- else
- if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
- return PARSE_OPERAND_FAIL;
+ }
+ else
+ {
+ char *q = p;
+ if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+ return PARSE_OPERAND_FAIL;
+ /* If the offset is 0, find out if it's a +0 or -0. */
+ if (inst.reloc.exp.X_op == O_constant
+ && inst.reloc.exp.X_add_number == 0)
+ {
+ skip_whitespace (q);
+ if (*q == '#')
+ {
+ q++;
+ skip_whitespace (q);
+ }
+ if (*q == '-')
+ inst.operands[i].negative = 1;
+ }
+ }
}
}
else if (skip_past_char (&p, ':') == SUCCESS)
/* FIXME: '@' should be used here, but it's filtered out by generic code
before we get to see it here. This may be subject to change. */
parse_operand_result result = parse_neon_alignment (&p, i);
-
+
if (result != PARSE_OPERAND_SUCCESS)
return result;
}
if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
{
- /* We might be using the immediate for alignment already. If we
- are, OR the register number into the low-order bits. */
- if (inst.operands[i].immisalign)
- inst.operands[i].imm |= reg;
- else
- inst.operands[i].imm = reg;
+ /* We might be using the immediate for alignment already. If we
+ are, OR the register number into the low-order bits. */
+ if (inst.operands[i].immisalign)
+ inst.operands[i].imm |= reg;
+ else
+ inst.operands[i].imm = reg;
inst.operands[i].immisreg = 1;
if (skip_past_comma (&p) == SUCCESS)
}
else
{
+ char *q = p;
if (inst.operands[i].negative)
{
inst.operands[i].negative = 0;
}
if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
return PARSE_OPERAND_FAIL;
+ /* If the offset is 0, find out if it's a +0 or -0. */
+ if (inst.reloc.exp.X_op == O_constant
+ && inst.reloc.exp.X_add_number == 0)
+ {
+ skip_whitespace (q);
+ if (*q == '#')
+ {
+ q++;
+ skip_whitespace (q);
+ }
+ if (*q == '-')
+ inst.operands[i].negative = 1;
+ }
}
}
}
parse_address (char **str, int i)
{
return parse_address_main (str, i, 0, GROUP_LDR) == PARSE_OPERAND_SUCCESS
- ? SUCCESS : FAIL;
+ ? SUCCESS : FAIL;
}
static parse_operand_result
bfd_boolean is_apsr = FALSE;
bfd_boolean m_profile = ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m);
+ /* PR gas/12698: If the user has specified -march=all then m_profile will
+ be TRUE, but we want to ignore it in this case as we are building for any
+ CPU type, including non-m variants. */
+ if (selected_cpu.core == arm_arch_any.core)
+ m_profile = FALSE;
+
/* CPSR's and SPSR's can now be lowercase. This is just a convenience
feature for ease of use and backwards compatibility. */
p = *str;
{
if (m_profile)
goto unsupported_psr;
-
+
psr_field = SPSR_BIT;
}
else if (strncasecmp (p, "CPSR", 4) == 0)
p = start + strcspn (start, "rR") + 1;
psr = (const struct asm_psr *) hash_find_n (arm_v7m_psr_hsh, start,
- p - start);
+ p - start);
if (!psr)
return FAIL;
unsigned int nzcvq_bits = 0;
unsigned int g_bit = 0;
char *bit;
-
+
for (bit = start; bit != p; bit++)
{
switch (TOLOWER (*bit))
- {
+ {
case 'n':
nzcvq_bits |= (nzcvq_bits & 0x01) ? 0x20 : 0x01;
break;
case 'v':
nzcvq_bits |= (nzcvq_bits & 0x08) ? 0x20 : 0x08;
break;
-
+
case 'q':
nzcvq_bits |= (nzcvq_bits & 0x10) ? 0x20 : 0x10;
break;
-
+
case 'g':
g_bit |= (g_bit & 0x1) ? 0x2 : 0x1;
break;
-
+
default:
inst.error = _("unexpected bit specified after APSR");
return FAIL;
}
}
-
+
if (nzcvq_bits == 0x1f)
psr_field |= PSR_f;
-
+
if (g_bit == 0x1)
{
if (!ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp))
- {
+ {
inst.error = _("selected processor does not "
"support DSP extension");
return FAIL;
psr_field |= PSR_s;
}
-
+
if ((nzcvq_bits & 0x20) != 0
|| (nzcvq_bits != 0x1f && nzcvq_bits != 0)
|| (g_bit & 0x2) != 0)
}
}
else
- {
+ {
psr = (const struct asm_psr *) hash_find_n (arm_psr_hsh, start,
- p - start);
+ p - start);
if (!psr)
- goto error;
+ goto error;
psr_field |= psr->field;
}
goto error; /* Garbage after "[CS]PSR". */
/* Unadorned APSR is equivalent to APSR_nzcvq/CPSR_f (for writes). This
- is deprecated, but allow it anyway. */
+ is deprecated, but allow it anyway. */
if (is_apsr && lhs)
{
psr_field |= PSR_f;
return c->value;
}
+/* If the given feature available in the selected CPU, mark it as used.
+ Returns TRUE iff feature is available. */
+static bfd_boolean
+mark_feature_used (const arm_feature_set *feature)
+{
+ /* Ensure the option is valid on the current architecture. */
+ if (!ARM_CPU_HAS_FEATURE (cpu_variant, *feature))
+ return FALSE;
+
+ /* Add the appropriate architecture feature for the barrier option used.
+ */
+ if (thumb_mode)
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, *feature);
+ else
+ ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used, *feature);
+
+ return TRUE;
+}
+
/* Parse an option for a barrier instruction. Returns the encoding for the
option, or FAIL. */
static int
q++;
o = (const struct asm_barrier_opt *) hash_find_n (arm_barrier_opt_hsh, p,
- q - p);
+ q - p);
if (!o)
return FAIL;
+ if (!mark_feature_used (&o->arch))
+ return FAIL;
+
*str = q;
return o->value;
}
inst.operands[i++].present = 1;
if (skip_past_comma (&ptr) == FAIL)
- goto wanted_comma;
+ goto wanted_comma;
if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL)
- goto wanted_arm;
+ goto wanted_arm;
inst.operands[i].reg = val;
inst.operands[i].isreg = 1;
inst.operands[i].present = 1;
}
else if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_NSDQ, &rtype, &optype))
- != FAIL)
+ != FAIL)
{
/* Cases 0, 1, 2, 3, 5 (D only). */
if (skip_past_comma (&ptr) == FAIL)
- goto wanted_comma;
+ goto wanted_comma;
inst.operands[i].reg = val;
inst.operands[i].isreg = 1;
inst.operands[i++].present = 1;
if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) != FAIL)
- {
- /* Case 5: VMOV<c><q> <Dm>, <Rd>, <Rn>.
- Case 13: VMOV <Sd>, <Rm> */
- inst.operands[i].reg = val;
- inst.operands[i].isreg = 1;
- inst.operands[i].present = 1;
-
- if (rtype == REG_TYPE_NQ)
- {
- first_error (_("can't use Neon quad register here"));
- return FAIL;
- }
- else if (rtype != REG_TYPE_VFS)
- {
- i++;
- if (skip_past_comma (&ptr) == FAIL)
- goto wanted_comma;
- if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL)
- goto wanted_arm;
- inst.operands[i].reg = val;
- inst.operands[i].isreg = 1;
- inst.operands[i].present = 1;
- }
- }
+ {
+ /* Case 5: VMOV<c><q> <Dm>, <Rd>, <Rn>.
+ Case 13: VMOV <Sd>, <Rm> */
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].present = 1;
+
+ if (rtype == REG_TYPE_NQ)
+ {
+ first_error (_("can't use Neon quad register here"));
+ return FAIL;
+ }
+ else if (rtype != REG_TYPE_VFS)
+ {
+ i++;
+ if (skip_past_comma (&ptr) == FAIL)
+ goto wanted_comma;
+ if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL)
+ goto wanted_arm;
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].present = 1;
+ }
+ }
else if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_NSDQ, &rtype,
- &optype)) != FAIL)
- {
- /* Case 0: VMOV<c><q> <Qd>, <Qm>
- Case 1: VMOV<c><q> <Dd>, <Dm>
- Case 8: VMOV.F32 <Sd>, <Sm>
- Case 15: VMOV <Sd>, <Se>, <Rn>, <Rm> */
-
- inst.operands[i].reg = val;
- inst.operands[i].isreg = 1;
- inst.operands[i].isquad = (rtype == REG_TYPE_NQ);
- inst.operands[i].issingle = (rtype == REG_TYPE_VFS);
- inst.operands[i].isvec = 1;
- inst.operands[i].vectype = optype;
- inst.operands[i].present = 1;
-
- if (skip_past_comma (&ptr) == SUCCESS)
- {
- /* Case 15. */
- i++;
-
- if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL)
- goto wanted_arm;
-
- inst.operands[i].reg = val;
- inst.operands[i].isreg = 1;
- inst.operands[i++].present = 1;
-
- if (skip_past_comma (&ptr) == FAIL)
- goto wanted_comma;
-
- if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL)
- goto wanted_arm;
-
- inst.operands[i].reg = val;
- inst.operands[i].isreg = 1;
- inst.operands[i++].present = 1;
- }
- }
+ &optype)) != FAIL)
+ {
+ /* Case 0: VMOV<c><q> <Qd>, <Qm>
+ Case 1: VMOV<c><q> <Dd>, <Dm>
+ Case 8: VMOV.F32 <Sd>, <Sm>
+ Case 15: VMOV <Sd>, <Se>, <Rn>, <Rm> */
+
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].isquad = (rtype == REG_TYPE_NQ);
+ inst.operands[i].issingle = (rtype == REG_TYPE_VFS);
+ inst.operands[i].isvec = 1;
+ inst.operands[i].vectype = optype;
+ inst.operands[i].present = 1;
+
+ if (skip_past_comma (&ptr) == SUCCESS)
+ {
+ /* Case 15. */
+ i++;
+
+ if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL)
+ goto wanted_arm;
+
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i++].present = 1;
+
+ if (skip_past_comma (&ptr) == FAIL)
+ goto wanted_comma;
+
+ if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) == FAIL)
+ goto wanted_arm;
+
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].present = 1;
+ }
+ }
else if (parse_qfloat_immediate (&ptr, &inst.operands[i].imm) == SUCCESS)
- /* Case 2: VMOV<c><q>.<dt> <Qd>, #<float-imm>
- Case 3: VMOV<c><q>.<dt> <Dd>, #<float-imm>
- Case 10: VMOV.F32 <Sd>, #<imm>
- Case 11: VMOV.F64 <Dd>, #<imm> */
- inst.operands[i].immisfloat = 1;
+ /* Case 2: VMOV<c><q>.<dt> <Qd>, #<float-imm>
+ Case 3: VMOV<c><q>.<dt> <Dd>, #<float-imm>
+ Case 10: VMOV.F32 <Sd>, #<imm>
+ Case 11: VMOV.F64 <Dd>, #<imm> */
+ inst.operands[i].immisfloat = 1;
else if (parse_big_immediate (&ptr, i) == SUCCESS)
- /* Case 2: VMOV<c><q>.<dt> <Qd>, #<imm>
- Case 3: VMOV<c><q>.<dt> <Dd>, #<imm> */
- ;
+ /* Case 2: VMOV<c><q>.<dt> <Qd>, #<imm>
+ Case 3: VMOV<c><q>.<dt> <Dd>, #<imm> */
+ ;
else
- {
- first_error (_("expected <Rm> or <Dm> or <Qm> operand"));
- return FAIL;
- }
+ {
+ first_error (_("expected <Rm> or <Dm> or <Qm> operand"));
+ return FAIL;
+ }
}
else if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) != FAIL)
{
inst.operands[i++].present = 1;
if (skip_past_comma (&ptr) == FAIL)
- goto wanted_comma;
+ goto wanted_comma;
if ((val = parse_scalar (&ptr, 8, &optype)) != FAIL)
- {
- /* Case 6: VMOV<c><q>.<dt> <Rd>, <Dn[x]> */
- inst.operands[i].reg = val;
- inst.operands[i].isscalar = 1;
- inst.operands[i].present = 1;
- inst.operands[i].vectype = optype;
- }
+ {
+ /* Case 6: VMOV<c><q>.<dt> <Rd>, <Dn[x]> */
+ inst.operands[i].reg = val;
+ inst.operands[i].isscalar = 1;
+ inst.operands[i].present = 1;
+ inst.operands[i].vectype = optype;
+ }
else if ((val = arm_reg_parse (&ptr, REG_TYPE_RN)) != FAIL)
- {
- /* Case 7: VMOV<c><q> <Rd>, <Rn>, <Dm> */
- inst.operands[i].reg = val;
- inst.operands[i].isreg = 1;
- inst.operands[i++].present = 1;
-
- if (skip_past_comma (&ptr) == FAIL)
- goto wanted_comma;
-
- if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_VFSD, &rtype, &optype))
- == FAIL)
- {
- first_error (_(reg_expected_msgs[REG_TYPE_VFSD]));
- return FAIL;
- }
-
- inst.operands[i].reg = val;
- inst.operands[i].isreg = 1;
- inst.operands[i].isvec = 1;
- inst.operands[i].issingle = (rtype == REG_TYPE_VFS);
- inst.operands[i].vectype = optype;
- inst.operands[i].present = 1;
-
- if (rtype == REG_TYPE_VFS)
- {
- /* Case 14. */
- i++;
- if (skip_past_comma (&ptr) == FAIL)
- goto wanted_comma;
- if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_VFS, NULL,
- &optype)) == FAIL)
- {
- first_error (_(reg_expected_msgs[REG_TYPE_VFS]));
- return FAIL;
- }
- inst.operands[i].reg = val;
- inst.operands[i].isreg = 1;
- inst.operands[i].isvec = 1;
- inst.operands[i].issingle = 1;
- inst.operands[i].vectype = optype;
- inst.operands[i].present = 1;
- }
- }
+ {
+ /* Case 7: VMOV<c><q> <Rd>, <Rn>, <Dm> */
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i++].present = 1;
+
+ if (skip_past_comma (&ptr) == FAIL)
+ goto wanted_comma;
+
+ if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_VFSD, &rtype, &optype))
+ == FAIL)
+ {
+ first_error (_(reg_expected_msgs[REG_TYPE_VFSD]));
+ return FAIL;
+ }
+
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].isvec = 1;
+ inst.operands[i].issingle = (rtype == REG_TYPE_VFS);
+ inst.operands[i].vectype = optype;
+ inst.operands[i].present = 1;
+
+ if (rtype == REG_TYPE_VFS)
+ {
+ /* Case 14. */
+ i++;
+ if (skip_past_comma (&ptr) == FAIL)
+ goto wanted_comma;
+ if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_VFS, NULL,
+ &optype)) == FAIL)
+ {
+ first_error (_(reg_expected_msgs[REG_TYPE_VFS]));
+ return FAIL;
+ }
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].isvec = 1;
+ inst.operands[i].issingle = 1;
+ inst.operands[i].vectype = optype;
+ inst.operands[i].present = 1;
+ }
+ }
else if ((val = arm_typed_reg_parse (&ptr, REG_TYPE_VFS, NULL, &optype))
- != FAIL)
- {
- /* Case 13. */
- inst.operands[i].reg = val;
- inst.operands[i].isreg = 1;
- inst.operands[i].isvec = 1;
- inst.operands[i].issingle = 1;
- inst.operands[i].vectype = optype;
- inst.operands[i++].present = 1;
- }
+ != FAIL)
+ {
+ /* Case 13. */
+ inst.operands[i].reg = val;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].isvec = 1;
+ inst.operands[i].issingle = 1;
+ inst.operands[i].vectype = optype;
+ inst.operands[i].present = 1;
+ }
}
else
{
OP_RRnpc, /* ARM register, not r15 */
OP_RRnpcsp, /* ARM register, neither r15 nor r13 (a.k.a. 'BadReg') */
OP_RRnpcb, /* ARM register, not r15, in square brackets */
- OP_RRnpctw, /* ARM register, not r15 in Thumb-state or with writeback,
+ OP_RRnpctw, /* ARM register, not r15 in Thumb-state or with writeback,
optional trailing ! */
OP_RRw, /* ARM register, not r15, optional trailing ! */
OP_RCP, /* Coprocessor number */
OP_oI7b, /* immediate, prefix optional, 0 .. 7 */
OP_oI31b, /* 0 .. 31 */
OP_oI32b, /* 1 .. 32 */
+ OP_oI32z, /* 0 .. 32 */
OP_oIffffb, /* 0 .. 65535 */
OP_oI255c, /* curly-brace enclosed, 0 .. 255 */
unsigned const int *upat = pattern;
char *backtrack_pos = 0;
const char *backtrack_error = 0;
- int i, val, backtrack_index = 0;
+ int i, val = 0, backtrack_index = 0;
enum arm_reg_type rtype;
parse_operand_result result;
unsigned int op_parse_code;
do \
{ \
if (skip_past_char (&str, chr) == FAIL) \
- goto bad_args; \
+ goto bad_args; \
} \
while (0)
do \
{ \
val = arm_typed_reg_parse (& str, regtype, & rtype, \
- & inst.operands[i].vectype); \
+ & inst.operands[i].vectype); \
if (val == FAIL) \
- { \
- first_error (_(reg_expected_msgs[regtype])); \
- goto failure; \
- } \
+ { \
+ first_error (_(reg_expected_msgs[regtype])); \
+ goto failure; \
+ } \
inst.operands[i].reg = val; \
inst.operands[i].isreg = 1; \
inst.operands[i].isquad = (rtype == REG_TYPE_NQ); \
inst.operands[i].issingle = (rtype == REG_TYPE_VFS); \
inst.operands[i].isvec = (rtype == REG_TYPE_VFS \
- || rtype == REG_TYPE_VFD \
- || rtype == REG_TYPE_NQ); \
+ || rtype == REG_TYPE_VFD \
+ || rtype == REG_TYPE_NQ); \
} \
while (0)
inst.operands[i].isquad = (rtype == REG_TYPE_NQ); \
inst.operands[i].issingle = (rtype == REG_TYPE_VFS); \
inst.operands[i].isvec = (rtype == REG_TYPE_VFS \
- || rtype == REG_TYPE_VFD \
+ || rtype == REG_TYPE_VFD \
|| rtype == REG_TYPE_NQ); \
} \
while (0)
do \
{ \
val = parse_barrier (&str); \
- if (val == FAIL) \
- { \
- if (ISALPHA (*str)) \
- goto failure; \
- else \
- goto immediate; \
- } \
- else \
+ if (val == FAIL && ! ISALPHA (*str)) \
+ goto immediate; \
+ if (val == FAIL \
+ /* ISB can only take SY as an option. */ \
+ || ((inst.instruction & 0xf0) == 0x60 \
+ && val != 0xf)) \
{ \
- if ((inst.instruction & 0xf0) == 0x60 \
- && val != 0xf) \
- { \
- /* ISB can only take SY as an option. */ \
- inst.error = _("invalid barrier type"); \
- goto failure; \
- } \
+ inst.error = _("invalid barrier type"); \
+ backtrack_pos = 0; \
+ goto failure; \
} \
} \
while (0)
case OP_RF: po_reg_or_fail (REG_TYPE_FN); break;
case OP_RVS: po_reg_or_fail (REG_TYPE_VFS); break;
case OP_RVD: po_reg_or_fail (REG_TYPE_VFD); break;
- case OP_oRND:
+ case OP_oRND:
case OP_RND: po_reg_or_fail (REG_TYPE_VFD); break;
case OP_RVC:
po_reg_or_goto (REG_TYPE_VFC, coproc_reg);
case OP_RIWC: po_reg_or_fail (REG_TYPE_MMXWC); break;
case OP_RIWG: po_reg_or_fail (REG_TYPE_MMXWCG); break;
case OP_RXA: po_reg_or_fail (REG_TYPE_XSCALE); break;
- case OP_oRNQ:
+ case OP_oRNQ:
case OP_RNQ: po_reg_or_fail (REG_TYPE_NQ); break;
- case OP_oRNDQ:
+ case OP_oRNDQ:
case OP_RNDQ: po_reg_or_fail (REG_TYPE_NDQ); break;
- case OP_RVSD: po_reg_or_fail (REG_TYPE_VFSD); break;
- case OP_oRNSDQ:
- case OP_RNSDQ: po_reg_or_fail (REG_TYPE_NSDQ); break;
-
- /* Neon scalar. Using an element size of 8 means that some invalid
- scalars are accepted here, so deal with those in later code. */
- case OP_RNSC: po_scalar_or_goto (8, failure); break;
-
- case OP_RNDQ_I0:
- {
- po_reg_or_goto (REG_TYPE_NDQ, try_imm0);
- break;
- try_imm0:
- po_imm_or_fail (0, 0, TRUE);
- }
- break;
-
- case OP_RVSD_I0:
- po_reg_or_goto (REG_TYPE_VFSD, try_imm0);
- break;
-
- case OP_RR_RNSC:
- {
- po_scalar_or_goto (8, try_rr);
- break;
- try_rr:
- po_reg_or_fail (REG_TYPE_RN);
- }
- break;
-
- case OP_RNSDQ_RNSC:
- {
- po_scalar_or_goto (8, try_nsdq);
- break;
- try_nsdq:
- po_reg_or_fail (REG_TYPE_NSDQ);
- }
- break;
-
- case OP_RNDQ_RNSC:
- {
- po_scalar_or_goto (8, try_ndq);
- break;
- try_ndq:
- po_reg_or_fail (REG_TYPE_NDQ);
- }
- break;
-
- case OP_RND_RNSC:
- {
- po_scalar_or_goto (8, try_vfd);
- break;
- try_vfd:
- po_reg_or_fail (REG_TYPE_VFD);
- }
- break;
-
- case OP_VMOV:
- /* WARNING: parse_neon_mov can move the operand counter, i. If we're
- not careful then bad things might happen. */
- po_misc_or_fail (parse_neon_mov (&str, &i) == FAIL);
- break;
-
- case OP_RNDQ_Ibig:
- {
- po_reg_or_goto (REG_TYPE_NDQ, try_immbig);
- break;
- try_immbig:
- /* There's a possibility of getting a 64-bit immediate here, so
- we need special handling. */
- if (parse_big_immediate (&str, i) == FAIL)
- {
- inst.error = _("immediate value is out of range");
- goto failure;
- }
- }
- break;
-
- case OP_RNDQ_I63b:
- {
- po_reg_or_goto (REG_TYPE_NDQ, try_shimm);
- break;
- try_shimm:
- po_imm_or_fail (0, 63, TRUE);
- }
- break;
+ case OP_RVSD: po_reg_or_fail (REG_TYPE_VFSD); break;
+ case OP_oRNSDQ:
+ case OP_RNSDQ: po_reg_or_fail (REG_TYPE_NSDQ); break;
- case OP_RRnpcb:
- po_char_or_fail ('[');
- po_reg_or_fail (REG_TYPE_RN);
- po_char_or_fail (']');
+ /* Neon scalar. Using an element size of 8 means that some invalid
+ scalars are accepted here, so deal with those in later code. */
+ case OP_RNSC: po_scalar_or_goto (8, failure); break;
+
+ case OP_RNDQ_I0:
+ {
+ po_reg_or_goto (REG_TYPE_NDQ, try_imm0);
+ break;
+ try_imm0:
+ po_imm_or_fail (0, 0, TRUE);
+ }
break;
- case OP_RRnpctw:
- case OP_RRw:
- case OP_oRRw:
+ case OP_RVSD_I0:
+ po_reg_or_goto (REG_TYPE_VFSD, try_imm0);
+ break;
+
+ case OP_RR_RNSC:
+ {
+ po_scalar_or_goto (8, try_rr);
+ break;
+ try_rr:
+ po_reg_or_fail (REG_TYPE_RN);
+ }
+ break;
+
+ case OP_RNSDQ_RNSC:
+ {
+ po_scalar_or_goto (8, try_nsdq);
+ break;
+ try_nsdq:
+ po_reg_or_fail (REG_TYPE_NSDQ);
+ }
+ break;
+
+ case OP_RNDQ_RNSC:
+ {
+ po_scalar_or_goto (8, try_ndq);
+ break;
+ try_ndq:
+ po_reg_or_fail (REG_TYPE_NDQ);
+ }
+ break;
+
+ case OP_RND_RNSC:
+ {
+ po_scalar_or_goto (8, try_vfd);
+ break;
+ try_vfd:
+ po_reg_or_fail (REG_TYPE_VFD);
+ }
+ break;
+
+ case OP_VMOV:
+ /* WARNING: parse_neon_mov can move the operand counter, i. If we're
+ not careful then bad things might happen. */
+ po_misc_or_fail (parse_neon_mov (&str, &i) == FAIL);
+ break;
+
+ case OP_RNDQ_Ibig:
+ {
+ po_reg_or_goto (REG_TYPE_NDQ, try_immbig);
+ break;
+ try_immbig:
+ /* There's a possibility of getting a 64-bit immediate here, so
+ we need special handling. */
+ if (parse_big_immediate (&str, i) == FAIL)
+ {
+ inst.error = _("immediate value is out of range");
+ goto failure;
+ }
+ }
+ break;
+
+ case OP_RNDQ_I63b:
+ {
+ po_reg_or_goto (REG_TYPE_NDQ, try_shimm);
+ break;
+ try_shimm:
+ po_imm_or_fail (0, 63, TRUE);
+ }
+ break;
+
+ case OP_RRnpcb:
+ po_char_or_fail ('[');
+ po_reg_or_fail (REG_TYPE_RN);
+ po_char_or_fail (']');
+ break;
+
+ case OP_RRnpctw:
+ case OP_RRw:
+ case OP_oRRw:
po_reg_or_fail (REG_TYPE_RN);
if (skip_past_char (&str, '!') == SUCCESS)
inst.operands[i].writeback = 1;
case OP_I7: po_imm_or_fail ( 0, 7, FALSE); break;
case OP_I15: po_imm_or_fail ( 0, 15, FALSE); break;
case OP_I16: po_imm_or_fail ( 1, 16, FALSE); break;
- case OP_I16z: po_imm_or_fail ( 0, 16, FALSE); break;
+ case OP_I16z: po_imm_or_fail ( 0, 16, FALSE); break;
case OP_I31: po_imm_or_fail ( 0, 31, FALSE); break;
case OP_I32: po_imm_or_fail ( 1, 32, FALSE); break;
- case OP_I32z: po_imm_or_fail ( 0, 32, FALSE); break;
+ case OP_I32z: po_imm_or_fail ( 0, 32, FALSE); break;
case OP_I63s: po_imm_or_fail (-64, 63, FALSE); break;
- case OP_I63: po_imm_or_fail ( 0, 63, FALSE); break;
- case OP_I64: po_imm_or_fail ( 1, 64, FALSE); break;
- case OP_I64z: po_imm_or_fail ( 0, 64, FALSE); break;
+ case OP_I63: po_imm_or_fail ( 0, 63, FALSE); break;
+ case OP_I64: po_imm_or_fail ( 1, 64, FALSE); break;
+ case OP_I64z: po_imm_or_fail ( 0, 64, FALSE); break;
case OP_I255: po_imm_or_fail ( 0, 255, FALSE); break;
case OP_I4b: po_imm_or_fail ( 1, 4, TRUE); break;
case OP_I15b: po_imm_or_fail ( 0, 15, TRUE); break;
case OP_oI31b:
case OP_I31b: po_imm_or_fail ( 0, 31, TRUE); break;
- case OP_oI32b: po_imm_or_fail ( 1, 32, TRUE); break;
+ case OP_oI32b: po_imm_or_fail ( 1, 32, TRUE); break;
+ case OP_oI32z: po_imm_or_fail ( 0, 32, TRUE); break;
case OP_oIffffb: po_imm_or_fail ( 0, 0xffff, TRUE); break;
/* Immediate variants */
po_barrier_or_imm (str); break;
immediate:
if (parse_immediate (&str, &val, 0, 15, TRUE) == FAIL)
- goto failure;
+ goto failure;
break;
- case OP_wPSR:
+ case OP_wPSR:
case OP_rPSR:
po_reg_or_goto (REG_TYPE_RNB, try_psr);
if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_virt))
val = parse_psr (&str, op_parse_code == OP_wPSR);
break;
- case OP_APSR_RR:
- po_reg_or_goto (REG_TYPE_RN, try_apsr);
- break;
- try_apsr:
- /* Parse "APSR_nvzc" operand (for FMSTAT-equivalent MRS
- instruction). */
- if (strncasecmp (str, "APSR_", 5) == 0)
- {
- unsigned found = 0;
- str += 5;
- while (found < 15)
- switch (*str++)
- {
- case 'c': found = (found & 1) ? 16 : found | 1; break;
- case 'n': found = (found & 2) ? 16 : found | 2; break;
- case 'z': found = (found & 4) ? 16 : found | 4; break;
- case 'v': found = (found & 8) ? 16 : found | 8; break;
- default: found = 16;
- }
- if (found != 15)
- goto failure;
- inst.operands[i].isvec = 1;
+ case OP_APSR_RR:
+ po_reg_or_goto (REG_TYPE_RN, try_apsr);
+ break;
+ try_apsr:
+ /* Parse "APSR_nvzc" operand (for FMSTAT-equivalent MRS
+ instruction). */
+ if (strncasecmp (str, "APSR_", 5) == 0)
+ {
+ unsigned found = 0;
+ str += 5;
+ while (found < 15)
+ switch (*str++)
+ {
+ case 'c': found = (found & 1) ? 16 : found | 1; break;
+ case 'n': found = (found & 2) ? 16 : found | 2; break;
+ case 'z': found = (found & 4) ? 16 : found | 4; break;
+ case 'v': found = (found & 8) ? 16 : found | 8; break;
+ default: found = 16;
+ }
+ if (found != 15)
+ goto failure;
+ inst.operands[i].isvec = 1;
/* APSR_nzcv is encoded in instructions as if it were the REG_PC. */
inst.operands[i].reg = REG_PC;
- }
- else
- goto failure;
- break;
+ }
+ else
+ goto failure;
+ break;
case OP_TB:
po_misc_or_fail (parse_tb (&str));
val = parse_vfp_reg_list (&str, &inst.operands[i].reg, REGLIST_VFP_D);
break;
- case OP_VRSDLST:
- /* Allow Q registers too. */
- val = parse_vfp_reg_list (&str, &inst.operands[i].reg,
- REGLIST_NEON_D);
- if (val == FAIL)
- {
- inst.error = NULL;
- val = parse_vfp_reg_list (&str, &inst.operands[i].reg,
- REGLIST_VFP_S);
- inst.operands[i].issingle = 1;
- }
- break;
-
- case OP_NRDLST:
- val = parse_vfp_reg_list (&str, &inst.operands[i].reg,
- REGLIST_NEON_D);
- break;
+ case OP_VRSDLST:
+ /* Allow Q registers too. */
+ val = parse_vfp_reg_list (&str, &inst.operands[i].reg,
+ REGLIST_NEON_D);
+ if (val == FAIL)
+ {
+ inst.error = NULL;
+ val = parse_vfp_reg_list (&str, &inst.operands[i].reg,
+ REGLIST_VFP_S);
+ inst.operands[i].issingle = 1;
+ }
+ break;
+
+ case OP_NRDLST:
+ val = parse_vfp_reg_list (&str, &inst.operands[i].reg,
+ REGLIST_NEON_D);
+ break;
case OP_NSTRLST:
- val = parse_neon_el_struct_list (&str, &inst.operands[i].reg,
- &inst.operands[i].vectype);
- break;
+ val = parse_neon_el_struct_list (&str, &inst.operands[i].reg,
+ &inst.operands[i].vectype);
+ break;
/* Addressing modes */
case OP_ADDR:
case OP_ADDRGLDR:
po_misc_or_fail_no_backtrack (
- parse_address_group_reloc (&str, i, GROUP_LDR));
+ parse_address_group_reloc (&str, i, GROUP_LDR));
break;
case OP_ADDRGLDRS:
po_misc_or_fail_no_backtrack (
- parse_address_group_reloc (&str, i, GROUP_LDRS));
+ parse_address_group_reloc (&str, i, GROUP_LDRS));
break;
case OP_ADDRGLDC:
po_misc_or_fail_no_backtrack (
- parse_address_group_reloc (&str, i, GROUP_LDC));
+ parse_address_group_reloc (&str, i, GROUP_LDC));
break;
case OP_SH:
case OP_SHG:
po_misc_or_fail_no_backtrack (
- parse_shifter_operand_group_reloc (&str, i));
+ parse_shifter_operand_group_reloc (&str, i));
break;
case OP_oSHll:
break;
case OP_RRnpctw:
- if (inst.operands[i].isreg
- && inst.operands[i].reg == REG_PC
+ if (inst.operands[i].isreg
+ && inst.operands[i].reg == REG_PC
&& (inst.operands[i].writeback || thumb))
inst.error = BAD_PC;
break;
case OP_REGLST:
case OP_VRSLST:
case OP_VRDLST:
- case OP_VRSDLST:
- case OP_NRDLST:
- case OP_NSTRLST:
+ case OP_VRSDLST:
+ case OP_NRDLST:
+ case OP_NSTRLST:
if (val == FAIL)
goto failure;
inst.operands[i].imm = val;
&& reg > 15)
{
if (ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_d32))
- {
- if (thumb_mode)
- ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used,
- fpu_vfp_ext_d32);
- else
- ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used,
- fpu_vfp_ext_d32);
- }
+ {
+ if (thumb_mode)
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used,
+ fpu_vfp_ext_d32);
+ else
+ ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used,
+ fpu_vfp_ext_d32);
+ }
else
- {
- first_error (_("D register out of range for selected VFP version"));
- return;
- }
+ {
+ first_error (_("D register out of range for selected VFP version"));
+ return;
+ }
}
switch (pos)
encode_arm_shift (i);
}
else
- inst.instruction |= INST_IMMEDIATE;
+ {
+ inst.instruction |= INST_IMMEDIATE;
+ if (inst.reloc.type != BFD_RELOC_ARM_IMMEDIATE)
+ inst.instruction |= inst.operands[i].imm;
+ }
}
/* Subroutine of encode_arm_addr_mode_2 and encode_arm_addr_mode_3. */
static void
encode_arm_addr_mode_common (int i, bfd_boolean is_t)
{
- gas_assert (inst.operands[i].isreg);
+ /* PR 14260:
+ Generate an error if the operand is not a register. */
+ constraint (!inst.operands[i].isreg,
+ _("Instruction does not support =N addresses"));
+
inst.instruction |= inst.operands[i].reg << 16;
if (inst.operands[i].preind)
}
if (inst.reloc.type == BFD_RELOC_UNUSED)
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
+ {
+ /* Prefer + for zero encoded value. */
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
+ }
}
}
if (inst.operands[i].immisreg)
{
constraint ((inst.operands[i].imm == REG_PC
- || inst.operands[i].reg == REG_PC),
+ || (is_t && inst.operands[i].reg == REG_PC)),
BAD_PC_ADDRESSING);
+ constraint (inst.operands[i].reg == REG_PC && inst.operands[i].writeback,
+ BAD_PC_WRITEBACK);
inst.instruction |= inst.operands[i].imm;
if (!inst.operands[i].negative)
inst.instruction |= INDEX_UP;
BAD_PC_WRITEBACK);
inst.instruction |= HWOFFSET_IMM;
if (inst.reloc.type == BFD_RELOC_UNUSED)
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+ {
+ /* Prefer + for zero encoded value. */
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
+
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+ }
}
}
if (reloc_override)
inst.reloc.type = (bfd_reloc_code_real_type) reloc_override;
else if ((inst.reloc.type < BFD_RELOC_ARM_ALU_PC_G0_NC
- || inst.reloc.type > BFD_RELOC_ARM_LDC_SB_G2)
- && inst.reloc.type != BFD_RELOC_ARM_LDR_PC_G0)
+ || inst.reloc.type > BFD_RELOC_ARM_LDC_SB_G2)
+ && inst.reloc.type != BFD_RELOC_ARM_LDR_PC_G0)
{
if (thumb_mode)
- inst.reloc.type = BFD_RELOC_ARM_T32_CP_OFF_IMM;
+ inst.reloc.type = BFD_RELOC_ARM_T32_CP_OFF_IMM;
else
- inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+ inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
}
+ /* Prefer + for zero encoded value. */
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
+
return SUCCESS;
}
}
static void
+do_rm_rn (void)
+{
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 16;
+}
+
+static void
do_rd_rn (void)
{
inst.instruction |= inst.operands[0].reg << 12;
inst.instruction |= inst.operands[1].reg << 12;
}
+static bfd_boolean
+check_obsolete (const arm_feature_set *feature, const char *msg)
+{
+ if (ARM_CPU_IS_ANY (cpu_variant))
+ {
+ as_warn ("%s", msg);
+ return TRUE;
+ }
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, *feature))
+ {
+ as_bad ("%s", msg);
+ return TRUE;
+ }
+
+ return FALSE;
+}
+
static void
do_rd_rm_rn (void)
{
constraint (Rn == inst.operands[0].reg || Rn == inst.operands[1].reg,
_("Rn must not overlap other operands"));
- /* SWP{b} is deprecated for ARMv6* and ARMv7. */
- if (warn_on_deprecated
- && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6))
- as_warn (_("swp{b} use is deprecated for this architecture"));
-
+ /* SWP{b} is obsolete for ARMv8-A, and deprecated for ARMv6* and ARMv7.
+ */
+ if (!check_obsolete (&arm_ext_v8,
+ _("swp{b} use is obsoleted for ARMv8 and later"))
+ && warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6))
+ as_warn (_("swp{b} use is deprecated for ARMv6 and ARMv7"));
}
+
inst.instruction |= inst.operands[0].reg << 12;
inst.instruction |= inst.operands[1].reg;
inst.instruction |= Rn << 16;
do_barrier (void)
{
if (inst.operands[0].present)
- {
- constraint ((inst.instruction & 0xf0) != 0x40
- && inst.operands[0].imm > 0xf
- && inst.operands[0].imm < 0x0,
- _("bad barrier type"));
- inst.instruction |= inst.operands[0].imm;
- }
+ inst.instruction |= inst.operands[0].imm;
else
inst.instruction |= 0xf;
}
No special properties. */
+struct deprecated_coproc_regs_s
+{
+ unsigned cp;
+ int opc1;
+ unsigned crn;
+ unsigned crm;
+ int opc2;
+ arm_feature_set deprecated;
+ arm_feature_set obsoleted;
+ const char *dep_msg;
+ const char *obs_msg;
+};
+
+#define DEPR_ACCESS_V8 \
+ N_("This coprocessor register access is deprecated in ARMv8")
+
+/* Table of all deprecated coprocessor registers. */
+static struct deprecated_coproc_regs_s deprecated_coproc_regs[] =
+{
+ {15, 0, 7, 10, 5, /* CP15DMB. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+ {15, 0, 7, 10, 4, /* CP15DSB. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+ {15, 0, 7, 5, 4, /* CP15ISB. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+ {14, 6, 1, 0, 0, /* TEEHBR. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+ {14, 6, 0, 0, 0, /* TEECR. */
+ ARM_FEATURE (ARM_EXT_V8, 0), ARM_FEATURE (0, 0),
+ DEPR_ACCESS_V8, NULL},
+};
+
+#undef DEPR_ACCESS_V8
+
+static const size_t deprecated_coproc_reg_count =
+ sizeof (deprecated_coproc_regs) / sizeof (deprecated_coproc_regs[0]);
+
static void
do_co_reg (void)
{
unsigned Rd;
+ size_t i;
Rd = inst.operands[2].reg;
if (thumb_mode)
constraint (Rd == REG_PC, BAD_PC);
}
+ for (i = 0; i < deprecated_coproc_reg_count; ++i)
+ {
+ const struct deprecated_coproc_regs_s *r =
+ deprecated_coproc_regs + i;
+
+ if (inst.operands[0].reg == r->cp
+ && inst.operands[1].imm == r->opc1
+ && inst.operands[3].reg == r->crn
+ && inst.operands[4].reg == r->crm
+ && inst.operands[5].imm == r->opc2)
+ {
+ if (! ARM_CPU_IS_ANY (cpu_variant)
+ && warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, r->deprecated))
+ as_warn ("%s", r->dep_msg);
+ }
+ }
inst.instruction |= inst.operands[0].reg << 8;
inst.instruction |= inst.operands[1].imm << 21;
}
}
+/* If there is only one register in the register list,
+ then return its register number. Otherwise return -1. */
+static int
+only_one_reg_in_list (int range)
+{
+ int i = ffs (range) - 1;
+ return (i > 15 || range != (1 << i)) ? -1 : i;
+}
+
static void
-do_ldmstm (void)
+encode_ldmstm(int from_push_pop_mnem)
{
int base_reg = inst.operands[0].reg;
int range = inst.operands[1].imm;
+ int one_reg;
inst.instruction |= base_reg << 16;
inst.instruction |= range;
as_warn (_("if writeback register is in list, it must be the lowest reg in the list"));
}
}
+
+ /* If PUSH/POP has only one register, then use the A2 encoding. */
+ one_reg = only_one_reg_in_list (range);
+ if (from_push_pop_mnem && one_reg >= 0)
+ {
+ int is_push = (inst.instruction & A_PUSH_POP_OP_MASK) == A1_OPCODE_PUSH;
+
+ inst.instruction &= A_COND_MASK;
+ inst.instruction |= is_push ? A2_OPCODE_PUSH : A2_OPCODE_POP;
+ inst.instruction |= one_reg << 12;
+ }
+}
+
+static void
+do_ldmstm (void)
+{
+ encode_ldmstm (/*from_push_pop_mnem=*/FALSE);
}
/* ARMv5TE load-consecutive (argument parse)
do_ldrd (void)
{
constraint (inst.operands[0].reg % 2 != 0,
- _("first destination register must be even"));
+ _("first transfer register must be even"));
constraint (inst.operands[1].present
&& inst.operands[1].reg != inst.operands[0].reg + 1,
- _("can only load two consecutive registers"));
+ _("can only transfer two consecutive registers"));
constraint (inst.operands[0].reg == REG_LR, _("r14 not allowed here"));
constraint (!inst.operands[2].isreg, _("'[' expected"));
if (!inst.operands[1].present)
inst.operands[1].reg = inst.operands[0].reg + 1;
- if (inst.instruction & LOAD_BIT)
- {
- /* encode_arm_addr_mode_3 will diagnose overlap between the base
- register and the first register written; we have to diagnose
- overlap between the base and the second register written here. */
+ /* encode_arm_addr_mode_3 will diagnose overlap between the base
+ register and the first register written; we have to diagnose
+ overlap between the base and the second register written here. */
- if (inst.operands[2].reg == inst.operands[1].reg
- && (inst.operands[2].writeback || inst.operands[2].postind))
- as_warn (_("base register written back, and overlaps "
- "second destination register"));
+ if (inst.operands[2].reg == inst.operands[1].reg
+ && (inst.operands[2].writeback || inst.operands[2].postind))
+ as_warn (_("base register written back, and overlaps "
+ "second transfer register"));
+ if (!(inst.instruction & V4_STR_BIT))
+ {
/* For an index-register load, the index register must not overlap the
- destination (even if not write-back). */
- else if (inst.operands[2].immisreg
- && ((unsigned) inst.operands[2].imm == inst.operands[0].reg
- || (unsigned) inst.operands[2].imm == inst.operands[1].reg))
- as_warn (_("index register overlaps destination register"));
+ destination (even if not write-back). */
+ if (inst.operands[2].immisreg
+ && ((unsigned) inst.operands[2].imm == inst.operands[0].reg
+ || (unsigned) inst.operands[2].imm == inst.operands[1].reg))
+ as_warn (_("index register overlaps transfer register"));
}
-
inst.instruction |= inst.operands[0].reg << 12;
encode_arm_addr_mode_3 (2, /*is_t=*/FALSE);
}
inst.instruction |= inst.operands[2].reg << 16;
}
+/* In both ARM and thumb state 'ldr pc, #imm' with an immediate
+ which is not a multiple of four is UNPREDICTABLE. */
+static void
+check_ldr_r15_aligned (void)
+{
+ constraint (!(inst.operands[1].immisreg)
+ && (inst.operands[0].reg == REG_PC
+ && inst.operands[1].reg == REG_PC
+ && (inst.reloc.exp.X_add_number & 0x3)),
+ _("ldr to register 15 must be 4-byte alligned"));
+}
+
static void
do_ldst (void)
{
if (move_or_literal_pool (0, /*thumb_p=*/FALSE, /*mode_3=*/FALSE))
return;
encode_arm_addr_mode_2 (1, /*is_t=*/FALSE);
+ check_ldr_r15_aligned ();
}
static void
if (inst.operands[0].isvec)
{
if (inst.operands[1].reg != 1)
- first_error (_("operand 1 must be FPSCR"));
+ first_error (_("operand 1 must be FPSCR"));
memset (&inst.operands[0], '\0', sizeof (inst.operands[0]));
memset (&inst.operands[1], '\0', sizeof (inst.operands[1]));
do_vfp_nsyn_opcode ("fmstat");
do_vmrs (void)
{
unsigned Rt = inst.operands[0].reg;
-
- if (thumb_mode && inst.operands[0].reg == REG_SP)
+
+ if (thumb_mode && Rt == REG_SP)
{
inst.error = BAD_SP;
return;
}
/* APSR_ sets isvec. All other refs to PC are illegal. */
- if (!inst.operands[0].isvec && inst.operands[0].reg == REG_PC)
+ if (!inst.operands[0].isvec && Rt == REG_PC)
{
inst.error = BAD_PC;
return;
}
- if (inst.operands[1].reg != 1)
- first_error (_("operand 1 must be FPSCR"));
-
+ /* If we get through parsing the register name, we just insert the number
+ generated into the instruction without further validation. */
+ inst.instruction |= (inst.operands[1].reg << 16);
inst.instruction |= (Rt << 12);
}
do_vmsr (void)
{
unsigned Rt = inst.operands[1].reg;
-
+
if (thumb_mode)
reject_bad_reg (Rt);
else if (Rt == REG_PC)
return;
}
- if (inst.operands[0].reg != 1)
- first_error (_("operand 0 must be FPSCR"));
-
+ /* If we get through parsing the register name, we just insert the number
+ generated into the instruction without further validation. */
+ inst.instruction |= (inst.operands[0].reg << 16);
inst.instruction |= (Rt << 12);
}
inst.operands[0].isreg = 1;
inst.operands[0].writeback = 1;
inst.operands[0].reg = REG_SP;
- do_ldmstm ();
+ encode_ldmstm (/*from_push_pop_mnem=*/TRUE);
}
/* ARM V6 RFE (Return from Exception) loads the PC and CPSR from the
static void
do_setend (void)
{
+ if (warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v8))
+ as_warn (_("setend use is deprecated for ARMv8"));
+
if (inst.operands[0].imm)
inst.instruction |= 0x200;
}
{
inst.instruction |= inst.operands[2].reg << 8;
inst.instruction |= SHIFT_BY_REG;
+ /* PR 12854: Error on extraneous shifts. */
+ constraint (inst.operands[2].shifted,
+ _("extraneous shift as part of operand to shift insn"));
}
else
inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
}
static void
+do_t_strexbh (void)
+{
+ constraint (!inst.operands[2].isreg || !inst.operands[2].preind
+ || inst.operands[2].postind || inst.operands[2].writeback
+ || inst.operands[2].immisreg || inst.operands[2].shifted
+ || inst.operands[2].negative,
+ BAD_ADDR_MODE);
+
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
+
+ do_rm_rd_rn ();
+}
+
+static void
do_strexd (void)
{
constraint (inst.operands[1].reg % 2 != 0,
inst.instruction |= inst.operands[3].reg << 16;
}
+/* ARM V8 STRL. */
+static void
+do_stlex (void)
+{
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
+
+ do_rd_rm_rn ();
+}
+
+static void
+do_t_stlex (void)
+{
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
+
+ do_rm_rd_rn ();
+}
+
/* ARM V6 SXTAH extracts a 16-bit value from a register, sign
extends it to 32-bits, and adds the result to a value in another
register. You can specify a rotation by 0, 8, 16, or 24 bits
static void
vfp_conv (int srcsize)
{
- unsigned immbits = srcsize - inst.operands[1].imm;
+ int immbits = srcsize - inst.operands[1].imm;
+
+ if (srcsize == 16 && !(immbits >= 0 && immbits <= srcsize))
+ {
+ /* If srcsize is 16, inst.operands[1].imm must be in the range 0-16.
+ i.e. immbits must be in range 0 - 16. */
+ inst.error = _("immediate value out of range, expected range [0, 16]");
+ return;
+ }
+ else if (srcsize == 32 && !(immbits >= 0 && immbits < srcsize))
+ {
+ /* If srcsize is 32, inst.operands[1].imm must be in the range 1-32.
+ i.e. immbits must be in range 0 - 31. */
+ inst.error = _("immediate value out of range, expected range [1, 32]");
+ return;
+ }
+
inst.instruction |= (immbits & 1) << 5;
inst.instruction |= (immbits >> 1);
}
constraint (is_pc && inst.operands[i].writeback, BAD_PC_WRITEBACK);
constraint (is_t && inst.operands[i].writeback,
_("cannot use writeback with this instruction"));
- constraint (is_pc && ((inst.instruction & THUMB2_LOAD_BIT) == 0)
- && !inst.reloc.pc_rel, BAD_PC_ADDRESSING);
+ constraint (is_pc && ((inst.instruction & THUMB2_LOAD_BIT) == 0),
+ BAD_PC_ADDRESSING);
if (is_d)
{
X(_yield, bf10, f3af8001), \
X(_wfe, bf20, f3af8002), \
X(_wfi, bf30, f3af8003), \
- X(_sev, bf40, f3af8004),
+ X(_sev, bf40, f3af8004), \
+ X(_sevl, bf50, f3af8005), \
+ X(_udf, de00, f7f0a000)
/* To catch errors in encoding functions, the codes are all offset by
0xF800, putting them in one of the 32-bit prefix ranges, ergo undefined
if (inst.size_req != 4)
{
/* Attempt to use a narrow opcode, with relaxation if
- appropriate. */
+ appropriate. */
if (Rd == REG_SP && Rs == REG_SP && !flags)
opcode = add ? T_MNEM_inc_sp : T_MNEM_dec_sp;
else if (Rd <= 7 && Rs == REG_SP && add && !flags)
}
else
{
+ unsigned int value = inst.reloc.exp.X_add_number;
+ unsigned int shift = inst.operands[2].shift_kind;
+
Rn = inst.operands[2].reg;
/* See if we can do this with a 16-bit instruction. */
if (!inst.operands[2].shifted && inst.size_req != 4)
inst.instruction = THUMB_OP32 (inst.instruction);
inst.instruction |= Rd << 8;
inst.instruction |= Rs << 16;
+ constraint (Rd == REG_SP && Rs == REG_SP && value > 3,
+ _("shift value over 3 not allowed in thumb mode"));
+ constraint (Rd == REG_SP && Rs == REG_SP && shift != SHIFT_LSL,
+ _("only LSL shift allowed in thumb mode"));
encode_thumb32_shifted_operand (2);
}
}
}
static void
-do_t_barrier (void)
-{
- if (inst.operands[0].present)
- {
- constraint ((inst.instruction & 0xf0) != 0x40
- && inst.operands[0].imm > 0xf
- && inst.operands[0].imm < 0x0,
- _("bad barrier type"));
- inst.instruction |= inst.operands[0].imm;
- }
- else
- inst.instruction |= 0xf;
-}
-
-static void
do_t_bfc (void)
{
unsigned Rd;
if (in_it_block ())
{
/* Conditional branches inside IT blocks are encoded as unconditional
- branches. */
+ branches. */
cond = COND_ALWAYS;
}
else
inst.reloc.pc_rel = 1;
}
+/* Actually do the work for Thumb state bkpt and hlt. The only difference
+ between the two is the maximum immediate allowed - which is passed in
+ RANGE. */
static void
-do_t_bkpt (void)
+do_t_bkpt_hlt1 (int range)
{
constraint (inst.cond != COND_ALWAYS,
_("instruction is always unconditional"));
if (inst.operands[0].present)
{
- constraint (inst.operands[0].imm > 255,
+ constraint (inst.operands[0].imm > range,
_("immediate value out of range"));
inst.instruction |= inst.operands[0].imm;
- set_it_insn_type (NEUTRAL_IT_INSN);
}
+
+ set_it_insn_type (NEUTRAL_IT_INSN);
+}
+
+static void
+do_t_hlt (void)
+{
+ do_t_bkpt_hlt1 (63);
+}
+
+static void
+do_t_bkpt (void)
+{
+ do_t_bkpt_hlt1 (255);
}
static void
{
set_it_insn_type_last ();
encode_branch (BFD_RELOC_THUMB_PCREL_BRANCH23);
-
+
/* md_apply_fix blows up with 'bl foo(PLT)' where foo is defined in
this file. We used to simply ignore the PLT reloc type here --
the branch encoding is now needed to deal with TLSCALL relocs.
set_it_insn_type (IT_INSN);
now_it.mask = (inst.instruction & 0xf) | 0x10;
now_it.cc = cond;
+ now_it.warn_deprecated = FALSE;
/* If the condition is a negative condition, invert the mask. */
if ((cond & 0x1) == 0x0)
unsigned int mask = inst.instruction & 0x000f;
if ((mask & 0x7) == 0)
- /* no conversion needed */;
+ {
+ /* No conversion needed. */
+ now_it.block_length = 1;
+ }
else if ((mask & 0x3) == 0)
- mask ^= 0x8;
+ {
+ mask ^= 0x8;
+ now_it.block_length = 2;
+ }
else if ((mask & 0x1) == 0)
- mask ^= 0xC;
+ {
+ mask ^= 0xC;
+ now_it.block_length = 3;
+ }
else
- mask ^= 0xE;
+ {
+ mask ^= 0xE;
+ now_it.block_length = 4;
+ }
inst.instruction &= 0xfff0;
inst.instruction |= mask;
if (load)
{
if (mask & (1 << 15))
- {
- if (mask & (1 << 14))
- inst.error = _("LR and PC should not both be in register list");
- else
- set_it_insn_type_last ();
- }
+ {
+ if (mask & (1 << 14))
+ inst.error = _("LR and PC should not both be in register list");
+ else
+ set_it_insn_type_last ();
+ }
}
else
{
? inst.operands[0].writeback
: (inst.operands[0].writeback
== !(inst.operands[1].imm & mask)))
- {
+ {
if (inst.instruction == T_MNEM_stmia
&& (inst.operands[1].imm & mask)
&& (inst.operands[1].imm & (mask - 1)))
/* This means 1 register in reg list one of 3 situations:
1. Instruction is stmia, but without writeback.
2. lmdia without writeback, but with Rn not in
- reglist.
+ reglist.
3. ldmia with writeback, but with Rn in reglist.
Case 3 is UNPREDICTABLE behaviour, so we handle
case 1 and 2 which can be converted into a 16-bit
/* First, record an error for Case 3. */
if (inst.operands[1].imm & mask
&& inst.operands[0].writeback)
- inst.error =
+ inst.error =
_("having the base register in the register list when "
"using write back is UNPREDICTABLE");
-
- opcode = (inst.instruction == T_MNEM_stmia ? T_MNEM_str
+
+ opcode = (inst.instruction == T_MNEM_stmia ? T_MNEM_str
: T_MNEM_ldr);
inst.instruction = THUMB_OP16 (opcode);
inst.instruction |= inst.operands[0].reg << 3;
{
if (inst.operands[0].writeback)
{
- inst.instruction =
+ inst.instruction =
THUMB_OP16 (inst.instruction == T_MNEM_stmia
- ? T_MNEM_push : T_MNEM_pop);
+ ? T_MNEM_push : T_MNEM_pop);
inst.instruction |= inst.operands[1].imm;
- narrow = TRUE;
+ narrow = TRUE;
}
else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0)
{
- inst.instruction =
+ inst.instruction =
THUMB_OP16 (inst.instruction == T_MNEM_stmia
- ? T_MNEM_str_sp : T_MNEM_ldr_sp);
+ ? T_MNEM_str_sp : T_MNEM_ldr_sp);
inst.instruction |= ((ffs (inst.operands[1].imm)-1) << 8);
- narrow = TRUE;
+ narrow = TRUE;
}
}
}
{
if (no_cpu_selected ()
|| (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7)
- && !ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7a)
- && !ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7r)))
+ && !ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7a)
+ && !ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7r)))
as_warn (_("This instruction may be unpredictable "
"if executed on M-profile cores "
"with interrupts enabled."));
}
/* Do some validations regarding addressing modes. */
- if (inst.operands[1].immisreg && opcode != T_MNEM_ldr
- && opcode != T_MNEM_str)
+ if (inst.operands[1].immisreg)
reject_bad_reg (inst.operands[1].imm);
+ constraint (inst.operands[1].writeback == 1
+ && inst.operands[0].reg == inst.operands[1].reg,
+ BAD_OVERLAP);
+
inst.instruction = THUMB_OP32 (opcode);
inst.instruction |= inst.operands[0].reg << 12;
encode_thumb32_addr_mode (1, /*is_t=*/FALSE, /*is_d=*/FALSE);
+ check_ldr_r15_aligned ();
return;
}
inst.operands[1].reg = inst.operands[0].reg + 1;
constraint (inst.operands[0].reg == REG_LR,
_("r14 not allowed here"));
+ constraint (inst.operands[0].reg == REG_R12,
+ _("r12 not allowed here"));
}
+
+ if (inst.operands[2].writeback
+ && (inst.operands[0].reg == inst.operands[2].reg
+ || inst.operands[1].reg == inst.operands[2].reg))
+ as_warn (_("base register written back, and overlaps "
+ "one of transfer registers"));
+
inst.instruction |= inst.operands[0].reg << 12;
inst.instruction |= inst.operands[1].reg << 8;
encode_thumb32_addr_mode (2, /*is_t=*/FALSE, /*is_d=*/TRUE);
switch (inst.instruction)
{
case T_MNEM_mov:
+ /* In v4t or v5t a move of two lowregs produces unpredictable
+ results. Don't allow this. */
+ if (low_regs)
+ {
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6),
+ "MOV Rd, Rs with two low registers is not "
+ "permitted on this architecture");
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used,
+ arm_ext_v6);
+ }
+
inst.instruction = T_OPCODE_MOV_HR;
inst.instruction |= (Rn & 0x8) << 4;
inst.instruction |= (Rn & 0x7);
|| inst.operands[1].shifted
|| Rn > 7 || Rm > 7)
narrow = FALSE;
- else if (inst.instruction == T_MNEM_cmn)
+ else if (inst.instruction == T_MNEM_cmn
+ || inst.instruction == T_MNEM_tst)
narrow = TRUE;
else if (THUMB_SETS_FLAGS (inst.instruction))
narrow = !in_it_block ();
int flags = inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT);
if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m))
- constraint (flags != 0, _("selected processor does not support "
- "requested special purpose register"));
+ {
+ /* PR gas/12698: The constraint is only applied for m_profile.
+ If the user has specified -march=all, we want to ignore it as
+ we are building for any CPU type, including non-m variants. */
+ bfd_boolean m_profile = selected_cpu.core != arm_arch_any.core;
+ constraint ((flags != 0) && m_profile, _("selected processor does "
+ "not support requested special purpose register"));
+ }
else
/* mrs only accepts APSR/CPSR/SPSR/CPSR_all/SPSR_all (for non-M profile
devices). */
{
int bits = inst.operands[0].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT);
- constraint ((ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)
- && (bits & ~(PSR_s | PSR_f)) != 0)
- || (!ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)
- && bits != PSR_f),
- _("selected processor does not support requested special "
- "purpose register"));
+ /* PR gas/12698: The constraint is only applied for m_profile.
+ If the user has specified -march=all, we want to ignore it as
+ we are building for any CPU type, including non-m variants. */
+ bfd_boolean m_profile = selected_cpu.core != arm_arch_any.core;
+ constraint (((ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)
+ && (bits & ~(PSR_s | PSR_f)) != 0)
+ || (!ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6_dsp)
+ && bits != PSR_f)) && m_profile,
+ _("selected processor does not support requested special "
+ "purpose register"));
}
else
constraint ((flags & 0xff) != 0, _("selected processor does not support "
_("expression too complex"));
mask = inst.operands[0].imm;
- if ((mask & ~0xff) == 0)
+ if (inst.size_req != 4 && (mask & ~0xff) == 0)
inst.instruction = THUMB_OP16 (inst.instruction) | mask;
- else if ((inst.instruction == T_MNEM_push
- && (mask & ~0xff) == 1 << REG_LR)
- || (inst.instruction == T_MNEM_pop
- && (mask & ~0xff) == 1 << REG_PC))
+ else if (inst.size_req != 4
+ && (mask & ~0xff) == (1 << (inst.instruction == T_MNEM_push
+ ? REG_LR : REG_PC)))
{
inst.instruction = THUMB_OP16 (inst.instruction);
inst.instruction |= THUMB_PP_PC_LR;
narrow = FALSE;
/* Turn rsb #0 into 16-bit neg. We should probably do this via
- relaxation, but it doesn't seem worth the hassle. */
+ relaxation, but it doesn't seem worth the hassle. */
if (narrow)
{
inst.reloc.type = BFD_RELOC_UNUSED;
static void
do_t_setend (void)
{
+ if (warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v8))
+ as_warn (_("setend use is deprecated for ARMv8"));
+
set_it_insn_type (OUTSIDE_IT_INSN);
if (inst.operands[0].imm)
inst.instruction |= 0x8;
inst.instruction |= inst.operands[0].reg << 8;
inst.instruction |= inst.operands[1].reg << 16;
inst.instruction |= inst.operands[2].reg;
+
+ /* PR 12854: Error on extraneous shifts. */
+ constraint (inst.operands[2].shifted,
+ _("extraneous shift as part of operand to shift insn"));
}
else
{
inst.instruction |= inst.operands[0].reg;
inst.instruction |= inst.operands[2].reg << 3;
+
+ /* PR 12854: Error on extraneous shifts. */
+ constraint (inst.operands[2].shifted,
+ _("extraneous shift as part of operand to shift insn"));
}
else
{
inst.instruction |= inst.operands[0].reg;
inst.instruction |= inst.operands[2].reg << 3;
+
+ /* PR 12854: Error on extraneous shifts. */
+ constraint (inst.operands[2].shifted,
+ _("extraneous shift as part of operand to shift insn"));
}
else
{
inst.instruction |= (value & 0xf000) >> 12;
inst.instruction |= (value & 0x0ff0);
inst.instruction |= (value & 0x000f) << 16;
+ /* PR gas/15623: SMC instructions must be last in an IT block. */
+ set_it_insn_type_last ();
}
static void
}
static void
+do_t_udf (void)
+{
+ if (!inst.operands[0].present)
+ inst.operands[0].imm = 0;
+
+ if ((unsigned int) inst.operands[0].imm > 255 || inst.size_req == 4)
+ {
+ constraint (inst.size_req == 2,
+ _("immediate value out of range"));
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= (inst.operands[0].imm & 0xf000u) << 4;
+ inst.instruction |= (inst.operands[0].imm & 0x0fffu) << 0;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].imm;
+ }
+
+ set_it_insn_type (NEUTRAL_IT_INSN);
+}
+
+
+static void
do_t_usat (void)
{
do_t_ssat_usat (0);
X(vcmp, 0xeb40a40, 0xeb40b40, N_INV), \
X(vcmpz, 0xeb50a40, 0xeb50b40, N_INV), \
X(vcmpe, 0xeb40ac0, 0xeb40bc0, N_INV), \
- X(vcmpez, 0xeb50ac0, 0xeb50bc0, N_INV)
+ X(vcmpez, 0xeb50ac0, 0xeb50bc0, N_INV), \
+ X(vseleq, 0xe000a00, N_INV, N_INV), \
+ X(vselvs, 0xe100a00, N_INV, N_INV), \
+ X(vselge, 0xe200a00, N_INV, N_INV), \
+ X(vselgt, 0xe300a00, N_INV, N_INV), \
+ X(vmaxnm, 0xe800a00, 0x3000f10, N_INV), \
+ X(vminnm, 0xe800a40, 0x3200f10, N_INV), \
+ X(vcvta, 0xebc0a40, 0x3bb0000, N_INV), \
+ X(vrintr, 0xeb60a40, 0x3ba0400, N_INV), \
+ X(vrinta, 0xeb80a40, 0x3ba0400, N_INV), \
+ X(aes, 0x3b00300, N_INV, N_INV), \
+ X(sha3op, 0x2000c00, N_INV, N_INV), \
+ X(sha1h, 0x3b902c0, N_INV, N_INV), \
+ X(sha2op, 0x3ba0380, N_INV, N_INV)
enum neon_opc
{
((neon_enc_tab[(X) & 0x0fffffff].integer) | ((X) & 0xf0000000))
#define NEON_ENC_DOUBLE_(X) \
((neon_enc_tab[(X) & 0x0fffffff].float_or_poly) | ((X) & 0xf0000000))
+#define NEON_ENC_FPV8_(X) \
+ ((neon_enc_tab[(X) & 0x0fffffff].integer) | ((X) & 0xf000000))
#define NEON_ENCODE(type, inst) \
do \
N_F16 = 0x0040000,
N_F32 = 0x0080000,
N_F64 = 0x0100000,
+ N_P64 = 0x0200000,
N_KEY = 0x1000000, /* Key element (main type specifier). */
N_EQK = 0x2000000, /* Given operand has the same type & size as the key. */
N_VFP = 0x4000000, /* VFP mode: operand size must match register width. */
+ N_UNT = 0x8000000, /* Must be explicitly untyped. */
N_DBL = 0x0000001, /* If N_EQK, this operand is twice the size. */
N_HLF = 0x0000002, /* If N_EQK, this operand is half the size. */
N_SGN = 0x0000004, /* If N_EQK, this operand is forced to be signed. */
N_FLT = 0x0000020, /* If N_EQK, this operand is forced to be float. */
N_SIZ = 0x0000040, /* If N_EQK, this operand is forced to be size-only. */
N_UTYP = 0,
- N_MAX_NONSPECIAL = N_F64
+ N_MAX_NONSPECIAL = N_P64
};
#define N_ALLMODS (N_DBL | N_HLF | N_SGN | N_UNS | N_INT | N_FLT | N_SIZ)
int matches = 1;
for (j = 0; j < neon_shape_tab[shape].els; j++)
- {
- if (!inst.operands[j].present)
- {
- matches = 0;
- break;
- }
-
- switch (neon_shape_tab[shape].el[j])
- {
- case SE_F:
- if (!(inst.operands[j].isreg
- && inst.operands[j].isvec
- && inst.operands[j].issingle
- && !inst.operands[j].isquad))
- matches = 0;
- break;
-
- case SE_D:
- if (!(inst.operands[j].isreg
- && inst.operands[j].isvec
- && !inst.operands[j].isquad
- && !inst.operands[j].issingle))
- matches = 0;
- break;
-
- case SE_R:
- if (!(inst.operands[j].isreg
- && !inst.operands[j].isvec))
- matches = 0;
- break;
-
- case SE_Q:
- if (!(inst.operands[j].isreg
- && inst.operands[j].isvec
- && inst.operands[j].isquad
- && !inst.operands[j].issingle))
- matches = 0;
- break;
-
- case SE_I:
- if (!(!inst.operands[j].isreg
- && !inst.operands[j].isscalar))
- matches = 0;
- break;
-
- case SE_S:
- if (!(!inst.operands[j].isreg
- && inst.operands[j].isscalar))
- matches = 0;
- break;
-
- case SE_L:
- break;
- }
- if (!matches)
- break;
- }
- if (matches)
- break;
- }
+ {
+ if (!inst.operands[j].present)
+ {
+ matches = 0;
+ break;
+ }
- va_end (ap);
+ switch (neon_shape_tab[shape].el[j])
+ {
+ case SE_F:
+ if (!(inst.operands[j].isreg
+ && inst.operands[j].isvec
+ && inst.operands[j].issingle
+ && !inst.operands[j].isquad))
+ matches = 0;
+ break;
+
+ case SE_D:
+ if (!(inst.operands[j].isreg
+ && inst.operands[j].isvec
+ && !inst.operands[j].isquad
+ && !inst.operands[j].issingle))
+ matches = 0;
+ break;
+
+ case SE_R:
+ if (!(inst.operands[j].isreg
+ && !inst.operands[j].isvec))
+ matches = 0;
+ break;
+
+ case SE_Q:
+ if (!(inst.operands[j].isreg
+ && inst.operands[j].isvec
+ && inst.operands[j].isquad
+ && !inst.operands[j].issingle))
+ matches = 0;
+ break;
+
+ case SE_I:
+ if (!(!inst.operands[j].isreg
+ && !inst.operands[j].isscalar))
+ matches = 0;
+ break;
+
+ case SE_S:
+ if (!(!inst.operands[j].isreg
+ && inst.operands[j].isscalar))
+ matches = 0;
+ break;
+
+ case SE_L:
+ break;
+ }
+ if (!matches)
+ break;
+ }
+ if (matches && (j >= ARM_IT_MAX_OPERANDS || !inst.operands[j].present))
+ /* We've matched all the entries in the shape table, and we don't
+ have any left over operands which have not been matched. */
+ break;
+ }
+
+ va_end (ap);
if (shape == NS_NULL && first_shape != NS_NULL)
first_error (_("invalid instruction shape"));
static void
neon_modify_type_size (unsigned typebits, enum neon_el_type *g_type,
- unsigned *g_size)
+ unsigned *g_size)
{
/* Allow modification to be made to types which are constrained to be
based on the key element, based on bits set alongside N_EQK. */
if ((typebits & N_SGN) != 0)
*g_type = NT_signed;
else if ((typebits & N_UNS) != 0)
- *g_type = NT_unsigned;
+ *g_type = NT_unsigned;
else if ((typebits & N_INT) != 0)
- *g_type = NT_integer;
+ *g_type = NT_integer;
else if ((typebits & N_FLT) != 0)
- *g_type = NT_float;
+ *g_type = NT_float;
else if ((typebits & N_SIZ) != 0)
- *g_type = NT_untyped;
+ *g_type = NT_untyped;
}
}
{
case NT_untyped:
switch (size)
- {
- case 8: return N_8;
- case 16: return N_16;
- case 32: return N_32;
- case 64: return N_64;
- default: ;
- }
+ {
+ case 8: return N_8;
+ case 16: return N_16;
+ case 32: return N_32;
+ case 64: return N_64;
+ default: ;
+ }
break;
case NT_integer:
switch (size)
- {
- case 8: return N_I8;
- case 16: return N_I16;
- case 32: return N_I32;
- case 64: return N_I64;
- default: ;
- }
+ {
+ case 8: return N_I8;
+ case 16: return N_I16;
+ case 32: return N_I32;
+ case 64: return N_I64;
+ default: ;
+ }
break;
case NT_float:
switch (size)
- {
+ {
case 16: return N_F16;
- case 32: return N_F32;
- case 64: return N_F64;
- default: ;
- }
+ case 32: return N_F32;
+ case 64: return N_F64;
+ default: ;
+ }
break;
case NT_poly:
switch (size)
- {
- case 8: return N_P8;
- case 16: return N_P16;
- default: ;
- }
+ {
+ case 8: return N_P8;
+ case 16: return N_P16;
+ case 64: return N_P64;
+ default: ;
+ }
break;
case NT_signed:
switch (size)
- {
- case 8: return N_S8;
- case 16: return N_S16;
- case 32: return N_S32;
- case 64: return N_S64;
- default: ;
- }
+ {
+ case 8: return N_S8;
+ case 16: return N_S16;
+ case 32: return N_S32;
+ case 64: return N_S64;
+ default: ;
+ }
break;
case NT_unsigned:
switch (size)
- {
- case 8: return N_U8;
- case 16: return N_U16;
- case 32: return N_U32;
- case 64: return N_U64;
- default: ;
- }
+ {
+ case 8: return N_U8;
+ case 16: return N_U16;
+ case 32: return N_U32;
+ case 64: return N_U64;
+ default: ;
+ }
break;
default: ;
static int
el_type_of_type_chk (enum neon_el_type *type, unsigned *size,
- enum neon_type_mask mask)
+ enum neon_type_mask mask)
{
if ((mask & N_EQK) != 0)
return FAIL;
if ((mask & (N_S8 | N_U8 | N_I8 | N_8 | N_P8)) != 0)
*size = 8;
- else if ((mask & (N_S16 | N_U16 | N_I16 | N_16 | N_P16)) != 0)
+ else if ((mask & (N_S16 | N_U16 | N_I16 | N_16 | N_F16 | N_P16)) != 0)
*size = 16;
else if ((mask & (N_S32 | N_U32 | N_I32 | N_32 | N_F32)) != 0)
*size = 32;
- else if ((mask & (N_S64 | N_U64 | N_I64 | N_64 | N_F64)) != 0)
+ else if ((mask & (N_S64 | N_U64 | N_I64 | N_64 | N_F64 | N_P64)) != 0)
*size = 64;
else
return FAIL;
*type = NT_integer;
else if ((mask & (N_8 | N_16 | N_32 | N_64)) != 0)
*type = NT_untyped;
- else if ((mask & (N_P8 | N_P16)) != 0)
+ else if ((mask & (N_P8 | N_P16 | N_P64)) != 0)
*type = NT_poly;
- else if ((mask & (N_F32 | N_F64)) != 0)
+ else if ((mask & (N_F16 | N_F32 | N_F64)) != 0)
*type = NT_float;
else
return FAIL;
for (i = 1; i <= N_MAX_NONSPECIAL; i <<= 1)
{
if (el_type_of_type_chk (&type, &size,
- (enum neon_type_mask) (allowed & i)) == SUCCESS)
- {
- neon_modify_type_size (mods, &type, &size);
- destmask |= type_chk_of_el_type (type, size);
- }
+ (enum neon_type_mask) (allowed & i)) == SUCCESS)
+ {
+ neon_modify_type_size (mods, &type, &size);
+ destmask |= type_chk_of_el_type (type, size);
+ }
}
return destmask;
{
unsigned thisarg = va_arg (ap, unsigned);
if (thisarg == N_IGNORE_TYPE)
- {
- va_end (ap);
- return badtype;
- }
+ {
+ va_end (ap);
+ return badtype;
+ }
types[i] = thisarg;
if ((thisarg & N_KEY) != 0)
- key_el = i;
+ key_el = i;
}
va_end (ap);
if (inst.vectype.elems > 0)
for (i = 0; i < els; i++)
if (inst.operands[i].vectype.type != NT_invtype)
- {
- first_error (_("types specified in both the mnemonic and operands"));
- return badtype;
- }
+ {
+ first_error (_("types specified in both the mnemonic and operands"));
+ return badtype;
+ }
/* Duplicate inst.vectype elements here as necessary.
FIXME: No idea if this is exactly the same as the ARM assembler,
inst.vectype.elems = els;
inst.vectype.el[key_el] = inst.vectype.el[0];
for (j = 0; j < els; j++)
- if (j != key_el)
- inst.vectype.el[j] = neon_type_promote (&inst.vectype.el[key_el],
- types[j]);
+ if (j != key_el)
+ inst.vectype.el[j] = neon_type_promote (&inst.vectype.el[key_el],
+ types[j]);
}
else if (inst.vectype.elems == 0 && els > 0)
{
unsigned j;
/* No types were given after the mnemonic, so look for types specified
- after each operand. We allow some flexibility here; as long as the
- "key" operand has a type, we can infer the others. */
+ after each operand. We allow some flexibility here; as long as the
+ "key" operand has a type, we can infer the others. */
for (j = 0; j < els; j++)
- if (inst.operands[j].vectype.type != NT_invtype)
- inst.vectype.el[j] = inst.operands[j].vectype;
+ if (inst.operands[j].vectype.type != NT_invtype)
+ inst.vectype.el[j] = inst.operands[j].vectype;
if (inst.operands[key_el].vectype.type != NT_invtype)
- {
- for (j = 0; j < els; j++)
- if (inst.operands[j].vectype.type == NT_invtype)
- inst.vectype.el[j] = neon_type_promote (&inst.vectype.el[key_el],
- types[j]);
- }
+ {
+ for (j = 0; j < els; j++)
+ if (inst.operands[j].vectype.type == NT_invtype)
+ inst.vectype.el[j] = neon_type_promote (&inst.vectype.el[key_el],
+ types[j]);
+ }
else
- {
- first_error (_("operand types can't be inferred"));
- return badtype;
- }
+ {
+ first_error (_("operand types can't be inferred"));
+ return badtype;
+ }
}
else if (inst.vectype.elems != els)
{
for (pass = 0; pass < 2; pass++)
{
for (i = 0; i < els; i++)
- {
- unsigned thisarg = types[i];
- unsigned types_allowed = ((thisarg & N_EQK) != 0 && pass != 0)
- ? modify_types_allowed (key_allowed, thisarg) : thisarg;
- enum neon_el_type g_type = inst.vectype.el[i].type;
- unsigned g_size = inst.vectype.el[i].size;
-
- /* Decay more-specific signed & unsigned types to sign-insensitive
+ {
+ unsigned thisarg = types[i];
+ unsigned types_allowed = ((thisarg & N_EQK) != 0 && pass != 0)
+ ? modify_types_allowed (key_allowed, thisarg) : thisarg;
+ enum neon_el_type g_type = inst.vectype.el[i].type;
+ unsigned g_size = inst.vectype.el[i].size;
+
+ /* Decay more-specific signed & unsigned types to sign-insensitive
integer types if sign-specific variants are unavailable. */
- if ((g_type == NT_signed || g_type == NT_unsigned)
+ if ((g_type == NT_signed || g_type == NT_unsigned)
&& (types_allowed & N_SU_ALL) == 0)
g_type = NT_integer;
- /* If only untyped args are allowed, decay any more specific types to
+ /* If only untyped args are allowed, decay any more specific types to
them. Some instructions only care about signs for some element
sizes, so handle that properly. */
- if ((g_size == 8 && (types_allowed & N_8) != 0)
- || (g_size == 16 && (types_allowed & N_16) != 0)
- || (g_size == 32 && (types_allowed & N_32) != 0)
- || (g_size == 64 && (types_allowed & N_64) != 0))
+ if (((types_allowed & N_UNT) == 0)
+ && ((g_size == 8 && (types_allowed & N_8) != 0)
+ || (g_size == 16 && (types_allowed & N_16) != 0)
+ || (g_size == 32 && (types_allowed & N_32) != 0)
+ || (g_size == 64 && (types_allowed & N_64) != 0)))
g_type = NT_untyped;
- if (pass == 0)
- {
- if ((thisarg & N_KEY) != 0)
- {
- k_type = g_type;
- k_size = g_size;
- key_allowed = thisarg & ~N_KEY;
- }
- }
- else
- {
- if ((thisarg & N_VFP) != 0)
- {
- enum neon_shape_el regshape;
- unsigned regwidth, match;
+ if (pass == 0)
+ {
+ if ((thisarg & N_KEY) != 0)
+ {
+ k_type = g_type;
+ k_size = g_size;
+ key_allowed = thisarg & ~N_KEY;
+ }
+ }
+ else
+ {
+ if ((thisarg & N_VFP) != 0)
+ {
+ enum neon_shape_el regshape;
+ unsigned regwidth, match;
/* PR 11136: Catch the case where we are passed a shape of NS_NULL. */
if (ns == NS_NULL)
first_error (_("invalid instruction shape"));
return badtype;
}
- regshape = neon_shape_tab[ns].el[i];
- regwidth = neon_shape_el_size[regshape];
-
- /* In VFP mode, operands must match register widths. If we
- have a key operand, use its width, else use the width of
- the current operand. */
- if (k_size != -1u)
- match = k_size;
- else
- match = g_size;
-
- if (regwidth != match)
- {
- first_error (_("operand size must match register width"));
- return badtype;
- }
- }
-
- if ((thisarg & N_EQK) == 0)
- {
- unsigned given_type = type_chk_of_el_type (g_type, g_size);
-
- if ((given_type & types_allowed) == 0)
- {
- first_error (_("bad type in Neon instruction"));
- return badtype;
- }
- }
- else
- {
- enum neon_el_type mod_k_type = k_type;
- unsigned mod_k_size = k_size;
- neon_modify_type_size (thisarg, &mod_k_type, &mod_k_size);
- if (g_type != mod_k_type || g_size != mod_k_size)
- {
- first_error (_("inconsistent types in Neon instruction"));
- return badtype;
- }
- }
- }
- }
+ regshape = neon_shape_tab[ns].el[i];
+ regwidth = neon_shape_el_size[regshape];
+
+ /* In VFP mode, operands must match register widths. If we
+ have a key operand, use its width, else use the width of
+ the current operand. */
+ if (k_size != -1u)
+ match = k_size;
+ else
+ match = g_size;
+
+ if (regwidth != match)
+ {
+ first_error (_("operand size must match register width"));
+ return badtype;
+ }
+ }
+
+ if ((thisarg & N_EQK) == 0)
+ {
+ unsigned given_type = type_chk_of_el_type (g_type, g_size);
+
+ if ((given_type & types_allowed) == 0)
+ {
+ first_error (_("bad type in Neon instruction"));
+ return badtype;
+ }
+ }
+ else
+ {
+ enum neon_el_type mod_k_type = k_type;
+ unsigned mod_k_size = k_size;
+ neon_modify_type_size (thisarg, &mod_k_type, &mod_k_size);
+ if (g_type != mod_k_type || g_size != mod_k_size)
+ {
+ first_error (_("inconsistent types in Neon instruction"));
+ return badtype;
+ }
+ }
+ }
+ }
}
return inst.vectype.el[key_el];
abort ();
constraint (!ARM_CPU_HAS_FEATURE (cpu_variant,
- thumb_mode ? *opcode->tvariant : *opcode->avariant),
- _(BAD_FPU));
+ thumb_mode ? *opcode->tvariant : *opcode->avariant),
+ _(BAD_FPU));
inst.is_neon = 1;
if (rs == NS_FFF)
{
if (is_add)
- do_vfp_nsyn_opcode ("fadds");
+ do_vfp_nsyn_opcode ("fadds");
else
- do_vfp_nsyn_opcode ("fsubs");
+ do_vfp_nsyn_opcode ("fsubs");
}
else
{
if (is_add)
- do_vfp_nsyn_opcode ("faddd");
+ do_vfp_nsyn_opcode ("faddd");
else
- do_vfp_nsyn_opcode ("fsubd");
+ do_vfp_nsyn_opcode ("fsubd");
}
}
case 2:
rs = neon_select_shape (NS_FF, NS_DD, NS_NULL);
et = neon_check_type (2, rs,
- N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+ N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
break;
case 3:
rs = neon_select_shape (NS_FFF, NS_DDD, NS_NULL);
et = neon_check_type (3, rs,
- N_EQK | N_VFP, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+ N_EQK | N_VFP, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
break;
default:
if (rs == NS_FFF)
{
if (is_mla)
- do_vfp_nsyn_opcode ("fmacs");
+ do_vfp_nsyn_opcode ("fmacs");
else
- do_vfp_nsyn_opcode ("fnmacs");
+ do_vfp_nsyn_opcode ("fnmacs");
}
else
{
if (is_mla)
- do_vfp_nsyn_opcode ("fmacd");
+ do_vfp_nsyn_opcode ("fmacd");
else
- do_vfp_nsyn_opcode ("fnmacd");
+ do_vfp_nsyn_opcode ("fnmacd");
}
}
if (rs == NS_FFF)
{
if (is_fma)
- do_vfp_nsyn_opcode ("ffmas");
+ do_vfp_nsyn_opcode ("ffmas");
else
- do_vfp_nsyn_opcode ("ffnmas");
+ do_vfp_nsyn_opcode ("ffnmas");
}
else
{
if (is_fma)
- do_vfp_nsyn_opcode ("ffmad");
+ do_vfp_nsyn_opcode ("ffmad");
else
- do_vfp_nsyn_opcode ("ffnmad");
+ do_vfp_nsyn_opcode ("ffnmad");
}
}
if (rs == NS_FF)
{
if (is_neg)
- do_vfp_nsyn_opcode ("fnegs");
+ do_vfp_nsyn_opcode ("fnegs");
else
- do_vfp_nsyn_opcode ("fabss");
+ do_vfp_nsyn_opcode ("fabss");
}
else
{
if (is_neg)
- do_vfp_nsyn_opcode ("fnegd");
+ do_vfp_nsyn_opcode ("fnegd");
else
- do_vfp_nsyn_opcode ("fabsd");
+ do_vfp_nsyn_opcode ("fabsd");
}
}
if (is_ldm)
{
if (is_dbmode)
- do_vfp_nsyn_opcode ("fldmdbs");
+ do_vfp_nsyn_opcode ("fldmdbs");
else
- do_vfp_nsyn_opcode ("fldmias");
+ do_vfp_nsyn_opcode ("fldmias");
}
else
{
if (is_dbmode)
- do_vfp_nsyn_opcode ("fstmdbs");
+ do_vfp_nsyn_opcode ("fstmdbs");
else
- do_vfp_nsyn_opcode ("fstmias");
+ do_vfp_nsyn_opcode ("fstmias");
}
}
neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
if (rs == NS_FF)
- {
- NEON_ENCODE (SINGLE, inst);
- do_vfp_sp_monadic ();
- }
+ {
+ NEON_ENCODE (SINGLE, inst);
+ do_vfp_sp_monadic ();
+ }
else
- {
- NEON_ENCODE (DOUBLE, inst);
- do_vfp_dp_rd_rm ();
- }
+ {
+ NEON_ENCODE (DOUBLE, inst);
+ do_vfp_dp_rd_rm ();
+ }
}
else
{
neon_check_type (2, rs, N_F32 | N_F64 | N_KEY | N_VFP, N_EQK);
switch (inst.instruction & 0x0fffffff)
- {
- case N_MNEM_vcmp:
- inst.instruction += N_MNEM_vcmpz - N_MNEM_vcmp;
- break;
- case N_MNEM_vcmpe:
- inst.instruction += N_MNEM_vcmpez - N_MNEM_vcmpe;
- break;
- default:
- abort ();
- }
+ {
+ case N_MNEM_vcmp:
+ inst.instruction += N_MNEM_vcmpz - N_MNEM_vcmp;
+ break;
+ case N_MNEM_vcmpe:
+ inst.instruction += N_MNEM_vcmpez - N_MNEM_vcmpe;
+ break;
+ default:
+ abort ();
+ }
if (rs == NS_FI)
- {
- NEON_ENCODE (SINGLE, inst);
- do_vfp_sp_compare_z ();
- }
+ {
+ NEON_ENCODE (SINGLE, inst);
+ do_vfp_sp_compare_z ();
+ }
else
- {
- NEON_ENCODE (DOUBLE, inst);
- do_vfp_dp_rd ();
- }
+ {
+ NEON_ENCODE (DOUBLE, inst);
+ do_vfp_dp_rd ();
+ }
}
do_vfp_cond_or_thumb ();
}
{
/* The U bit is at bit 24 by default. Move to bit 28 in Thumb mode. */
if (i & (1 << 24))
- i |= 1 << 28;
+ i |= 1 << 28;
i &= ~(1 << 24);
static void
neon_imm_shift (int write_ubit, int uval, int isquad, struct neon_type_el et,
- unsigned immbits)
+ unsigned immbits)
{
unsigned size = et.size >> 3;
inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
{
enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
struct neon_type_el et = neon_check_type (3, rs,
- N_EQK, N_SU_ALL | N_KEY, N_EQK | N_SGN);
+ N_EQK, N_SU_ALL | N_KEY, N_EQK | N_SGN);
unsigned int tmp;
/* VSHL/VQSHL 3-register variants have syntax such as:
- vshl.xx Dd, Dm, Dn
- whereas other 3-register operations encoded by neon_three_same have
- syntax like:
- vadd.xx Dd, Dn, Dm
- (i.e. with Dn & Dm reversed). Swap operands[1].reg and operands[2].reg
- here. */
+ vshl.xx Dd, Dm, Dn
+ whereas other 3-register operations encoded by neon_three_same have
+ syntax like:
+ vadd.xx Dd, Dn, Dm
+ (i.e. with Dn & Dm reversed). Swap operands[1].reg and operands[2].reg
+ here. */
tmp = inst.operands[2].reg;
inst.operands[2].reg = inst.operands[1].reg;
inst.operands[1].reg = tmp;
NEON_ENCODE (IMMED, inst);
neon_imm_shift (TRUE, et.type == NT_unsigned, neon_quad (rs), et,
- inst.operands[2].imm);
+ inst.operands[2].imm);
}
else
{
enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
struct neon_type_el et = neon_check_type (3, rs,
- N_EQK, N_SU_ALL | N_KEY, N_EQK | N_SGN);
+ N_EQK, N_SU_ALL | N_KEY, N_EQK | N_SGN);
unsigned int tmp;
/* See note in do_neon_shl_imm. */
if (size == 8)
{
/* Unfortunately, this will make everything apart from zero out-of-range.
- FIXME is this the intended semantics? There doesn't seem much point in
- accepting .I8 if so. */
+ FIXME is this the intended semantics? There doesn't seem much point in
+ accepting .I8 if so. */
immediate |= immediate << 8;
size = 16;
}
neon_bits_same_in_bytes (unsigned imm)
{
return ((imm & 0x000000ff) == 0 || (imm & 0x000000ff) == 0x000000ff)
- && ((imm & 0x0000ff00) == 0 || (imm & 0x0000ff00) == 0x0000ff00)
- && ((imm & 0x00ff0000) == 0 || (imm & 0x00ff0000) == 0x00ff0000)
- && ((imm & 0xff000000) == 0 || (imm & 0xff000000) == 0xff000000);
+ && ((imm & 0x0000ff00) == 0 || (imm & 0x0000ff00) == 0x0000ff00)
+ && ((imm & 0x00ff0000) == 0 || (imm & 0x00ff0000) == 0x00ff0000)
+ && ((imm & 0xff000000) == 0 || (imm & 0xff000000) == 0xff000000);
}
/* For immediate of above form, return 0bABCD. */
neon_squash_bits (unsigned imm)
{
return (imm & 0x01) | ((imm & 0x0100) >> 7) | ((imm & 0x010000) >> 14)
- | ((imm & 0x01000000) >> 21);
+ | ((imm & 0x01000000) >> 21);
}
/* Compress quarter-float representation to 0b...000 abcdefgh. */
if (type == NT_float && is_quarter_float (immlo) && immhi == 0)
{
if (size != 32 || *op == 1)
- return FAIL;
+ return FAIL;
*immbits = neon_qfloat_bits (immlo);
return 0xf;
}
? neon_select_shape (NS_DDI, NS_QQI, NS_NULL)
: neon_select_shape (NS_DI, NS_QI, NS_NULL));
struct neon_type_el et = neon_check_type (2, rs,
- N_I8 | N_I16 | N_I32 | N_I64 | N_F32 | N_KEY, N_EQK);
+ N_I8 | N_I16 | N_I32 | N_I64 | N_F32 | N_KEY, N_EQK);
enum neon_opc opcode = (enum neon_opc) inst.instruction & 0x0fffffff;
unsigned immbits;
int cmode;
if (et.type == NT_invtype)
- return;
+ return;
if (three_ops_form)
constraint (inst.operands[0].reg != inst.operands[1].reg,
}
switch (opcode)
- {
- case N_MNEM_vbic:
- cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
- break;
-
- case N_MNEM_vorr:
- cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
- break;
-
- case N_MNEM_vand:
- /* Pseudo-instruction for VBIC. */
- neon_invert_size (&immbits, 0, et.size);
- cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
- break;
-
- case N_MNEM_vorn:
- /* Pseudo-instruction for VORR. */
- neon_invert_size (&immbits, 0, et.size);
- cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
- break;
-
- default:
- abort ();
- }
+ {
+ case N_MNEM_vbic:
+ cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
+ break;
+
+ case N_MNEM_vorr:
+ cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
+ break;
+
+ case N_MNEM_vand:
+ /* Pseudo-instruction for VBIC. */
+ neon_invert_size (&immbits, 0, et.size);
+ cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
+ break;
+
+ case N_MNEM_vorn:
+ /* Pseudo-instruction for VORR. */
+ neon_invert_size (&immbits, 0, et.size);
+ cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
+ break;
+
+ default:
+ abort ();
+ }
if (cmode == FAIL)
- return;
+ return;
inst.instruction |= neon_quad (rs) << 6;
inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
static void
neon_dyadic_misc (enum neon_el_type ubit_meaning, unsigned types,
- unsigned destbits)
+ unsigned destbits)
{
enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
struct neon_type_el et = neon_check_type (3, rs, N_EQK | destbits, N_EQK,
- types | N_KEY);
+ types | N_KEY);
if (et.type == NT_float)
{
NEON_ENCODE (FLOAT, inst);
enum vfp_or_neon_is_neon_bits
{
NEON_CHECK_CC = 1,
- NEON_CHECK_ARCH = 2
+ NEON_CHECK_ARCH = 2,
+ NEON_CHECK_ARCH8 = 4
};
/* Call this function if an instruction which may have belonged to the VFP or
if (!thumb_mode && (check & NEON_CHECK_CC))
{
if (inst.cond != COND_ALWAYS)
- {
- first_error (_(BAD_COND));
- return FAIL;
- }
+ {
+ first_error (_(BAD_COND));
+ return FAIL;
+ }
if (inst.uncond_value != -1)
- inst.instruction |= inst.uncond_value << 28;
+ inst.instruction |= inst.uncond_value << 28;
}
if ((check & NEON_CHECK_ARCH)
- && !ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1))
+ && !mark_feature_used (&fpu_neon_ext_v1))
+ {
+ first_error (_(BAD_FPU));
+ return FAIL;
+ }
+
+ if ((check & NEON_CHECK_ARCH8)
+ && !mark_feature_used (&fpu_neon_ext_armv8))
{
first_error (_(BAD_FPU));
return FAIL;
if (inst.operands[2].isreg)
{
if (invert)
- neon_exchange_operands ();
+ neon_exchange_operands ();
neon_dyadic_misc (NT_unsigned, regtypes, N_SIZ);
}
else
{
enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_NULL);
struct neon_type_el et = neon_check_type (2, rs,
- N_EQK | N_SIZ, immtypes | N_KEY);
+ N_EQK | N_SIZ, immtypes | N_KEY);
NEON_ENCODE (IMMED, inst);
inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
{
case 16:
if (regno > 7 || elno > 3)
- goto bad_scalar;
+ goto bad_scalar;
return regno | (elno << 3);
case 32:
if (regno > 15 || elno > 1)
- goto bad_scalar;
+ goto bad_scalar;
return regno | (elno << 4);
default:
{
enum neon_shape rs = neon_select_shape (NS_DDS, NS_QQS, NS_NULL);
struct neon_type_el et = neon_check_type (3, rs,
- N_EQK, N_EQK, N_I16 | N_I32 | N_F32 | N_KEY);
+ N_EQK, N_EQK, N_I16 | N_I32 | N_F32 | N_KEY);
NEON_ENCODE (SCALAR, inst);
neon_mul_mac (et, neon_quad (rs));
}
{
enum neon_shape rs = neon_select_shape (NS_DDS, NS_QQS, NS_NULL);
struct neon_type_el et = neon_check_type (3, rs,
- N_EQK, N_EQK, N_S16 | N_S32 | N_KEY);
+ N_EQK, N_EQK, N_S16 | N_S32 | N_KEY);
NEON_ENCODE (SCALAR, inst);
neon_mul_mac (et, neon_quad (rs));
}
{
enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
struct neon_type_el et = neon_check_type (3, rs,
- N_EQK, N_EQK, N_S16 | N_S32 | N_KEY);
+ N_EQK, N_EQK, N_S16 | N_S32 | N_KEY);
NEON_ENCODE (INTEGER, inst);
/* The U bit (rounding) comes from bit mask. */
neon_three_same (neon_quad (rs), 0, et.size);
N_EQK, N_8 | N_16 | N_32 | N_64 | N_KEY);
int imm = inst.operands[2].imm;
constraint (imm < 0 || (unsigned)imm >= et.size,
- _("immediate out of range for insert"));
+ _("immediate out of range for insert"));
neon_imm_shift (FALSE, 0, neon_quad (rs), et, imm);
}
N_EQK, N_8 | N_16 | N_32 | N_64 | N_KEY);
int imm = inst.operands[2].imm;
constraint (imm < 1 || (unsigned)imm > et.size,
- _("immediate out of range for insert"));
+ _("immediate out of range for insert"));
neon_imm_shift (FALSE, 0, neon_quad (rs), et, et.size - imm);
}
N_EQK | N_UNS, N_S8 | N_S16 | N_S32 | N_S64 | N_KEY);
int imm = inst.operands[2].imm;
constraint (imm < 0 || (unsigned)imm >= et.size,
- _("immediate out of range for shift"));
+ _("immediate out of range for shift"));
/* Only encodes the 'U present' variant of the instruction.
In this case, signed types have OP (bit 8) set to 0.
Unsigned types have OP set to 1. */
}
constraint (imm < 1 || (unsigned)imm > et.size,
- _("immediate out of range"));
+ _("immediate out of range"));
neon_imm_shift (TRUE, et.type == NT_unsigned, 0, et, et.size - imm);
}
}
constraint (imm < 1 || (unsigned)imm > et.size,
- _("immediate out of range"));
+ _("immediate out of range"));
/* FIXME: The manual is kind of unclear about what value U should have in
VQ{R}SHRUN instructions, but U=0, op=0 definitely encodes VRSHR, so it
must be 1. */
}
constraint (imm < 1 || (unsigned)imm > et.size,
- _("immediate out of range for narrowing operation"));
+ _("immediate out of range for narrowing operation"));
neon_imm_shift (FALSE, 0, 0, et, et.size - imm);
}
{
/* A more-specific type check for non-max versions. */
et = neon_check_type (2, NS_QDI,
- N_EQK | N_DBL, N_SU_32 | N_KEY);
+ N_EQK | N_DBL, N_SU_32 | N_KEY);
NEON_ENCODE (IMMED, inst);
neon_imm_shift (TRUE, et.type == NT_unsigned, 0, et, imm);
}
/* Check the various types for the VCVT instruction, and return which version
the current instruction is. */
-static int
-neon_cvt_flavour (enum neon_shape rs)
+#define CVT_FLAVOUR_VAR \
+ CVT_VAR (s32_f32, N_S32, N_F32, whole_reg, "ftosls", "ftosis", "ftosizs") \
+ CVT_VAR (u32_f32, N_U32, N_F32, whole_reg, "ftouls", "ftouis", "ftouizs") \
+ CVT_VAR (f32_s32, N_F32, N_S32, whole_reg, "fsltos", "fsitos", NULL) \
+ CVT_VAR (f32_u32, N_F32, N_U32, whole_reg, "fultos", "fuitos", NULL) \
+ /* Half-precision conversions. */ \
+ CVT_VAR (f32_f16, N_F32, N_F16, whole_reg, NULL, NULL, NULL) \
+ CVT_VAR (f16_f32, N_F16, N_F32, whole_reg, NULL, NULL, NULL) \
+ /* VFP instructions. */ \
+ CVT_VAR (f32_f64, N_F32, N_F64, N_VFP, NULL, "fcvtsd", NULL) \
+ CVT_VAR (f64_f32, N_F64, N_F32, N_VFP, NULL, "fcvtds", NULL) \
+ CVT_VAR (s32_f64, N_S32, N_F64 | key, N_VFP, "ftosld", "ftosid", "ftosizd") \
+ CVT_VAR (u32_f64, N_U32, N_F64 | key, N_VFP, "ftould", "ftouid", "ftouizd") \
+ CVT_VAR (f64_s32, N_F64 | key, N_S32, N_VFP, "fsltod", "fsitod", NULL) \
+ CVT_VAR (f64_u32, N_F64 | key, N_U32, N_VFP, "fultod", "fuitod", NULL) \
+ /* VFP instructions with bitshift. */ \
+ CVT_VAR (f32_s16, N_F32 | key, N_S16, N_VFP, "fshtos", NULL, NULL) \
+ CVT_VAR (f32_u16, N_F32 | key, N_U16, N_VFP, "fuhtos", NULL, NULL) \
+ CVT_VAR (f64_s16, N_F64 | key, N_S16, N_VFP, "fshtod", NULL, NULL) \
+ CVT_VAR (f64_u16, N_F64 | key, N_U16, N_VFP, "fuhtod", NULL, NULL) \
+ CVT_VAR (s16_f32, N_S16, N_F32 | key, N_VFP, "ftoshs", NULL, NULL) \
+ CVT_VAR (u16_f32, N_U16, N_F32 | key, N_VFP, "ftouhs", NULL, NULL) \
+ CVT_VAR (s16_f64, N_S16, N_F64 | key, N_VFP, "ftoshd", NULL, NULL) \
+ CVT_VAR (u16_f64, N_U16, N_F64 | key, N_VFP, "ftouhd", NULL, NULL)
+
+#define CVT_VAR(C, X, Y, R, BSN, CN, ZN) \
+ neon_cvt_flavour_##C,
+
+/* The different types of conversions we can do. */
+enum neon_cvt_flavour
+{
+ CVT_FLAVOUR_VAR
+ neon_cvt_flavour_invalid,
+ neon_cvt_flavour_first_fp = neon_cvt_flavour_f32_f64
+};
+
+#undef CVT_VAR
+
+static enum neon_cvt_flavour
+get_neon_cvt_flavour (enum neon_shape rs)
{
-#define CVT_VAR(C,X,Y) \
- et = neon_check_type (2, rs, whole_reg | (X), whole_reg | (Y)); \
- if (et.type != NT_invtype) \
- { \
- inst.error = NULL; \
- return (C); \
+#define CVT_VAR(C,X,Y,R,BSN,CN,ZN) \
+ et = neon_check_type (2, rs, (R) | (X), (R) | (Y)); \
+ if (et.type != NT_invtype) \
+ { \
+ inst.error = NULL; \
+ return (neon_cvt_flavour_##C); \
}
+
struct neon_type_el et;
unsigned whole_reg = (rs == NS_FFI || rs == NS_FD || rs == NS_DF
- || rs == NS_FF) ? N_VFP : 0;
+ || rs == NS_FF) ? N_VFP : 0;
/* The instruction versions which take an immediate take one register
argument, which is extended to the width of the full register. Thus the
"source" and "destination" registers must have the same width. Hack that
here by making the size equal to the key (wider, in this case) operand. */
unsigned key = (rs == NS_QQI || rs == NS_DDI || rs == NS_FFI) ? N_KEY : 0;
- CVT_VAR (0, N_S32, N_F32);
- CVT_VAR (1, N_U32, N_F32);
- CVT_VAR (2, N_F32, N_S32);
- CVT_VAR (3, N_F32, N_U32);
- /* Half-precision conversions. */
- CVT_VAR (4, N_F32, N_F16);
- CVT_VAR (5, N_F16, N_F32);
-
- whole_reg = N_VFP;
-
- /* VFP instructions. */
- CVT_VAR (6, N_F32, N_F64);
- CVT_VAR (7, N_F64, N_F32);
- CVT_VAR (8, N_S32, N_F64 | key);
- CVT_VAR (9, N_U32, N_F64 | key);
- CVT_VAR (10, N_F64 | key, N_S32);
- CVT_VAR (11, N_F64 | key, N_U32);
- /* VFP instructions with bitshift. */
- CVT_VAR (12, N_F32 | key, N_S16);
- CVT_VAR (13, N_F32 | key, N_U16);
- CVT_VAR (14, N_F64 | key, N_S16);
- CVT_VAR (15, N_F64 | key, N_U16);
- CVT_VAR (16, N_S16, N_F32 | key);
- CVT_VAR (17, N_U16, N_F32 | key);
- CVT_VAR (18, N_S16, N_F64 | key);
- CVT_VAR (19, N_U16, N_F64 | key);
+ CVT_FLAVOUR_VAR;
- return -1;
+ return neon_cvt_flavour_invalid;
#undef CVT_VAR
}
+enum neon_cvt_mode
+{
+ neon_cvt_mode_a,
+ neon_cvt_mode_n,
+ neon_cvt_mode_p,
+ neon_cvt_mode_m,
+ neon_cvt_mode_z,
+ neon_cvt_mode_x,
+ neon_cvt_mode_r
+};
+
/* Neon-syntax VFP conversions. */
static void
-do_vfp_nsyn_cvt (enum neon_shape rs, int flavour)
+do_vfp_nsyn_cvt (enum neon_shape rs, enum neon_cvt_flavour flavour)
{
const char *opname = 0;
{
/* Conversions with immediate bitshift. */
const char *enc[] =
- {
- "ftosls",
- "ftouls",
- "fsltos",
- "fultos",
- NULL,
- NULL,
- NULL,
- NULL,
- "ftosld",
- "ftould",
- "fsltod",
- "fultod",
- "fshtos",
- "fuhtos",
- "fshtod",
- "fuhtod",
- "ftoshs",
- "ftouhs",
- "ftoshd",
- "ftouhd"
- };
-
- if (flavour >= 0 && flavour < (int) ARRAY_SIZE (enc))
- {
- opname = enc[flavour];
- constraint (inst.operands[0].reg != inst.operands[1].reg,
- _("operands 0 and 1 must be the same register"));
- inst.operands[1] = inst.operands[2];
- memset (&inst.operands[2], '\0', sizeof (inst.operands[2]));
- }
+ {
+#define CVT_VAR(C,A,B,R,BSN,CN,ZN) BSN,
+ CVT_FLAVOUR_VAR
+ NULL
+#undef CVT_VAR
+ };
+
+ if (flavour < (int) ARRAY_SIZE (enc))
+ {
+ opname = enc[flavour];
+ constraint (inst.operands[0].reg != inst.operands[1].reg,
+ _("operands 0 and 1 must be the same register"));
+ inst.operands[1] = inst.operands[2];
+ memset (&inst.operands[2], '\0', sizeof (inst.operands[2]));
+ }
}
else
{
/* Conversions without bitshift. */
const char *enc[] =
- {
- "ftosis",
- "ftouis",
- "fsitos",
- "fuitos",
- "NULL",
- "NULL",
- "fcvtsd",
- "fcvtds",
- "ftosid",
- "ftouid",
- "fsitod",
- "fuitod"
- };
-
- if (flavour >= 0 && flavour < (int) ARRAY_SIZE (enc))
- opname = enc[flavour];
+ {
+#define CVT_VAR(C,A,B,R,BSN,CN,ZN) CN,
+ CVT_FLAVOUR_VAR
+ NULL
+#undef CVT_VAR
+ };
+
+ if (flavour < (int) ARRAY_SIZE (enc))
+ opname = enc[flavour];
}
if (opname)
do_vfp_nsyn_cvtz (void)
{
enum neon_shape rs = neon_select_shape (NS_FF, NS_FD, NS_NULL);
- int flavour = neon_cvt_flavour (rs);
+ enum neon_cvt_flavour flavour = get_neon_cvt_flavour (rs);
const char *enc[] =
{
- "ftosizs",
- "ftouizs",
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- "ftosizd",
- "ftouizd"
+#define CVT_VAR(C,A,B,R,BSN,CN,ZN) ZN,
+ CVT_FLAVOUR_VAR
+ NULL
+#undef CVT_VAR
};
- if (flavour >= 0 && flavour < (int) ARRAY_SIZE (enc) && enc[flavour])
+ if (flavour < (int) ARRAY_SIZE (enc) && enc[flavour])
do_vfp_nsyn_opcode (enc[flavour]);
}
static void
-do_neon_cvt_1 (bfd_boolean round_to_zero ATTRIBUTE_UNUSED)
+do_vfp_nsyn_cvt_fpv8 (enum neon_cvt_flavour flavour,
+ enum neon_cvt_mode mode)
+{
+ int sz, op;
+ int rm;
+
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ switch (flavour)
+ {
+ case neon_cvt_flavour_s32_f64:
+ sz = 1;
+ op = 0;
+ break;
+ case neon_cvt_flavour_s32_f32:
+ sz = 0;
+ op = 1;
+ break;
+ case neon_cvt_flavour_u32_f64:
+ sz = 1;
+ op = 0;
+ break;
+ case neon_cvt_flavour_u32_f32:
+ sz = 0;
+ op = 0;
+ break;
+ default:
+ first_error (_("invalid instruction shape"));
+ return;
+ }
+
+ switch (mode)
+ {
+ case neon_cvt_mode_a: rm = 0; break;
+ case neon_cvt_mode_n: rm = 1; break;
+ case neon_cvt_mode_p: rm = 2; break;
+ case neon_cvt_mode_m: rm = 3; break;
+ default: first_error (_("invalid rounding mode")); return;
+ }
+
+ NEON_ENCODE (FPV8, inst);
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ encode_arm_vfp_reg (inst.operands[1].reg, sz == 1 ? VFP_REG_Dm : VFP_REG_Sm);
+ inst.instruction |= sz << 8;
+ inst.instruction |= op << 7;
+ inst.instruction |= rm << 16;
+ inst.instruction |= 0xf0000000;
+ inst.is_neon = TRUE;
+}
+
+static void
+do_neon_cvt_1 (enum neon_cvt_mode mode)
{
enum neon_shape rs = neon_select_shape (NS_DDI, NS_QQI, NS_FFI, NS_DD, NS_QQ,
NS_FD, NS_DF, NS_FF, NS_QD, NS_DQ, NS_NULL);
- int flavour = neon_cvt_flavour (rs);
+ enum neon_cvt_flavour flavour = get_neon_cvt_flavour (rs);
/* PR11109: Handle round-to-zero for VCVT conversions. */
- if (round_to_zero
+ if (mode == neon_cvt_mode_z
&& ARM_CPU_HAS_FEATURE (cpu_variant, fpu_arch_vfp_v2)
- && (flavour == 0 || flavour == 1 || flavour == 8 || flavour == 9)
+ && (flavour == neon_cvt_flavour_s32_f32
+ || flavour == neon_cvt_flavour_u32_f32
+ || flavour == neon_cvt_flavour_s32_f64
+ || flavour == neon_cvt_flavour_u32_f64)
&& (rs == NS_FD || rs == NS_FF))
{
do_vfp_nsyn_cvtz ();
}
/* VFP rather than Neon conversions. */
- if (flavour >= 6)
+ if (flavour >= neon_cvt_flavour_first_fp)
{
- do_vfp_nsyn_cvt (rs, flavour);
+ if (mode == neon_cvt_mode_x || mode == neon_cvt_mode_z)
+ do_vfp_nsyn_cvt (rs, flavour);
+ else
+ do_vfp_nsyn_cvt_fpv8 (flavour, mode);
+
return;
}
case NS_DDI:
case NS_QQI:
{
- unsigned immbits;
- unsigned enctab[] = { 0x0000100, 0x1000100, 0x0, 0x1000000 };
+ unsigned immbits;
+ unsigned enctab[] = { 0x0000100, 0x1000100, 0x0, 0x1000000 };
- if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
- return;
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
- /* Fixed-point conversion with #0 immediate is encoded as an
- integer conversion. */
- if (inst.operands[2].present && inst.operands[2].imm == 0)
- goto int_encode;
+ /* Fixed-point conversion with #0 immediate is encoded as an
+ integer conversion. */
+ if (inst.operands[2].present && inst.operands[2].imm == 0)
+ goto int_encode;
immbits = 32 - inst.operands[2].imm;
- NEON_ENCODE (IMMED, inst);
- if (flavour != -1)
- inst.instruction |= enctab[flavour];
- inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
- inst.instruction |= HI1 (inst.operands[0].reg) << 22;
- inst.instruction |= LOW4 (inst.operands[1].reg);
- inst.instruction |= HI1 (inst.operands[1].reg) << 5;
- inst.instruction |= neon_quad (rs) << 6;
- inst.instruction |= 1 << 21;
- inst.instruction |= immbits << 16;
-
- neon_dp_fixup (&inst);
+ NEON_ENCODE (IMMED, inst);
+ if (flavour != neon_cvt_flavour_invalid)
+ inst.instruction |= enctab[flavour];
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= 1 << 21;
+ inst.instruction |= immbits << 16;
+
+ neon_dp_fixup (&inst);
}
break;
case NS_DD:
case NS_QQ:
+ if (mode != neon_cvt_mode_x && mode != neon_cvt_mode_z)
+ {
+ NEON_ENCODE (FLOAT, inst);
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH8) == FAIL)
+ return;
+
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= (flavour == neon_cvt_flavour_u32_f32) << 7;
+ inst.instruction |= mode << 8;
+ if (thumb_mode)
+ inst.instruction |= 0xfc000000;
+ else
+ inst.instruction |= 0xf0000000;
+ }
+ else
+ {
int_encode:
- {
- unsigned enctab[] = { 0x100, 0x180, 0x0, 0x080 };
+ {
+ unsigned enctab[] = { 0x100, 0x180, 0x0, 0x080 };
- NEON_ENCODE (INTEGER, inst);
+ NEON_ENCODE (INTEGER, inst);
- if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
- return;
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
- if (flavour != -1)
- inst.instruction |= enctab[flavour];
+ if (flavour != neon_cvt_flavour_invalid)
+ inst.instruction |= enctab[flavour];
- inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
- inst.instruction |= HI1 (inst.operands[0].reg) << 22;
- inst.instruction |= LOW4 (inst.operands[1].reg);
- inst.instruction |= HI1 (inst.operands[1].reg) << 5;
- inst.instruction |= neon_quad (rs) << 6;
- inst.instruction |= 2 << 18;
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= 2 << 18;
- neon_dp_fixup (&inst);
- }
- break;
+ neon_dp_fixup (&inst);
+ }
+ }
+ break;
/* Half-precision conversions for Advanced SIMD -- neon. */
case NS_QD:
}
if (rs == NS_DQ)
- inst.instruction = 0x3b60600;
+ inst.instruction = 0x3b60600;
else
inst.instruction = 0x3b60700;
default:
/* Some VFP conversions go here (s32 <-> f32, u32 <-> f32). */
- do_vfp_nsyn_cvt (rs, flavour);
+ if (mode == neon_cvt_mode_x || mode == neon_cvt_mode_z)
+ do_vfp_nsyn_cvt (rs, flavour);
+ else
+ do_vfp_nsyn_cvt_fpv8 (flavour, mode);
}
}
static void
do_neon_cvtr (void)
{
- do_neon_cvt_1 (FALSE);
+ do_neon_cvt_1 (neon_cvt_mode_x);
}
static void
do_neon_cvt (void)
{
- do_neon_cvt_1 (TRUE);
+ do_neon_cvt_1 (neon_cvt_mode_z);
}
static void
-do_neon_cvtb (void)
+do_neon_cvta (void)
{
- inst.instruction = 0xeb20a40;
+ do_neon_cvt_1 (neon_cvt_mode_a);
+}
- /* The sizes are attached to the mnemonic. */
- if (inst.vectype.el[0].type != NT_invtype
- && inst.vectype.el[0].size == 16)
- inst.instruction |= 0x00010000;
+static void
+do_neon_cvtn (void)
+{
+ do_neon_cvt_1 (neon_cvt_mode_n);
+}
- /* Programmer's syntax: the sizes are attached to the operands. */
- else if (inst.operands[0].vectype.type != NT_invtype
- && inst.operands[0].vectype.size == 16)
- inst.instruction |= 0x00010000;
+static void
+do_neon_cvtp (void)
+{
+ do_neon_cvt_1 (neon_cvt_mode_p);
+}
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sm);
+static void
+do_neon_cvtm (void)
+{
+ do_neon_cvt_1 (neon_cvt_mode_m);
+}
+
+static void
+do_neon_cvttb_2 (bfd_boolean t, bfd_boolean to, bfd_boolean is_double)
+{
+ if (is_double)
+ mark_feature_used (&fpu_vfp_ext_armv8);
+
+ encode_arm_vfp_reg (inst.operands[0].reg,
+ (is_double && !to) ? VFP_REG_Dd : VFP_REG_Sd);
+ encode_arm_vfp_reg (inst.operands[1].reg,
+ (is_double && to) ? VFP_REG_Dm : VFP_REG_Sm);
+ inst.instruction |= to ? 0x10000 : 0;
+ inst.instruction |= t ? 0x80 : 0;
+ inst.instruction |= is_double ? 0x100 : 0;
do_vfp_cond_or_thumb ();
}
+static void
+do_neon_cvttb_1 (bfd_boolean t)
+{
+ enum neon_shape rs = neon_select_shape (NS_FF, NS_FD, NS_DF, NS_NULL);
+
+ if (rs == NS_NULL)
+ return;
+ else if (neon_check_type (2, rs, N_F16, N_F32 | N_VFP).type != NT_invtype)
+ {
+ inst.error = NULL;
+ do_neon_cvttb_2 (t, /*to=*/TRUE, /*is_double=*/FALSE);
+ }
+ else if (neon_check_type (2, rs, N_F32 | N_VFP, N_F16).type != NT_invtype)
+ {
+ inst.error = NULL;
+ do_neon_cvttb_2 (t, /*to=*/FALSE, /*is_double=*/FALSE);
+ }
+ else if (neon_check_type (2, rs, N_F16, N_F64 | N_VFP).type != NT_invtype)
+ {
+ inst.error = NULL;
+ do_neon_cvttb_2 (t, /*to=*/TRUE, /*is_double=*/TRUE);
+ }
+ else if (neon_check_type (2, rs, N_F64 | N_VFP, N_F16).type != NT_invtype)
+ {
+ inst.error = NULL;
+ do_neon_cvttb_2 (t, /*to=*/FALSE, /*is_double=*/TRUE);
+ }
+ else
+ return;
+}
+
+static void
+do_neon_cvtb (void)
+{
+ do_neon_cvttb_1 (FALSE);
+}
+
static void
do_neon_cvtt (void)
{
- do_neon_cvtb ();
- inst.instruction |= 0x80;
+ do_neon_cvttb_1 (TRUE);
}
static void
int op, cmode, float_p;
constraint (et.type == NT_invtype,
- _("operand size must be specified for immediate VMOV"));
+ _("operand size must be specified for immediate VMOV"));
/* We start out as an MVN instruction if OP = 1, MOV otherwise. */
op = (inst.instruction & (1 << 5)) != 0;
immhi = inst.operands[1].reg;
constraint (et.size < 32 && (immlo & ~((1 << et.size) - 1)) != 0,
- _("immediate has bits set outside the operand size"));
+ _("immediate has bits set outside the operand size"));
float_p = inst.operands[1].immisfloat;
if ((cmode = neon_cmode_for_move_imm (immlo, immhi, float_p, &immbits, &op,
- et.size, et.type)) == FAIL)
+ et.size, et.type)) == FAIL)
{
/* Invert relevant bits only. */
neon_invert_size (&immlo, &immhi, et.size);
/* Flip from VMOV/VMVN to VMVN/VMOV. Some immediate types are unavailable
- with one or the other; those cases are caught by
- neon_cmode_for_move_imm. */
+ with one or the other; those cases are caught by
+ neon_cmode_for_move_imm. */
op = !op;
if ((cmode = neon_cmode_for_move_imm (immlo, immhi, float_p, &immbits,
&op, et.size, et.type)) == FAIL)
- {
- first_error (_("immediate out of range"));
- return;
- }
+ {
+ first_error (_("immediate out of range"));
+ return;
+ }
}
inst.instruction &= ~(1 << 5);
if (inst.operands[2].isscalar)
{
struct neon_type_el et = neon_check_type (3, NS_QDS,
- N_EQK | N_DBL, N_EQK, regtypes | N_KEY);
+ N_EQK | N_DBL, N_EQK, regtypes | N_KEY);
NEON_ENCODE (SCALAR, inst);
neon_mul_mac (et, et.type == NT_unsigned);
}
else
{
struct neon_type_el et = neon_check_type (3, NS_QDD,
- N_EQK | N_DBL, N_EQK, scalartypes | N_KEY);
+ N_EQK | N_DBL, N_EQK, scalartypes | N_KEY);
NEON_ENCODE (INTEGER, inst);
neon_mixed_length (et, et.size);
}
else
{
struct neon_type_el et = neon_check_type (3, NS_QDD,
- N_EQK | N_DBL, N_EQK, N_SU_32 | N_P8 | N_KEY);
+ N_EQK | N_DBL, N_EQK, N_SU_32 | N_P8 | N_P64 | N_KEY);
+
if (et.type == NT_poly)
- NEON_ENCODE (POLY, inst);
+ NEON_ENCODE (POLY, inst);
else
- NEON_ENCODE (INTEGER, inst);
- /* For polynomial encoding, size field must be 0b00 and the U bit must be
- zero. Should be OK as-is. */
+ NEON_ENCODE (INTEGER, inst);
+
+ /* For polynomial encoding the U bit must be zero, and the size must
+ be 8 (encoded as 0b00) or, on ARMv8 or later 64 (encoded, non
+ obviously, as 0b10). */
+ if (et.size == 64)
+ {
+ /* Check we're on the correct architecture. */
+ if (!mark_feature_used (&fpu_crypto_ext_armv8))
+ inst.error =
+ _("Instruction form not available on this architecture.");
+
+ et.size = 32;
+ }
+
neon_mixed_length (et, et.size);
}
}
unsigned elsize = (op == 2) ? 16 : (op == 1) ? 32 : (op == 0) ? 64 : 0;
gas_assert (elsize != 0);
constraint (et.size >= elsize,
- _("elements must be smaller than reversal region"));
+ _("elements must be smaller than reversal region"));
neon_two_same (neon_quad (rs), 1, et.size);
}
{
enum neon_shape rs = neon_select_shape (NS_DS, NS_QS, NS_NULL);
struct neon_type_el et = neon_check_type (2, rs,
- N_EQK, N_8 | N_16 | N_32 | N_KEY);
+ N_EQK, N_8 | N_16 | N_32 | N_KEY);
unsigned sizebits = et.size >> 3;
unsigned dm = NEON_SCALAR_REG (inst.operands[1].reg);
int logsize = neon_logbits (et.size);
unsigned x = NEON_SCALAR_INDEX (inst.operands[1].reg) << logsize;
if (vfp_or_neon_is_neon (NEON_CHECK_CC) == FAIL)
- return;
+ return;
NEON_ENCODE (SCALAR, inst);
inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
{
enum neon_shape rs = neon_select_shape (NS_DR, NS_QR, NS_NULL);
struct neon_type_el et = neon_check_type (2, rs,
- N_8 | N_16 | N_32 | N_KEY, N_EQK);
+ N_8 | N_16 | N_32 | N_KEY, N_EQK);
/* Duplicate ARM register to lanes of vector. */
NEON_ENCODE (ARMREG, inst);
switch (et.size)
- {
- case 8: inst.instruction |= 0x400000; break;
- case 16: inst.instruction |= 0x000020; break;
- case 32: inst.instruction |= 0x000000; break;
- default: break;
- }
+ {
+ case 8: inst.instruction |= 0x400000; break;
+ case 16: inst.instruction |= 0x000020; break;
+ case 32: inst.instruction |= 0x000000; break;
+ default: break;
+ }
inst.instruction |= LOW4 (inst.operands[1].reg) << 12;
inst.instruction |= LOW4 (inst.operands[0].reg) << 16;
inst.instruction |= HI1 (inst.operands[0].reg) << 7;
inst.instruction |= neon_quad (rs) << 21;
/* The encoding for this instruction is identical for the ARM and Thumb
- variants, except for the condition field. */
+ variants, except for the condition field. */
do_vfp_cond_or_thumb ();
}
}
/* It is not an error here if no type is given. */
inst.error = NULL;
if (et.type == NT_float && et.size == 64)
- {
- do_vfp_nsyn_opcode ("fcpyd");
- break;
- }
+ {
+ do_vfp_nsyn_opcode ("fcpyd");
+ break;
+ }
/* fall through. */
case NS_QQ: /* case 0/1. */
{
- if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
- return;
- /* The architecture manual I have doesn't explicitly state which
- value the U bit should have for register->register moves, but
- the equivalent VORR instruction has U = 0, so do that. */
- inst.instruction = 0x0200110;
- inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
- inst.instruction |= HI1 (inst.operands[0].reg) << 22;
- inst.instruction |= LOW4 (inst.operands[1].reg);
- inst.instruction |= HI1 (inst.operands[1].reg) << 5;
- inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
- inst.instruction |= HI1 (inst.operands[1].reg) << 7;
- inst.instruction |= neon_quad (rs) << 6;
-
- neon_dp_fixup (&inst);
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+ /* The architecture manual I have doesn't explicitly state which
+ value the U bit should have for register->register moves, but
+ the equivalent VORR instruction has U = 0, so do that. */
+ inst.instruction = 0x0200110;
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
+ inst.instruction |= HI1 (inst.operands[1].reg) << 7;
+ inst.instruction |= neon_quad (rs) << 6;
+
+ neon_dp_fixup (&inst);
}
break;
et = neon_check_type (2, rs, N_EQK, N_F64 | N_KEY);
inst.error = NULL;
if (et.type == NT_float && et.size == 64)
- {
- /* case 11 (fconstd). */
- ldconst = "fconstd";
- goto encode_fconstd;
- }
+ {
+ /* case 11 (fconstd). */
+ ldconst = "fconstd";
+ goto encode_fconstd;
+ }
/* fall through. */
case NS_QI: /* case 2/3. */
if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
- return;
+ return;
inst.instruction = 0x0800010;
neon_move_immediate ();
neon_dp_fixup (&inst);
case NS_SR: /* case 4. */
{
- unsigned bcdebits = 0;
- int logsize;
- unsigned dn = NEON_SCALAR_REG (inst.operands[0].reg);
- unsigned x = NEON_SCALAR_INDEX (inst.operands[0].reg);
-
- et = neon_check_type (2, NS_NULL, N_8 | N_16 | N_32 | N_KEY, N_EQK);
- logsize = neon_logbits (et.size);
-
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v1),
- _(BAD_FPU));
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1)
- && et.size != 32, _(BAD_FPU));
- constraint (et.type == NT_invtype, _("bad type for scalar"));
- constraint (x >= 64 / et.size, _("scalar index out of range"));
-
- switch (et.size)
- {
- case 8: bcdebits = 0x8; break;
- case 16: bcdebits = 0x1; break;
- case 32: bcdebits = 0x0; break;
- default: ;
- }
-
- bcdebits |= x << logsize;
-
- inst.instruction = 0xe000b10;
- do_vfp_cond_or_thumb ();
- inst.instruction |= LOW4 (dn) << 16;
- inst.instruction |= HI1 (dn) << 7;
- inst.instruction |= inst.operands[1].reg << 12;
- inst.instruction |= (bcdebits & 3) << 5;
- inst.instruction |= (bcdebits >> 2) << 21;
+ unsigned bcdebits = 0;
+ int logsize;
+ unsigned dn = NEON_SCALAR_REG (inst.operands[0].reg);
+ unsigned x = NEON_SCALAR_INDEX (inst.operands[0].reg);
+
+ /* .<size> is optional here, defaulting to .32. */
+ if (inst.vectype.elems == 0
+ && inst.operands[0].vectype.type == NT_invtype
+ && inst.operands[1].vectype.type == NT_invtype)
+ {
+ inst.vectype.el[0].type = NT_untyped;
+ inst.vectype.el[0].size = 32;
+ inst.vectype.elems = 1;
+ }
+
+ et = neon_check_type (2, NS_NULL, N_8 | N_16 | N_32 | N_KEY, N_EQK);
+ logsize = neon_logbits (et.size);
+
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v1),
+ _(BAD_FPU));
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1)
+ && et.size != 32, _(BAD_FPU));
+ constraint (et.type == NT_invtype, _("bad type for scalar"));
+ constraint (x >= 64 / et.size, _("scalar index out of range"));
+
+ switch (et.size)
+ {
+ case 8: bcdebits = 0x8; break;
+ case 16: bcdebits = 0x1; break;
+ case 32: bcdebits = 0x0; break;
+ default: ;
+ }
+
+ bcdebits |= x << logsize;
+
+ inst.instruction = 0xe000b10;
+ do_vfp_cond_or_thumb ();
+ inst.instruction |= LOW4 (dn) << 16;
+ inst.instruction |= HI1 (dn) << 7;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= (bcdebits & 3) << 5;
+ inst.instruction |= (bcdebits >> 2) << 21;
}
break;
case NS_DRR: /* case 5 (fmdrr). */
constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v2),
- _(BAD_FPU));
+ _(BAD_FPU));
inst.instruction = 0xc400b10;
do_vfp_cond_or_thumb ();
case NS_RS: /* case 6. */
{
- unsigned logsize;
- unsigned dn = NEON_SCALAR_REG (inst.operands[1].reg);
- unsigned x = NEON_SCALAR_INDEX (inst.operands[1].reg);
- unsigned abcdebits = 0;
+ unsigned logsize;
+ unsigned dn = NEON_SCALAR_REG (inst.operands[1].reg);
+ unsigned x = NEON_SCALAR_INDEX (inst.operands[1].reg);
+ unsigned abcdebits = 0;
+
+ /* .<dt> is optional here, defaulting to .32. */
+ if (inst.vectype.elems == 0
+ && inst.operands[0].vectype.type == NT_invtype
+ && inst.operands[1].vectype.type == NT_invtype)
+ {
+ inst.vectype.el[0].type = NT_untyped;
+ inst.vectype.el[0].size = 32;
+ inst.vectype.elems = 1;
+ }
et = neon_check_type (2, NS_NULL,
N_EQK, N_S8 | N_S16 | N_U8 | N_U16 | N_32 | N_KEY);
- logsize = neon_logbits (et.size);
-
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v1),
- _(BAD_FPU));
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1)
- && et.size != 32, _(BAD_FPU));
- constraint (et.type == NT_invtype, _("bad type for scalar"));
- constraint (x >= 64 / et.size, _("scalar index out of range"));
-
- switch (et.size)
- {
- case 8: abcdebits = (et.type == NT_signed) ? 0x08 : 0x18; break;
- case 16: abcdebits = (et.type == NT_signed) ? 0x01 : 0x11; break;
- case 32: abcdebits = 0x00; break;
- default: ;
- }
-
- abcdebits |= x << logsize;
- inst.instruction = 0xe100b10;
- do_vfp_cond_or_thumb ();
- inst.instruction |= LOW4 (dn) << 16;
- inst.instruction |= HI1 (dn) << 7;
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= (abcdebits & 3) << 5;
- inst.instruction |= (abcdebits >> 2) << 21;
+ logsize = neon_logbits (et.size);
+
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v1),
+ _(BAD_FPU));
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1)
+ && et.size != 32, _(BAD_FPU));
+ constraint (et.type == NT_invtype, _("bad type for scalar"));
+ constraint (x >= 64 / et.size, _("scalar index out of range"));
+
+ switch (et.size)
+ {
+ case 8: abcdebits = (et.type == NT_signed) ? 0x08 : 0x18; break;
+ case 16: abcdebits = (et.type == NT_signed) ? 0x01 : 0x11; break;
+ case 32: abcdebits = 0x00; break;
+ default: ;
+ }
+
+ abcdebits |= x << logsize;
+ inst.instruction = 0xe100b10;
+ do_vfp_cond_or_thumb ();
+ inst.instruction |= LOW4 (dn) << 16;
+ inst.instruction |= HI1 (dn) << 7;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= (abcdebits & 3) << 5;
+ inst.instruction |= (abcdebits >> 2) << 21;
}
break;
case NS_RRD: /* case 7 (fmrrd). */
constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v2),
- _(BAD_FPU));
+ _(BAD_FPU));
inst.instruction = 0xc500b10;
do_vfp_cond_or_thumb ();
ldconst = "fconsts";
encode_fconstd:
if (is_quarter_float (inst.operands[1].imm))
- {
- inst.operands[1].imm = neon_qfloat_bits (inst.operands[1].imm);
- do_vfp_nsyn_opcode (ldconst);
- }
+ {
+ inst.operands[1].imm = neon_qfloat_bits (inst.operands[1].imm);
+ do_vfp_nsyn_opcode (ldconst);
+ }
else
- first_error (_("immediate out of range"));
+ first_error (_("immediate out of range"));
break;
case NS_RF: /* case 12 (fmrs). */
expect. */
case NS_RRFF: /* case 14 (fmrrs). */
constraint (inst.operands[3].reg != inst.operands[2].reg + 1,
- _("VFP registers must be adjacent"));
+ _("VFP registers must be adjacent"));
inst.operands[2].imm = 2;
memset (&inst.operands[3], '\0', sizeof (inst.operands[3]));
do_vfp_nsyn_opcode ("fmrrs");
case NS_FFRR: /* case 15 (fmsrr). */
constraint (inst.operands[1].reg != inst.operands[0].reg + 1,
- _("VFP registers must be adjacent"));
+ _("VFP registers must be adjacent"));
inst.operands[1] = inst.operands[2];
inst.operands[2] = inst.operands[3];
inst.operands[0].imm = 2;
do_vfp_nsyn_opcode ("fmsrr");
break;
- default:
+ case NS_NULL:
+ /* neon_select_shape has determined that the instruction
+ shape is wrong and has already set the error message. */
+ break;
+
+ default:
abort ();
}
}
}
constraint (imm < 1 || (unsigned)imm > et.size,
- _("immediate out of range for shift"));
+ _("immediate out of range for shift"));
neon_imm_shift (TRUE, et.type == NT_unsigned, neon_quad (rs), et,
- et.size - imm);
+ et.size - imm);
}
static void
}
constraint (is_dbmode && !inst.operands[0].writeback,
- _("writeback (!) must be used for VLDMDB and VSTMDB"));
+ _("writeback (!) must be used for VLDMDB and VSTMDB"));
constraint (inst.operands[1].imm < 1 || inst.operands[1].imm > 16,
- _("register list must contain at least 1 and at most 16 "
- "registers"));
+ _("register list must contain at least 1 and at most 16 "
+ "registers"));
inst.instruction |= inst.operands[0].reg << 16;
inst.instruction |= inst.operands[0].writeback << 21;
/* Use of PC in vstr in ARM mode is deprecated in ARMv7.
And is UNPREDICTABLE in thumb mode. */
- if (!is_ldr
+ if (!is_ldr
&& inst.operands[1].reg == REG_PC
- && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v7))
+ && (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v7) || thumb_mode))
{
- if (!thumb_mode && warn_on_deprecated)
- as_warn (_("Use of PC here is deprecated"));
- else
+ if (thumb_mode)
inst.error = _("Use of PC here is UNPREDICTABLE");
+ else if (warn_on_deprecated)
+ as_warn (_("Use of PC here is deprecated"));
}
if (inst.operands[0].issingle)
{
if (is_ldr)
- do_vfp_nsyn_opcode ("flds");
+ do_vfp_nsyn_opcode ("flds");
else
- do_vfp_nsyn_opcode ("fsts");
+ do_vfp_nsyn_opcode ("fsts");
}
else
{
if (is_ldr)
- do_vfp_nsyn_opcode ("fldd");
+ do_vfp_nsyn_opcode ("fldd");
else
- do_vfp_nsyn_opcode ("fstd");
+ do_vfp_nsyn_opcode ("fstd");
}
}
do_neon_ld_st_interleave (void)
{
struct neon_type_el et = neon_check_type (1, NS_NULL,
- N_8 | N_16 | N_32 | N_64);
+ N_8 | N_16 | N_32 | N_64);
unsigned alignbits = 0;
unsigned idx;
/* The bits in this table go:
{
case 64: alignbits = 1; break;
case 128:
- if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 2
+ if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 2
&& NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4)
- goto bad_alignment;
- alignbits = 2;
- break;
+ goto bad_alignment;
+ alignbits = 2;
+ break;
case 256:
- if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4)
- goto bad_alignment;
- alignbits = 3;
- break;
+ if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4)
+ goto bad_alignment;
+ alignbits = 3;
+ break;
default:
bad_alignment:
- first_error (_("bad alignment"));
- return;
+ first_error (_("bad alignment"));
+ return;
}
inst.instruction |= alignbits << 4;
up the right value for "type" in a table based on this value and the given
list style, then stick it back. */
idx = ((inst.operands[0].imm >> 4) & 7)
- | (((inst.instruction >> 8) & 3) << 3);
+ | (((inst.instruction >> 8) & 3) << 3);
typebits = typetable[idx];
constraint (typebits == -1, _("bad list type for instruction"));
+ constraint (((inst.instruction >> 8) & 3) && et.size == 64,
+ _("bad element type for instruction"));
inst.instruction &= ~0xf00;
inst.instruction |= typebits << 8;
{
thissize = va_arg (ap, int);
if (thissize == -1)
- break;
+ break;
thisalign = va_arg (ap, int);
if (size == thissize && align == thisalign)
- result = SUCCESS;
+ result = SUCCESS;
}
while (result != SUCCESS);
return;
constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != n + 1,
- _("bad list length"));
+ _("bad list length"));
constraint (NEON_LANE (inst.operands[0].imm) >= max_el,
- _("scalar index out of range"));
+ _("scalar index out of range"));
constraint (n != 0 && NEON_REG_STRIDE (inst.operands[0].imm) == 2
- && et.size == 8,
- _("stride of 2 unavailable when element size is 8"));
+ && et.size == 8,
+ _("stride of 2 unavailable when element size is 8"));
switch (n)
{
case 0: /* VLD1 / VST1. */
align_good = neon_alignment_bit (et.size, align, &do_align, 16, 16,
- 32, 32, -1);
+ 32, 32, -1);
if (align_good == FAIL)
- return;
+ return;
if (do_align)
- {
- unsigned alignbits = 0;
- switch (et.size)
- {
- case 16: alignbits = 0x1; break;
- case 32: alignbits = 0x3; break;
- default: ;
- }
- inst.instruction |= alignbits << 4;
- }
+ {
+ unsigned alignbits = 0;
+ switch (et.size)
+ {
+ case 16: alignbits = 0x1; break;
+ case 32: alignbits = 0x3; break;
+ default: ;
+ }
+ inst.instruction |= alignbits << 4;
+ }
break;
case 1: /* VLD2 / VST2. */
align_good = neon_alignment_bit (et.size, align, &do_align, 8, 16, 16, 32,
- 32, 64, -1);
+ 32, 64, -1);
if (align_good == FAIL)
- return;
+ return;
if (do_align)
- inst.instruction |= 1 << 4;
+ inst.instruction |= 1 << 4;
break;
case 2: /* VLD3 / VST3. */
constraint (inst.operands[1].immisalign,
- _("can't use alignment with this instruction"));
+ _("can't use alignment with this instruction"));
break;
case 3: /* VLD4 / VST4. */
align_good = neon_alignment_bit (et.size, align, &do_align, 8, 32,
- 16, 64, 32, 64, 32, 128, -1);
+ 16, 64, 32, 64, 32, 128, -1);
if (align_good == FAIL)
- return;
+ return;
if (do_align)
- {
- unsigned alignbits = 0;
- switch (et.size)
- {
- case 8: alignbits = 0x1; break;
- case 16: alignbits = 0x1; break;
- case 32: alignbits = (align == 64) ? 0x1 : 0x2; break;
- default: ;
- }
- inst.instruction |= alignbits << 4;
- }
+ {
+ unsigned alignbits = 0;
+ switch (et.size)
+ {
+ case 8: alignbits = 0x1; break;
+ case 16: alignbits = 0x1; break;
+ case 32: alignbits = (align == 64) ? 0x1 : 0x2; break;
+ default: ;
+ }
+ inst.instruction |= alignbits << 4;
+ }
break;
default: ;
case 0: /* VLD1. */
gas_assert (NEON_REG_STRIDE (inst.operands[0].imm) != 2);
align_good = neon_alignment_bit (et.size, inst.operands[1].imm >> 8,
- &do_align, 16, 16, 32, 32, -1);
+ &do_align, 16, 16, 32, 32, -1);
if (align_good == FAIL)
- return;
+ return;
switch (NEON_REGLIST_LENGTH (inst.operands[0].imm))
- {
- case 1: break;
- case 2: inst.instruction |= 1 << 5; break;
- default: first_error (_("bad list length")); return;
- }
+ {
+ case 1: break;
+ case 2: inst.instruction |= 1 << 5; break;
+ default: first_error (_("bad list length")); return;
+ }
inst.instruction |= neon_logbits (et.size) << 6;
break;
case 1: /* VLD2. */
align_good = neon_alignment_bit (et.size, inst.operands[1].imm >> 8,
- &do_align, 8, 16, 16, 32, 32, 64, -1);
+ &do_align, 8, 16, 16, 32, 32, 64, -1);
if (align_good == FAIL)
- return;
+ return;
constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 2,
- _("bad list length"));
+ _("bad list length"));
if (NEON_REG_STRIDE (inst.operands[0].imm) == 2)
- inst.instruction |= 1 << 5;
+ inst.instruction |= 1 << 5;
inst.instruction |= neon_logbits (et.size) << 6;
break;
case 2: /* VLD3. */
constraint (inst.operands[1].immisalign,
- _("can't use alignment with this instruction"));
+ _("can't use alignment with this instruction"));
constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 3,
- _("bad list length"));
+ _("bad list length"));
if (NEON_REG_STRIDE (inst.operands[0].imm) == 2)
- inst.instruction |= 1 << 5;
+ inst.instruction |= 1 << 5;
inst.instruction |= neon_logbits (et.size) << 6;
break;
case 3: /* VLD4. */
{
- int align = inst.operands[1].imm >> 8;
- align_good = neon_alignment_bit (et.size, align, &do_align, 8, 32,
- 16, 64, 32, 64, 32, 128, -1);
- if (align_good == FAIL)
- return;
- constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4,
- _("bad list length"));
- if (NEON_REG_STRIDE (inst.operands[0].imm) == 2)
- inst.instruction |= 1 << 5;
- if (et.size == 32 && align == 128)
- inst.instruction |= 0x3 << 6;
- else
- inst.instruction |= neon_logbits (et.size) << 6;
+ int align = inst.operands[1].imm >> 8;
+ align_good = neon_alignment_bit (et.size, align, &do_align, 8, 32,
+ 16, 64, 32, 64, 32, 128, -1);
+ if (align_good == FAIL)
+ return;
+ constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4,
+ _("bad list length"));
+ if (NEON_REG_STRIDE (inst.operands[0].imm) == 2)
+ inst.instruction |= 1 << 5;
+ if (et.size == 32 && align == 128)
+ inst.instruction |= 0x3 << 6;
+ else
+ inst.instruction |= neon_logbits (et.size) << 6;
}
break;
- default: ;
- }
+ default: ;
+ }
+
+ inst.instruction |= do_align << 4;
+}
+
+/* Disambiguate VLD<n> and VST<n> instructions, and fill in common bits (those
+ apart from bits [11:4]. */
+
+static void
+do_neon_ldx_stx (void)
+{
+ if (inst.operands[1].isreg)
+ constraint (inst.operands[1].reg == REG_PC, BAD_PC);
+
+ switch (NEON_LANE (inst.operands[0].imm))
+ {
+ case NEON_INTERLEAVE_LANES:
+ NEON_ENCODE (INTERLV, inst);
+ do_neon_ld_st_interleave ();
+ break;
+
+ case NEON_ALL_LANES:
+ NEON_ENCODE (DUP, inst);
+ if (inst.instruction == N_INV)
+ {
+ first_error ("only loads support such operands");
+ break;
+ }
+ do_neon_ld_dup ();
+ break;
+
+ default:
+ NEON_ENCODE (LANE, inst);
+ do_neon_ld_st_lane ();
+ }
+
+ /* L bit comes from bit mask. */
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= inst.operands[1].reg << 16;
+
+ if (inst.operands[1].postind)
+ {
+ int postreg = inst.operands[1].imm & 0xf;
+ constraint (!inst.operands[1].immisreg,
+ _("post-index must be a register"));
+ constraint (postreg == 0xd || postreg == 0xf,
+ _("bad register for post-index"));
+ inst.instruction |= postreg;
+ }
+ else
+ {
+ constraint (inst.operands[1].immisreg, BAD_ADDR_MODE);
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ BAD_ADDR_MODE);
+
+ if (inst.operands[1].writeback)
+ {
+ inst.instruction |= 0xd;
+ }
+ else
+ inst.instruction |= 0xf;
+ }
+
+ if (thumb_mode)
+ inst.instruction |= 0xf9000000;
+ else
+ inst.instruction |= 0xf4000000;
+}
+
+/* FP v8. */
+static void
+do_vfp_nsyn_fpv8 (enum neon_shape rs)
+{
+ NEON_ENCODE (FPV8, inst);
+
+ if (rs == NS_FFF)
+ do_vfp_sp_dyadic ();
+ else
+ do_vfp_dp_rd_rn_rm ();
+
+ if (rs == NS_DDD)
+ inst.instruction |= 0x100;
+
+ inst.instruction |= 0xf0000000;
+}
+
+static void
+do_vsel (void)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ if (try_vfp_nsyn (3, do_vfp_nsyn_fpv8) != SUCCESS)
+ first_error (_("invalid instruction shape"));
+}
+
+static void
+do_vmaxnm (void)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ if (try_vfp_nsyn (3, do_vfp_nsyn_fpv8) == SUCCESS)
+ return;
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH8) == FAIL)
+ return;
+
+ neon_dyadic_misc (NT_untyped, N_F32, 0);
+}
+
+static void
+do_vrint_1 (enum neon_cvt_mode mode)
+{
+ enum neon_shape rs = neon_select_shape (NS_FF, NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et;
+
+ if (rs == NS_NULL)
+ return;
+
+ et = neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+ if (et.type != NT_invtype)
+ {
+ /* VFP encodings. */
+ if (mode == neon_cvt_mode_a || mode == neon_cvt_mode_n
+ || mode == neon_cvt_mode_p || mode == neon_cvt_mode_m)
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ NEON_ENCODE (FPV8, inst);
+ if (rs == NS_FF)
+ do_vfp_sp_monadic ();
+ else
+ do_vfp_dp_rd_rm ();
+
+ switch (mode)
+ {
+ case neon_cvt_mode_r: inst.instruction |= 0x00000000; break;
+ case neon_cvt_mode_z: inst.instruction |= 0x00000080; break;
+ case neon_cvt_mode_x: inst.instruction |= 0x00010000; break;
+ case neon_cvt_mode_a: inst.instruction |= 0xf0000000; break;
+ case neon_cvt_mode_n: inst.instruction |= 0xf0010000; break;
+ case neon_cvt_mode_p: inst.instruction |= 0xf0020000; break;
+ case neon_cvt_mode_m: inst.instruction |= 0xf0030000; break;
+ default: abort ();
+ }
+
+ inst.instruction |= (rs == NS_DD) << 8;
+ do_vfp_cond_or_thumb ();
+ }
+ else
+ {
+ /* Neon encodings (or something broken...). */
+ inst.error = NULL;
+ et = neon_check_type (2, rs, N_EQK, N_F32 | N_KEY);
+
+ if (et.type == NT_invtype)
+ return;
+
+ set_it_insn_type (OUTSIDE_IT_INSN);
+ NEON_ENCODE (FLOAT, inst);
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH8) == FAIL)
+ return;
+
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ switch (mode)
+ {
+ case neon_cvt_mode_z: inst.instruction |= 3 << 7; break;
+ case neon_cvt_mode_x: inst.instruction |= 1 << 7; break;
+ case neon_cvt_mode_a: inst.instruction |= 2 << 7; break;
+ case neon_cvt_mode_n: inst.instruction |= 0 << 7; break;
+ case neon_cvt_mode_p: inst.instruction |= 7 << 7; break;
+ case neon_cvt_mode_m: inst.instruction |= 5 << 7; break;
+ case neon_cvt_mode_r: inst.error = _("invalid rounding mode"); break;
+ default: abort ();
+ }
+
+ if (thumb_mode)
+ inst.instruction |= 0xfc000000;
+ else
+ inst.instruction |= 0xf0000000;
+ }
+}
+
+static void
+do_vrintx (void)
+{
+ do_vrint_1 (neon_cvt_mode_x);
+}
+
+static void
+do_vrintz (void)
+{
+ do_vrint_1 (neon_cvt_mode_z);
+}
+
+static void
+do_vrintr (void)
+{
+ do_vrint_1 (neon_cvt_mode_r);
+}
+
+static void
+do_vrinta (void)
+{
+ do_vrint_1 (neon_cvt_mode_a);
+}
+
+static void
+do_vrintn (void)
+{
+ do_vrint_1 (neon_cvt_mode_n);
+}
+
+static void
+do_vrintp (void)
+{
+ do_vrint_1 (neon_cvt_mode_p);
+}
+
+static void
+do_vrintm (void)
+{
+ do_vrint_1 (neon_cvt_mode_m);
+}
+
+/* Crypto v1 instructions. */
+static void
+do_crypto_2op_1 (unsigned elttype, int op)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ if (neon_check_type (2, NS_QQ, N_EQK | N_UNT, elttype | N_UNT | N_KEY).type
+ == NT_invtype)
+ return;
+
+ inst.error = NULL;
+
+ NEON_ENCODE (INTEGER, inst);
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg);
+ inst.instruction |= HI1 (inst.operands[1].reg) << 5;
+ if (op != -1)
+ inst.instruction |= op << 6;
+
+ if (thumb_mode)
+ inst.instruction |= 0xfc000000;
+ else
+ inst.instruction |= 0xf0000000;
+}
+
+static void
+do_crypto_3op_1 (int u, int op)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+
+ if (neon_check_type (3, NS_QQQ, N_EQK | N_UNT, N_EQK | N_UNT,
+ N_32 | N_UNT | N_KEY).type == NT_invtype)
+ return;
+
+ inst.error = NULL;
+
+ NEON_ENCODE (INTEGER, inst);
+ neon_three_same (1, u, 8 << op);
+}
+
+static void
+do_aese (void)
+{
+ do_crypto_2op_1 (N_8, 0);
+}
+
+static void
+do_aesd (void)
+{
+ do_crypto_2op_1 (N_8, 1);
+}
+
+static void
+do_aesmc (void)
+{
+ do_crypto_2op_1 (N_8, 2);
+}
+
+static void
+do_aesimc (void)
+{
+ do_crypto_2op_1 (N_8, 3);
+}
+
+static void
+do_sha1c (void)
+{
+ do_crypto_3op_1 (0, 0);
+}
+
+static void
+do_sha1p (void)
+{
+ do_crypto_3op_1 (0, 1);
+}
+
+static void
+do_sha1m (void)
+{
+ do_crypto_3op_1 (0, 2);
+}
+
+static void
+do_sha1su0 (void)
+{
+ do_crypto_3op_1 (0, 3);
+}
+
+static void
+do_sha256h (void)
+{
+ do_crypto_3op_1 (1, 0);
+}
+
+static void
+do_sha256h2 (void)
+{
+ do_crypto_3op_1 (1, 1);
+}
+
+static void
+do_sha256su1 (void)
+{
+ do_crypto_3op_1 (1, 2);
+}
+
+static void
+do_sha1h (void)
+{
+ do_crypto_2op_1 (N_32, -1);
+}
+
+static void
+do_sha1su1 (void)
+{
+ do_crypto_2op_1 (N_32, 0);
+}
+
+static void
+do_sha256su0 (void)
+{
+ do_crypto_2op_1 (N_32, 1);
+}
+
+static void
+do_crc32_1 (unsigned int poly, unsigned int sz)
+{
+ unsigned int Rd = inst.operands[0].reg;
+ unsigned int Rn = inst.operands[1].reg;
+ unsigned int Rm = inst.operands[2].reg;
+
+ set_it_insn_type (OUTSIDE_IT_INSN);
+ inst.instruction |= LOW4 (Rd) << (thumb_mode ? 8 : 12);
+ inst.instruction |= LOW4 (Rn) << 16;
+ inst.instruction |= LOW4 (Rm);
+ inst.instruction |= sz << (thumb_mode ? 4 : 21);
+ inst.instruction |= poly << (thumb_mode ? 20 : 9);
- inst.instruction |= do_align << 4;
+ if (Rd == REG_PC || Rn == REG_PC || Rm == REG_PC)
+ as_warn (UNPRED_REG ("r15"));
+ if (thumb_mode && (Rd == REG_SP || Rn == REG_SP || Rm == REG_SP))
+ as_warn (UNPRED_REG ("r13"));
}
-/* Disambiguate VLD<n> and VST<n> instructions, and fill in common bits (those
- apart from bits [11:4]. */
-
static void
-do_neon_ldx_stx (void)
+do_crc32b (void)
{
- if (inst.operands[1].isreg)
- constraint (inst.operands[1].reg == REG_PC, BAD_PC);
-
- switch (NEON_LANE (inst.operands[0].imm))
- {
- case NEON_INTERLEAVE_LANES:
- NEON_ENCODE (INTERLV, inst);
- do_neon_ld_st_interleave ();
- break;
+ do_crc32_1 (0, 0);
+}
- case NEON_ALL_LANES:
- NEON_ENCODE (DUP, inst);
- do_neon_ld_dup ();
- break;
+static void
+do_crc32h (void)
+{
+ do_crc32_1 (0, 1);
+}
- default:
- NEON_ENCODE (LANE, inst);
- do_neon_ld_st_lane ();
- }
+static void
+do_crc32w (void)
+{
+ do_crc32_1 (0, 2);
+}
- /* L bit comes from bit mask. */
- inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
- inst.instruction |= HI1 (inst.operands[0].reg) << 22;
- inst.instruction |= inst.operands[1].reg << 16;
+static void
+do_crc32cb (void)
+{
+ do_crc32_1 (1, 0);
+}
- if (inst.operands[1].postind)
- {
- int postreg = inst.operands[1].imm & 0xf;
- constraint (!inst.operands[1].immisreg,
- _("post-index must be a register"));
- constraint (postreg == 0xd || postreg == 0xf,
- _("bad register for post-index"));
- inst.instruction |= postreg;
- }
- else if (inst.operands[1].writeback)
- {
- inst.instruction |= 0xd;
- }
- else
- inst.instruction |= 0xf;
+static void
+do_crc32ch (void)
+{
+ do_crc32_1 (1, 1);
+}
- if (thumb_mode)
- inst.instruction |= 0xf9000000;
- else
- inst.instruction |= 0xf4000000;
+static void
+do_crc32cw (void)
+{
+ do_crc32_1 (1, 2);
}
+
\f
/* Overall per-instruction processing. */
if (pc_rel)
{
/* Create an absolute valued symbol, so we have something to
- refer to in the object file. Unfortunately for us, gas's
- generic expression parsing will already have folded out
- any use of .set foo/.type foo %function that may have
- been used to set type information of the target location,
- that's being specified symbolically. We have to presume
- the user knows what they are doing. */
+ refer to in the object file. Unfortunately for us, gas's
+ generic expression parsing will already have folded out
+ any use of .set foo/.type foo %function that may have
+ been used to set type information of the target location,
+ that's being specified symbolically. We have to presume
+ the user knows what they are doing. */
char name[16 + 8];
symbolS *symbol;
case O_add:
case O_subtract:
new_fix = fix_new_exp (frag, where, size, exp, pc_rel,
- (enum bfd_reloc_code_real) reloc);
+ (enum bfd_reloc_code_real) reloc);
break;
default:
new_fix = (fixS *) fix_new (frag, where, size, make_expr_symbol (exp), 0,
- pc_rel, (enum bfd_reloc_code_real) reloc);
+ pc_rel, (enum bfd_reloc_code_real) reloc);
break;
}
and carries 0xF in its ARM condition field. */
OT_csuffix, /* Instruction takes a conditional suffix. */
OT_csuffixF, /* Some forms of the instruction take a conditional
- suffix, others place 0xF where the condition field
- would be. */
+ suffix, others place 0xF where the condition field
+ would be. */
OT_cinfix3, /* Instruction takes a conditional infix,
beginning at character index 3. (In
unified mode, it becomes a suffix.) */
int offset = 2;
/* The .w and .n suffixes are only valid if the unified syntax is in
- use. */
+ use. */
if (unified_syntax && end[1] == 'w')
inst.size_req = 4;
else if (unified_syntax && end[1] == 'n')
inst.size_req = 2;
else
- offset = 0;
+ offset = 0;
inst.vectype.elems = 0;
if (end[offset] == '.')
{
/* See if we have a Neon type suffix (possible in either unified or
- non-unified ARM syntax mode). */
- if (parse_neon_type (&inst.vectype, str) == FAIL)
+ non-unified ARM syntax mode). */
+ if (parse_neon_type (&inst.vectype, str) == FAIL)
return NULL;
- }
+ }
else if (end[offset] != '\0' && end[offset] != ' ')
- return NULL;
+ return NULL;
}
else
*str = end;
/* Look for unaffixed or special-case affixed mnemonic. */
opcode = (const struct asm_opcode *) hash_find_n (arm_ops_hsh, base,
- end - base);
+ end - base);
if (opcode)
{
/* step U */
affix = end - 2;
cond = (const struct asm_cond *) hash_find_n (arm_cond_hsh, affix, 2);
opcode = (const struct asm_opcode *) hash_find_n (arm_ops_hsh, base,
- affix - base);
+ affix - base);
if (opcode && cond)
{
/* step CE */
/* else fall through */
case OT_csuffix:
- case OT_csuffixF:
+ case OT_csuffixF:
case OT_csuf_or_in3:
inst.cond = cond->value;
return opcode;
memcpy (save, affix, 2);
memmove (affix, affix + 2, (end - affix) - 2);
opcode = (const struct asm_opcode *) hash_find_n (arm_ops_hsh, base,
- (end - base) - 2);
+ (end - base) - 2);
memmove (affix + 2, affix, (end - affix) - 2);
memcpy (affix, save, 2);
now_it.block_length = 1;
mapping_state (MAP_THUMB);
now_it.insn = output_it_inst (cond, now_it.mask, NULL);
+ now_it.warn_deprecated = FALSE;
+ now_it.insn_cond = TRUE;
}
/* Close an automatic IT block.
{
#define CLEAR_BIT(value, nbit) ((value) & ~(1 << (nbit)))
#define SET_BIT_VALUE(value, bitvalue, nbit) (CLEAR_BIT (value, nbit) \
- | ((bitvalue) << (nbit)))
+ | ((bitvalue) << (nbit)))
const int resulting_bit = (cond & 1);
now_it.mask &= 0xf;
now_it.mask = SET_BIT_VALUE (now_it.mask,
- resulting_bit,
- (5 - now_it.block_length));
+ resulting_bit,
+ (5 - now_it.block_length));
now_it.mask = SET_BIT_VALUE (now_it.mask,
- 1,
- ((5 - now_it.block_length) - 1) );
+ 1,
+ ((5 - now_it.block_length) - 1) );
output_it_inst (now_it.cc, now_it.mask, now_it.insn);
#undef CLEAR_BIT
Rationale:
1) md_assemble () calls it_fsm_pre_encode () before calling tencode (),
- initializing the IT insn type with a generic initial value depending
- on the inst.condition.
+ initializing the IT insn type with a generic initial value depending
+ on the inst.condition.
2) During the tencode function, two things may happen:
- a) The tencode function overrides the IT insn type by
- calling either set_it_insn_type (type) or set_it_insn_type_last ().
- b) The tencode function queries the IT block state by
- calling in_it_block () (i.e. to determine narrow/not narrow mode).
-
- Both set_it_insn_type and in_it_block run the internal FSM state
- handling function (handle_it_state), because: a) setting the IT insn
- type may incur in an invalid state (exiting the function),
- and b) querying the state requires the FSM to be updated.
- Specifically we want to avoid creating an IT block for conditional
- branches, so it_fsm_pre_encode is actually a guess and we can't
- determine whether an IT block is required until the tencode () routine
- has decided what type of instruction this actually it.
- Because of this, if set_it_insn_type and in_it_block have to be used,
- set_it_insn_type has to be called first.
-
- set_it_insn_type_last () is a wrapper of set_it_insn_type (type), that
- determines the insn IT type depending on the inst.cond code.
- When a tencode () routine encodes an instruction that can be
- either outside an IT block, or, in the case of being inside, has to be
- the last one, set_it_insn_type_last () will determine the proper
- IT instruction type based on the inst.cond code. Otherwise,
- set_it_insn_type can be called for overriding that logic or
- for covering other cases.
-
- Calling handle_it_state () may not transition the IT block state to
- OUTSIDE_IT_BLOCK immediatelly, since the (current) state could be
- still queried. Instead, if the FSM determines that the state should
- be transitioned to OUTSIDE_IT_BLOCK, a flag is marked to be closed
- after the tencode () function: that's what it_fsm_post_encode () does.
-
- Since in_it_block () calls the state handling function to get an
- updated state, an error may occur (due to invalid insns combination).
- In that case, inst.error is set.
- Therefore, inst.error has to be checked after the execution of
- the tencode () routine.
+ a) The tencode function overrides the IT insn type by
+ calling either set_it_insn_type (type) or set_it_insn_type_last ().
+ b) The tencode function queries the IT block state by
+ calling in_it_block () (i.e. to determine narrow/not narrow mode).
+
+ Both set_it_insn_type and in_it_block run the internal FSM state
+ handling function (handle_it_state), because: a) setting the IT insn
+ type may incur in an invalid state (exiting the function),
+ and b) querying the state requires the FSM to be updated.
+ Specifically we want to avoid creating an IT block for conditional
+ branches, so it_fsm_pre_encode is actually a guess and we can't
+ determine whether an IT block is required until the tencode () routine
+ has decided what type of instruction this actually it.
+ Because of this, if set_it_insn_type and in_it_block have to be used,
+ set_it_insn_type has to be called first.
+
+ set_it_insn_type_last () is a wrapper of set_it_insn_type (type), that
+ determines the insn IT type depending on the inst.cond code.
+ When a tencode () routine encodes an instruction that can be
+ either outside an IT block, or, in the case of being inside, has to be
+ the last one, set_it_insn_type_last () will determine the proper
+ IT instruction type based on the inst.cond code. Otherwise,
+ set_it_insn_type can be called for overriding that logic or
+ for covering other cases.
+
+ Calling handle_it_state () may not transition the IT block state to
+ OUTSIDE_IT_BLOCK immediatelly, since the (current) state could be
+ still queried. Instead, if the FSM determines that the state should
+ be transitioned to OUTSIDE_IT_BLOCK, a flag is marked to be closed
+ after the tencode () function: that's what it_fsm_post_encode () does.
+
+ Since in_it_block () calls the state handling function to get an
+ updated state, an error may occur (due to invalid insns combination).
+ In that case, inst.error is set.
+ Therefore, inst.error has to be checked after the execution of
+ the tencode () routine.
3) Back in md_assemble(), it_fsm_post_encode () is called to commit
- any pending state change (if any) that didn't take place in
- handle_it_state () as explained above. */
+ any pending state change (if any) that didn't take place in
+ handle_it_state () as explained above. */
static void
it_fsm_pre_encode (void)
handle_it_state (void)
{
now_it.state_handled = 1;
+ now_it.insn_cond = FALSE;
switch (now_it.state)
{
}
else
{
+ now_it.insn_cond = TRUE;
now_it_add_mask (inst.cond);
}
case NEUTRAL_IT_INSN:
now_it.block_length++;
+ now_it.insn_cond = TRUE;
if (now_it.block_length > 4)
force_automatic_it_block_close ();
now_it.mask <<= 1;
now_it.mask &= 0x1f;
is_last = (now_it.mask == 0x10);
+ now_it.insn_cond = TRUE;
switch (inst.it_insn_type)
{
return SUCCESS;
}
+struct depr_insn_mask
+{
+ unsigned long pattern;
+ unsigned long mask;
+ const char* description;
+};
+
+/* List of 16-bit instruction patterns deprecated in an IT block in
+ ARMv8. */
+static const struct depr_insn_mask depr_it_insns[] = {
+ { 0xc000, 0xc000, N_("Short branches, Undefined, SVC, LDM/STM") },
+ { 0xb000, 0xb000, N_("Miscellaneous 16-bit instructions") },
+ { 0xa000, 0xb800, N_("ADR") },
+ { 0x4800, 0xf800, N_("Literal loads") },
+ { 0x4478, 0xf478, N_("Hi-register ADD, MOV, CMP, BX, BLX using pc") },
+ { 0x4487, 0xfc87, N_("Hi-register ADD, MOV, CMP using pc") },
+ { 0, 0, NULL }
+};
+
static void
it_fsm_post_encode (void)
{
if (!now_it.state_handled)
handle_it_state ();
+ if (now_it.insn_cond
+ && !now_it.warn_deprecated
+ && warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v8))
+ {
+ if (inst.instruction >= 0x10000)
+ {
+ as_warn (_("IT blocks containing 32-bit Thumb instructions are "
+ "deprecated in ARMv8"));
+ now_it.warn_deprecated = TRUE;
+ }
+ else
+ {
+ const struct depr_insn_mask *p = depr_it_insns;
+
+ while (p->mask != 0)
+ {
+ if ((inst.instruction & p->mask) == p->pattern)
+ {
+ as_warn (_("IT blocks containing 16-bit Thumb instructions "
+ "of the following class are deprecated in ARMv8: "
+ "%s"), p->description);
+ now_it.warn_deprecated = TRUE;
+ break;
+ }
+
+ ++p;
+ }
+ }
+
+ if (now_it.block_length > 1)
+ {
+ as_warn (_("IT blocks containing more than one conditional "
+ "instruction are deprecated in ARMv8"));
+ now_it.warn_deprecated = TRUE;
+ }
+ }
+
is_last = (now_it.mask == 0x10);
if (is_last)
{
/* It wasn't an instruction, but it might be a register alias of
the form alias .req reg, or a Neon .dn/.qn directive. */
if (! create_register_alias (str, p)
- && ! create_neon_reg_alias (str, p))
+ && ! create_neon_reg_alias (str, p))
as_bad (_("bad instruction `%s'"), str);
return;
inst.instruction = opcode->tvalue;
if (!parse_operands (p, opcode->operands, /*thumb=*/TRUE))
- {
- /* Prepare the it_insn_type for those encodings that don't set
- it. */
- it_fsm_pre_encode ();
+ {
+ /* Prepare the it_insn_type for those encodings that don't set
+ it. */
+ it_fsm_pre_encode ();
- opcode->tencode ();
+ opcode->tencode ();
- it_fsm_post_encode ();
- }
+ it_fsm_post_encode ();
+ }
if (!(inst.error || inst.relax))
{
}
/* Something has gone badly wrong if we try to relax a fixed size
- instruction. */
+ instruction. */
gas_assert (inst.size_req == 0 || !inst.relax);
ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used,
inst.instruction |= inst.cond << 28;
inst.size = INSN_SIZE;
if (!parse_operands (p, opcode->operands, /*thumb=*/FALSE))
- {
- it_fsm_pre_encode ();
- opcode->aencode ();
- it_fsm_post_encode ();
- }
+ {
+ it_fsm_pre_encode ();
+ opcode->aencode ();
+ it_fsm_post_encode ();
+ }
/* Arm mode bx is marked as both v4T and v5 because it's still required
- on a hypothetical non-thumb v5 core. */
+ on a hypothetical non-thumb v5 core. */
if (is_bx)
ARM_MERGE_FEATURE_SETS (arm_arch_used, arm_arch_used, arm_ext_v4t);
else
REGDEF(R10_fiq,512|(10<<16),RNB), REGDEF(r10_fiq,512|(10<<16),RNB),
REGDEF(R11_fiq,512|(11<<16),RNB), REGDEF(r11_fiq,512|(11<<16),RNB),
REGDEF(R12_fiq,512|(12<<16),RNB), REGDEF(r12_fiq,512|(12<<16),RNB),
- REGDEF(SP_fiq,512|(13<<16),RNB), REGDEF(SP_fiq,512|(13<<16),RNB),
+ REGDEF(SP_fiq,512|(13<<16),RNB), REGDEF(sp_fiq,512|(13<<16),RNB),
REGDEF(LR_fiq,512|(14<<16),RNB), REGDEF(lr_fiq,512|(14<<16),RNB),
REGDEF(SPSR_fiq,512|(14<<16)|SPSR_BIT,RNB), REGDEF(spsr_fiq,512|(14<<16)|SPSR_BIT,RNB),
SPLRBANK(12,MON,RNB), SPLRBANK(12,mon,RNB),
REGDEF(elr_hyp,768|(14<<16),RNB), REGDEF(ELR_hyp,768|(14<<16),RNB),
REGDEF(sp_hyp,768|(15<<16),RNB), REGDEF(SP_hyp,768|(15<<16),RNB),
- REGDEF(spsr_hyp,768|(14<<16)|SPSR_BIT,RNB),
+ REGDEF(spsr_hyp,768|(14<<16)|SPSR_BIT,RNB),
REGDEF(SPSR_hyp,768|(14<<16)|SPSR_BIT,RNB),
/* FPA registers. */
{ "tpoff", BFD_RELOC_ARM_TLS_LE32}, { "TPOFF", BFD_RELOC_ARM_TLS_LE32},
{ "got_prel", BFD_RELOC_ARM_GOT_PREL}, { "GOT_PREL", BFD_RELOC_ARM_GOT_PREL},
{ "tlsdesc", BFD_RELOC_ARM_TLS_GOTDESC},
- { "TLSDESC", BFD_RELOC_ARM_TLS_GOTDESC},
+ { "TLSDESC", BFD_RELOC_ARM_TLS_GOTDESC},
{ "tlscall", BFD_RELOC_ARM_TLS_CALL},
- { "TLSCALL", BFD_RELOC_ARM_TLS_CALL},
+ { "TLSCALL", BFD_RELOC_ARM_TLS_CALL},
{ "tlsdescseq", BFD_RELOC_ARM_TLS_DESCSEQ},
- { "TLSDESCSEQ", BFD_RELOC_ARM_TLS_DESCSEQ}
+ { "TLSDESCSEQ", BFD_RELOC_ARM_TLS_DESCSEQ}
};
#endif
{"al", 0xe}
};
+#define UL_BARRIER(L,U,CODE,FEAT) \
+ { L, CODE, ARM_FEATURE (FEAT, 0) }, \
+ { U, CODE, ARM_FEATURE (FEAT, 0) }
+
static struct asm_barrier_opt barrier_opt_names[] =
{
- { "sy", 0xf }, { "SY", 0xf },
- { "un", 0x7 }, { "UN", 0x7 },
- { "st", 0xe }, { "ST", 0xe },
- { "unst", 0x6 }, { "UNST", 0x6 },
- { "ish", 0xb }, { "ISH", 0xb },
- { "sh", 0xb }, { "SH", 0xb },
- { "ishst", 0xa }, { "ISHST", 0xa },
- { "shst", 0xa }, { "SHST", 0xa },
- { "nsh", 0x7 }, { "NSH", 0x7 },
- { "nshst", 0x6 }, { "NSHST", 0x6 },
- { "osh", 0x3 }, { "OSH", 0x3 },
- { "oshst", 0x2 }, { "OSHST", 0x2 }
+ UL_BARRIER ("sy", "SY", 0xf, ARM_EXT_BARRIER),
+ UL_BARRIER ("st", "ST", 0xe, ARM_EXT_BARRIER),
+ UL_BARRIER ("ld", "LD", 0xd, ARM_EXT_V8),
+ UL_BARRIER ("ish", "ISH", 0xb, ARM_EXT_BARRIER),
+ UL_BARRIER ("sh", "SH", 0xb, ARM_EXT_BARRIER),
+ UL_BARRIER ("ishst", "ISHST", 0xa, ARM_EXT_BARRIER),
+ UL_BARRIER ("shst", "SHST", 0xa, ARM_EXT_BARRIER),
+ UL_BARRIER ("ishld", "ISHLD", 0x9, ARM_EXT_V8),
+ UL_BARRIER ("un", "UN", 0x7, ARM_EXT_BARRIER),
+ UL_BARRIER ("nsh", "NSH", 0x7, ARM_EXT_BARRIER),
+ UL_BARRIER ("unst", "UNST", 0x6, ARM_EXT_BARRIER),
+ UL_BARRIER ("nshst", "NSHST", 0x6, ARM_EXT_BARRIER),
+ UL_BARRIER ("nshld", "NSHLD", 0x5, ARM_EXT_V8),
+ UL_BARRIER ("osh", "OSH", 0x3, ARM_EXT_BARRIER),
+ UL_BARRIER ("oshst", "OSHST", 0x2, ARM_EXT_BARRIER),
+ UL_BARRIER ("oshld", "OSHLD", 0x1, ARM_EXT_V8)
};
+#undef UL_BARRIER
+
/* Table of ARM-format instructions. */
/* Macros for gluing together operand strings. N.B. In all cases
#define tC3w(mnem, aop, top, nops, ops, ae, te) \
TxC3w (mnem, aop, T_MNEM##top, nops, ops, ae, te)
-/* Mnemonic with a conditional infix in an unusual place. Each and every variant has to
- appear in the condition table. */
-#define TxCM_(m1, m2, m3, op, top, nops, ops, ae, te) \
- { m1 #m2 m3, OPS##nops ops, sizeof (#m2) == 1 ? OT_odd_infix_unc : OT_odd_infix_0 + sizeof (m1) - 1, \
- 0x##op, top, ARM_VARIANT, THUMB_VARIANT, do_##ae, do_##te }
-
-#define TxCM(m1, m2, op, top, nops, ops, ae, te) \
- TxCM_ (m1, , m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, eq, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, ne, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, cs, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, hs, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, cc, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, ul, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, lo, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, mi, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, pl, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, vs, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, vc, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, hi, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, ls, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, ge, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, lt, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, gt, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, le, m2, op, top, nops, ops, ae, te), \
- TxCM_ (m1, al, m2, op, top, nops, ops, ae, te)
-
-#define TCM(m1,m2, aop, top, nops, ops, ae, te) \
- TxCM (m1,m2, aop, 0x##top, nops, ops, ae, te)
-#define tCM(m1,m2, aop, top, nops, ops, ae, te) \
- TxCM (m1,m2, aop, T_MNEM##top, nops, ops, ae, te)
-
/* Mnemonic that cannot be conditionalized. The ARM condition-code
field is still 0xE. Many of the Thumb variants can be executed
conditionally, so this is checked separately. */
{ mnem, OPS##nops ops, OT_unconditional, 0x##op, 0x##top, ARM_VARIANT, \
THUMB_VARIANT, do_##ae, do_##te }
+/* Same as TUE but the encoding function for ARM and Thumb modes is the same.
+ Used by mnemonics that have very minimal differences in the encoding for
+ ARM and Thumb variants and can be handled in a common function. */
+#define TUEc(mnem, op, top, nops, ops, en) \
+ { mnem, OPS##nops ops, OT_unconditional, 0x##op, 0x##top, ARM_VARIANT, \
+ THUMB_VARIANT, do_##en, do_##en }
+
/* Mnemonic that cannot be conditionalized, and bears 0xF in its ARM
condition code field. */
#define TUF(mnem, op, top, nops, ops, ae, te) \
static const struct asm_opcode insns[] =
{
-#define ARM_VARIANT &arm_ext_v1 /* Core ARM Instructions. */
-#define THUMB_VARIANT &arm_ext_v4t
+#define ARM_VARIANT & arm_ext_v1 /* Core ARM Instructions. */
+#define THUMB_VARIANT & arm_ext_v4t
tCE("and", 0000000, _and, 3, (RR, oRR, SH), arit, t_arit3c),
tC3("ands", 0100000, _ands, 3, (RR, oRR, SH), arit, t_arit3c),
tCE("eor", 0200000, _eor, 3, (RR, oRR, SH), arit, t_arit3c),
tCE("adr", 28f0000, _adr, 2, (RR, EXP), adr, t_adr),
C3(adrl, 28f0000, 2, (RR, EXP), adrl),
tCE("nop", 1a00000, _nop, 1, (oI255c), nop, t_nop),
+ tCE("udf", 7f000f0, _udf, 1, (oIffffb), bkpt, t_udf),
/* Thumb-compatibility pseudo ops. */
tCE("lsl", 1a00000, _lsl, 3, (RR, oRR, SH), shift, t_shift),
tC3("strh", 00000b0, _strh, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst),
tC3("ldrsh", 01000f0, _ldrsh, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst),
tC3("ldrsb", 01000d0, _ldrsb, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst),
- tCM("ld","sh", 01000f0, _ldrsh, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst),
- tCM("ld","sb", 01000d0, _ldrsb, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst),
+ tC3("ldsh", 01000f0, _ldrsh, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst),
+ tC3("ldsb", 01000d0, _ldrsb, 2, (RRnpc_npcsp, ADDRGLDRS), ldstv4, t_ldst),
#undef ARM_VARIANT
#define ARM_VARIANT & arm_ext_v4t_5
TUF("mrc2", e100010, fe100010, 6, (RCP, I7b, RR, RCN, RCN, oI7b), co_reg, co_reg),
#undef ARM_VARIANT
-#define ARM_VARIANT & arm_ext_v5exp /* ARM Architecture 5TExP. */
-#undef THUMB_VARIANT
-#define THUMB_VARIANT &arm_ext_v5exp
+#define ARM_VARIANT & arm_ext_v5exp /* ARM Architecture 5TExP. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v5exp
TCE("smlabb", 1000080, fb100000, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
TCE("smlatb", 10000a0, fb100020, 4, (RRnpc, RRnpc, RRnpc, RRnpc), smla, t_mla),
TCE("qdsub", 1600050, fa80f0b0, 3, (RRnpc, RRnpc, RRnpc), rd_rm_rn, t_simd2),
#undef ARM_VARIANT
-#define ARM_VARIANT & arm_ext_v5e /* ARM Architecture 5TE. */
-#undef THUMB_VARIANT
-#define THUMB_VARIANT &arm_ext_v6t2
+#define ARM_VARIANT & arm_ext_v5e /* ARM Architecture 5TE. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6t2
TUF("pld", 450f000, f810f000, 1, (ADDR), pld, t_pld),
TC3("ldrd", 00000d0, e8500000, 3, (RRnpc_npcsp, oRRnpc_npcsp, ADDRGLDRS),
#undef THUMB_VARIANT
#define THUMB_VARIANT & arm_ext_v6_notm
TUF("rfeia", 8900a00, e990c000, 1, (RRw), rfe, rfe),
+ TUF("rfe", 8900a00, e990c000, 1, (RRw), rfe, rfe),
UF(rfeib, 9900a00, 1, (RRw), rfe),
UF(rfeda, 8100a00, 1, (RRw), rfe),
TUF("rfedb", 9100a00, e810c000, 1, (RRw), rfe, rfe),
TUF("rfefd", 8900a00, e990c000, 1, (RRw), rfe, rfe),
- UF(rfefa, 9900a00, 1, (RRw), rfe),
- UF(rfeea, 8100a00, 1, (RRw), rfe),
- TUF("rfeed", 9100a00, e810c000, 1, (RRw), rfe, rfe),
+ UF(rfefa, 8100a00, 1, (RRw), rfe),
+ TUF("rfeea", 9100a00, e810c000, 1, (RRw), rfe, rfe),
+ UF(rfeed, 9900a00, 1, (RRw), rfe),
TUF("srsia", 8c00500, e980c000, 2, (oRRw, I31w), srs, srs),
+ TUF("srs", 8c00500, e980c000, 2, (oRRw, I31w), srs, srs),
+ TUF("srsea", 8c00500, e980c000, 2, (oRRw, I31w), srs, srs),
UF(srsib, 9c00500, 2, (oRRw, I31w), srs),
+ UF(srsfa, 9c00500, 2, (oRRw, I31w), srs),
UF(srsda, 8400500, 2, (oRRw, I31w), srs),
+ UF(srsed, 8400500, 2, (oRRw, I31w), srs),
TUF("srsdb", 9400500, e800c000, 2, (oRRw, I31w), srs, srs),
+ TUF("srsfd", 9400500, e800c000, 2, (oRRw, I31w), srs, srs),
/* ARM V6 not included in V7M (eg. integer SIMD). */
#undef THUMB_VARIANT
TCE("qadd8", 6200f90, fa80f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("qasx", 6200f30, faa0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
/* Old name for QASX. */
- TCE("qaddsubx", 6200f30, faa0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("qaddsubx",6200f30, faa0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("qsax", 6200f50, fae0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
/* Old name for QSAX. */
- TCE("qsubaddx", 6200f50, fae0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("qsubaddx",6200f50, fae0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("qsub16", 6200f70, fad0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("qsub8", 6200ff0, fac0f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("sadd16", 6100f10, fa90f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("sadd8", 6100f90, fa80f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("sasx", 6100f30, faa0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
/* Old name for SASX. */
- TCE("saddsubx", 6100f30, faa0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("saddsubx",6100f30, faa0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("shadd16", 6300f10, fa90f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("shadd8", 6300f90, fa80f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
- TCE("shasx", 6300f30, faa0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("shasx", 6300f30, faa0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
/* Old name for SHASX. */
TCE("shaddsubx", 6300f30, faa0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
- TCE("shsax", 6300f50, fae0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("shsax", 6300f50, fae0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
/* Old name for SHSAX. */
TCE("shsubaddx", 6300f50, fae0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("shsub16", 6300f70, fad0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("shsub8", 6300ff0, fac0f020, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("ssax", 6100f50, fae0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
/* Old name for SSAX. */
- TCE("ssubaddx", 6100f50, fae0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("ssubaddx",6100f50, fae0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("ssub16", 6100f70, fad0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("ssub8", 6100ff0, fac0f000, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("uadd16", 6500f10, fa90f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("uadd8", 6500f90, fa80f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("uasx", 6500f30, faa0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
/* Old name for UASX. */
- TCE("uaddsubx", 6500f30, faa0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uaddsubx",6500f30, faa0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("uhadd16", 6700f10, fa90f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("uhadd8", 6700f90, fa80f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
- TCE("uhasx", 6700f30, faa0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uhasx", 6700f30, faa0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
/* Old name for UHASX. */
TCE("uhaddsubx", 6700f30, faa0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("uhsax", 6700f50, fae0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("uhsub8", 6700ff0, fac0f060, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("uqadd16", 6600f10, fa90f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("uqadd8", 6600f90, fa80f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
- TCE("uqasx", 6600f30, faa0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("uqasx", 6600f30, faa0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
/* Old name for UQASX. */
TCE("uqaddsubx", 6600f30, faa0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("uqsax", 6600f50, fae0f050, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("usub16", 6500f70, fad0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("usax", 6500f50, fae0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
/* Old name for USAX. */
- TCE("usubaddx", 6500f50, fae0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
+ TCE("usubaddx",6500f50, fae0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("usub8", 6500ff0, fac0f040, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
TCE("sxtah", 6b00070, fa00f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
TCE("sxtab16", 6800070, fa20f080, 4, (RRnpc, RRnpc, RRnpc, oROR), sxtah, t_sxtah),
TCE("ldrexh", 1f00f9f, e8d00f5f, 2, (RRnpc_npcsp, RRnpcb),
rd_rn, rd_rn),
TCE("strexb", 1c00f90, e8c00f40, 3, (RRnpc_npcsp, RRnpc_npcsp, ADDR),
- strex, rm_rd_rn),
+ strex, t_strexbh),
TCE("strexh", 1e00f90, e8c00f50, 3, (RRnpc_npcsp, RRnpc_npcsp, ADDR),
- strex, rm_rd_rn),
+ strex, t_strexbh),
TUF("clrex", 57ff01f, f3bf8f2f, 0, (), noargs, noargs),
#undef ARM_VARIANT
#define ARM_VARIANT & arm_ext_sec
-#undef THUMB_VARIANT
+#undef THUMB_VARIANT
#define THUMB_VARIANT & arm_ext_sec
TCE("smc", 1600070, f7f08000, 1, (EXPi), smc, t_smc),
TCE("eret", 160006e, f3de8f00, 0, (), noargs, noargs),
#undef ARM_VARIANT
-#define ARM_VARIANT & arm_ext_v6t2
+#define ARM_VARIANT & arm_ext_v6t2
#undef THUMB_VARIANT
#define THUMB_VARIANT & arm_ext_v6t2
TC3("strht", 02000b0, f8200e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt),
/* Thumb-only instructions. */
-#undef ARM_VARIANT
+#undef ARM_VARIANT
#define ARM_VARIANT NULL
TUE("cbnz", 0, b900, 2, (RR, EXP), 0, t_cbz),
TUE("cbz", 0, b100, 2, (RR, EXP), 0, t_cbz),
#undef THUMB_VARIANT
#define THUMB_VARIANT & arm_ext_barrier
- TUF("dmb", 57ff050, f3bf8f50, 1, (oBARRIER_I15), barrier, t_barrier),
- TUF("dsb", 57ff040, f3bf8f40, 1, (oBARRIER_I15), barrier, t_barrier),
- TUF("isb", 57ff060, f3bf8f60, 1, (oBARRIER_I15), barrier, t_barrier),
+ TUF("dmb", 57ff050, f3bf8f50, 1, (oBARRIER_I15), barrier, barrier),
+ TUF("dsb", 57ff040, f3bf8f40, 1, (oBARRIER_I15), barrier, barrier),
+ TUF("isb", 57ff060, f3bf8f60, 1, (oBARRIER_I15), barrier, barrier),
/* ARM V7 instructions. */
#undef ARM_VARIANT
TUF("pli", 450f000, f910f000, 1, (ADDR), pli, t_pld),
TCE("dbg", 320f0f0, f3af80f0, 1, (I15), dbg, t_dbg),
-#undef ARM_VARIANT
+#undef ARM_VARIANT
#define ARM_VARIANT & arm_ext_mp
-#undef THUMB_VARIANT
+#undef THUMB_VARIANT
#define THUMB_VARIANT & arm_ext_mp
TUF("pldw", 410f000, f830f000, 1, (ADDR), pld, t_pld),
+ /* AArchv8 instructions. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v8
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v8
+
+ tCE("sevl", 320f005, _sevl, 0, (), noargs, t_hint),
+ TUE("hlt", 1000070, ba80, 1, (oIffffb), bkpt, t_hlt),
+ TCE("ldaex", 1900e9f, e8d00fef, 2, (RRnpc, RRnpcb), rd_rn, rd_rn),
+ TCE("ldaexd", 1b00e9f, e8d000ff, 3, (RRnpc, oRRnpc, RRnpcb),
+ ldrexd, t_ldrexd),
+ TCE("ldaexb", 1d00e9f, e8d00fcf, 2, (RRnpc,RRnpcb), rd_rn, rd_rn),
+ TCE("ldaexh", 1f00e9f, e8d00fdf, 2, (RRnpc, RRnpcb), rd_rn, rd_rn),
+ TCE("stlex", 1800e90, e8c00fe0, 3, (RRnpc, RRnpc, RRnpcb),
+ stlex, t_stlex),
+ TCE("stlexd", 1a00e90, e8c000f0, 4, (RRnpc, RRnpc, oRRnpc, RRnpcb),
+ strexd, t_strexd),
+ TCE("stlexb", 1c00e90, e8c00fc0, 3, (RRnpc, RRnpc, RRnpcb),
+ stlex, t_stlex),
+ TCE("stlexh", 1e00e90, e8c00fd0, 3, (RRnpc, RRnpc, RRnpcb),
+ stlex, t_stlex),
+ TCE("lda", 1900c9f, e8d00faf, 2, (RRnpc, RRnpcb), rd_rn, rd_rn),
+ TCE("ldab", 1d00c9f, e8d00f8f, 2, (RRnpc, RRnpcb), rd_rn, rd_rn),
+ TCE("ldah", 1f00c9f, e8d00f9f, 2, (RRnpc, RRnpcb), rd_rn, rd_rn),
+ TCE("stl", 180fc90, e8c00faf, 2, (RRnpc, RRnpcb), rm_rn, rd_rn),
+ TCE("stlb", 1c0fc90, e8c00f8f, 2, (RRnpc, RRnpcb), rm_rn, rd_rn),
+ TCE("stlh", 1e0fc90, e8c00f9f, 2, (RRnpc, RRnpcb), rm_rn, rd_rn),
+
+ /* ARMv8 T32 only. */
+#undef ARM_VARIANT
+#define ARM_VARIANT NULL
+ TUF("dcps1", 0, f78f8001, 0, (), noargs, noargs),
+ TUF("dcps2", 0, f78f8002, 0, (), noargs, noargs),
+ TUF("dcps3", 0, f78f8003, 0, (), noargs, noargs),
+
+ /* FP for ARMv8. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_vfp_ext_armv8
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & fpu_vfp_ext_armv8
+
+ nUF(vseleq, _vseleq, 3, (RVSD, RVSD, RVSD), vsel),
+ nUF(vselvs, _vselvs, 3, (RVSD, RVSD, RVSD), vsel),
+ nUF(vselge, _vselge, 3, (RVSD, RVSD, RVSD), vsel),
+ nUF(vselgt, _vselgt, 3, (RVSD, RVSD, RVSD), vsel),
+ nUF(vmaxnm, _vmaxnm, 3, (RNSDQ, oRNSDQ, RNSDQ), vmaxnm),
+ nUF(vminnm, _vminnm, 3, (RNSDQ, oRNSDQ, RNSDQ), vmaxnm),
+ nUF(vcvta, _vcvta, 2, (RNSDQ, oRNSDQ), neon_cvta),
+ nUF(vcvtn, _vcvta, 2, (RNSDQ, oRNSDQ), neon_cvtn),
+ nUF(vcvtp, _vcvta, 2, (RNSDQ, oRNSDQ), neon_cvtp),
+ nUF(vcvtm, _vcvta, 2, (RNSDQ, oRNSDQ), neon_cvtm),
+ nCE(vrintr, _vrintr, 2, (RNSDQ, oRNSDQ), vrintr),
+ nCE(vrintz, _vrintr, 2, (RNSDQ, oRNSDQ), vrintz),
+ nCE(vrintx, _vrintr, 2, (RNSDQ, oRNSDQ), vrintx),
+ nUF(vrinta, _vrinta, 2, (RNSDQ, oRNSDQ), vrinta),
+ nUF(vrintn, _vrinta, 2, (RNSDQ, oRNSDQ), vrintn),
+ nUF(vrintp, _vrinta, 2, (RNSDQ, oRNSDQ), vrintp),
+ nUF(vrintm, _vrinta, 2, (RNSDQ, oRNSDQ), vrintm),
+
+ /* Crypto v1 extensions. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_crypto_ext_armv8
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & fpu_crypto_ext_armv8
+
+ nUF(aese, _aes, 2, (RNQ, RNQ), aese),
+ nUF(aesd, _aes, 2, (RNQ, RNQ), aesd),
+ nUF(aesmc, _aes, 2, (RNQ, RNQ), aesmc),
+ nUF(aesimc, _aes, 2, (RNQ, RNQ), aesimc),
+ nUF(sha1c, _sha3op, 3, (RNQ, RNQ, RNQ), sha1c),
+ nUF(sha1p, _sha3op, 3, (RNQ, RNQ, RNQ), sha1p),
+ nUF(sha1m, _sha3op, 3, (RNQ, RNQ, RNQ), sha1m),
+ nUF(sha1su0, _sha3op, 3, (RNQ, RNQ, RNQ), sha1su0),
+ nUF(sha256h, _sha3op, 3, (RNQ, RNQ, RNQ), sha256h),
+ nUF(sha256h2, _sha3op, 3, (RNQ, RNQ, RNQ), sha256h2),
+ nUF(sha256su1, _sha3op, 3, (RNQ, RNQ, RNQ), sha256su1),
+ nUF(sha1h, _sha1h, 2, (RNQ, RNQ), sha1h),
+ nUF(sha1su1, _sha2op, 2, (RNQ, RNQ), sha1su1),
+ nUF(sha256su0, _sha2op, 2, (RNQ, RNQ), sha256su0),
+
+#undef ARM_VARIANT
+#define ARM_VARIANT & crc_ext_armv8
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & crc_ext_armv8
+ TUEc("crc32b", 1000040, fac0f080, 3, (RR, oRR, RR), crc32b),
+ TUEc("crc32h", 1200040, fac0f090, 3, (RR, oRR, RR), crc32h),
+ TUEc("crc32w", 1400040, fac0f0a0, 3, (RR, oRR, RR), crc32w),
+ TUEc("crc32cb",1000240, fad0f080, 3, (RR, oRR, RR), crc32cb),
+ TUEc("crc32ch",1200240, fad0f090, 3, (RR, oRR, RR), crc32ch),
+ TUEc("crc32cw",1400240, fad0f0a0, 3, (RR, oRR, RR), crc32cw),
+
#undef ARM_VARIANT
#define ARM_VARIANT & fpu_fpa_ext_v1 /* Core FPA instruction set (V1). */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT NULL
cCE("wfs", e200110, 1, (RR), rd),
cCE("rfs", e300110, 1, (RR), rd),
cCE("fmrs", e100a10, 2, (RR, RVS), vfp_reg_from_sp),
cCE("fmsr", e000a10, 2, (RVS, RR), vfp_sp_from_reg),
cCE("fmstat", ef1fa10, 0, (), noargs),
- cCE("vmrs", ef10a10, 2, (APSR_RR, RVC), vmrs),
- cCE("vmsr", ee10a10, 2, (RVC, RR), vmsr),
+ cCE("vmrs", ef00a10, 2, (APSR_RR, RVC), vmrs),
+ cCE("vmsr", ee00a10, 2, (RVC, RR), vmsr),
cCE("fsitos", eb80ac0, 2, (RVS, RVS), vfp_sp_monadic),
cCE("fuitos", eb80a40, 2, (RVS, RVS), vfp_sp_monadic),
cCE("ftosis", ebd0a40, 2, (RVS, RVS), vfp_sp_monadic),
NCE(vldr, d100b00, 2, (RVSD, ADDRGLDC), neon_ldr_str),
NCE(vstr, d000b00, 2, (RVSD, ADDRGLDC), neon_ldr_str),
- nCEF(vcvt, _vcvt, 3, (RNSDQ, RNSDQ, oI32b), neon_cvt),
+ nCEF(vcvt, _vcvt, 3, (RNSDQ, RNSDQ, oI32z), neon_cvt),
nCEF(vcvtr, _vcvt, 2, (RNSDQ, RNSDQ), neon_cvtr),
- nCEF(vcvtb, _vcvt, 2, (RVS, RVS), neon_cvtb),
- nCEF(vcvtt, _vcvt, 2, (RVS, RVS), neon_cvtt),
+ NCEF(vcvtb, eb20a40, 2, (RVSD, RVSD), neon_cvtb),
+ NCEF(vcvtt, eb20a40, 2, (RVSD, RVSD), neon_cvtt),
/* NOTE: All VMOV encoding is special-cased! */
nUF(vst4, _vst4, 2, (NSTRLST, ADDR), neon_ldx_stx),
#undef THUMB_VARIANT
-#define THUMB_VARIANT &fpu_vfp_ext_v3xd
-#undef ARM_VARIANT
-#define ARM_VARIANT &fpu_vfp_ext_v3xd
+#define THUMB_VARIANT & fpu_vfp_ext_v3xd
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_vfp_ext_v3xd
cCE("fconsts", eb00a00, 2, (RVS, I255), vfp_sp_const),
cCE("fshtos", eba0a40, 2, (RVS, I16z), vfp_sp_conv_16),
cCE("fsltos", eba0ac0, 2, (RVS, I32), vfp_sp_conv_32),
cCE("ftouhs", ebf0a40, 2, (RVS, I16z), vfp_sp_conv_16),
cCE("ftouls", ebf0ac0, 2, (RVS, I32), vfp_sp_conv_32),
-#undef THUMB_VARIANT
+#undef THUMB_VARIANT
#define THUMB_VARIANT & fpu_vfp_ext_v3
#undef ARM_VARIANT
#define ARM_VARIANT & fpu_vfp_ext_v3
cCE("ftouhd", ebf0b40, 2, (RVD, I16z), vfp_dp_conv_16),
cCE("ftould", ebf0bc0, 2, (RVD, I32), vfp_dp_conv_32),
-#undef ARM_VARIANT
-#define ARM_VARIANT &fpu_vfp_ext_fma
-#undef THUMB_VARIANT
-#define THUMB_VARIANT &fpu_vfp_ext_fma
+#undef ARM_VARIANT
+#define ARM_VARIANT & fpu_vfp_ext_fma
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & fpu_vfp_ext_fma
/* Mnemonics shared by Neon and VFP. These are included in the
VFP FMA variant; NEON and VFP FMA always includes the NEON
FMA instructions. */
cCE("textrcb", e130170, 2, (RR, I7), iwmmxt_textrc),
cCE("textrch", e530170, 2, (RR, I7), iwmmxt_textrc),
cCE("textrcw", e930170, 2, (RR, I7), iwmmxt_textrc),
- cCE("textrmub", e100070, 3, (RR, RIWR, I7), iwmmxt_textrm),
- cCE("textrmuh", e500070, 3, (RR, RIWR, I7), iwmmxt_textrm),
- cCE("textrmuw", e900070, 3, (RR, RIWR, I7), iwmmxt_textrm),
- cCE("textrmsb", e100078, 3, (RR, RIWR, I7), iwmmxt_textrm),
- cCE("textrmsh", e500078, 3, (RR, RIWR, I7), iwmmxt_textrm),
- cCE("textrmsw", e900078, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ cCE("textrmub",e100070, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ cCE("textrmuh",e500070, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ cCE("textrmuw",e900070, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ cCE("textrmsb",e100078, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ cCE("textrmsh",e500078, 3, (RR, RIWR, I7), iwmmxt_textrm),
+ cCE("textrmsw",e900078, 3, (RR, RIWR, I7), iwmmxt_textrm),
cCE("tinsrb", e600010, 3, (RIWR, RR, I7), iwmmxt_tinsr),
cCE("tinsrh", e600050, 3, (RIWR, RR, I7), iwmmxt_tinsr),
cCE("tinsrw", e600090, 3, (RIWR, RR, I7), iwmmxt_tinsr),
cCE("tmiabt", e2d0010, 3, (RIWR, RR, RR), iwmmxt_tmia),
cCE("tmiatb", e2e0010, 3, (RIWR, RR, RR), iwmmxt_tmia),
cCE("tmiatt", e2f0010, 3, (RIWR, RR, RR), iwmmxt_tmia),
- cCE("tmovmskb", e100030, 2, (RR, RIWR), rd_rn),
- cCE("tmovmskh", e500030, 2, (RR, RIWR), rd_rn),
- cCE("tmovmskw", e900030, 2, (RR, RIWR), rd_rn),
+ cCE("tmovmskb",e100030, 2, (RR, RIWR), rd_rn),
+ cCE("tmovmskh",e500030, 2, (RR, RIWR), rd_rn),
+ cCE("tmovmskw",e900030, 2, (RR, RIWR), rd_rn),
cCE("tmrc", e100110, 2, (RR, RIWC_RIWG), rd_rn),
cCE("tmrrc", c500000, 3, (RR, RR, RIWR), rd_rn_rm),
cCE("torcb", e13f150, 1, (RR), iwmmxt_tandorc),
cCE("waddw", e800180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE("waddwus", e900180, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE("waligni", e000020, 4, (RIWR, RIWR, RIWR, I7), iwmmxt_waligni),
- cCE("walignr0", e800020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("walignr1", e900020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("walignr2", ea00020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("walignr3", eb00020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("walignr0",e800020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("walignr1",e900020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("walignr2",ea00020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("walignr3",eb00020, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE("wand", e200000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE("wandn", e300000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE("wavg2b", e800000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE("wcmpeqb", e000060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE("wcmpeqh", e400060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE("wcmpeqw", e800060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("wcmpgtub", e100060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("wcmpgtuh", e500060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("wcmpgtuw", e900060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("wcmpgtsb", e300060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("wcmpgtsh", e700060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("wcmpgtsw", eb00060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpgtub",e100060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpgtuh",e500060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpgtuw",e900060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpgtsb",e300060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpgtsh",e700060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wcmpgtsw",eb00060, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE("wldrb", c100000, 2, (RIWR, ADDR), iwmmxt_wldstbh),
cCE("wldrh", c500000, 2, (RIWR, ADDR), iwmmxt_wldstbh),
cCE("wldrw", c100100, 2, (RIWR_RIWC, ADDR), iwmmxt_wldstw),
cCE("wmulum", e100100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE("wmulul", e000100, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE("wor", e000000, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("wpackhss", e700080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("wpackhus", e500080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("wpackwss", eb00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("wpackwus", e900080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("wpackdss", ef00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
- cCE("wpackdus", ed00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wpackhss",e700080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wpackhus",e500080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wpackwss",eb00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wpackwus",e900080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wpackdss",ef00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
+ cCE("wpackdus",ed00080, 3, (RIWR, RIWR, RIWR), rd_rn_rm),
cCE("wrorh", e700040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE("wrorhg", e700148, 3, (RIWR, RIWR, RIWG), rd_rn_rm),
cCE("wrorw", eb00040, 3, (RIWR, RIWR, RIWR_I32z),iwmmxt_wrwrwr_or_imm5),
cCE("cfmvrdl", e100410, 2, (RR, RMD), rd_rn),
cCE("cfmvdhr", e000430, 2, (RMD, RR), rn_rd),
cCE("cfmvrdh", e100430, 2, (RR, RMD), rd_rn),
- cCE("cfmv64lr", e000510, 2, (RMDX, RR), rn_rd),
- cCE("cfmvr64l", e100510, 2, (RR, RMDX), rd_rn),
- cCE("cfmv64hr", e000530, 2, (RMDX, RR), rn_rd),
- cCE("cfmvr64h", e100530, 2, (RR, RMDX), rd_rn),
- cCE("cfmval32", e200440, 2, (RMAX, RMFX), rd_rn),
- cCE("cfmv32al", e100440, 2, (RMFX, RMAX), rd_rn),
- cCE("cfmvam32", e200460, 2, (RMAX, RMFX), rd_rn),
- cCE("cfmv32am", e100460, 2, (RMFX, RMAX), rd_rn),
- cCE("cfmvah32", e200480, 2, (RMAX, RMFX), rd_rn),
- cCE("cfmv32ah", e100480, 2, (RMFX, RMAX), rd_rn),
+ cCE("cfmv64lr",e000510, 2, (RMDX, RR), rn_rd),
+ cCE("cfmvr64l",e100510, 2, (RR, RMDX), rd_rn),
+ cCE("cfmv64hr",e000530, 2, (RMDX, RR), rn_rd),
+ cCE("cfmvr64h",e100530, 2, (RR, RMDX), rd_rn),
+ cCE("cfmval32",e200440, 2, (RMAX, RMFX), rd_rn),
+ cCE("cfmv32al",e100440, 2, (RMFX, RMAX), rd_rn),
+ cCE("cfmvam32",e200460, 2, (RMAX, RMFX), rd_rn),
+ cCE("cfmv32am",e100460, 2, (RMFX, RMAX), rd_rn),
+ cCE("cfmvah32",e200480, 2, (RMAX, RMFX), rd_rn),
+ cCE("cfmv32ah",e100480, 2, (RMFX, RMAX), rd_rn),
cCE("cfmva32", e2004a0, 2, (RMAX, RMFX), rd_rn),
cCE("cfmv32a", e1004a0, 2, (RMFX, RMAX), rd_rn),
cCE("cfmva64", e2004c0, 2, (RMAX, RMDX), rd_rn),
cCE("cfmv64a", e1004c0, 2, (RMDX, RMAX), rd_rn),
- cCE("cfmvsc32", e2004e0, 2, (RMDS, RMDX), mav_dspsc),
- cCE("cfmv32sc", e1004e0, 2, (RMDX, RMDS), rd),
+ cCE("cfmvsc32",e2004e0, 2, (RMDS, RMDX), mav_dspsc),
+ cCE("cfmv32sc",e1004e0, 2, (RMDX, RMDS), rd),
cCE("cfcpys", e000400, 2, (RMF, RMF), rd_rn),
cCE("cfcpyd", e000420, 2, (RMD, RMD), rd_rn),
cCE("cfcvtsd", e000460, 2, (RMD, RMF), rd_rn),
cCE("cfcvtds", e000440, 2, (RMF, RMD), rd_rn),
- cCE("cfcvt32s", e000480, 2, (RMF, RMFX), rd_rn),
- cCE("cfcvt32d", e0004a0, 2, (RMD, RMFX), rd_rn),
- cCE("cfcvt64s", e0004c0, 2, (RMF, RMDX), rd_rn),
- cCE("cfcvt64d", e0004e0, 2, (RMD, RMDX), rd_rn),
- cCE("cfcvts32", e100580, 2, (RMFX, RMF), rd_rn),
- cCE("cfcvtd32", e1005a0, 2, (RMFX, RMD), rd_rn),
+ cCE("cfcvt32s",e000480, 2, (RMF, RMFX), rd_rn),
+ cCE("cfcvt32d",e0004a0, 2, (RMD, RMFX), rd_rn),
+ cCE("cfcvt64s",e0004c0, 2, (RMF, RMDX), rd_rn),
+ cCE("cfcvt64d",e0004e0, 2, (RMD, RMDX), rd_rn),
+ cCE("cfcvts32",e100580, 2, (RMFX, RMF), rd_rn),
+ cCE("cfcvtd32",e1005a0, 2, (RMFX, RMD), rd_rn),
cCE("cftruncs32",e1005c0, 2, (RMFX, RMF), rd_rn),
cCE("cftruncd32",e1005e0, 2, (RMFX, RMD), rd_rn),
- cCE("cfrshl32", e000550, 3, (RMFX, RMFX, RR), mav_triple),
- cCE("cfrshl64", e000570, 3, (RMDX, RMDX, RR), mav_triple),
+ cCE("cfrshl32",e000550, 3, (RMFX, RMFX, RR), mav_triple),
+ cCE("cfrshl64",e000570, 3, (RMDX, RMDX, RR), mav_triple),
cCE("cfsh32", e000500, 3, (RMFX, RMFX, I63s), mav_shift),
cCE("cfsh64", e200500, 3, (RMDX, RMDX, I63s), mav_shift),
cCE("cfcmps", e100490, 3, (RR, RMF, RMF), rd_rn_rm),
cCE("cfmul64", e100520, 3, (RMDX, RMDX, RMDX), rd_rn_rm),
cCE("cfmac32", e100540, 3, (RMFX, RMFX, RMFX), rd_rn_rm),
cCE("cfmsc32", e100560, 3, (RMFX, RMFX, RMFX), rd_rn_rm),
- cCE("cfmadd32", e000600, 4, (RMAX, RMFX, RMFX, RMFX), mav_quad),
- cCE("cfmsub32", e100600, 4, (RMAX, RMFX, RMFX, RMFX), mav_quad),
+ cCE("cfmadd32",e000600, 4, (RMAX, RMFX, RMFX, RMFX), mav_quad),
+ cCE("cfmsub32",e100600, 4, (RMAX, RMFX, RMFX, RMFX), mav_quad),
cCE("cfmadda32", e200600, 4, (RMAX, RMAX, RMFX, RMFX), mav_quad),
cCE("cfmsuba32", e300600, 4, (RMAX, RMAX, RMFX, RMFX), mav_quad),
};
#undef ARM_VARIANT
#undef THUMB_VARIANT
#undef TCE
-#undef TCM
#undef TUE
#undef TUF
#undef TCC
/* MD interface: Sections. */
+/* Calculate the maximum variable size (i.e., excluding fr_fix)
+ that an rs_machine_dependent frag may reach. */
+
+unsigned int
+arm_frag_max_var (fragS *fragp)
+{
+ /* We only use rs_machine_dependent for variable-size Thumb instructions,
+ which are either THUMB_SIZE (2) or INSN_SIZE (4).
+
+ Note that we generate relaxable instructions even for cases that don't
+ really need it, like an immediate that's a trivial constant. So we're
+ overestimating the instruction size for some of those cases. Rather
+ than putting more intelligence here, it would probably be better to
+ avoid generating a relaxation frag in the first place when it can be
+ determined up front that a short instruction will suffice. */
+
+ gas_assert (fragp->fr_type == rs_machine_dependent);
+ return INSN_SIZE;
+}
+
/* Estimate the size of a frag before relaxing. Assume everything fits in
2 bytes. */
return relax_immediate (fragp, 3, 0);
}
+/* Return TRUE iff the definition of symbol S could be pre-empted
+ (overridden) at link or load time. */
+static bfd_boolean
+symbol_preemptible (symbolS *s)
+{
+ /* Weak symbols can always be pre-empted. */
+ if (S_IS_WEAK (s))
+ return TRUE;
+
+ /* Non-global symbols cannot be pre-empted. */
+ if (! S_IS_EXTERNAL (s))
+ return FALSE;
+
+#ifdef OBJ_ELF
+ /* In ELF, a global symbol can be marked protected, or private. In that
+ case it can't be pre-empted (other definitions in the same link unit
+ would violate the ODR). */
+ if (ELF_ST_VISIBILITY (S_GET_OTHER (s)) > STV_DEFAULT)
+ return FALSE;
+#endif
+
+ /* Other global symbols might be pre-empted. */
+ return TRUE;
+}
/* Return the size of a relaxable branch instruction. BITS is the
size of the offset field in the narrow instruction. */
return 4;
#ifdef OBJ_ELF
+ /* A branch to a function in ARM state will require interworking. */
if (S_IS_DEFINED (fragp->fr_symbol)
&& ARM_IS_FUNC (fragp->fr_symbol))
return 4;
+#endif
- /* PR 12532. Global symbols with default visibility might
- be preempted, so do not relax relocations to them. */
- if ((ELF_ST_VISIBILITY (S_GET_OTHER (fragp->fr_symbol)) == STV_DEFAULT)
- && (! S_IS_LOCAL (fragp->fr_symbol)))
+ if (symbol_preemptible (fragp->fr_symbol))
return 4;
-#endif
val = relaxed_symbol_addr (fragp, stretch);
addr = fragp->fr_address + fragp->fr_fix + 4;
{
char err_msg[128];
- sprintf (err_msg,
- _("alignments greater than %d bytes not supported in .text sections."),
- MAX_MEM_FOR_RS_ALIGN_CODE + 1);
+ sprintf (err_msg,
+ _("alignments greater than %d bytes not supported in .text sections."),
+ MAX_MEM_FOR_RS_ALIGN_CODE + 1);
as_fatal ("%s", err_msg);
}
unwind.opcode_alloc += ARM_OPCODE_CHUNK_SIZE;
if (unwind.opcodes)
unwind.opcodes = (unsigned char *) xrealloc (unwind.opcodes,
- unwind.opcode_alloc);
+ unwind.opcode_alloc);
else
unwind.opcodes = (unsigned char *) xmalloc (unwind.opcode_alloc);
}
size = unwind.opcode_count - 2;
}
else
- /* An extra byte is required for the opcode count. */
- size = unwind.opcode_count + 1;
+ {
+ gas_assert (unwind.personality_index == -1);
+
+ /* An extra byte is required for the opcode count. */
+ size = unwind.opcode_count + 1;
+ }
size = (size + 3) >> 2;
if (size > 0xff)
/* Allocate the table entry. */
ptr = frag_more ((size << 2) + 4);
+ /* PR 13449: Zero the table entries in case some of them are not used. */
+ memset (ptr, 0, (size << 2) + 4);
where = frag_now_fix () - ((size << 2) + 4);
switch (unwind.personality_index)
ptr += 4;
/* Set the first byte to the number of additional words. */
- data = size - 1;
+ data = size > 0 ? size - 1 : 0;
n = 3;
break;
&& (S_GET_SEGMENT (fixP->fx_addsy) == seg)
&& !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
&& ARM_IS_FUNC (fixP->fx_addsy)
- && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
- base = fixP->fx_where + fixP->fx_frag->fr_address;
+ && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
+ base = fixP->fx_where + fixP->fx_frag->fr_address;
return base + 4;
/* BLX is like branches above, but forces the low two bits of PC to
if (fixP->fx_addsy
&& (S_GET_SEGMENT (fixP->fx_addsy) == seg)
&& !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
- && THUMB_IS_FUNC (fixP->fx_addsy)
- && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
- base = fixP->fx_where + fixP->fx_frag->fr_address;
+ && THUMB_IS_FUNC (fixP->fx_addsy)
+ && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
+ base = fixP->fx_where + fixP->fx_frag->fr_address;
return (base + 4) & ~3;
/* ARM mode branches are offset by +8. However, the Windows CE
if (fixP->fx_addsy
&& (S_GET_SEGMENT (fixP->fx_addsy) == seg)
&& !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
- && ARM_IS_FUNC (fixP->fx_addsy)
- && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
- base = fixP->fx_where + fixP->fx_frag->fr_address;
+ && ARM_IS_FUNC (fixP->fx_addsy)
+ && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
+ base = fixP->fx_where + fixP->fx_frag->fr_address;
return base + 8;
case BFD_RELOC_ARM_PCREL_CALL:
if (fixP->fx_addsy
&& (S_GET_SEGMENT (fixP->fx_addsy) == seg)
&& !S_FORCE_RELOC (fixP->fx_addsy, TRUE)
- && THUMB_IS_FUNC (fixP->fx_addsy)
- && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
- base = fixP->fx_where + fixP->fx_frag->fr_address;
+ && THUMB_IS_FUNC (fixP->fx_addsy)
+ && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t))
+ base = fixP->fx_where + fixP->fx_frag->fr_address;
return base + 8;
case BFD_RELOC_ARM_PCREL_BRANCH:
case BFD_RELOC_ARM_PLT32:
#ifdef TE_WINCE
/* When handling fixups immediately, because we have already
- discovered the value of a symbol, or the address of the frag involved
+ discovered the value of a symbol, or the address of the frag involved
we must account for the offset by +8, as the OS loader will never see the reloc.
- see fixup_segment() in write.c
- The S_IS_EXTERNAL test handles the case of global symbols.
- Those need the calculated base, not just the pipe compensation the linker will need. */
+ see fixup_segment() in write.c
+ The S_IS_EXTERNAL test handles the case of global symbols.
+ Those need the calculated base, not just the pipe compensation the linker will need. */
if (fixP->fx_pcrel
&& fixP->fx_addsy != NULL
&& (S_GET_SEGMENT (fixP->fx_addsy) == seg)
I1 = (value >> 23) & 0x01;
I2 = (value >> 22) & 0x01;
hi = (value >> 12) & 0x3ff;
- lo = (value >> 1) & 0x7ff;
+ lo = (value >> 1) & 0x7ff;
newval = md_chars_to_number (buf, THUMB_SIZE);
newval2 = md_chars_to_number (buf + THUMB_SIZE, THUMB_SIZE);
newval |= (S << 10) | hi;
}
}
- newimm = encode_arm_immediate (value);
temp = md_chars_to_number (buf, INSN_SIZE);
- /* If the instruction will fail, see if we can fix things up by
- changing the opcode. */
- if (newimm == (unsigned int) FAIL
- && (newimm = negate_data_op (&temp, value)) == (unsigned int) FAIL)
+ /* If the offset is negative, we should use encoding A2 for ADR. */
+ if ((temp & 0xfff0000) == 0x28f0000 && value < 0)
+ newimm = negate_data_op (&temp, value);
+ else
+ {
+ newimm = encode_arm_immediate (value);
+
+ /* If the instruction will fail, see if we can fix things up by
+ changing the opcode. */
+ if (newimm == (unsigned int) FAIL)
+ newimm = negate_data_op (&temp, value);
+ }
+
+ if (newimm == (unsigned int) FAIL)
{
as_bad_where (fixP->fx_file, fixP->fx_line,
_("invalid constant (%lx) after fixup"),
break;
}
}
-
+
newimm = encode_arm_immediate (value);
temp = md_chars_to_number (buf, INSN_SIZE);
value = 0;
case BFD_RELOC_ARM_LITERAL:
- sign = value >= 0;
+ sign = value > 0;
if (value < 0)
value = - value;
}
newval = md_chars_to_number (buf, INSN_SIZE);
- newval &= 0xff7ff000;
- newval |= value | (sign ? INDEX_UP : 0);
+ if (value == 0)
+ newval &= 0xfffff000;
+ else
+ {
+ newval &= 0xff7ff000;
+ newval |= value | (sign ? INDEX_UP : 0);
+ }
md_number_to_chars (buf, newval, INSN_SIZE);
break;
case BFD_RELOC_ARM_OFFSET_IMM8:
case BFD_RELOC_ARM_HWLITERAL:
- sign = value >= 0;
+ sign = value > 0;
if (value < 0)
value = - value;
as_bad_where (fixP->fx_file, fixP->fx_line,
_("invalid literal constant: pool needs to be closer"));
else
- as_bad (_("bad immediate value for 8-bit offset (%ld)"),
- (long) value);
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad immediate value for 8-bit offset (%ld)"),
+ (long) value);
break;
}
newval = md_chars_to_number (buf, INSN_SIZE);
- newval &= 0xff7ff0f0;
- newval |= ((value >> 4) << 8) | (value & 0xf) | (sign ? INDEX_UP : 0);
+ if (value == 0)
+ newval &= 0xfffff0f0;
+ else
+ {
+ newval &= 0xff7ff0f0;
+ newval |= ((value >> 4) << 8) | (value & 0xf) | (sign ? INDEX_UP : 0);
+ }
md_number_to_chars (buf, newval, INSN_SIZE);
break;
load/store instruction with immediate offset:
1110 100P u1WL NNNN XXXX YYYY iiii iiii - +/-(U) pre/post(P) 8-bit,
- *4, optional writeback(W)
+ *4, optional writeback(W)
(doubleword load/store)
1111 100S uTTL 1111 XXXX iiii iiii iiii - +/-(U) 12-bit PC-rel
#ifdef OBJ_ELF
if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4)
- fixP->fx_r_type = BFD_RELOC_ARM_PCREL_CALL;
+ fixP->fx_r_type = BFD_RELOC_ARM_PCREL_CALL;
#endif
arm_branch_common:
_("misaligned branch destination"));
if ((value & (offsetT)0xfe000000) != (offsetT)0
&& (value & (offsetT)0xfe000000) != (offsetT)0xfe000000)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch out of range"));
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
if (fixP->fx_done || !seg->use_rela_p)
{
/* CBZ can only branch forward. */
/* Attempts to use CBZ to branch to the next instruction
- (which, strictly speaking, are prohibited) will be turned into
- no-ops.
+ (which, strictly speaking, are prohibited) will be turned into
+ no-ops.
FIXME: It may be better to remove the instruction completely and
perform relaxation. */
else
{
if (value & ~0x7e)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch out of range"));
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
- if (fixP->fx_done || !seg->use_rela_p)
+ if (fixP->fx_done || !seg->use_rela_p)
{
newval = md_chars_to_number (buf, THUMB_SIZE);
newval |= ((value & 0x3e) << 2) | ((value & 0x40) << 3);
case BFD_RELOC_THUMB_PCREL_BRANCH9: /* Conditional branch. */
if ((value & ~0xff) && ((value & ~0xff) != ~0xff))
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch out of range"));
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
if (fixP->fx_done || !seg->use_rela_p)
{
case BFD_RELOC_THUMB_PCREL_BRANCH12: /* Unconditional branch. */
if ((value & ~0x7ff) && ((value & ~0x7ff) != ~0x7ff))
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch out of range"));
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
if (fixP->fx_done || !seg->use_rela_p)
{
/* Force a relocation for a branch 20 bits wide. */
fixP->fx_done = 0;
}
- if ((value & ~0x1fffff) && ((value & ~0x1fffff) != ~0x1fffff))
+ if ((value & ~0x1fffff) && ((value & ~0x0fffff) != ~0x0fffff))
as_bad_where (fixP->fx_file, fixP->fx_line,
_("conditional branch out of range"));
break;
case BFD_RELOC_THUMB_PCREL_BLX:
-
/* If there is a blx from a thumb state function to
another thumb function flip this to a bl and warn
about it. */
goto thumb_bl_common;
case BFD_RELOC_THUMB_PCREL_BRANCH23:
-
/* A bl from Thumb state ISA to an internal ARM state function
is converted to a blx. */
if (fixP->fx_addsy
thumb_bl_common:
-#ifdef OBJ_ELF
- if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4 &&
- fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
- fixP->fx_r_type = BFD_RELOC_THUMB_PCREL_BRANCH23;
-#endif
-
if (fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
/* For a BLX instruction, make sure that the relocation is rounded up
to a word boundary. This follows the semantics of the instruction
which specifies that bit 1 of the target address will come from bit
1 of the base address. */
- value = (value + 1) & ~ 1;
+ value = (value + 3) & ~ 3;
+#ifdef OBJ_ELF
+ if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4
+ && fixP->fx_r_type == BFD_RELOC_THUMB_PCREL_BLX)
+ fixP->fx_r_type = BFD_RELOC_THUMB_PCREL_BRANCH23;
+#endif
- if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff))
+ if ((value & ~0x3fffff) && ((value & ~0x3fffff) != ~0x3fffff))
{
if (!(ARM_CPU_HAS_FEATURE (cpu_variant, arm_arch_t2)))
- {
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch out of range"));
- }
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
else if ((value & ~0x1ffffff)
&& ((value & ~0x1ffffff) != ~0x1ffffff))
- {
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("Thumb2 branch out of range"));
- }
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Thumb2 branch out of range"));
}
if (fixP->fx_done || !seg->use_rela_p)
break;
case BFD_RELOC_THUMB_PCREL_BRANCH25:
- if ((value & ~0x1ffffff) && ((value & ~0x1ffffff) != ~0x1ffffff))
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch out of range"));
+ if ((value & ~0x0ffffff) && ((value & ~0x0ffffff) != ~0x0ffffff))
+ as_bad_where (fixP->fx_file, fixP->fx_line, BAD_RANGE);
if (fixP->fx_done || !seg->use_rela_p)
encode_thumb2_b_bl_offset (buf, value);
case BFD_RELOC_ARM_GOT_PREL:
if (fixP->fx_done || !seg->use_rela_p)
- md_number_to_chars (buf, value, 4);
+ md_number_to_chars (buf, value, 4);
break;
case BFD_RELOC_ARM_TARGET2:
/* TARGET2 is not partial-inplace, so we need to write the
- addend here for REL targets, because it won't be written out
- during reloc processing later. */
+ addend here for REL targets, because it won't be written out
+ during reloc processing later. */
if (fixP->fx_done || !seg->use_rela_p)
md_number_to_chars (buf, fixP->fx_offset, 4);
break;
as_bad_where (fixP->fx_file, fixP->fx_line,
_("co-processor offset out of range"));
cp_off_common:
- sign = value >= 0;
+ sign = value > 0;
if (value < 0)
value = -value;
if (fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM
newval = md_chars_to_number (buf, INSN_SIZE);
else
newval = get_thumb32_insn (buf);
- newval &= 0xff7fff00;
- newval |= (value >> 2) | (sign ? INDEX_UP : 0);
+ if (value == 0)
+ newval &= 0xffffff00;
+ else
+ {
+ newval &= 0xff7fff00;
+ newval |= (value >> 2) | (sign ? INDEX_UP : 0);
+ }
if (fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM
|| fixP->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM_S2)
md_number_to_chars (buf, newval, INSN_SIZE);
gas_assert (!fixP->fx_done);
if (!seg->use_rela_p)
{
- bfd_vma insn;
- bfd_vma encoded_addend;
- bfd_vma addend_abs = abs (value);
-
- /* Check that the absolute value of the addend can be
- expressed as an 8-bit constant plus a rotation. */
- encoded_addend = encode_arm_immediate (addend_abs);
- if (encoded_addend == (unsigned int) FAIL)
+ bfd_vma insn;
+ bfd_vma encoded_addend;
+ bfd_vma addend_abs = abs (value);
+
+ /* Check that the absolute value of the addend can be
+ expressed as an 8-bit constant plus a rotation. */
+ encoded_addend = encode_arm_immediate (addend_abs);
+ if (encoded_addend == (unsigned int) FAIL)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("the offset 0x%08lX is not representable"),
- (unsigned long) addend_abs);
-
- /* Extract the instruction. */
- insn = md_chars_to_number (buf, INSN_SIZE);
-
- /* If the addend is positive, use an ADD instruction.
- Otherwise use a SUB. Take care not to destroy the S bit. */
- insn &= 0xff1fffff;
- if (value < 0)
- insn |= 1 << 22;
- else
- insn |= 1 << 23;
-
- /* Place the encoded addend into the first 12 bits of the
- instruction. */
- insn &= 0xfffff000;
- insn |= encoded_addend;
-
- /* Update the instruction. */
- md_number_to_chars (buf, insn, INSN_SIZE);
+ _("the offset 0x%08lX is not representable"),
+ (unsigned long) addend_abs);
+
+ /* Extract the instruction. */
+ insn = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the addend is positive, use an ADD instruction.
+ Otherwise use a SUB. Take care not to destroy the S bit. */
+ insn &= 0xff1fffff;
+ if (value < 0)
+ insn |= 1 << 22;
+ else
+ insn |= 1 << 23;
+
+ /* Place the encoded addend into the first 12 bits of the
+ instruction. */
+ insn &= 0xfffff000;
+ insn |= encoded_addend;
+
+ /* Update the instruction. */
+ md_number_to_chars (buf, insn, INSN_SIZE);
}
break;
case BFD_RELOC_ARM_LDR_SB_G2:
gas_assert (!fixP->fx_done);
if (!seg->use_rela_p)
- {
- bfd_vma insn;
- bfd_vma addend_abs = abs (value);
+ {
+ bfd_vma insn;
+ bfd_vma addend_abs = abs (value);
- /* Check that the absolute value of the addend can be
- encoded in 12 bits. */
- if (addend_abs >= 0x1000)
+ /* Check that the absolute value of the addend can be
+ encoded in 12 bits. */
+ if (addend_abs >= 0x1000)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("bad offset 0x%08lX (only 12 bits available for the magnitude)"),
- (unsigned long) addend_abs);
-
- /* Extract the instruction. */
- insn = md_chars_to_number (buf, INSN_SIZE);
-
- /* If the addend is negative, clear bit 23 of the instruction.
- Otherwise set it. */
- if (value < 0)
- insn &= ~(1 << 23);
- else
- insn |= 1 << 23;
-
- /* Place the absolute value of the addend into the first 12 bits
- of the instruction. */
- insn &= 0xfffff000;
- insn |= addend_abs;
-
- /* Update the instruction. */
- md_number_to_chars (buf, insn, INSN_SIZE);
- }
+ _("bad offset 0x%08lX (only 12 bits available for the magnitude)"),
+ (unsigned long) addend_abs);
+
+ /* Extract the instruction. */
+ insn = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the addend is negative, clear bit 23 of the instruction.
+ Otherwise set it. */
+ if (value < 0)
+ insn &= ~(1 << 23);
+ else
+ insn |= 1 << 23;
+
+ /* Place the absolute value of the addend into the first 12 bits
+ of the instruction. */
+ insn &= 0xfffff000;
+ insn |= addend_abs;
+
+ /* Update the instruction. */
+ md_number_to_chars (buf, insn, INSN_SIZE);
+ }
break;
case BFD_RELOC_ARM_LDRS_PC_G0:
case BFD_RELOC_ARM_LDRS_SB_G2:
gas_assert (!fixP->fx_done);
if (!seg->use_rela_p)
- {
- bfd_vma insn;
- bfd_vma addend_abs = abs (value);
+ {
+ bfd_vma insn;
+ bfd_vma addend_abs = abs (value);
- /* Check that the absolute value of the addend can be
- encoded in 8 bits. */
- if (addend_abs >= 0x100)
+ /* Check that the absolute value of the addend can be
+ encoded in 8 bits. */
+ if (addend_abs >= 0x100)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("bad offset 0x%08lX (only 8 bits available for the magnitude)"),
- (unsigned long) addend_abs);
-
- /* Extract the instruction. */
- insn = md_chars_to_number (buf, INSN_SIZE);
-
- /* If the addend is negative, clear bit 23 of the instruction.
- Otherwise set it. */
- if (value < 0)
- insn &= ~(1 << 23);
- else
- insn |= 1 << 23;
-
- /* Place the first four bits of the absolute value of the addend
- into the first 4 bits of the instruction, and the remaining
- four into bits 8 .. 11. */
- insn &= 0xfffff0f0;
- insn |= (addend_abs & 0xf) | ((addend_abs & 0xf0) << 4);
-
- /* Update the instruction. */
- md_number_to_chars (buf, insn, INSN_SIZE);
- }
+ _("bad offset 0x%08lX (only 8 bits available for the magnitude)"),
+ (unsigned long) addend_abs);
+
+ /* Extract the instruction. */
+ insn = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the addend is negative, clear bit 23 of the instruction.
+ Otherwise set it. */
+ if (value < 0)
+ insn &= ~(1 << 23);
+ else
+ insn |= 1 << 23;
+
+ /* Place the first four bits of the absolute value of the addend
+ into the first 4 bits of the instruction, and the remaining
+ four into bits 8 .. 11. */
+ insn &= 0xfffff0f0;
+ insn |= (addend_abs & 0xf) | ((addend_abs & 0xf0) << 4);
+
+ /* Update the instruction. */
+ md_number_to_chars (buf, insn, INSN_SIZE);
+ }
break;
case BFD_RELOC_ARM_LDC_PC_G0:
case BFD_RELOC_ARM_LDC_SB_G2:
gas_assert (!fixP->fx_done);
if (!seg->use_rela_p)
- {
- bfd_vma insn;
- bfd_vma addend_abs = abs (value);
+ {
+ bfd_vma insn;
+ bfd_vma addend_abs = abs (value);
- /* Check that the absolute value of the addend is a multiple of
- four and, when divided by four, fits in 8 bits. */
- if (addend_abs & 0x3)
+ /* Check that the absolute value of the addend is a multiple of
+ four and, when divided by four, fits in 8 bits. */
+ if (addend_abs & 0x3)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("bad offset 0x%08lX (must be word-aligned)"),
- (unsigned long) addend_abs);
+ _("bad offset 0x%08lX (must be word-aligned)"),
+ (unsigned long) addend_abs);
- if ((addend_abs >> 2) > 0xff)
+ if ((addend_abs >> 2) > 0xff)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("bad offset 0x%08lX (must be an 8-bit number of words)"),
- (unsigned long) addend_abs);
-
- /* Extract the instruction. */
- insn = md_chars_to_number (buf, INSN_SIZE);
-
- /* If the addend is negative, clear bit 23 of the instruction.
- Otherwise set it. */
- if (value < 0)
- insn &= ~(1 << 23);
- else
- insn |= 1 << 23;
-
- /* Place the addend (divided by four) into the first eight
- bits of the instruction. */
- insn &= 0xfffffff0;
- insn |= addend_abs >> 2;
-
- /* Update the instruction. */
- md_number_to_chars (buf, insn, INSN_SIZE);
- }
+ _("bad offset 0x%08lX (must be an 8-bit number of words)"),
+ (unsigned long) addend_abs);
+
+ /* Extract the instruction. */
+ insn = md_chars_to_number (buf, INSN_SIZE);
+
+ /* If the addend is negative, clear bit 23 of the instruction.
+ Otherwise set it. */
+ if (value < 0)
+ insn &= ~(1 << 23);
+ else
+ insn |= 1 << 23;
+
+ /* Place the addend (divided by four) into the first eight
+ bits of the instruction. */
+ insn &= 0xfffffff0;
+ insn |= addend_abs >> 2;
+
+ /* Update the instruction. */
+ md_number_to_chars (buf, insn, INSN_SIZE);
+ }
break;
case BFD_RELOC_ARM_V4BX:
return (target_big_endian
? "elf32-bigarm-vxworks"
: "elf32-littlearm-vxworks");
+#elif defined (TE_NACL)
+ return (target_big_endian
+ ? "elf32-bigarm-nacl"
+ : "elf32-littlearm-nacl");
#else
if (target_big_endian)
return "elf32-bigarm";
hash_insert (arm_psr_hsh, psrs[i].template_name, (void *) (psrs + i));
for (i = 0; i < sizeof (v7m_psrs) / sizeof (struct asm_psr); i++)
hash_insert (arm_v7m_psr_hsh, v7m_psrs[i].template_name,
- (void *) (v7m_psrs + i));
+ (void *) (v7m_psrs + i));
for (i = 0; i < sizeof (reg_names) / sizeof (struct reg_entry); i++)
hash_insert (arm_reg_hsh, reg_names[i].name, (void *) (reg_names + i));
for (i = 0;
hash_insert (arm_barrier_opt_hsh, barrier_opt_names[i].template_name,
(void *) (barrier_opt_names + i));
#ifdef OBJ_ELF
- for (i = 0; i < sizeof (reloc_names) / sizeof (struct reloc_entry); i++)
- hash_insert (arm_reloc_hsh, reloc_names[i].name, (void *) (reloc_names + i));
+ for (i = 0; i < ARRAY_SIZE (reloc_names); i++)
+ {
+ struct reloc_entry * entry = reloc_names + i;
+
+ if (arm_is_eabi() && entry->reloc == BFD_RELOC_ARM_PLT32)
+ /* This makes encode_branch() use the EABI versions of this relocation. */
+ entry->reloc = BFD_RELOC_UNUSED;
+
+ hash_insert (arm_reloc_hsh, entry->name, (void *) entry);
+ }
#endif
set_constant_flonums ();
struct arm_cpu_option_table
{
char *name;
+ size_t name_len;
const arm_feature_set value;
/* For some CPUs we assume an FPU unless the user explicitly sets
-mfpu=... */
/* This list should, at a minimum, contain all the cpu names
recognized by GCC. */
+#define ARM_CPU_OPT(N, V, DF, CN) { N, sizeof (N) - 1, V, DF, CN }
static const struct arm_cpu_option_table arm_cpus[] =
{
- {"all", ARM_ANY, FPU_ARCH_FPA, NULL},
- {"arm1", ARM_ARCH_V1, FPU_ARCH_FPA, NULL},
- {"arm2", ARM_ARCH_V2, FPU_ARCH_FPA, NULL},
- {"arm250", ARM_ARCH_V2S, FPU_ARCH_FPA, NULL},
- {"arm3", ARM_ARCH_V2S, FPU_ARCH_FPA, NULL},
- {"arm6", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm60", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm600", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm610", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm620", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm7", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm7m", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL},
- {"arm7d", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm7dm", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL},
- {"arm7di", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm7dmi", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL},
- {"arm70", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm700", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm700i", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm710", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm710t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
- {"arm720", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm720t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
- {"arm740t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
- {"arm710c", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm7100", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm7500", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm7500fe", ARM_ARCH_V3, FPU_ARCH_FPA, NULL},
- {"arm7t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
- {"arm7tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
- {"arm7tdmi-s", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
- {"arm8", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
- {"arm810", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
- {"strongarm", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
- {"strongarm1", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
- {"strongarm110", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
- {"strongarm1100", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
- {"strongarm1110", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
- {"arm9", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
- {"arm920", ARM_ARCH_V4T, FPU_ARCH_FPA, "ARM920T"},
- {"arm920t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
- {"arm922t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
- {"arm940t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
- {"arm9tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL},
- {"fa526", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
- {"fa626", ARM_ARCH_V4, FPU_ARCH_FPA, NULL},
+ ARM_CPU_OPT ("all", ARM_ANY, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm1", ARM_ARCH_V1, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm2", ARM_ARCH_V2, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm250", ARM_ARCH_V2S, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm3", ARM_ARCH_V2S, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm6", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm60", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm600", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm610", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm620", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7m", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7d", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7dm", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7di", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7dmi", ARM_ARCH_V3M, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm70", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm700", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm700i", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm710", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm710t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm720", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm720t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm740t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm710c", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7100", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7500", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7500fe", ARM_ARCH_V3, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm7tdmi-s", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm8", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm810", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("strongarm", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("strongarm1", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("strongarm110", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("strongarm1100", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("strongarm1110", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm9", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm920", ARM_ARCH_V4T, FPU_ARCH_FPA, "ARM920T"),
+ ARM_CPU_OPT ("arm920t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm922t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm940t", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("arm9tdmi", ARM_ARCH_V4T, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("fa526", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
+ ARM_CPU_OPT ("fa626", ARM_ARCH_V4, FPU_ARCH_FPA, NULL),
/* For V5 or later processors we default to using VFP; but the user
should really set the FPU type explicitly. */
- {"arm9e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL},
- {"arm9e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
- {"arm926ej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM926EJ-S"},
- {"arm926ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM926EJ-S"},
- {"arm926ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, NULL},
- {"arm946e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL},
- {"arm946e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM946E-S"},
- {"arm946e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
- {"arm966e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL},
- {"arm966e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM966E-S"},
- {"arm966e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
- {"arm968e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
- {"arm10t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL},
- {"arm10tdmi", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL},
- {"arm10e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
- {"arm1020", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM1020E"},
- {"arm1020t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL},
- {"arm1020e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
- {"arm1022e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
- {"arm1026ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM1026EJ-S"},
- {"arm1026ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, NULL},
- {"fa606te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
- {"fa616te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
- {"fa626te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
- {"fmp626", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
- {"fa726te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL},
- {"arm1136js", ARM_ARCH_V6, FPU_NONE, "ARM1136J-S"},
- {"arm1136j-s", ARM_ARCH_V6, FPU_NONE, NULL},
- {"arm1136jfs", ARM_ARCH_V6, FPU_ARCH_VFP_V2, "ARM1136JF-S"},
- {"arm1136jf-s", ARM_ARCH_V6, FPU_ARCH_VFP_V2, NULL},
- {"mpcore", ARM_ARCH_V6K, FPU_ARCH_VFP_V2, "MPCore"},
- {"mpcorenovfp", ARM_ARCH_V6K, FPU_NONE, "MPCore"},
- {"arm1156t2-s", ARM_ARCH_V6T2, FPU_NONE, NULL},
- {"arm1156t2f-s", ARM_ARCH_V6T2, FPU_ARCH_VFP_V2, NULL},
- {"arm1176jz-s", ARM_ARCH_V6ZK, FPU_NONE, NULL},
- {"arm1176jzf-s", ARM_ARCH_V6ZK, FPU_ARCH_VFP_V2, NULL},
- {"cortex-a5", ARM_ARCH_V7A_MP_SEC,
- FPU_NONE, "Cortex-A5"},
- {"cortex-a8", ARM_ARCH_V7A_SEC,
- ARM_FEATURE (0, FPU_VFP_V3
- | FPU_NEON_EXT_V1),
- "Cortex-A8"},
- {"cortex-a9", ARM_ARCH_V7A_MP_SEC,
- ARM_FEATURE (0, FPU_VFP_V3
- | FPU_NEON_EXT_V1),
- "Cortex-A9"},
- {"cortex-a15", ARM_ARCH_V7A_IDIV_MP_SEC_VIRT,
- FPU_ARCH_NEON_VFP_V4,
- "Cortex-A15"},
- {"cortex-r4", ARM_ARCH_V7R, FPU_NONE, "Cortex-R4"},
- {"cortex-r4f", ARM_ARCH_V7R, FPU_ARCH_VFP_V3D16,
- "Cortex-R4F"},
- {"cortex-m4", ARM_ARCH_V7EM, FPU_NONE, "Cortex-M4"},
- {"cortex-m3", ARM_ARCH_V7M, FPU_NONE, "Cortex-M3"},
- {"cortex-m1", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M1"},
- {"cortex-m0", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M0"},
+ ARM_CPU_OPT ("arm9e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm9e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm926ej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM926EJ-S"),
+ ARM_CPU_OPT ("arm926ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, "ARM926EJ-S"),
+ ARM_CPU_OPT ("arm926ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm946e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm946e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM946E-S"),
+ ARM_CPU_OPT ("arm946e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm966e-r0", ARM_ARCH_V5TExP, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm966e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM966E-S"),
+ ARM_CPU_OPT ("arm966e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm968e-s", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm10t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL),
+ ARM_CPU_OPT ("arm10tdmi", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL),
+ ARM_CPU_OPT ("arm10e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm1020", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, "ARM1020E"),
+ ARM_CPU_OPT ("arm1020t", ARM_ARCH_V5T, FPU_ARCH_VFP_V1, NULL),
+ ARM_CPU_OPT ("arm1020e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm1022e", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm1026ejs", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2,
+ "ARM1026EJ-S"),
+ ARM_CPU_OPT ("arm1026ej-s", ARM_ARCH_V5TEJ, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("fa606te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("fa616te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("fa626te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("fmp626", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("fa726te", ARM_ARCH_V5TE, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm1136js", ARM_ARCH_V6, FPU_NONE, "ARM1136J-S"),
+ ARM_CPU_OPT ("arm1136j-s", ARM_ARCH_V6, FPU_NONE, NULL),
+ ARM_CPU_OPT ("arm1136jfs", ARM_ARCH_V6, FPU_ARCH_VFP_V2,
+ "ARM1136JF-S"),
+ ARM_CPU_OPT ("arm1136jf-s", ARM_ARCH_V6, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("mpcore", ARM_ARCH_V6K, FPU_ARCH_VFP_V2, "MPCore"),
+ ARM_CPU_OPT ("mpcorenovfp", ARM_ARCH_V6K, FPU_NONE, "MPCore"),
+ ARM_CPU_OPT ("arm1156t2-s", ARM_ARCH_V6T2, FPU_NONE, NULL),
+ ARM_CPU_OPT ("arm1156t2f-s", ARM_ARCH_V6T2, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("arm1176jz-s", ARM_ARCH_V6ZK, FPU_NONE, NULL),
+ ARM_CPU_OPT ("arm1176jzf-s", ARM_ARCH_V6ZK, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("cortex-a5", ARM_ARCH_V7A_MP_SEC,
+ FPU_NONE, "Cortex-A5"),
+ ARM_CPU_OPT ("cortex-a7", ARM_ARCH_V7A_IDIV_MP_SEC_VIRT,
+ FPU_ARCH_NEON_VFP_V4,
+ "Cortex-A7"),
+ ARM_CPU_OPT ("cortex-a8", ARM_ARCH_V7A_SEC,
+ ARM_FEATURE (0, FPU_VFP_V3
+ | FPU_NEON_EXT_V1),
+ "Cortex-A8"),
+ ARM_CPU_OPT ("cortex-a9", ARM_ARCH_V7A_MP_SEC,
+ ARM_FEATURE (0, FPU_VFP_V3
+ | FPU_NEON_EXT_V1),
+ "Cortex-A9"),
+ ARM_CPU_OPT ("cortex-a12", ARM_ARCH_V7A_IDIV_MP_SEC_VIRT,
+ FPU_ARCH_NEON_VFP_V4,
+ "Cortex-A12"),
+ ARM_CPU_OPT ("cortex-a15", ARM_ARCH_V7A_IDIV_MP_SEC_VIRT,
+ FPU_ARCH_NEON_VFP_V4,
+ "Cortex-A15"),
+ ARM_CPU_OPT ("cortex-a53", ARM_ARCH_V8A, FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ "Cortex-A53"),
+ ARM_CPU_OPT ("cortex-a57", ARM_ARCH_V8A, FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ "Cortex-A57"),
+ ARM_CPU_OPT ("cortex-r4", ARM_ARCH_V7R, FPU_NONE, "Cortex-R4"),
+ ARM_CPU_OPT ("cortex-r4f", ARM_ARCH_V7R, FPU_ARCH_VFP_V3D16,
+ "Cortex-R4F"),
+ ARM_CPU_OPT ("cortex-r5", ARM_ARCH_V7R_IDIV,
+ FPU_NONE, "Cortex-R5"),
+ ARM_CPU_OPT ("cortex-r7", ARM_ARCH_V7R_IDIV,
+ FPU_ARCH_VFP_V3D16,
+ "Cortex-R7"),
+ ARM_CPU_OPT ("cortex-m4", ARM_ARCH_V7EM, FPU_NONE, "Cortex-M4"),
+ ARM_CPU_OPT ("cortex-m3", ARM_ARCH_V7M, FPU_NONE, "Cortex-M3"),
+ ARM_CPU_OPT ("cortex-m1", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M1"),
+ ARM_CPU_OPT ("cortex-m0", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M0"),
+ ARM_CPU_OPT ("cortex-m0plus", ARM_ARCH_V6SM, FPU_NONE, "Cortex-M0+"),
/* ??? XSCALE is really an architecture. */
- {"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL},
+ ARM_CPU_OPT ("xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL),
/* ??? iwmmxt is not a processor. */
- {"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP_V2, NULL},
- {"iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP_V2, NULL},
- {"i80200", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL},
+ ARM_CPU_OPT ("iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP_V2, NULL),
+ ARM_CPU_OPT ("i80200", ARM_ARCH_XSCALE, FPU_ARCH_VFP_V2, NULL),
/* Maverick */
- {"ep9312", ARM_FEATURE (ARM_AEXT_V4T, ARM_CEXT_MAVERICK), FPU_ARCH_MAVERICK, "ARM920T"},
- {NULL, ARM_ARCH_NONE, ARM_ARCH_NONE, NULL}
+ ARM_CPU_OPT ("ep9312", ARM_FEATURE (ARM_AEXT_V4T, ARM_CEXT_MAVERICK),
+ FPU_ARCH_MAVERICK, "ARM920T"),
+ /* Marvell processors. */
+ ARM_CPU_OPT ("marvell-pj4", ARM_FEATURE (ARM_AEXT_V7A | ARM_EXT_MP | ARM_EXT_SEC, 0),
+ FPU_ARCH_VFP_V3D16, NULL),
+
+ { NULL, 0, ARM_ARCH_NONE, ARM_ARCH_NONE, NULL }
};
+#undef ARM_CPU_OPT
struct arm_arch_option_table
{
char *name;
+ size_t name_len;
const arm_feature_set value;
const arm_feature_set default_fpu;
};
/* This list should, at a minimum, contain all the architecture names
recognized by GCC. */
+#define ARM_ARCH_OPT(N, V, DF) { N, sizeof (N) - 1, V, DF }
static const struct arm_arch_option_table arm_archs[] =
{
- {"all", ARM_ANY, FPU_ARCH_FPA},
- {"armv1", ARM_ARCH_V1, FPU_ARCH_FPA},
- {"armv2", ARM_ARCH_V2, FPU_ARCH_FPA},
- {"armv2a", ARM_ARCH_V2S, FPU_ARCH_FPA},
- {"armv2s", ARM_ARCH_V2S, FPU_ARCH_FPA},
- {"armv3", ARM_ARCH_V3, FPU_ARCH_FPA},
- {"armv3m", ARM_ARCH_V3M, FPU_ARCH_FPA},
- {"armv4", ARM_ARCH_V4, FPU_ARCH_FPA},
- {"armv4xm", ARM_ARCH_V4xM, FPU_ARCH_FPA},
- {"armv4t", ARM_ARCH_V4T, FPU_ARCH_FPA},
- {"armv4txm", ARM_ARCH_V4TxM, FPU_ARCH_FPA},
- {"armv5", ARM_ARCH_V5, FPU_ARCH_VFP},
- {"armv5t", ARM_ARCH_V5T, FPU_ARCH_VFP},
- {"armv5txm", ARM_ARCH_V5TxM, FPU_ARCH_VFP},
- {"armv5te", ARM_ARCH_V5TE, FPU_ARCH_VFP},
- {"armv5texp", ARM_ARCH_V5TExP, FPU_ARCH_VFP},
- {"armv5tej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP},
- {"armv6", ARM_ARCH_V6, FPU_ARCH_VFP},
- {"armv6j", ARM_ARCH_V6, FPU_ARCH_VFP},
- {"armv6k", ARM_ARCH_V6K, FPU_ARCH_VFP},
- {"armv6z", ARM_ARCH_V6Z, FPU_ARCH_VFP},
- {"armv6zk", ARM_ARCH_V6ZK, FPU_ARCH_VFP},
- {"armv6t2", ARM_ARCH_V6T2, FPU_ARCH_VFP},
- {"armv6kt2", ARM_ARCH_V6KT2, FPU_ARCH_VFP},
- {"armv6zt2", ARM_ARCH_V6ZT2, FPU_ARCH_VFP},
- {"armv6zkt2", ARM_ARCH_V6ZKT2, FPU_ARCH_VFP},
- {"armv6-m", ARM_ARCH_V6M, FPU_ARCH_VFP},
- {"armv6s-m", ARM_ARCH_V6SM, FPU_ARCH_VFP},
- {"armv7", ARM_ARCH_V7, FPU_ARCH_VFP},
+ ARM_ARCH_OPT ("all", ARM_ANY, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv1", ARM_ARCH_V1, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv2", ARM_ARCH_V2, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv2a", ARM_ARCH_V2S, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv2s", ARM_ARCH_V2S, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv3", ARM_ARCH_V3, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv3m", ARM_ARCH_V3M, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv4", ARM_ARCH_V4, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv4xm", ARM_ARCH_V4xM, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv4t", ARM_ARCH_V4T, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv4txm", ARM_ARCH_V4TxM, FPU_ARCH_FPA),
+ ARM_ARCH_OPT ("armv5", ARM_ARCH_V5, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv5t", ARM_ARCH_V5T, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv5txm", ARM_ARCH_V5TxM, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv5te", ARM_ARCH_V5TE, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv5texp", ARM_ARCH_V5TExP, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv5tej", ARM_ARCH_V5TEJ, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6", ARM_ARCH_V6, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6j", ARM_ARCH_V6, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6k", ARM_ARCH_V6K, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6z", ARM_ARCH_V6Z, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6zk", ARM_ARCH_V6ZK, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6t2", ARM_ARCH_V6T2, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6kt2", ARM_ARCH_V6KT2, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6zt2", ARM_ARCH_V6ZT2, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6zkt2", ARM_ARCH_V6ZKT2, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6-m", ARM_ARCH_V6M, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6s-m", ARM_ARCH_V6SM, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7", ARM_ARCH_V7, FPU_ARCH_VFP),
/* The official spelling of the ARMv7 profile variants is the dashed form.
Accept the non-dashed form for compatibility with old toolchains. */
- {"armv7a", ARM_ARCH_V7A, FPU_ARCH_VFP},
- {"armv7r", ARM_ARCH_V7R, FPU_ARCH_VFP},
- {"armv7m", ARM_ARCH_V7M, FPU_ARCH_VFP},
- {"armv7-a", ARM_ARCH_V7A, FPU_ARCH_VFP},
- {"armv7-r", ARM_ARCH_V7R, FPU_ARCH_VFP},
- {"armv7-m", ARM_ARCH_V7M, FPU_ARCH_VFP},
- {"armv7e-m", ARM_ARCH_V7EM, FPU_ARCH_VFP},
- {"xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP},
- {"iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP},
- {"iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP},
- {NULL, ARM_ARCH_NONE, ARM_ARCH_NONE}
+ ARM_ARCH_OPT ("armv7a", ARM_ARCH_V7A, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7r", ARM_ARCH_V7R, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7m", ARM_ARCH_V7M, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7-a", ARM_ARCH_V7A, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7-r", ARM_ARCH_V7R, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7-m", ARM_ARCH_V7M, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv7e-m", ARM_ARCH_V7EM, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv8-a", ARM_ARCH_V8A, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("xscale", ARM_ARCH_XSCALE, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("iwmmxt", ARM_ARCH_IWMMXT, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("iwmmxt2", ARM_ARCH_IWMMXT2,FPU_ARCH_VFP),
+ { NULL, 0, ARM_ARCH_NONE, ARM_ARCH_NONE }
};
+#undef ARM_ARCH_OPT
/* ISA extensions in the co-processor and main instruction set space. */
struct arm_option_extension_value_table
{
char *name;
+ size_t name_len;
const arm_feature_set value;
const arm_feature_set allowed_archs;
};
/* The following table must be in alphabetical order with a NULL last entry.
*/
+#define ARM_EXT_OPT(N, V, AA) { N, sizeof (N) - 1, V, AA }
static const struct arm_option_extension_value_table arm_extensions[] =
{
- {"idiv", ARM_FEATURE (ARM_EXT_ADIV | ARM_EXT_DIV, 0),
- ARM_FEATURE (ARM_EXT_V7A, 0)},
- {"iwmmxt", ARM_FEATURE (0, ARM_CEXT_IWMMXT), ARM_ANY},
- {"iwmmxt2", ARM_FEATURE (0, ARM_CEXT_IWMMXT2), ARM_ANY},
- {"maverick", ARM_FEATURE (0, ARM_CEXT_MAVERICK), ARM_ANY},
- {"mp", ARM_FEATURE (ARM_EXT_MP, 0),
- ARM_FEATURE (ARM_EXT_V7A | ARM_EXT_V7R, 0)},
- {"os", ARM_FEATURE (ARM_EXT_OS, 0),
- ARM_FEATURE (ARM_EXT_V6M, 0)},
- {"sec", ARM_FEATURE (ARM_EXT_SEC, 0),
- ARM_FEATURE (ARM_EXT_V6K | ARM_EXT_V7A, 0)},
- {"virt", ARM_FEATURE (ARM_EXT_VIRT | ARM_EXT_ADIV | ARM_EXT_DIV, 0),
- ARM_FEATURE (ARM_EXT_V7A, 0)},
- {"xscale", ARM_FEATURE (0, ARM_CEXT_XSCALE), ARM_ANY},
- {NULL, ARM_ARCH_NONE, ARM_ARCH_NONE}
+ ARM_EXT_OPT ("crc", ARCH_CRC_ARMV8, ARM_FEATURE (ARM_EXT_V8, 0)),
+ ARM_EXT_OPT ("crypto", FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ ARM_FEATURE (ARM_EXT_V8, 0)),
+ ARM_EXT_OPT ("fp", FPU_ARCH_VFP_ARMV8,
+ ARM_FEATURE (ARM_EXT_V8, 0)),
+ ARM_EXT_OPT ("idiv", ARM_FEATURE (ARM_EXT_ADIV | ARM_EXT_DIV, 0),
+ ARM_FEATURE (ARM_EXT_V7A | ARM_EXT_V7R, 0)),
+ ARM_EXT_OPT ("iwmmxt",ARM_FEATURE (0, ARM_CEXT_IWMMXT), ARM_ANY),
+ ARM_EXT_OPT ("iwmmxt2",
+ ARM_FEATURE (0, ARM_CEXT_IWMMXT2), ARM_ANY),
+ ARM_EXT_OPT ("maverick",
+ ARM_FEATURE (0, ARM_CEXT_MAVERICK), ARM_ANY),
+ ARM_EXT_OPT ("mp", ARM_FEATURE (ARM_EXT_MP, 0),
+ ARM_FEATURE (ARM_EXT_V7A | ARM_EXT_V7R, 0)),
+ ARM_EXT_OPT ("simd", FPU_ARCH_NEON_VFP_ARMV8,
+ ARM_FEATURE (ARM_EXT_V8, 0)),
+ ARM_EXT_OPT ("os", ARM_FEATURE (ARM_EXT_OS, 0),
+ ARM_FEATURE (ARM_EXT_V6M, 0)),
+ ARM_EXT_OPT ("sec", ARM_FEATURE (ARM_EXT_SEC, 0),
+ ARM_FEATURE (ARM_EXT_V6K | ARM_EXT_V7A, 0)),
+ ARM_EXT_OPT ("virt", ARM_FEATURE (ARM_EXT_VIRT | ARM_EXT_ADIV
+ | ARM_EXT_DIV, 0),
+ ARM_FEATURE (ARM_EXT_V7A, 0)),
+ ARM_EXT_OPT ("xscale",ARM_FEATURE (0, ARM_CEXT_XSCALE), ARM_ANY),
+ { NULL, 0, ARM_ARCH_NONE, ARM_ARCH_NONE }
};
+#undef ARM_EXT_OPT
/* ISA floating-point and Advanced SIMD extensions. */
struct arm_option_fpu_value_table
{"vfpv4-d16", FPU_ARCH_VFP_V4D16},
{"fpv4-sp-d16", FPU_ARCH_VFP_V4_SP_D16},
{"neon-vfpv4", FPU_ARCH_NEON_VFP_V4},
+ {"fp-armv8", FPU_ARCH_VFP_ARMV8},
+ {"neon-fp-armv8", FPU_ARCH_NEON_VFP_ARMV8},
+ {"crypto-neon-fp-armv8",
+ FPU_ARCH_CRYPTO_NEON_VFP_ARMV8},
{NULL, ARM_ARCH_NONE}
};
};
static bfd_boolean
-arm_parse_extension (char * str, const arm_feature_set **opt_p)
+arm_parse_extension (char *str, const arm_feature_set **opt_p)
{
arm_feature_set *ext_set = (arm_feature_set *)
xmalloc (sizeof (arm_feature_set));
/* We insist on extensions being specified in alphabetical order, and with
- extensions being added before being removed. We achieve this by having
- the global ARM_EXTENSIONS table in alphabetical order, and using the
+ extensions being added before being removed. We achieve this by having
+ the global ARM_EXTENSIONS table in alphabetical order, and using the
ADDING_VALUE variable to indicate whether we are adding an extension (1)
- or removing it (0) and only allowing it to change in the order
+ or removing it (0) and only allowing it to change in the order
-1 -> 1 -> 0. */
const struct arm_option_extension_value_table * opt = NULL;
int adding_value = -1;
while (str != NULL && *str != 0)
{
- char * ext;
- size_t optlen;
+ char *ext;
+ size_t len;
if (*str != '+')
{
ext = strchr (str, '+');
if (ext != NULL)
- optlen = ext - str;
+ len = ext - str;
else
- optlen = strlen (str);
+ len = strlen (str);
- if (optlen >= 2
- && strncmp (str, "no", 2) == 0)
+ if (len >= 2 && strncmp (str, "no", 2) == 0)
{
if (adding_value != 0)
{
opt = arm_extensions;
}
- optlen -= 2;
+ len -= 2;
str += 2;
}
- else if (optlen > 0)
+ else if (len > 0)
{
if (adding_value == -1)
{
}
}
- if (optlen == 0)
+ if (len == 0)
{
as_bad (_("missing architectural extension"));
return FALSE;
/* Scan over the options table trying to find an exact match. */
for (; opt->name != NULL; opt++)
- if (strncmp (opt->name, str, optlen) == 0
- && strlen (opt->name) == optlen)
+ if (opt->name_len == len && strncmp (opt->name, str, len) == 0)
{
/* Check we can apply the extension to this architecture. */
if (!ARM_CPU_HAS_FEATURE (*ext_set, opt->allowed_archs))
alphabetical order, or because it does not exist? */
for (opt = arm_extensions; opt->name != NULL; opt++)
- if (strncmp (opt->name, str, optlen) == 0)
+ if (opt->name_len == len && strncmp (opt->name, str, len) == 0)
break;
if (opt->name == NULL)
}
static bfd_boolean
-arm_parse_cpu (char * str)
+arm_parse_cpu (char *str)
{
- const struct arm_cpu_option_table * opt;
- char * ext = strchr (str, '+');
- int optlen;
+ const struct arm_cpu_option_table *opt;
+ char *ext = strchr (str, '+');
+ size_t len;
if (ext != NULL)
- optlen = ext - str;
+ len = ext - str;
else
- optlen = strlen (str);
+ len = strlen (str);
- if (optlen == 0)
+ if (len == 0)
{
as_bad (_("missing cpu name `%s'"), str);
return FALSE;
}
for (opt = arm_cpus; opt->name != NULL; opt++)
- if (strncmp (opt->name, str, optlen) == 0)
+ if (opt->name_len == len && strncmp (opt->name, str, len) == 0)
{
mcpu_cpu_opt = &opt->value;
mcpu_fpu_opt = &opt->default_fpu;
strcpy (selected_cpu_name, opt->canonical_name);
else
{
- int i;
+ size_t i;
- for (i = 0; i < optlen; i++)
+ for (i = 0; i < len; i++)
selected_cpu_name[i] = TOUPPER (opt->name[i]);
selected_cpu_name[i] = 0;
}
}
static bfd_boolean
-arm_parse_arch (char * str)
+arm_parse_arch (char *str)
{
const struct arm_arch_option_table *opt;
char *ext = strchr (str, '+');
- int optlen;
+ size_t len;
if (ext != NULL)
- optlen = ext - str;
+ len = ext - str;
else
- optlen = strlen (str);
+ len = strlen (str);
- if (optlen == 0)
+ if (len == 0)
{
as_bad (_("missing architecture name `%s'"), str);
return FALSE;
}
for (opt = arm_archs; opt->name != NULL; opt++)
- if (strncmp (opt->name, str, optlen) == 0)
+ if (opt->name_len == len && strncmp (opt->name, str, len) == 0)
{
march_cpu_opt = &opt->value;
march_fpu_opt = &opt->default_fpu;
else
{
as_bad (_("unknown implicit IT mode `%s', should be "\
- "arm, thumb, always, or never."), str);
+ "arm, thumb, always, or never."), str);
ret = FALSE;
}
{11, ARM_ARCH_V6M},
{12, ARM_ARCH_V6SM},
{8, ARM_ARCH_V6T2},
- {10, ARM_ARCH_V7A},
+ {10, ARM_ARCH_V7A_IDIV_MP_SEC_VIRT},
{10, ARM_ARCH_V7R},
{10, ARM_ARCH_V7M},
+ {14, ARM_ARCH_V8A},
{0, ARM_ARCH_NONE}
};
aeabi_set_public_attributes (void)
{
int arch;
+ char profile;
int virt_sec = 0;
+ int fp16_optional = 0;
arm_feature_set flags;
arm_feature_set tmp;
const cpu_arch_ver_table *p;
ARM_MERGE_FEATURE_SETS (flags, arm_arch_used, thumb_arch_used);
ARM_MERGE_FEATURE_SETS (flags, flags, *mfpu_opt);
ARM_MERGE_FEATURE_SETS (flags, flags, selected_cpu);
- /*Allow the user to override the reported architecture. */
+
+ if (ARM_CPU_HAS_FEATURE (arm_arch_used, arm_arch_any))
+ ARM_MERGE_FEATURE_SETS (flags, flags, arm_ext_v1);
+
+ if (ARM_CPU_HAS_FEATURE (thumb_arch_used, arm_arch_any))
+ ARM_MERGE_FEATURE_SETS (flags, flags, arm_ext_v4t);
+
+ /* Allow the user to override the reported architecture. */
if (object_arch)
{
ARM_CLEAR_FEATURE (flags, flags, arm_arch_any);
when the only v6S-M feature in use is the Operating System Extensions. */
if (ARM_CPU_HAS_FEATURE (flags, arm_ext_os))
if (!ARM_CPU_HAS_FEATURE (flags, arm_arch_v6m_only))
- ARM_CLEAR_FEATURE (flags, flags, arm_ext_os);
+ ARM_CLEAR_FEATURE (flags, flags, arm_ext_os);
tmp = flags;
arch = 0;
/* Tag_CPU_arch_profile. */
if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7a))
- aeabi_set_attribute_int (Tag_CPU_arch_profile, 'A');
+ profile = 'A';
else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7r))
- aeabi_set_attribute_int (Tag_CPU_arch_profile, 'R');
+ profile = 'R';
else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_m))
- aeabi_set_attribute_int (Tag_CPU_arch_profile, 'M');
+ profile = 'M';
+ else
+ profile = '\0';
+
+ if (profile != '\0')
+ aeabi_set_attribute_int (Tag_CPU_arch_profile, profile);
/* Tag_ARM_ISA_use. */
if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v1)
ARM_CPU_HAS_FEATURE (flags, arm_arch_t2) ? 2 : 1);
/* Tag_VFP_arch. */
- if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_fma))
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_armv8))
+ aeabi_set_attribute_int (Tag_VFP_arch, 7);
+ else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_fma))
aeabi_set_attribute_int (Tag_VFP_arch,
ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_d32)
? 5 : 6);
else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_d32))
- aeabi_set_attribute_int (Tag_VFP_arch, 3);
+ {
+ fp16_optional = 1;
+ aeabi_set_attribute_int (Tag_VFP_arch, 3);
+ }
else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v3xd))
- aeabi_set_attribute_int (Tag_VFP_arch, 4);
+ {
+ aeabi_set_attribute_int (Tag_VFP_arch, 4);
+ fp16_optional = 1;
+ }
else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v2))
aeabi_set_attribute_int (Tag_VFP_arch, 2);
else if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1)
- || ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1xd))
+ || ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_v1xd))
aeabi_set_attribute_int (Tag_VFP_arch, 1);
/* Tag_ABI_HardFP_use. */
aeabi_set_attribute_int (Tag_WMMX_arch, 1);
/* Tag_Advanced_SIMD_arch (formerly Tag_NEON_arch). */
- if (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_v1))
- aeabi_set_attribute_int
- (Tag_Advanced_SIMD_arch, (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_fma)
- ? 2 : 1));
-
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_armv8))
+ aeabi_set_attribute_int (Tag_Advanced_SIMD_arch, 3);
+ else if (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_v1))
+ {
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_neon_ext_fma))
+ {
+ aeabi_set_attribute_int (Tag_Advanced_SIMD_arch, 2);
+ }
+ else
+ {
+ aeabi_set_attribute_int (Tag_Advanced_SIMD_arch, 1);
+ fp16_optional = 1;
+ }
+ }
+
/* Tag_VFP_HP_extension (formerly Tag_NEON_FP16_arch). */
- if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_fp16))
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_fp16) && fp16_optional)
aeabi_set_attribute_int (Tag_VFP_HP_extension, 1);
- /* Tag_DIV_use. */
- if (ARM_CPU_HAS_FEATURE (flags, arm_ext_adiv))
- aeabi_set_attribute_int (Tag_DIV_use, 2);
- else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_div))
+ /* Tag_DIV_use.
+
+ We set Tag_DIV_use to two when integer divide instructions have been used
+ in ARM state, or when Thumb integer divide instructions have been used,
+ but we have no architecture profile set, nor have we any ARM instructions.
+
+ For ARMv8 we set the tag to 0 as integer divide is implied by the base
+ architecture.
+
+ For new architectures we will have to check these tests. */
+ gas_assert (arch <= TAG_CPU_ARCH_V8);
+ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v8))
aeabi_set_attribute_int (Tag_DIV_use, 0);
- else
- aeabi_set_attribute_int (Tag_DIV_use, 1);
+ else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_adiv)
+ || (profile == '\0'
+ && ARM_CPU_HAS_FEATURE (flags, arm_ext_div)
+ && !ARM_CPU_HAS_FEATURE (arm_arch_used, arm_arch_any)))
+ aeabi_set_attribute_int (Tag_DIV_use, 2);
/* Tag_MP_extension_use. */
if (ARM_CPU_HAS_FEATURE (flags, arm_ext_mp))
int i;
for (i = 0; opt->name[i]; i++)
selected_cpu_name[i] = TOUPPER (opt->name[i]);
+
selected_cpu_name[i] = 0;
}
ARM_MERGE_FEATURE_SETS (cpu_variant, *mcpu_cpu_opt, *mfpu_opt);
}
if (opt->name == NULL)
- as_bad (_("unknown architecture `%s'\n"), name);
+ as_bad (_("unknown architecture extension `%s'\n"), name);
*input_line_pointer = saved_char;
ignore_rest_of_line ();