/* 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 (C) 1994-2016 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
/* Warn on using deprecated features. */
static int warn_on_deprecated = TRUE;
+/* Understand CodeComposer Studio assembly syntax. */
+bfd_boolean codecomposer_syntax = FALSE;
/* Variables that we set while parsing command-line options. Once all
options have been read we re-process these values to set the real
/* Constants for known architecture features. */
static const arm_feature_set fpu_default = FPU_DEFAULT;
-static const arm_feature_set fpu_arch_vfp_v1 = FPU_ARCH_VFP_V1;
+static const arm_feature_set fpu_arch_vfp_v1 ATTRIBUTE_UNUSED = FPU_ARCH_VFP_V1;
static const arm_feature_set fpu_arch_vfp_v2 = FPU_ARCH_VFP_V2;
-static const arm_feature_set fpu_arch_vfp_v3 = FPU_ARCH_VFP_V3;
-static const arm_feature_set fpu_arch_neon_v1 = FPU_ARCH_NEON_V1;
+static const arm_feature_set fpu_arch_vfp_v3 ATTRIBUTE_UNUSED = FPU_ARCH_VFP_V3;
+static const arm_feature_set fpu_arch_neon_v1 ATTRIBUTE_UNUSED = FPU_ARCH_NEON_V1;
static const arm_feature_set fpu_arch_fpa = FPU_ARCH_FPA;
static const arm_feature_set fpu_any_hard = FPU_ANY_HARD;
static const arm_feature_set fpu_arch_maverick = FPU_ARCH_MAVERICK;
static const arm_feature_set cpu_default = CPU_DEFAULT;
#endif
-static const arm_feature_set arm_ext_v1 = ARM_FEATURE (ARM_EXT_V1, 0);
-static const arm_feature_set arm_ext_v2 = ARM_FEATURE (ARM_EXT_V1, 0);
-static const arm_feature_set arm_ext_v2s = ARM_FEATURE (ARM_EXT_V2S, 0);
-static const arm_feature_set arm_ext_v3 = ARM_FEATURE (ARM_EXT_V3, 0);
-static const arm_feature_set arm_ext_v3m = ARM_FEATURE (ARM_EXT_V3M, 0);
-static const arm_feature_set arm_ext_v4 = ARM_FEATURE (ARM_EXT_V4, 0);
-static const arm_feature_set arm_ext_v4t = ARM_FEATURE (ARM_EXT_V4T, 0);
-static const arm_feature_set arm_ext_v5 = ARM_FEATURE (ARM_EXT_V5, 0);
+static const arm_feature_set arm_ext_v1 = ARM_FEATURE_CORE_LOW (ARM_EXT_V1);
+static const arm_feature_set arm_ext_v2 = ARM_FEATURE_CORE_LOW (ARM_EXT_V1);
+static const arm_feature_set arm_ext_v2s = ARM_FEATURE_CORE_LOW (ARM_EXT_V2S);
+static const arm_feature_set arm_ext_v3 = ARM_FEATURE_CORE_LOW (ARM_EXT_V3);
+static const arm_feature_set arm_ext_v3m = ARM_FEATURE_CORE_LOW (ARM_EXT_V3M);
+static const arm_feature_set arm_ext_v4 = ARM_FEATURE_CORE_LOW (ARM_EXT_V4);
+static const arm_feature_set arm_ext_v4t = ARM_FEATURE_CORE_LOW (ARM_EXT_V4T);
+static const arm_feature_set arm_ext_v5 = ARM_FEATURE_CORE_LOW (ARM_EXT_V5);
static const arm_feature_set arm_ext_v4t_5 =
- ARM_FEATURE (ARM_EXT_V4T | ARM_EXT_V5, 0);
-static const arm_feature_set arm_ext_v5t = ARM_FEATURE (ARM_EXT_V5T, 0);
-static const arm_feature_set arm_ext_v5e = ARM_FEATURE (ARM_EXT_V5E, 0);
-static const arm_feature_set arm_ext_v5exp = ARM_FEATURE (ARM_EXT_V5ExP, 0);
-static const arm_feature_set arm_ext_v5j = ARM_FEATURE (ARM_EXT_V5J, 0);
-static const arm_feature_set arm_ext_v6 = ARM_FEATURE (ARM_EXT_V6, 0);
-static const arm_feature_set arm_ext_v6k = ARM_FEATURE (ARM_EXT_V6K, 0);
-static const arm_feature_set arm_ext_v6t2 = ARM_FEATURE (ARM_EXT_V6T2, 0);
-static const arm_feature_set arm_ext_v6m = ARM_FEATURE (ARM_EXT_V6M, 0);
-static const arm_feature_set arm_ext_v6_notm = ARM_FEATURE (ARM_EXT_V6_NOTM, 0);
-static const arm_feature_set arm_ext_v6_dsp = ARM_FEATURE (ARM_EXT_V6_DSP, 0);
-static const arm_feature_set arm_ext_barrier = ARM_FEATURE (ARM_EXT_BARRIER, 0);
-static const arm_feature_set arm_ext_msr = ARM_FEATURE (ARM_EXT_THUMB_MSR, 0);
-static const arm_feature_set arm_ext_div = ARM_FEATURE (ARM_EXT_DIV, 0);
-static const arm_feature_set arm_ext_v7 = ARM_FEATURE (ARM_EXT_V7, 0);
-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);
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V4T | ARM_EXT_V5);
+static const arm_feature_set arm_ext_v5t = ARM_FEATURE_CORE_LOW (ARM_EXT_V5T);
+static const arm_feature_set arm_ext_v5e = ARM_FEATURE_CORE_LOW (ARM_EXT_V5E);
+static const arm_feature_set arm_ext_v5exp = ARM_FEATURE_CORE_LOW (ARM_EXT_V5ExP);
+static const arm_feature_set arm_ext_v5j = ARM_FEATURE_CORE_LOW (ARM_EXT_V5J);
+static const arm_feature_set arm_ext_v6 = ARM_FEATURE_CORE_LOW (ARM_EXT_V6);
+static const arm_feature_set arm_ext_v6k = ARM_FEATURE_CORE_LOW (ARM_EXT_V6K);
+static const arm_feature_set arm_ext_v6t2 = ARM_FEATURE_CORE_LOW (ARM_EXT_V6T2);
+static const arm_feature_set arm_ext_v6m = ARM_FEATURE_CORE_LOW (ARM_EXT_V6M);
+static const arm_feature_set arm_ext_v6_notm =
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V6_NOTM);
+static const arm_feature_set arm_ext_v6_dsp =
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V6_DSP);
+static const arm_feature_set arm_ext_barrier =
+ ARM_FEATURE_CORE_LOW (ARM_EXT_BARRIER);
+static const arm_feature_set arm_ext_msr =
+ ARM_FEATURE_CORE_LOW (ARM_EXT_THUMB_MSR);
+static const arm_feature_set arm_ext_div = ARM_FEATURE_CORE_LOW (ARM_EXT_DIV);
+static const arm_feature_set arm_ext_v7 = ARM_FEATURE_CORE_LOW (ARM_EXT_V7);
+static const arm_feature_set arm_ext_v7a = ARM_FEATURE_CORE_LOW (ARM_EXT_V7A);
+static const arm_feature_set arm_ext_v7r = ARM_FEATURE_CORE_LOW (ARM_EXT_V7R);
+static const arm_feature_set arm_ext_v7m = ARM_FEATURE_CORE_LOW (ARM_EXT_V7M);
+static const arm_feature_set arm_ext_v8 = ARM_FEATURE_CORE_LOW (ARM_EXT_V8);
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 arm_ext_sec = ARM_FEATURE (ARM_EXT_SEC, 0);
-static const arm_feature_set arm_ext_os = ARM_FEATURE (ARM_EXT_OS, 0);
-static const arm_feature_set arm_ext_adiv = ARM_FEATURE (ARM_EXT_ADIV, 0);
-static const arm_feature_set arm_ext_virt = ARM_FEATURE (ARM_EXT_VIRT, 0);
+ ARM_FEATURE_CORE (ARM_EXT_V6M | ARM_EXT_OS | ARM_EXT_V7M, ARM_EXT2_V8M);
+static const arm_feature_set arm_ext_mp = ARM_FEATURE_CORE_LOW (ARM_EXT_MP);
+static const arm_feature_set arm_ext_sec = ARM_FEATURE_CORE_LOW (ARM_EXT_SEC);
+static const arm_feature_set arm_ext_os = ARM_FEATURE_CORE_LOW (ARM_EXT_OS);
+static const arm_feature_set arm_ext_adiv = ARM_FEATURE_CORE_LOW (ARM_EXT_ADIV);
+static const arm_feature_set arm_ext_virt = ARM_FEATURE_CORE_LOW (ARM_EXT_VIRT);
+static const arm_feature_set arm_ext_pan = ARM_FEATURE_CORE_HIGH (ARM_EXT2_PAN);
+static const arm_feature_set arm_ext_v8m = ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8M);
+static const arm_feature_set arm_ext_v6t2_v8m =
+ ARM_FEATURE_CORE_HIGH (ARM_EXT2_V6T2_V8M);
+/* Instructions shared between ARMv8-A and ARMv8-M. */
+static const arm_feature_set arm_ext_atomics =
+ ARM_FEATURE_CORE_HIGH (ARM_EXT2_ATOMICS);
+static const arm_feature_set arm_ext_v8_2 =
+ ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8_2A);
+/* FP16 instructions. */
+static const arm_feature_set arm_ext_fp16 =
+ ARM_FEATURE_CORE_HIGH (ARM_EXT2_FP16_INST);
static const arm_feature_set arm_arch_any = ARM_ANY;
-static const arm_feature_set arm_arch_full = ARM_FEATURE (-1, -1);
+static const arm_feature_set arm_arch_full ATTRIBUTE_UNUSED = ARM_FEATURE (-1, -1, -1);
static const arm_feature_set arm_arch_t2 = ARM_ARCH_THUMB2;
static const arm_feature_set arm_arch_none = ARM_ARCH_NONE;
static const arm_feature_set arm_arch_v6m_only = ARM_ARCH_V6M_ONLY;
static const arm_feature_set arm_cext_iwmmxt2 =
- ARM_FEATURE (0, ARM_CEXT_IWMMXT2);
+ ARM_FEATURE_COPROC (ARM_CEXT_IWMMXT2);
static const arm_feature_set arm_cext_iwmmxt =
- ARM_FEATURE (0, ARM_CEXT_IWMMXT);
+ ARM_FEATURE_COPROC (ARM_CEXT_IWMMXT);
static const arm_feature_set arm_cext_xscale =
- ARM_FEATURE (0, ARM_CEXT_XSCALE);
+ ARM_FEATURE_COPROC (ARM_CEXT_XSCALE);
static const arm_feature_set arm_cext_maverick =
- ARM_FEATURE (0, ARM_CEXT_MAVERICK);
-static const arm_feature_set fpu_fpa_ext_v1 = ARM_FEATURE (0, FPU_FPA_EXT_V1);
-static const arm_feature_set fpu_fpa_ext_v2 = ARM_FEATURE (0, FPU_FPA_EXT_V2);
+ ARM_FEATURE_COPROC (ARM_CEXT_MAVERICK);
+static const arm_feature_set fpu_fpa_ext_v1 =
+ ARM_FEATURE_COPROC (FPU_FPA_EXT_V1);
+static const arm_feature_set fpu_fpa_ext_v2 =
+ ARM_FEATURE_COPROC (FPU_FPA_EXT_V2);
static const arm_feature_set fpu_vfp_ext_v1xd =
- ARM_FEATURE (0, FPU_VFP_EXT_V1xD);
-static const arm_feature_set fpu_vfp_ext_v1 = ARM_FEATURE (0, FPU_VFP_EXT_V1);
-static const arm_feature_set fpu_vfp_ext_v2 = ARM_FEATURE (0, FPU_VFP_EXT_V2);
-static const arm_feature_set fpu_vfp_ext_v3xd = ARM_FEATURE (0, FPU_VFP_EXT_V3xD);
-static const arm_feature_set fpu_vfp_ext_v3 = ARM_FEATURE (0, FPU_VFP_EXT_V3);
+ ARM_FEATURE_COPROC (FPU_VFP_EXT_V1xD);
+static const arm_feature_set fpu_vfp_ext_v1 =
+ ARM_FEATURE_COPROC (FPU_VFP_EXT_V1);
+static const arm_feature_set fpu_vfp_ext_v2 =
+ ARM_FEATURE_COPROC (FPU_VFP_EXT_V2);
+static const arm_feature_set fpu_vfp_ext_v3xd =
+ ARM_FEATURE_COPROC (FPU_VFP_EXT_V3xD);
+static const arm_feature_set fpu_vfp_ext_v3 =
+ ARM_FEATURE_COPROC (FPU_VFP_EXT_V3);
static const arm_feature_set fpu_vfp_ext_d32 =
- ARM_FEATURE (0, FPU_VFP_EXT_D32);
-static const arm_feature_set fpu_neon_ext_v1 = ARM_FEATURE (0, FPU_NEON_EXT_V1);
+ ARM_FEATURE_COPROC (FPU_VFP_EXT_D32);
+static const arm_feature_set fpu_neon_ext_v1 =
+ ARM_FEATURE_COPROC (FPU_NEON_EXT_V1);
static const arm_feature_set fpu_vfp_v3_or_neon_ext =
- ARM_FEATURE (0, FPU_NEON_EXT_V1 | FPU_VFP_EXT_V3);
-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);
+ ARM_FEATURE_COPROC (FPU_NEON_EXT_V1 | FPU_VFP_EXT_V3);
+static const arm_feature_set fpu_vfp_fp16 =
+ ARM_FEATURE_COPROC (FPU_VFP_EXT_FP16);
+static const arm_feature_set fpu_neon_ext_fma =
+ ARM_FEATURE_COPROC (FPU_NEON_EXT_FMA);
+static const arm_feature_set fpu_vfp_ext_fma =
+ ARM_FEATURE_COPROC (FPU_VFP_EXT_FMA);
+static const arm_feature_set fpu_vfp_ext_armv8 =
+ ARM_FEATURE_COPROC (FPU_VFP_EXT_ARMV8);
+static const arm_feature_set fpu_vfp_ext_armv8xd =
+ ARM_FEATURE_COPROC (FPU_VFP_EXT_ARMV8xD);
+static const arm_feature_set fpu_neon_ext_armv8 =
+ ARM_FEATURE_COPROC (FPU_NEON_EXT_ARMV8);
+static const arm_feature_set fpu_crypto_ext_armv8 =
+ ARM_FEATURE_COPROC (FPU_CRYPTO_EXT_ARMV8);
+static const arm_feature_set crc_ext_armv8 =
+ ARM_FEATURE_COPROC (CRC_EXT_ARMV8);
+static const arm_feature_set fpu_neon_ext_v8_1 =
+ ARM_FEATURE_COPROC (FPU_NEON_EXT_RDMA);
static int mfloat_abi_opt = -1;
/* Record user cpu selection for object attributes. */
static arm_feature_set selected_cpu = ARM_ARCH_NONE;
/* Must be long enough to hold any of the names in arm_cpus. */
-static char selected_cpu_name[16];
+static char selected_cpu_name[20];
+
+extern FLONUM_TYPE generic_floating_point_number;
+
+/* Return if no cpu was selected on command-line. */
+static bfd_boolean
+no_cpu_selected (void)
+{
+ return ARM_FEATURE_EQUAL (selected_cpu, arm_arch_none);
+}
+
#ifdef OBJ_ELF
# ifdef EABI_DEFAULT
static int meabi_flags = EABI_DEFAULT;
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. */
struct reloc_entry
{
- char * name;
+ const char * name;
bfd_reloc_code_real_type reloc;
};
};
/* 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 LITERAL_MASK 0xf000f000
#define OPCODE_MASK 0xfe1fffff
#define V4_STR_BIT 0x00000020
+#define VLDR_VMOV_SAME 0x0040f000
#define T2_SUBS_PC_LR 0xf3de8f00
#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 BAD_FP16 _("selected processor does not support fp16 instruction")
+#define UNPRED_REG(R) _("using " R " results in unpredictable behaviour")
+#define THUMB1_RELOC_ONLY _("relocation valid in thumb1 code only")
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;
+ unsigned int alignment;
} literal_pool;
/* Pointer to a linked list of literal pools. */
literal_pool * list_of_pools = NULL;
+typedef enum asmfunc_states
+{
+ OUTSIDE_ASMFUNC,
+ WAITING_ASMFUNC_NAME,
+ WAITING_ENDASMFUNC
+} asmfunc_states;
+
+static asmfunc_states asmfunc_state = OUTSIDE_ASMFUNC;
+
#ifdef OBJ_ELF
# define now_it seg_info (now_seg)->tc_segment_info_data.current_it
#else
{ \
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)
/* This array holds the chars that always start a comment. If the
pre-processor is disabled, these aren't very useful. */
-const char comment_chars[] = "@";
+char arm_comment_chars[] = "@";
/* This array holds the chars that only start a comment at the beginning of
a line. If the line seems to have the form '# 123 filename'
/* Also note that comments like this one will always work. */
const char line_comment_chars[] = "#";
-const char line_separator_chars[] = ";";
+char arm_line_separator_chars[] = ";";
/* Chars that can be used to separate mant
from exp in floating point numbers. */
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");
??? The format of 12 byte floats is uncertain according to gcc's arm.h. */
-char *
+const char *
md_atof (int type, char * litP, int * sizeP)
{
int prec;
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;
const char *const incr_error = _("register stride must be 1 or 2");
const char *const type_error = _("mismatched element/structure types in list");
struct neon_typed_alias firsttype;
+ firsttype.defined = 0;
+ firsttype.eltype.type = NT_invtype;
+ firsttype.eltype.size = -1;
+ firsttype.index = -1;
if (skip_past_char (&ptr, '{') == SUCCESS)
leading_brace = 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)
{
}
name = xstrdup (str);
- new_reg = (struct reg_entry *) xmalloc (sizeof (struct reg_entry));
+ new_reg = XNEW (struct reg_entry);
new_reg->name = name;
new_reg->number = number;
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));
+ reg->neon = XNEW (struct neon_typed_alias);
*reg->neon = *atype;
}
}
nlen = strlen (newname);
#endif
- nbuf = (char *) alloca (nlen + 1);
+ nbuf = xmalloc (nlen + 1);
memcpy (nbuf, newname, nlen);
nbuf[nlen] = '\0';
the artificial FOO alias because it has already been created by the
first .req. */
if (insert_reg_alias (nbuf, old->number, old->type) == NULL)
- return TRUE;
+ {
+ free (nbuf);
+ return TRUE;
+ }
}
for (p = nbuf; *p; p++)
insert_reg_alias (nbuf, old->number, old->type);
}
+ free (nbuf);
return TRUE;
}
/* 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
namelen = strlen (newname);
#endif
- namebuf = (char *) alloca (namelen + 1);
+ namebuf = xmalloc (namelen + 1);
strncpy (namebuf, newname, namelen);
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);
+ free (namebuf);
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);
else if (reg->builtin)
- as_warn (_("ignoring attempt to undefine built-in register '%s'"),
+ as_warn (_("ignoring attempt to use .unreq on fixed register name: '%s'"),
name);
else
{
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.
/* Set the mapping state to STATE. Only call this when about to
emit some STATE bytes to the file. */
+#define TRANSITION(from, to) (mapstate == (from) && state == (to))
void
mapping_state (enum mstate state)
{
enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
-#define TRANSITION(from, to) (mapstate == (from) && state == (to))
-
if (mapstate == state)
/* The mapping symbol has already been emitted.
There is nothing else to do. */
return;
- else if (TRANSITION (MAP_UNDEFINED, MAP_DATA))
- /* This case will be evaluated later in the next else. */
- return;
- else if (TRANSITION (MAP_UNDEFINED, MAP_ARM)
- || 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. */
- 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);
- }
+ 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. */
+ return;
mapping_state_2 (state, 0);
-#undef TRANSITION
}
/* Same as mapping_state, but MAX_CHARS bytes have already been
There is nothing else to do. */
return;
+ if (TRANSITION (MAP_UNDEFINED, MAP_ARM)
+ || TRANSITION (MAP_UNDEFINED, MAP_THUMB))
+ {
+ 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);
+ }
+
seg_info (now_seg)->tc_segment_info_data.mapstate = state;
make_mapping_symbol (state, (valueT) frag_now_fix () - max_chars, frag_now);
}
+#undef TRANSITION
#else
#define mapping_state(x) ((void)0)
#define mapping_state_2(x, y) ((void)0)
if (S_IS_LOCAL (symbolP) || name[0] == '.')
return symbolP;
- real_start = ACONCAT ((STUB_NAME, name, NULL));
+ real_start = concat (STUB_NAME, name, NULL);
new_target = symbol_find (real_start);
+ free (real_start);
if (new_target == NULL)
{
/* Especial apologies for the random logic:
This just grew, and could be parsed much more simply!
Dean - in haste. */
- name = input_line_pointer;
- delim = get_symbol_end ();
+ delim = get_symbol_name (& name);
end_name = input_line_pointer;
- *end_name = delim;
+ (void) restore_line_pointer (delim);
if (*input_line_pointer != ',')
{
{
char *name, delim;
- name = input_line_pointer;
- delim = get_symbol_end ();
+ delim = get_symbol_name (& name);
if (!strcasecmp (name, "unified"))
unified_syntax = TRUE;
as_bad (_("unrecognized syntax mode \"%s\""), name);
return;
}
- *input_line_pointer = delim;
+ (void) restore_line_pointer (delim);
demand_empty_rest_of_line ();
}
/* Directives: sectioning and alignment. */
-/* Same as s_align_ptwo but align 0 => align 2. */
-
-static void
-s_align (int unused ATTRIBUTE_UNUSED)
-{
- int temp;
- bfd_boolean fill_p;
- long temp_fill;
- long max_alignment = 15;
-
- temp = get_absolute_expression ();
- if (temp > max_alignment)
- as_bad (_("alignment too large: %d assumed"), temp = max_alignment);
- else if (temp < 0)
- {
- as_bad (_("alignment negative. 0 assumed."));
- temp = 0;
- }
-
- if (*input_line_pointer == ',')
- {
- input_line_pointer++;
- temp_fill = get_absolute_expression ();
- fill_p = TRUE;
- }
- else
- {
- fill_p = FALSE;
- temp_fill = 0;
- }
-
- if (!temp)
- temp = 2;
-
- /* Only make a frag if we HAVE to. */
- if (temp && !need_pass_2)
- {
- if (!fill_p && subseg_text_p (now_seg))
- frag_align_code (temp, 0);
- else
- frag_align (temp, (int) temp_fill, 0);
- }
- demand_empty_rest_of_line ();
-
- record_alignment (now_seg, temp);
-}
-
static void
s_bss (int ignore ATTRIBUTE_UNUSED)
{
demand_empty_rest_of_line ();
}
+/* Directives: CodeComposer Studio. */
+
+/* .ref (for CodeComposer Studio syntax only). */
+static void
+s_ccs_ref (int unused ATTRIBUTE_UNUSED)
+{
+ if (codecomposer_syntax)
+ ignore_rest_of_line ();
+ else
+ as_bad (_(".ref pseudo-op only available with -mccs flag."));
+}
+
+/* If name is not NULL, then it is used for marking the beginning of a
+ function, wherease if it is NULL then it means the function end. */
+static void
+asmfunc_debug (const char * name)
+{
+ static const char * last_name = NULL;
+
+ if (name != NULL)
+ {
+ gas_assert (last_name == NULL);
+ last_name = name;
+
+ if (debug_type == DEBUG_STABS)
+ stabs_generate_asm_func (name, name);
+ }
+ else
+ {
+ gas_assert (last_name != NULL);
+
+ if (debug_type == DEBUG_STABS)
+ stabs_generate_asm_endfunc (last_name, last_name);
+
+ last_name = NULL;
+ }
+}
+
+static void
+s_ccs_asmfunc (int unused ATTRIBUTE_UNUSED)
+{
+ if (codecomposer_syntax)
+ {
+ switch (asmfunc_state)
+ {
+ case OUTSIDE_ASMFUNC:
+ asmfunc_state = WAITING_ASMFUNC_NAME;
+ break;
+
+ case WAITING_ASMFUNC_NAME:
+ as_bad (_(".asmfunc repeated."));
+ break;
+
+ case WAITING_ENDASMFUNC:
+ as_bad (_(".asmfunc without function."));
+ break;
+ }
+ demand_empty_rest_of_line ();
+ }
+ else
+ as_bad (_(".asmfunc pseudo-op only available with -mccs flag."));
+}
+
+static void
+s_ccs_endasmfunc (int unused ATTRIBUTE_UNUSED)
+{
+ if (codecomposer_syntax)
+ {
+ switch (asmfunc_state)
+ {
+ case OUTSIDE_ASMFUNC:
+ as_bad (_(".endasmfunc without a .asmfunc."));
+ break;
+
+ case WAITING_ASMFUNC_NAME:
+ as_bad (_(".endasmfunc without function."));
+ break;
+
+ case WAITING_ENDASMFUNC:
+ asmfunc_state = OUTSIDE_ASMFUNC;
+ asmfunc_debug (NULL);
+ break;
+ }
+ demand_empty_rest_of_line ();
+ }
+ else
+ as_bad (_(".endasmfunc pseudo-op only available with -mccs flag."));
+}
+
+static void
+s_ccs_def (int name)
+{
+ if (codecomposer_syntax)
+ s_globl (name);
+ else
+ as_bad (_(".def pseudo-op only available with -mccs flag."));
+}
+
/* Directives: Literal pools. */
static literal_pool *
if (pool == NULL)
{
/* Create a new pool. */
- pool = (literal_pool *) xmalloc (sizeof (* pool));
+ pool = XNEW (literal_pool);
if (! pool)
return NULL;
pool->sub_section = now_subseg;
pool->next = list_of_pools;
pool->symbol = NULL;
+ pool->alignment = 2;
/* Add it to the list. */
list_of_pools = pool;
structure to the relevant literal pool. */
static int
-add_to_lit_pool (void)
+add_to_lit_pool (unsigned int nbytes)
{
+#define PADDING_SLOT 0x1
+#define LIT_ENTRY_SIZE_MASK 0xFF
literal_pool * pool;
- unsigned int entry;
+ unsigned int entry, pool_size = 0;
+ bfd_boolean padding_slot_p = FALSE;
+ unsigned imm1 = 0;
+ unsigned imm2 = 0;
+
+ if (nbytes == 8)
+ {
+ imm1 = inst.operands[1].imm;
+ imm2 = (inst.operands[1].regisimm ? inst.operands[1].reg
+ : inst.reloc.exp.X_unsigned ? 0
+ : ((bfd_int64_t) inst.operands[1].imm) >> 32);
+ if (target_big_endian)
+ {
+ imm1 = imm2;
+ imm2 = inst.operands[1].imm;
+ }
+ }
pool = find_or_make_literal_pool ();
/* Check if this literal value is already in the pool. */
for (entry = 0; entry < pool->next_free_entry; entry ++)
{
- if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
- && (inst.reloc.exp.X_op == O_constant)
- && (pool->literals[entry].X_add_number
- == inst.reloc.exp.X_add_number)
- && (pool->literals[entry].X_unsigned
- == inst.reloc.exp.X_unsigned))
+ if (nbytes == 4)
+ {
+ if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
+ && (inst.reloc.exp.X_op == O_constant)
+ && (pool->literals[entry].X_add_number
+ == inst.reloc.exp.X_add_number)
+ && (pool->literals[entry].X_md == nbytes)
+ && (pool->literals[entry].X_unsigned
+ == inst.reloc.exp.X_unsigned))
+ break;
+
+ if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
+ && (inst.reloc.exp.X_op == O_symbol)
+ && (pool->literals[entry].X_add_number
+ == inst.reloc.exp.X_add_number)
+ && (pool->literals[entry].X_add_symbol
+ == inst.reloc.exp.X_add_symbol)
+ && (pool->literals[entry].X_op_symbol
+ == inst.reloc.exp.X_op_symbol)
+ && (pool->literals[entry].X_md == nbytes))
+ break;
+ }
+ else if ((nbytes == 8)
+ && !(pool_size & 0x7)
+ && ((entry + 1) != pool->next_free_entry)
+ && (pool->literals[entry].X_op == O_constant)
+ && (pool->literals[entry].X_add_number == (offsetT) imm1)
+ && (pool->literals[entry].X_unsigned
+ == inst.reloc.exp.X_unsigned)
+ && (pool->literals[entry + 1].X_op == O_constant)
+ && (pool->literals[entry + 1].X_add_number == (offsetT) imm2)
+ && (pool->literals[entry + 1].X_unsigned
+ == inst.reloc.exp.X_unsigned))
break;
- if ((pool->literals[entry].X_op == inst.reloc.exp.X_op)
- && (inst.reloc.exp.X_op == O_symbol)
- && (pool->literals[entry].X_add_number
- == inst.reloc.exp.X_add_number)
- && (pool->literals[entry].X_add_symbol
- == inst.reloc.exp.X_add_symbol)
- && (pool->literals[entry].X_op_symbol
- == inst.reloc.exp.X_op_symbol))
+ padding_slot_p = ((pool->literals[entry].X_md >> 8) == PADDING_SLOT);
+ if (padding_slot_p && (nbytes == 4))
break;
+
+ pool_size += 4;
}
/* Do we need to create a new entry? */
return FAIL;
}
- pool->literals[entry] = inst.reloc.exp;
- pool->next_free_entry += 1;
- }
+ if (nbytes == 8)
+ {
+ /* For 8-byte entries, we align to an 8-byte boundary,
+ and split it into two 4-byte entries, because on 32-bit
+ host, 8-byte constants are treated as big num, thus
+ saved in "generic_bignum" which will be overwritten
+ by later assignments.
+
+ We also need to make sure there is enough space for
+ the split.
+
+ We also check to make sure the literal operand is a
+ constant number. */
+ if (!(inst.reloc.exp.X_op == O_constant
+ || inst.reloc.exp.X_op == O_big))
+ {
+ inst.error = _("invalid type for literal pool");
+ return FAIL;
+ }
+ else if (pool_size & 0x7)
+ {
+ if ((entry + 2) >= MAX_LITERAL_POOL_SIZE)
+ {
+ inst.error = _("literal pool overflow");
+ return FAIL;
+ }
- inst.reloc.exp.X_op = O_symbol;
- inst.reloc.exp.X_add_number = ((int) entry) * 4;
- inst.reloc.exp.X_add_symbol = pool->symbol;
+ pool->literals[entry] = inst.reloc.exp;
+ pool->literals[entry].X_add_number = 0;
+ pool->literals[entry++].X_md = (PADDING_SLOT << 8) | 4;
+ pool->next_free_entry += 1;
+ pool_size += 4;
+ }
+ else if ((entry + 1) >= MAX_LITERAL_POOL_SIZE)
+ {
+ inst.error = _("literal pool overflow");
+ return FAIL;
+ }
+
+ pool->literals[entry] = inst.reloc.exp;
+ pool->literals[entry].X_op = O_constant;
+ pool->literals[entry].X_add_number = imm1;
+ pool->literals[entry].X_unsigned = inst.reloc.exp.X_unsigned;
+ pool->literals[entry++].X_md = 4;
+ pool->literals[entry] = inst.reloc.exp;
+ pool->literals[entry].X_op = O_constant;
+ pool->literals[entry].X_add_number = imm2;
+ pool->literals[entry].X_unsigned = inst.reloc.exp.X_unsigned;
+ pool->literals[entry].X_md = 4;
+ pool->alignment = 3;
+ pool->next_free_entry += 1;
+ }
+ else
+ {
+ pool->literals[entry] = inst.reloc.exp;
+ pool->literals[entry].X_md = 4;
+ }
+
+#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;
+ }
+ else if (padding_slot_p)
+ {
+ pool->literals[entry] = inst.reloc.exp;
+ pool->literals[entry].X_md = nbytes;
+ }
+
+ inst.reloc.exp.X_op = O_symbol;
+ inst.reloc.exp.X_add_number = pool_size;
+ inst.reloc.exp.X_add_symbol = pool->symbol;
return SUCCESS;
}
+bfd_boolean
+tc_start_label_without_colon (void)
+{
+ bfd_boolean ret = TRUE;
+
+ if (codecomposer_syntax && asmfunc_state == WAITING_ASMFUNC_NAME)
+ {
+ const char *label = input_line_pointer;
+
+ while (!is_end_of_line[(int) label[-1]])
+ --label;
+
+ if (*label == '.')
+ {
+ as_bad (_("Invalid label '%s'"), label);
+ ret = FALSE;
+ }
+
+ asmfunc_debug (label);
+
+ asmfunc_state = WAITING_ENDASMFUNC;
+ }
+
+ return ret;
+}
+
/* Can't use symbol_new here, so have to create a symbol and then at
a later date assign it a value. Thats what these functions do. */
valueT valu, /* Symbol value. */
fragS * frag) /* Associated fragment. */
{
- unsigned int name_length;
+ size_t name_length;
char * preserved_copy_of_name;
name_length = strlen (name) + 1; /* +1 for \0. */
#endif /* DEBUG_SYMS */
}
-
static void
s_ltorg (int ignored ATTRIBUTE_UNUSED)
{
|| pool->next_free_entry == 0)
return;
- mapping_state (MAP_DATA);
-
/* Align pool as you have word accesses.
Only make a frag if we have to. */
if (!need_pass_2)
- frag_align (2, 0, 0);
+ frag_align (pool->alignment, 0, 0);
record_alignment (now_seg, 2);
+#ifdef OBJ_ELF
+ seg_info (now_seg)->tc_segment_info_data.mapstate = MAP_DATA;
+ make_mapping_symbol (MAP_DATA, (valueT) frag_now_fix (), frag_now);
+#endif
sprintf (sym_name, "$$lit_\002%x", pool->id);
symbol_locate (pool->symbol, sym_name, now_seg,
#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]),
+ pool->literals[entry].X_md & LIT_ENTRY_SIZE_MASK);
+ }
/* 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)
XXX Surely there is a cleaner way to do this. */
char *p = input_line_pointer;
int offset;
- char *save_buf = (char *) alloca (input_line_pointer - base);
+ char *save_buf = XNEWVEC (char, input_line_pointer - base);
+
memcpy (save_buf, base, input_line_pointer - base);
memmove (base + (input_line_pointer - before_reloc),
base, before_reloc - base);
memcpy (base, save_buf, p - base);
offset = nbytes - size;
- p = frag_more ((int) nbytes);
+ p = frag_more (nbytes);
+ memset (p, 0, nbytes);
fix_new_exp (frag_now, p - frag_now->fr_literal + offset,
size, &exp, 0, (enum bfd_reloc_code_real) reloc);
+ free (save_buf);
}
}
}
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. */
if (unwind.personality_routine || unwind.personality_index != -1)
as_bad (_("duplicate .personality directive"));
- name = input_line_pointer;
- c = get_symbol_end ();
+ c = get_symbol_name (& name);
p = input_line_pointer;
+ if (c == '"')
+ ++ input_line_pointer;
unwind.personality_routine = symbol_find_or_make (name);
*p = c;
demand_empty_rest_of_line ();
}
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 ();
s_arm_unwind_save_fpa (reg->number);
return;
- case REG_TYPE_RN: s_arm_unwind_save_core (); return;
+ 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;
- case REG_TYPE_MMXWR: s_arm_unwind_save_mmxwr (); return;
- case REG_TYPE_MMXWCG: s_arm_unwind_save_mmxwcg (); return;
default:
as_bad (_(".unwind_save does not support this kind of register"));
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;
{ "qn", s_qn, 0 },
{ "unreq", s_unreq, 0 },
{ "bss", s_bss, 0 },
- { "align", s_align, 0 },
+ { "align", s_align_ptwo, 2 },
{ "arm", s_arm, 0 },
{ "thumb", s_thumb, 0 },
{ "code", s_code, 0 },
#ifdef TE_PE
{"secrel32", pe_directive_secrel, 0},
#endif
+
+ /* These are for compatibility with CodeComposer Studio. */
+ {"ref", s_ccs_ref, 0},
+ {"def", s_ccs_def, 0},
+ {"asmfunc", s_ccs_asmfunc, 0},
+ {"endasmfunc", s_ccs_endasmfunc, 0},
+
{ 0, 0, 0 }
};
\f
instructions. Puts the result directly in inst.operands[i]. */
static int
-parse_big_immediate (char **str, int i)
+parse_big_immediate (char **str, int i, expressionS *in_exp,
+ bfd_boolean allow_symbol_p)
{
expressionS exp;
+ expressionS *exp_p = in_exp ? in_exp : &exp;
char *ptr = *str;
- my_get_expression (&exp, &ptr, GE_OPT_PREFIX_BIG);
+ my_get_expression (exp_p, &ptr, GE_OPT_PREFIX_BIG);
- if (exp.X_op == O_constant)
+ if (exp_p->X_op == O_constant)
{
- inst.operands[i].imm = exp.X_add_number & 0xffffffff;
+ inst.operands[i].imm = exp_p->X_add_number & 0xffffffff;
/* If we're on a 64-bit host, then a 64-bit number can be returned using
O_constant. We have to be careful not to break compilation for
32-bit X_add_number, though. */
- if ((exp.X_add_number & ~0xffffffffl) != 0)
+ if ((exp_p->X_add_number & ~(offsetT)(0xffffffffU)) != 0)
{
- /* X >> 32 is illegal if sizeof (exp.X_add_number) == 4. */
- inst.operands[i].reg = ((exp.X_add_number >> 16) >> 16) & 0xffffffff;
+ /* X >> 32 is illegal if sizeof (exp_p->X_add_number) == 4. */
+ inst.operands[i].reg = (((exp_p->X_add_number >> 16) >> 16)
+ & 0xffffffff);
inst.operands[i].regisimm = 1;
}
}
- else if (exp.X_op == O_big
- && LITTLENUM_NUMBER_OF_BITS * exp.X_add_number > 32)
+ else if (exp_p->X_op == O_big
+ && LITTLENUM_NUMBER_OF_BITS * exp_p->X_add_number > 32)
{
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.
PR 11972: Bignums can now be sign-extended to the
size of a .octa so check that the out of range bits
are all zero or all one. */
- if (LITTLENUM_NUMBER_OF_BITS * exp.X_add_number > 64)
+ if (LITTLENUM_NUMBER_OF_BITS * exp_p->X_add_number > 64)
{
LITTLENUM_TYPE m = -1;
&& generic_bignum[parts * 2] != m)
return FAIL;
- for (j = parts * 2 + 1; j < (unsigned) exp.X_add_number; j++)
+ for (j = parts * 2 + 1; j < (unsigned) exp_p->X_add_number; j++)
if (generic_bignum[j] != generic_bignum[j-1])
return FAIL;
}
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 if (!(exp_p->X_op == O_symbol && allow_symbol_p))
return FAIL;
*str = ptr;
{
/* FIXME: 5 = X_PRECISION, should be #define'd where we can use it.
Ditto for 15. */
- if (gen_to_words (words, 5, (long) 15) == 0)
+#define X_PRECISION 5
+#define E_PRECISION 15L
+ if (gen_to_words (words, X_PRECISION, E_PRECISION) == 0)
{
for (i = 0; i < NUM_FLOAT_VALS; i++)
{
return (imm & 0x7ffff) == 0 && ((imm & 0x7e000000) ^ bs) == 0;
}
+
+/* Detect the presence of a floating point or integer zero constant,
+ i.e. #0.0 or #0. */
+
+static bfd_boolean
+parse_ifimm_zero (char **in)
+{
+ int error_code;
+
+ if (!is_immediate_prefix (**in))
+ return FALSE;
+
+ ++*in;
+
+ /* Accept #0x0 as a synonym for #0. */
+ if (strncmp (*in, "0x", 2) == 0)
+ {
+ int val;
+ if (parse_immediate (in, &val, 0, 0, TRUE) == FAIL)
+ return FALSE;
+ return TRUE;
+ }
+
+ error_code = atof_generic (in, ".", EXP_CHARS,
+ &generic_floating_point_number);
+
+ if (!error_code
+ && generic_floating_point_number.sign == '+'
+ && (generic_floating_point_number.low
+ > generic_floating_point_number.leader))
+ return TRUE;
+
+ return FALSE;
+}
+
/* Parse an 8-bit "quarter-precision" floating point number of the form:
0baBbbbbbc defgh000 00000000 00000000.
The zero and minus-zero cases need special handling, since they can't be
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;
BFD_RELOC_ARM_ALU_SB_G2, /* ALU */
BFD_RELOC_ARM_LDR_SB_G2, /* LDR */
BFD_RELOC_ARM_LDRS_SB_G2, /* LDRS */
- BFD_RELOC_ARM_LDC_SB_G2 } }; /* LDC */
+ BFD_RELOC_ARM_LDC_SB_G2 }, /* LDC */
+ /* Absolute thumb alu relocations. */
+ { "lower0_7",
+ BFD_RELOC_ARM_THUMB_ALU_ABS_G0_NC,/* ALU. */
+ 0, /* LDR. */
+ 0, /* LDRS. */
+ 0 }, /* LDC. */
+ { "lower8_15",
+ BFD_RELOC_ARM_THUMB_ALU_ABS_G1_NC,/* ALU. */
+ 0, /* LDR. */
+ 0, /* LDRS. */
+ 0 }, /* LDC. */
+ { "upper0_7",
+ BFD_RELOC_ARM_THUMB_ALU_ABS_G2_NC,/* ALU. */
+ 0, /* LDR. */
+ 0, /* LDRS. */
+ 0 }, /* LDC. */
+ { "upper8_15",
+ BFD_RELOC_ARM_THUMB_ALU_ABS_G3_NC,/* ALU. */
+ 0, /* LDR. */
+ 0, /* LDRS. */
+ 0 } }; /* LDC. */
/* Given the address of a pointer pointing to the textual name of a group
relocation as may appear in assembler source, attempt to find its details
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;
inst.operands[i].reg = REG_PC;
inst.operands[i].isreg = 1;
inst.operands[i].preind = 1;
- }
- /* Otherwise a load-constant pseudo op, no special treatment needed here. */
- if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
+ if (my_get_expression (&inst.reloc.exp, &p, GE_OPT_PREFIX_BIG))
+ return PARSE_OPERAND_FAIL;
+ }
+ else if (parse_big_immediate (&p, i, &inst.reloc.exp,
+ /*allow_symbol_p=*/TRUE))
return PARSE_OPERAND_FAIL;
*str = p;
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 (ARM_FEATURE_CORE_EQUAL (selected_cpu, arm_arch_any))
+ 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;
}
+/* Record a use of the given feature. */
+static void
+record_feature_use (const arm_feature_set *feature)
+{
+ 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);
+}
+
+/* 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.
+ */
+ record_feature_use (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;
- 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>, #<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, NULL, /*allow_symbol_p=*/FALSE)
+ == SUCCESS)
+ /* 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_RNDQ_I0, /* Neon D or Q reg, or immediate zero. */
OP_RVSD_I0, /* VFP S or D reg, or immediate zero. */
+ OP_RSVD_FI0, /* VFP S or D reg, or floating point immediate zero. */
OP_RR_RNSC, /* ARM reg or Neon scalar. */
OP_RNSDQ_RNSC, /* Vector S, D or Q reg, or Neon scalar. */
OP_RNDQ_RNSC, /* Neon D or Q reg, or Neon scalar. */
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 (val == FAIL && ! ISALPHA (*str)) \
+ goto immediate; \
+ if (val == FAIL \
+ /* ISB can only take SY as an option. */ \
+ || ((inst.instruction & 0xf0) == 0x60 \
+ && val != 0xf)) \
{ \
- if (ISALPHA (*str)) \
- goto failure; \
- else \
- goto immediate; \
- } \
- else \
- { \
- 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;
+
+ /* 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_RSVD_FI0:
+ {
+ po_reg_or_goto (REG_TYPE_VFSD, try_ifimm0);
+ break;
+ try_ifimm0:
+ if (parse_ifimm_zero (&str))
+ inst.operands[i].imm = 0;
+ else
+ {
+ inst.error
+ = _("only floating point zero is allowed as immediate value");
+ goto failure;
+ }
+ }
+ 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, NULL, /*allow_symbol_p=*/FALSE)
+ == 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 ('[');
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));
break;
val = parse_reg_list (&str);
if (*str == '^')
{
- inst.operands[1].writeback = 1;
+ inst.operands[i].writeback = 1;
str++;
}
break;
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;
#define warn_deprecated_sp(reg) \
do \
if (warn_on_deprecated && reg == REG_SP) \
- as_warn (_("use of r13 is deprecated")); \
+ as_tsktsk (_("use of r13 is deprecated")); \
while (0)
/* Functions for operand encoding. ARM, then Thumb. */
-#define rotate_left(v, n) (v << n | v >> (32 - n))
+#define rotate_left(v, n) (v << (n & 31) | v >> ((32 - n) & 31))
+
+/* If the current inst is scalar ARMv8.2 fp16 instruction, do special encoding.
+
+ The only binary encoding difference is the Coprocessor number. Coprocessor
+ 9 is used for half-precision calculations or conversions. The format of the
+ instruction is the same as the equivalent Coprocessor 10 instuction that
+ exists for Single-Precision operation. */
+
+static void
+do_scalar_fp16_v82_encode (void)
+{
+ if (inst.cond != COND_ALWAYS)
+ as_warn (_("ARMv8.2 scalar fp16 instruction cannot be conditional,"
+ " the behaviour is UNPREDICTABLE"));
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_fp16),
+ _(BAD_FP16));
+
+ inst.instruction = (inst.instruction & 0xfffff0ff) | 0x900;
+ mark_feature_used (&arm_ext_fp16);
+}
/* If VAL can be encoded in the immediate field of an ARM instruction,
return the encoded form. Otherwise, return FAIL. */
{
unsigned int a, i;
- for (i = 0; i < 32; i += 2)
+ if (val <= 0xff)
+ return val;
+
+ for (i = 2; i < 32; i += 2)
if ((a = rotate_left (val, i)) <= 0xff)
return a | (i << 7); /* 12-bit pack: [shift-cnt,const]. */
&& 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 (warn_on_deprecated
&& !is_load
&& ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v7))
- as_warn (_("use of PC in this instruction is deprecated"));
+ as_tsktsk (_("use of PC in this instruction is deprecated"));
}
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;
+ }
}
}
-/* inst.operands[i] was set up by parse_address. Encode it into an
- ARM-format instruction. Reject all forms which cannot be encoded
- into a coprocessor load/store instruction. If wb_ok is false,
- reject use of writeback; if unind_ok is false, reject use of
- unindexed addressing. If reloc_override is not 0, use it instead
- of BFD_ARM_CP_OFF_IMM, unless the initial relocation is a group one
- (in which case it is preserved). */
+/* Write immediate bits [7:0] to the following locations:
-static int
-encode_arm_cp_address (int i, int wb_ok, int unind_ok, int reloc_override)
-{
- inst.instruction |= inst.operands[i].reg << 16;
+ |28/24|23 19|18 16|15 4|3 0|
+ | a |x x x x x|b c d|x x x x x x x x x x x x|e f g h|
- gas_assert (!(inst.operands[i].preind && inst.operands[i].postind));
+ This function is used by VMOV/VMVN/VORR/VBIC. */
- if (!inst.operands[i].preind && !inst.operands[i].postind) /* unindexed */
- {
- gas_assert (!inst.operands[i].writeback);
- if (!unind_ok)
- {
- inst.error = _("instruction does not support unindexed addressing");
- return FAIL;
- }
- inst.instruction |= inst.operands[i].imm;
- inst.instruction |= INDEX_UP;
- return SUCCESS;
- }
+static void
+neon_write_immbits (unsigned immbits)
+{
+ inst.instruction |= immbits & 0xf;
+ inst.instruction |= ((immbits >> 4) & 0x7) << 16;
+ inst.instruction |= ((immbits >> 7) & 0x1) << (thumb_mode ? 28 : 24);
+}
- if (inst.operands[i].preind)
- inst.instruction |= PRE_INDEX;
+/* Invert low-order SIZE bits of XHI:XLO. */
- if (inst.operands[i].writeback)
- {
- if (inst.operands[i].reg == REG_PC)
- {
- inst.error = _("pc may not be used with write-back");
- return FAIL;
- }
- if (!wb_ok)
- {
- inst.error = _("instruction does not support writeback");
- return FAIL;
- }
- inst.instruction |= WRITE_BACK;
- }
+static void
+neon_invert_size (unsigned *xlo, unsigned *xhi, int size)
+{
+ unsigned immlo = xlo ? *xlo : 0;
+ unsigned immhi = xhi ? *xhi : 0;
- 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)
+ switch (size)
{
- if (thumb_mode)
- inst.reloc.type = BFD_RELOC_ARM_T32_CP_OFF_IMM;
- else
- inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
- }
-
- return SUCCESS;
-}
-
-/* inst.reloc.exp describes an "=expr" load pseudo-operation.
- Determine whether it can be performed with a move instruction; if
- it can, convert inst.instruction to that move instruction and
- return TRUE; if it can't, convert inst.instruction to a literal-pool
- load and return FALSE. If this is not a valid thing to do in the
- current context, set inst.error and return TRUE.
+ case 8:
+ immlo = (~immlo) & 0xff;
+ break;
- inst.operands[i] describes the destination register. */
+ case 16:
+ immlo = (~immlo) & 0xffff;
+ break;
-static bfd_boolean
-move_or_literal_pool (int i, bfd_boolean thumb_p, bfd_boolean mode_3)
-{
- unsigned long tbit;
+ case 64:
+ immhi = (~immhi) & 0xffffffff;
+ /* fall through. */
- if (thumb_p)
- tbit = (inst.instruction > 0xffff) ? THUMB2_LOAD_BIT : THUMB_LOAD_BIT;
- else
- tbit = LOAD_BIT;
+ case 32:
+ immlo = (~immlo) & 0xffffffff;
+ break;
- if ((inst.instruction & tbit) == 0)
- {
- inst.error = _("invalid pseudo operation");
- return TRUE;
- }
- if (inst.reloc.exp.X_op != O_constant && inst.reloc.exp.X_op != O_symbol)
- {
- inst.error = _("constant expression expected");
- return TRUE;
+ default:
+ abort ();
}
- if (inst.reloc.exp.X_op == O_constant)
- {
- if (thumb_p)
- {
- if (!unified_syntax && (inst.reloc.exp.X_add_number & ~0xFF) == 0)
- {
- /* This can be done with a mov(1) instruction. */
- inst.instruction = T_OPCODE_MOV_I8 | (inst.operands[i].reg << 8);
- inst.instruction |= inst.reloc.exp.X_add_number;
- return TRUE;
- }
- }
- else
- {
- int value = encode_arm_immediate (inst.reloc.exp.X_add_number);
- if (value != FAIL)
- {
- /* This can be done with a mov instruction. */
- inst.instruction &= LITERAL_MASK;
- inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
- inst.instruction |= value & 0xfff;
- return TRUE;
- }
- value = encode_arm_immediate (~inst.reloc.exp.X_add_number);
- if (value != FAIL)
- {
- /* This can be done with a mvn instruction. */
- inst.instruction &= LITERAL_MASK;
- inst.instruction |= INST_IMMEDIATE | (OPCODE_MVN << DATA_OP_SHIFT);
- inst.instruction |= value & 0xfff;
- return TRUE;
- }
- }
- }
+ if (xlo)
+ *xlo = immlo;
- if (add_to_lit_pool () == FAIL)
- {
- inst.error = _("literal pool insertion failed");
- return TRUE;
- }
- inst.operands[1].reg = REG_PC;
- inst.operands[1].isreg = 1;
- inst.operands[1].preind = 1;
- inst.reloc.pc_rel = 1;
- inst.reloc.type = (thumb_p
- ? BFD_RELOC_ARM_THUMB_OFFSET
- : (mode_3
- ? BFD_RELOC_ARM_HWLITERAL
- : BFD_RELOC_ARM_LITERAL));
- return FALSE;
+ if (xhi)
+ *xhi = immhi;
}
-/* Functions for instruction encoding, sorted by sub-architecture.
- First some generics; their names are taken from the conventional
- bit positions for register arguments in ARM format instructions. */
+/* True if IMM has form 0bAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD for bits
+ A, B, C, D. */
-static void
-do_noargs (void)
+static int
+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);
}
-static void
-do_rd (void)
-{
- inst.instruction |= inst.operands[0].reg << 12;
-}
+/* For immediate of above form, return 0bABCD. */
-static void
-do_rd_rm (void)
+static unsigned
+neon_squash_bits (unsigned imm)
{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg;
+ return (imm & 0x01) | ((imm & 0x0100) >> 7) | ((imm & 0x010000) >> 14)
+ | ((imm & 0x01000000) >> 21);
}
-static void
-do_rd_rn (void)
-{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
-}
+/* Compress quarter-float representation to 0b...000 abcdefgh. */
-static void
-do_rn_rd (void)
+static unsigned
+neon_qfloat_bits (unsigned imm)
{
- inst.instruction |= inst.operands[0].reg << 16;
- inst.instruction |= inst.operands[1].reg << 12;
+ return ((imm >> 19) & 0x7f) | ((imm >> 24) & 0x80);
}
-static void
-do_rd_rm_rn (void)
-{
- unsigned Rn = inst.operands[2].reg;
- /* Enforce restrictions on SWP instruction. */
- if ((inst.instruction & 0x0fbfffff) == 0x01000090)
- {
- constraint (Rn == inst.operands[0].reg || Rn == inst.operands[1].reg,
- _("Rn must not overlap other operands"));
+/* Returns CMODE. IMMBITS [7:0] is set to bits suitable for inserting into
+ the instruction. *OP is passed as the initial value of the op field, and
+ may be set to a different value depending on the constant (i.e.
+ "MOV I64, 0bAAAAAAAABBBB..." which uses OP = 1 despite being MOV not
+ MVN). If the immediate looks like a repeated pattern then also
+ try smaller element sizes. */
- /* 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"));
+static int
+neon_cmode_for_move_imm (unsigned immlo, unsigned immhi, int float_p,
+ unsigned *immbits, int *op, int size,
+ enum neon_el_type type)
+{
+ /* Only permit float immediates (including 0.0/-0.0) if the operand type is
+ float. */
+ if (type == NT_float && !float_p)
+ return FAIL;
+ if (type == NT_float && is_quarter_float (immlo) && immhi == 0)
+ {
+ if (size != 32 || *op == 1)
+ return FAIL;
+ *immbits = neon_qfloat_bits (immlo);
+ return 0xf;
}
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg;
- inst.instruction |= Rn << 16;
-}
-static void
-do_rd_rn_rm (void)
-{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.instruction |= inst.operands[2].reg;
-}
+ if (size == 64)
+ {
+ if (neon_bits_same_in_bytes (immhi)
+ && neon_bits_same_in_bytes (immlo))
+ {
+ if (*op == 1)
+ return FAIL;
+ *immbits = (neon_squash_bits (immhi) << 4)
+ | neon_squash_bits (immlo);
+ *op = 1;
+ return 0xe;
+ }
-static void
-do_rm_rd_rn (void)
-{
- constraint ((inst.operands[2].reg == REG_PC), BAD_PC);
- constraint (((inst.reloc.exp.X_op != O_constant
- && inst.reloc.exp.X_op != O_illegal)
- || inst.reloc.exp.X_add_number != 0),
- BAD_ADDR_MODE);
- inst.instruction |= inst.operands[0].reg;
- inst.instruction |= inst.operands[1].reg << 12;
- inst.instruction |= inst.operands[2].reg << 16;
-}
+ if (immhi != immlo)
+ return FAIL;
+ }
-static void
-do_imm0 (void)
-{
- inst.instruction |= inst.operands[0].imm;
-}
+ if (size >= 32)
+ {
+ if (immlo == (immlo & 0x000000ff))
+ {
+ *immbits = immlo;
+ return 0x0;
+ }
+ else if (immlo == (immlo & 0x0000ff00))
+ {
+ *immbits = immlo >> 8;
+ return 0x2;
+ }
+ else if (immlo == (immlo & 0x00ff0000))
+ {
+ *immbits = immlo >> 16;
+ return 0x4;
+ }
+ else if (immlo == (immlo & 0xff000000))
+ {
+ *immbits = immlo >> 24;
+ return 0x6;
+ }
+ else if (immlo == ((immlo & 0x0000ff00) | 0x000000ff))
+ {
+ *immbits = (immlo >> 8) & 0xff;
+ return 0xc;
+ }
+ else if (immlo == ((immlo & 0x00ff0000) | 0x0000ffff))
+ {
+ *immbits = (immlo >> 16) & 0xff;
+ return 0xd;
+ }
-static void
-do_rd_cpaddr (void)
-{
- inst.instruction |= inst.operands[0].reg << 12;
- encode_arm_cp_address (1, TRUE, TRUE, 0);
-}
+ if ((immlo & 0xffff) != (immlo >> 16))
+ return FAIL;
+ immlo &= 0xffff;
+ }
-/* ARM instructions, in alphabetical order by function name (except
- that wrapper functions appear immediately after the function they
- wrap). */
+ if (size >= 16)
+ {
+ if (immlo == (immlo & 0x000000ff))
+ {
+ *immbits = immlo;
+ return 0x8;
+ }
+ else if (immlo == (immlo & 0x0000ff00))
+ {
+ *immbits = immlo >> 8;
+ return 0xa;
+ }
-/* This is a pseudo-op of the form "adr rd, label" to be converted
- into a relative address of the form "add rd, pc, #label-.-8". */
+ if ((immlo & 0xff) != (immlo >> 8))
+ return FAIL;
+ immlo &= 0xff;
+ }
-static void
-do_adr (void)
-{
- inst.instruction |= (inst.operands[0].reg << 12); /* Rd */
+ if (immlo == (immlo & 0x000000ff))
+ {
+ /* Don't allow MVN with 8-bit immediate. */
+ if (*op == 1)
+ return FAIL;
+ *immbits = immlo;
+ return 0xe;
+ }
- /* Frag hacking will turn this into a sub instruction if the offset turns
- out to be negative. */
- inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
- inst.reloc.pc_rel = 1;
- inst.reloc.exp.X_add_number -= 8;
+ return FAIL;
}
-/* This is a pseudo-op of the form "adrl rd, label" to be converted
- into a relative address of the form:
- add rd, pc, #low(label-.-8)"
- add rd, rd, #high(label-.-8)" */
+#if defined BFD_HOST_64_BIT
+/* Returns TRUE if double precision value V may be cast
+ to single precision without loss of accuracy. */
-static void
-do_adrl (void)
+static bfd_boolean
+is_double_a_single (bfd_int64_t v)
{
- inst.instruction |= (inst.operands[0].reg << 12); /* Rd */
+ int exp = (int)((v >> 52) & 0x7FF);
+ bfd_int64_t mantissa = (v & (bfd_int64_t)0xFFFFFFFFFFFFFULL);
- /* Frag hacking will turn this into a sub instruction if the offset turns
- out to be negative. */
- inst.reloc.type = BFD_RELOC_ARM_ADRL_IMMEDIATE;
- inst.reloc.pc_rel = 1;
- inst.size = INSN_SIZE * 2;
- inst.reloc.exp.X_add_number -= 8;
+ return (exp == 0 || exp == 0x7FF
+ || (exp >= 1023 - 126 && exp <= 1023 + 127))
+ && (mantissa & 0x1FFFFFFFl) == 0;
}
-static void
-do_arit (void)
-{
- if (!inst.operands[1].present)
- inst.operands[1].reg = inst.operands[0].reg;
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- encode_arm_shifter_operand (2);
-}
+/* Returns a double precision value casted to single precision
+ (ignoring the least significant bits in exponent and mantissa). */
-static void
-do_barrier (void)
+static int
+double_to_single (bfd_int64_t v)
{
- if (inst.operands[0].present)
+ int sign = (int) ((v >> 63) & 1l);
+ int exp = (int) ((v >> 52) & 0x7FF);
+ bfd_int64_t mantissa = (v & (bfd_int64_t)0xFFFFFFFFFFFFFULL);
+
+ if (exp == 0x7FF)
+ exp = 0xFF;
+ else
{
- constraint ((inst.instruction & 0xf0) != 0x40
- && inst.operands[0].imm > 0xf
- && inst.operands[0].imm < 0x0,
- _("bad barrier type"));
- inst.instruction |= inst.operands[0].imm;
+ exp = exp - 1023 + 127;
+ if (exp >= 0xFF)
+ {
+ /* Infinity. */
+ exp = 0x7F;
+ mantissa = 0;
+ }
+ else if (exp < 0)
+ {
+ /* No denormalized numbers. */
+ exp = 0;
+ mantissa = 0;
+ }
}
- else
- inst.instruction |= 0xf;
+ mantissa >>= 29;
+ return (sign << 31) | (exp << 23) | mantissa;
}
+#endif /* BFD_HOST_64_BIT */
-static void
-do_bfc (void)
+enum lit_type
{
- unsigned int msb = inst.operands[1].imm + inst.operands[2].imm;
- constraint (msb > 32, _("bit-field extends past end of register"));
- /* The instruction encoding stores the LSB and MSB,
- not the LSB and width. */
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].imm << 7;
- inst.instruction |= (msb - 1) << 16;
-}
+ CONST_THUMB,
+ CONST_ARM,
+ CONST_VEC
+};
-static void
-do_bfi (void)
-{
- unsigned int msb;
+static void do_vfp_nsyn_opcode (const char *);
- /* #0 in second position is alternative syntax for bfc, which is
- the same instruction but with REG_PC in the Rm field. */
- if (!inst.operands[1].isreg)
- inst.operands[1].reg = REG_PC;
+/* inst.reloc.exp describes an "=expr" load pseudo-operation.
+ Determine whether it can be performed with a move instruction; if
+ it can, convert inst.instruction to that move instruction and
+ return TRUE; if it can't, convert inst.instruction to a literal-pool
+ load and return FALSE. If this is not a valid thing to do in the
+ current context, set inst.error and return TRUE.
- msb = inst.operands[2].imm + inst.operands[3].imm;
- constraint (msb > 32, _("bit-field extends past end of register"));
- /* The instruction encoding stores the LSB and MSB,
- not the LSB and width. */
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg;
- inst.instruction |= inst.operands[2].imm << 7;
- inst.instruction |= (msb - 1) << 16;
-}
+ inst.operands[i] describes the destination register. */
-static void
-do_bfx (void)
+static bfd_boolean
+move_or_literal_pool (int i, enum lit_type t, bfd_boolean mode_3)
{
- constraint (inst.operands[2].imm + inst.operands[3].imm > 32,
- _("bit-field extends past end of register"));
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg;
- inst.instruction |= inst.operands[2].imm << 7;
- inst.instruction |= (inst.operands[3].imm - 1) << 16;
-}
-
-/* ARM V5 breakpoint instruction (argument parse)
- BKPT <16 bit unsigned immediate>
- Instruction is not conditional.
- The bit pattern given in insns[] has the COND_ALWAYS condition,
- and it is an error if the caller tried to override that. */
+ unsigned long tbit;
+ bfd_boolean thumb_p = (t == CONST_THUMB);
+ bfd_boolean arm_p = (t == CONST_ARM);
-static void
-do_bkpt (void)
-{
- /* Top 12 of 16 bits to bits 19:8. */
- inst.instruction |= (inst.operands[0].imm & 0xfff0) << 4;
+ if (thumb_p)
+ tbit = (inst.instruction > 0xffff) ? THUMB2_LOAD_BIT : THUMB_LOAD_BIT;
+ else
+ tbit = LOAD_BIT;
- /* Bottom 4 of 16 bits to bits 3:0. */
- inst.instruction |= inst.operands[0].imm & 0xf;
-}
+ if ((inst.instruction & tbit) == 0)
+ {
+ inst.error = _("invalid pseudo operation");
+ return TRUE;
+ }
-static void
-encode_branch (int default_reloc)
-{
- if (inst.operands[0].hasreloc)
+ if (inst.reloc.exp.X_op != O_constant
+ && inst.reloc.exp.X_op != O_symbol
+ && inst.reloc.exp.X_op != O_big)
{
- constraint (inst.operands[0].imm != BFD_RELOC_ARM_PLT32
- && inst.operands[0].imm != BFD_RELOC_ARM_TLS_CALL,
- _("the only valid suffixes here are '(plt)' and '(tlscall)'"));
- inst.reloc.type = inst.operands[0].imm == BFD_RELOC_ARM_PLT32
- ? BFD_RELOC_ARM_PLT32
- : thumb_mode ? BFD_RELOC_ARM_THM_TLS_CALL : BFD_RELOC_ARM_TLS_CALL;
+ inst.error = _("constant expression expected");
+ return TRUE;
}
- else
- inst.reloc.type = (bfd_reloc_code_real_type) default_reloc;
- inst.reloc.pc_rel = 1;
-}
-static void
-do_branch (void)
-{
-#ifdef OBJ_ELF
- if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4)
- encode_branch (BFD_RELOC_ARM_PCREL_JUMP);
- else
+ if (inst.reloc.exp.X_op == O_constant
+ || inst.reloc.exp.X_op == O_big)
+ {
+#if defined BFD_HOST_64_BIT
+ bfd_int64_t v;
+#else
+ offsetT v;
#endif
- encode_branch (BFD_RELOC_ARM_PCREL_BRANCH);
-}
+ if (inst.reloc.exp.X_op == O_big)
+ {
+ LITTLENUM_TYPE w[X_PRECISION];
+ LITTLENUM_TYPE * l;
-static void
-do_bl (void)
-{
-#ifdef OBJ_ELF
- if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4)
- {
- if (inst.cond == COND_ALWAYS)
- encode_branch (BFD_RELOC_ARM_PCREL_CALL);
+ if (inst.reloc.exp.X_add_number == -1)
+ {
+ gen_to_words (w, X_PRECISION, E_PRECISION);
+ l = w;
+ /* FIXME: Should we check words w[2..5] ? */
+ }
+ else
+ l = generic_bignum;
+
+#if defined BFD_HOST_64_BIT
+ v =
+ ((((((((bfd_int64_t) l[3] & LITTLENUM_MASK)
+ << LITTLENUM_NUMBER_OF_BITS)
+ | ((bfd_int64_t) l[2] & LITTLENUM_MASK))
+ << LITTLENUM_NUMBER_OF_BITS)
+ | ((bfd_int64_t) l[1] & LITTLENUM_MASK))
+ << LITTLENUM_NUMBER_OF_BITS)
+ | ((bfd_int64_t) l[0] & LITTLENUM_MASK));
+#else
+ v = ((l[1] & LITTLENUM_MASK) << LITTLENUM_NUMBER_OF_BITS)
+ | (l[0] & LITTLENUM_MASK);
+#endif
+ }
else
- encode_branch (BFD_RELOC_ARM_PCREL_JUMP);
- }
- else
+ v = inst.reloc.exp.X_add_number;
+
+ if (!inst.operands[i].issingle)
+ {
+ if (thumb_p)
+ {
+ /* This can be encoded only for a low register. */
+ if ((v & ~0xFF) == 0 && (inst.operands[i].reg < 8))
+ {
+ /* This can be done with a mov(1) instruction. */
+ inst.instruction = T_OPCODE_MOV_I8 | (inst.operands[i].reg << 8);
+ inst.instruction |= v;
+ return TRUE;
+ }
+
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6t2)
+ || ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6t2_v8m))
+ {
+ /* Check if on thumb2 it can be done with a mov.w, mvn or
+ movw instruction. */
+ unsigned int newimm;
+ bfd_boolean isNegated;
+
+ newimm = encode_thumb32_immediate (v);
+ if (newimm != (unsigned int) FAIL)
+ isNegated = FALSE;
+ else
+ {
+ newimm = encode_thumb32_immediate (~v);
+ if (newimm != (unsigned int) FAIL)
+ isNegated = TRUE;
+ }
+
+ /* The number can be loaded with a mov.w or mvn
+ instruction. */
+ if (newimm != (unsigned int) FAIL
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6t2))
+ {
+ inst.instruction = (0xf04f0000 /* MOV.W. */
+ | (inst.operands[i].reg << 8));
+ /* Change to MOVN. */
+ inst.instruction |= (isNegated ? 0x200000 : 0);
+ inst.instruction |= (newimm & 0x800) << 15;
+ inst.instruction |= (newimm & 0x700) << 4;
+ inst.instruction |= (newimm & 0x0ff);
+ return TRUE;
+ }
+ /* The number can be loaded with a movw instruction. */
+ else if ((v & ~0xFFFF) == 0
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6t2_v8m))
+ {
+ int imm = v & 0xFFFF;
+
+ inst.instruction = 0xf2400000; /* MOVW. */
+ inst.instruction |= (inst.operands[i].reg << 8);
+ inst.instruction |= (imm & 0xf000) << 4;
+ inst.instruction |= (imm & 0x0800) << 15;
+ inst.instruction |= (imm & 0x0700) << 4;
+ inst.instruction |= (imm & 0x00ff);
+ return TRUE;
+ }
+ }
+ }
+ else if (arm_p)
+ {
+ int value = encode_arm_immediate (v);
+
+ if (value != FAIL)
+ {
+ /* This can be done with a mov instruction. */
+ inst.instruction &= LITERAL_MASK;
+ inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
+ inst.instruction |= value & 0xfff;
+ return TRUE;
+ }
+
+ value = encode_arm_immediate (~ v);
+ if (value != FAIL)
+ {
+ /* This can be done with a mvn instruction. */
+ inst.instruction &= LITERAL_MASK;
+ inst.instruction |= INST_IMMEDIATE | (OPCODE_MVN << DATA_OP_SHIFT);
+ inst.instruction |= value & 0xfff;
+ return TRUE;
+ }
+ }
+ else if (t == CONST_VEC)
+ {
+ int op = 0;
+ unsigned immbits = 0;
+ unsigned immlo = inst.operands[1].imm;
+ unsigned immhi = inst.operands[1].regisimm
+ ? inst.operands[1].reg
+ : inst.reloc.exp.X_unsigned
+ ? 0
+ : ((bfd_int64_t)((int) immlo)) >> 32;
+ int cmode = neon_cmode_for_move_imm (immlo, immhi, FALSE, &immbits,
+ &op, 64, NT_invtype);
+
+ if (cmode == FAIL)
+ {
+ neon_invert_size (&immlo, &immhi, 64);
+ op = !op;
+ cmode = neon_cmode_for_move_imm (immlo, immhi, FALSE, &immbits,
+ &op, 64, NT_invtype);
+ }
+
+ if (cmode != FAIL)
+ {
+ inst.instruction = (inst.instruction & VLDR_VMOV_SAME)
+ | (1 << 23)
+ | (cmode << 8)
+ | (op << 5)
+ | (1 << 4);
+
+ /* Fill other bits in vmov encoding for both thumb and arm. */
+ if (thumb_mode)
+ inst.instruction |= (0x7U << 29) | (0xF << 24);
+ else
+ inst.instruction |= (0xFU << 28) | (0x1 << 25);
+ neon_write_immbits (immbits);
+ return TRUE;
+ }
+ }
+ }
+
+ if (t == CONST_VEC)
+ {
+ /* Check if vldr Rx, =constant could be optimized to vmov Rx, #constant. */
+ if (inst.operands[i].issingle
+ && is_quarter_float (inst.operands[1].imm)
+ && ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v3xd))
+ {
+ inst.operands[1].imm =
+ neon_qfloat_bits (v);
+ do_vfp_nsyn_opcode ("fconsts");
+ return TRUE;
+ }
+
+ /* If our host does not support a 64-bit type then we cannot perform
+ the following optimization. This mean that there will be a
+ discrepancy between the output produced by an assembler built for
+ a 32-bit-only host and the output produced from a 64-bit host, but
+ this cannot be helped. */
+#if defined BFD_HOST_64_BIT
+ else if (!inst.operands[1].issingle
+ && ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v3))
+ {
+ if (is_double_a_single (v)
+ && is_quarter_float (double_to_single (v)))
+ {
+ inst.operands[1].imm =
+ neon_qfloat_bits (double_to_single (v));
+ do_vfp_nsyn_opcode ("fconstd");
+ return TRUE;
+ }
+ }
#endif
- encode_branch (BFD_RELOC_ARM_PCREL_BRANCH);
+ }
+ }
+
+ if (add_to_lit_pool ((!inst.operands[i].isvec
+ || inst.operands[i].issingle) ? 4 : 8) == FAIL)
+ return TRUE;
+
+ inst.operands[1].reg = REG_PC;
+ inst.operands[1].isreg = 1;
+ inst.operands[1].preind = 1;
+ inst.reloc.pc_rel = 1;
+ inst.reloc.type = (thumb_p
+ ? BFD_RELOC_ARM_THUMB_OFFSET
+ : (mode_3
+ ? BFD_RELOC_ARM_HWLITERAL
+ : BFD_RELOC_ARM_LITERAL));
+ return FALSE;
}
-/* ARM V5 branch-link-exchange instruction (argument parse)
- BLX <target_addr> ie BLX(1)
- BLX{<condition>} <Rm> ie BLX(2)
- Unfortunately, there are two different opcodes for this mnemonic.
- So, the insns[].value is not used, and the code here zaps values
- into inst.instruction.
- Also, the <target_addr> can be 25 bits, hence has its own reloc. */
+/* inst.operands[i] was set up by parse_address. Encode it into an
+ ARM-format instruction. Reject all forms which cannot be encoded
+ into a coprocessor load/store instruction. If wb_ok is false,
+ reject use of writeback; if unind_ok is false, reject use of
+ unindexed addressing. If reloc_override is not 0, use it instead
+ of BFD_ARM_CP_OFF_IMM, unless the initial relocation is a group one
+ (in which case it is preserved). */
-static void
-do_blx (void)
+static int
+encode_arm_cp_address (int i, int wb_ok, int unind_ok, int reloc_override)
{
- if (inst.operands[0].isreg)
+ if (!inst.operands[i].isreg)
{
- /* Arg is a register; the opcode provided by insns[] is correct.
- It is not illegal to do "blx pc", just useless. */
- if (inst.operands[0].reg == REG_PC)
- as_tsktsk (_("use of r15 in blx in ARM mode is not really useful"));
-
- inst.instruction |= inst.operands[0].reg;
+ /* PR 18256 */
+ if (! inst.operands[0].isvec)
+ {
+ inst.error = _("invalid co-processor operand");
+ return FAIL;
+ }
+ if (move_or_literal_pool (0, CONST_VEC, /*mode_3=*/FALSE))
+ return SUCCESS;
}
- else
+
+ inst.instruction |= inst.operands[i].reg << 16;
+
+ gas_assert (!(inst.operands[i].preind && inst.operands[i].postind));
+
+ if (!inst.operands[i].preind && !inst.operands[i].postind) /* unindexed */
{
- /* Arg is an address; this instruction cannot be executed
- conditionally, and the opcode must be adjusted.
- We retain the BFD_RELOC_ARM_PCREL_BLX till the very end
- where we generate out a BFD_RELOC_ARM_PCREL_CALL instead. */
- constraint (inst.cond != COND_ALWAYS, BAD_COND);
- inst.instruction = 0xfa000000;
- encode_branch (BFD_RELOC_ARM_PCREL_BLX);
+ gas_assert (!inst.operands[i].writeback);
+ if (!unind_ok)
+ {
+ inst.error = _("instruction does not support unindexed addressing");
+ return FAIL;
+ }
+ inst.instruction |= inst.operands[i].imm;
+ inst.instruction |= INDEX_UP;
+ return SUCCESS;
}
-}
-static void
-do_bx (void)
-{
- bfd_boolean want_reloc;
+ if (inst.operands[i].preind)
+ inst.instruction |= PRE_INDEX;
- if (inst.operands[0].reg == REG_PC)
- as_tsktsk (_("use of r15 in bx in ARM mode is not really useful"));
+ if (inst.operands[i].writeback)
+ {
+ if (inst.operands[i].reg == REG_PC)
+ {
+ inst.error = _("pc may not be used with write-back");
+ return FAIL;
+ }
+ if (!wb_ok)
+ {
+ inst.error = _("instruction does not support writeback");
+ return FAIL;
+ }
+ inst.instruction |= WRITE_BACK;
+ }
- inst.instruction |= inst.operands[0].reg;
- /* Output R_ARM_V4BX relocations if is an EABI object that looks like
- it is for ARMv4t or earlier. */
- want_reloc = !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5);
- if (object_arch && !ARM_CPU_HAS_FEATURE (*object_arch, arm_ext_v5))
- want_reloc = TRUE;
+ 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)
+ {
+ if (thumb_mode)
+ inst.reloc.type = BFD_RELOC_ARM_T32_CP_OFF_IMM;
+ else
+ inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+ }
-#ifdef OBJ_ELF
- if (EF_ARM_EABI_VERSION (meabi_flags) < EF_ARM_EABI_VER4)
-#endif
- want_reloc = FALSE;
+ /* Prefer + for zero encoded value. */
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
- if (want_reloc)
- inst.reloc.type = BFD_RELOC_ARM_V4BX;
+ return SUCCESS;
}
+/* Functions for instruction encoding, sorted by sub-architecture.
+ First some generics; their names are taken from the conventional
+ bit positions for register arguments in ARM format instructions. */
-/* ARM v5TEJ. Jump to Jazelle code. */
+static void
+do_noargs (void)
+{
+}
static void
-do_bxj (void)
+do_rd (void)
{
- if (inst.operands[0].reg == REG_PC)
- as_tsktsk (_("use of r15 in bxj is not really useful"));
+ inst.instruction |= inst.operands[0].reg << 12;
+}
+
+static void
+do_rd_rm (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+}
+static void
+do_rm_rn (void)
+{
inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 16;
}
-/* Co-processor data operation:
- CDP{cond} <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>{, <opcode_2>}
- CDP2 <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>{, <opcode_2>} */
static void
-do_cdp (void)
+do_rd_rn (void)
{
- inst.instruction |= inst.operands[0].reg << 8;
- inst.instruction |= inst.operands[1].imm << 20;
- inst.instruction |= inst.operands[2].reg << 12;
- inst.instruction |= inst.operands[3].reg << 16;
- inst.instruction |= inst.operands[4].reg;
- inst.instruction |= inst.operands[5].imm << 5;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
}
static void
-do_cmp (void)
+do_rn_rd (void)
{
inst.instruction |= inst.operands[0].reg << 16;
- encode_arm_shifter_operand (1);
+ inst.instruction |= inst.operands[1].reg << 12;
}
-/* Transfer between coprocessor and ARM registers.
- MRC{cond} <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>{, <opcode_2>}
- MRC2
- MCR{cond}
- MCR2
-
- No special properties. */
-
static void
-do_co_reg (void)
+do_tt (void)
{
- unsigned Rd;
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+}
- Rd = inst.operands[2].reg;
- if (thumb_mode)
+static bfd_boolean
+check_obsolete (const arm_feature_set *feature, const char *msg)
+{
+ if (ARM_CPU_IS_ANY (cpu_variant))
{
- if (inst.instruction == 0xee000010
- || inst.instruction == 0xfe000010)
- /* MCR, MCR2 */
- reject_bad_reg (Rd);
- else
- /* MRC, MRC2 */
- constraint (Rd == REG_SP, BAD_SP);
+ as_tsktsk ("%s", msg);
+ return TRUE;
}
- else
+ else if (ARM_CPU_HAS_FEATURE (cpu_variant, *feature))
{
- /* MCR */
- if (inst.instruction == 0xe000010)
- constraint (Rd == REG_PC, BAD_PC);
+ as_bad ("%s", msg);
+ return TRUE;
}
-
- inst.instruction |= inst.operands[0].reg << 8;
- inst.instruction |= inst.operands[1].imm << 21;
- inst.instruction |= Rd << 12;
- inst.instruction |= inst.operands[3].reg << 16;
- inst.instruction |= inst.operands[4].reg;
- inst.instruction |= inst.operands[5].imm << 5;
+ return FALSE;
}
-/* Transfer between coprocessor register and pair of ARM registers.
- MCRR{cond} <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
- MCRR2
- MRRC{cond}
- MRRC2
-
- Two XScale instructions are special cases of these:
-
- MAR{cond} acc0, <RdLo>, <RdHi> == MCRR{cond} p0, #0, <RdLo>, <RdHi>, c0
- MRA{cond} acc0, <RdLo>, <RdHi> == MRRC{cond} p0, #0, <RdLo>, <RdHi>, c0
-
- Result unpredictable if Rd or Rn is R15. */
-
static void
-do_co_reg2c (void)
+do_rd_rm_rn (void)
{
- unsigned Rd, Rn;
-
- Rd = inst.operands[2].reg;
- Rn = inst.operands[3].reg;
-
- if (thumb_mode)
- {
- reject_bad_reg (Rd);
- reject_bad_reg (Rn);
- }
- else
+ unsigned Rn = inst.operands[2].reg;
+ /* Enforce restrictions on SWP instruction. */
+ if ((inst.instruction & 0x0fbfffff) == 0x01000090)
{
- constraint (Rd == REG_PC, BAD_PC);
- constraint (Rn == REG_PC, BAD_PC);
+ constraint (Rn == inst.operands[0].reg || Rn == inst.operands[1].reg,
+ _("Rn must not overlap other operands"));
+
+ /* 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_tsktsk (_("swp{b} use is deprecated for ARMv6 and ARMv7"));
}
- inst.instruction |= inst.operands[0].reg << 8;
- inst.instruction |= inst.operands[1].imm << 4;
- inst.instruction |= Rd << 12;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
inst.instruction |= Rn << 16;
- inst.instruction |= inst.operands[4].reg;
}
static void
-do_cpsi (void)
+do_rd_rn_rm (void)
{
- inst.instruction |= inst.operands[0].imm << 6;
- if (inst.operands[1].present)
- {
- inst.instruction |= CPSI_MMOD;
- inst.instruction |= inst.operands[1].imm;
- }
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
}
static void
-do_dbg (void)
+do_rm_rd_rn (void)
{
- inst.instruction |= inst.operands[0].imm;
+ constraint ((inst.operands[2].reg == REG_PC), BAD_PC);
+ constraint (((inst.reloc.exp.X_op != O_constant
+ && inst.reloc.exp.X_op != O_illegal)
+ || inst.reloc.exp.X_add_number != 0),
+ BAD_ADDR_MODE);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
}
static void
-do_div (void)
+do_imm0 (void)
{
- unsigned Rd, Rn, Rm;
-
- Rd = inst.operands[0].reg;
- Rn = (inst.operands[1].present
- ? inst.operands[1].reg : Rd);
- Rm = inst.operands[2].reg;
-
- constraint ((Rd == REG_PC), BAD_PC);
- constraint ((Rn == REG_PC), BAD_PC);
- constraint ((Rm == REG_PC), BAD_PC);
-
- inst.instruction |= Rd << 16;
- inst.instruction |= Rn << 0;
- inst.instruction |= Rm << 8;
+ inst.instruction |= inst.operands[0].imm;
}
static void
-do_it (void)
+do_rd_cpaddr (void)
{
- /* There is no IT instruction in ARM mode. We
- process it to do the validation as if in
- thumb mode, just in case the code gets
- assembled for thumb using the unified syntax. */
-
- inst.size = 0;
- if (unified_syntax)
- {
- set_it_insn_type (IT_INSN);
- now_it.mask = (inst.instruction & 0xf) | 0x10;
- now_it.cc = inst.operands[0].imm;
- }
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_cp_address (1, TRUE, TRUE, 0);
}
-static void
-do_ldmstm (void)
-{
- int base_reg = inst.operands[0].reg;
- int range = inst.operands[1].imm;
+/* ARM instructions, in alphabetical order by function name (except
+ that wrapper functions appear immediately after the function they
+ wrap). */
- inst.instruction |= base_reg << 16;
- inst.instruction |= range;
+/* This is a pseudo-op of the form "adr rd, label" to be converted
+ into a relative address of the form "add rd, pc, #label-.-8". */
- if (inst.operands[1].writeback)
- inst.instruction |= LDM_TYPE_2_OR_3;
+static void
+do_adr (void)
+{
+ inst.instruction |= (inst.operands[0].reg << 12); /* Rd */
- if (inst.operands[0].writeback)
- {
- inst.instruction |= WRITE_BACK;
- /* Check for unpredictable uses of writeback. */
- if (inst.instruction & LOAD_BIT)
- {
- /* Not allowed in LDM type 2. */
- if ((inst.instruction & LDM_TYPE_2_OR_3)
- && ((range & (1 << REG_PC)) == 0))
- as_warn (_("writeback of base register is UNPREDICTABLE"));
- /* Only allowed if base reg not in list for other types. */
- else if (range & (1 << base_reg))
- as_warn (_("writeback of base register when in register list is UNPREDICTABLE"));
- }
- else /* STM. */
- {
- /* Not allowed for type 2. */
- if (inst.instruction & LDM_TYPE_2_OR_3)
- as_warn (_("writeback of base register is UNPREDICTABLE"));
- /* Only allowed if base reg not in list, or first in list. */
- else if ((range & (1 << base_reg))
- && (range & ((1 << base_reg) - 1)))
- as_warn (_("if writeback register is in list, it must be the lowest reg in the list"));
- }
- }
+ /* Frag hacking will turn this into a sub instruction if the offset turns
+ out to be negative. */
+ inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+ inst.reloc.pc_rel = 1;
+ inst.reloc.exp.X_add_number -= 8;
}
-/* ARMv5TE load-consecutive (argument parse)
- Mode is like LDRH.
-
- LDRccD R, mode
- STRccD R, mode. */
+/* This is a pseudo-op of the form "adrl rd, label" to be converted
+ into a relative address of the form:
+ add rd, pc, #low(label-.-8)"
+ add rd, rd, #high(label-.-8)" */
static void
-do_ldrd (void)
+do_adrl (void)
{
- constraint (inst.operands[0].reg % 2 != 0,
- _("first destination register must be even"));
- constraint (inst.operands[1].present
- && inst.operands[1].reg != inst.operands[0].reg + 1,
- _("can only load 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. */
-
- 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"));
-
- /* 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"));
- }
+ inst.instruction |= (inst.operands[0].reg << 12); /* Rd */
- inst.instruction |= inst.operands[0].reg << 12;
- encode_arm_addr_mode_3 (2, /*is_t=*/FALSE);
+ /* Frag hacking will turn this into a sub instruction if the offset turns
+ out to be negative. */
+ inst.reloc.type = BFD_RELOC_ARM_ADRL_IMMEDIATE;
+ inst.reloc.pc_rel = 1;
+ inst.size = INSN_SIZE * 2;
+ inst.reloc.exp.X_add_number -= 8;
}
static void
-do_ldrex (void)
+do_arit (void)
{
- constraint (!inst.operands[1].isreg || !inst.operands[1].preind
- || inst.operands[1].postind || inst.operands[1].writeback
- || inst.operands[1].immisreg || inst.operands[1].shifted
- || inst.operands[1].negative
- /* This can arise if the programmer has written
- strex rN, rM, foo
- or if they have mistakenly used a register name as the last
- operand, eg:
- strex rN, rM, rX
- It is very difficult to distinguish between these two cases
- because "rX" might actually be a label. ie the register
- name has been occluded by a symbol of the same name. So we
- just generate a general 'bad addressing mode' type error
- message and leave it up to the programmer to discover the
- true cause and fix their mistake. */
- || (inst.operands[1].reg == REG_PC),
- BAD_ADDR_MODE);
-
- constraint (inst.reloc.exp.X_op != O_constant
- || inst.reloc.exp.X_add_number != 0,
- _("offset must be zero in ARM encoding"));
-
- constraint ((inst.operands[1].reg == REG_PC), BAD_PC);
-
+ constraint (inst.reloc.type >= BFD_RELOC_ARM_THUMB_ALU_ABS_G0_NC
+ && inst.reloc.type <= BFD_RELOC_ARM_THUMB_ALU_ABS_G3_NC ,
+ THUMB1_RELOC_ONLY);
+ if (!inst.operands[1].present)
+ inst.operands[1].reg = inst.operands[0].reg;
inst.instruction |= inst.operands[0].reg << 12;
inst.instruction |= inst.operands[1].reg << 16;
- inst.reloc.type = BFD_RELOC_UNUSED;
+ encode_arm_shifter_operand (2);
}
static void
-do_ldrexd (void)
+do_barrier (void)
{
- constraint (inst.operands[0].reg % 2 != 0,
- _("even register required"));
- constraint (inst.operands[1].present
- && inst.operands[1].reg != inst.operands[0].reg + 1,
- _("can only load two consecutive registers"));
- /* If op 1 were present and equal to PC, this function wouldn't
- have been called in the first place. */
- constraint (inst.operands[0].reg == REG_LR, _("r14 not allowed here"));
-
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[2].reg << 16;
+ if (inst.operands[0].present)
+ inst.instruction |= inst.operands[0].imm;
+ else
+ inst.instruction |= 0xf;
}
static void
-do_ldst (void)
+do_bfc (void)
{
+ unsigned int msb = inst.operands[1].imm + inst.operands[2].imm;
+ constraint (msb > 32, _("bit-field extends past end of register"));
+ /* The instruction encoding stores the LSB and MSB,
+ not the LSB and width. */
inst.instruction |= inst.operands[0].reg << 12;
- if (!inst.operands[1].isreg)
- if (move_or_literal_pool (0, /*thumb_p=*/FALSE, /*mode_3=*/FALSE))
- return;
- encode_arm_addr_mode_2 (1, /*is_t=*/FALSE);
+ inst.instruction |= inst.operands[1].imm << 7;
+ inst.instruction |= (msb - 1) << 16;
}
static void
-do_ldstt (void)
+do_bfi (void)
{
- /* ldrt/strt always use post-indexed addressing. Turn [Rn] into [Rn]! and
- reject [Rn,...]. */
- if (inst.operands[1].preind)
- {
- constraint (inst.reloc.exp.X_op != O_constant
- || inst.reloc.exp.X_add_number != 0,
- _("this instruction requires a post-indexed address"));
+ unsigned int msb;
- inst.operands[1].preind = 0;
- inst.operands[1].postind = 1;
- inst.operands[1].writeback = 1;
- }
+ /* #0 in second position is alternative syntax for bfc, which is
+ the same instruction but with REG_PC in the Rm field. */
+ if (!inst.operands[1].isreg)
+ inst.operands[1].reg = REG_PC;
+
+ msb = inst.operands[2].imm + inst.operands[3].imm;
+ constraint (msb > 32, _("bit-field extends past end of register"));
+ /* The instruction encoding stores the LSB and MSB,
+ not the LSB and width. */
inst.instruction |= inst.operands[0].reg << 12;
- encode_arm_addr_mode_2 (1, /*is_t=*/TRUE);
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].imm << 7;
+ inst.instruction |= (msb - 1) << 16;
}
-/* Halfword and signed-byte load/store operations. */
-
static void
-do_ldstv4 (void)
+do_bfx (void)
{
- constraint (inst.operands[0].reg == REG_PC, BAD_PC);
- inst.instruction |= inst.operands[0].reg << 12;
- if (!inst.operands[1].isreg)
- if (move_or_literal_pool (0, /*thumb_p=*/FALSE, /*mode_3=*/TRUE))
- return;
- encode_arm_addr_mode_3 (1, /*is_t=*/FALSE);
-}
-
-static void
-do_ldsttv4 (void)
-{
- /* ldrt/strt always use post-indexed addressing. Turn [Rn] into [Rn]! and
- reject [Rn,...]. */
- if (inst.operands[1].preind)
- {
- constraint (inst.reloc.exp.X_op != O_constant
- || inst.reloc.exp.X_add_number != 0,
- _("this instruction requires a post-indexed address"));
-
- inst.operands[1].preind = 0;
- inst.operands[1].postind = 1;
- inst.operands[1].writeback = 1;
- }
+ constraint (inst.operands[2].imm + inst.operands[3].imm > 32,
+ _("bit-field extends past end of register"));
inst.instruction |= inst.operands[0].reg << 12;
- encode_arm_addr_mode_3 (1, /*is_t=*/TRUE);
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].imm << 7;
+ inst.instruction |= (inst.operands[3].imm - 1) << 16;
}
-/* Co-processor register load/store.
- Format: <LDC|STC>{cond}[L] CP#,CRd,<address> */
+/* ARM V5 breakpoint instruction (argument parse)
+ BKPT <16 bit unsigned immediate>
+ Instruction is not conditional.
+ The bit pattern given in insns[] has the COND_ALWAYS condition,
+ and it is an error if the caller tried to override that. */
+
static void
-do_lstc (void)
+do_bkpt (void)
{
- inst.instruction |= inst.operands[0].reg << 8;
- inst.instruction |= inst.operands[1].reg << 12;
- encode_arm_cp_address (2, TRUE, TRUE, 0);
+ /* Top 12 of 16 bits to bits 19:8. */
+ inst.instruction |= (inst.operands[0].imm & 0xfff0) << 4;
+
+ /* Bottom 4 of 16 bits to bits 3:0. */
+ inst.instruction |= inst.operands[0].imm & 0xf;
}
static void
-do_mlas (void)
+encode_branch (int default_reloc)
{
- /* This restriction does not apply to mls (nor to mla in v6 or later). */
- if (inst.operands[0].reg == inst.operands[1].reg
- && !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6)
- && !(inst.instruction & 0x00400000))
- as_tsktsk (_("Rd and Rm should be different in mla"));
-
- inst.instruction |= inst.operands[0].reg << 16;
- inst.instruction |= inst.operands[1].reg;
- inst.instruction |= inst.operands[2].reg << 8;
- inst.instruction |= inst.operands[3].reg << 12;
+ if (inst.operands[0].hasreloc)
+ {
+ constraint (inst.operands[0].imm != BFD_RELOC_ARM_PLT32
+ && inst.operands[0].imm != BFD_RELOC_ARM_TLS_CALL,
+ _("the only valid suffixes here are '(plt)' and '(tlscall)'"));
+ inst.reloc.type = inst.operands[0].imm == BFD_RELOC_ARM_PLT32
+ ? BFD_RELOC_ARM_PLT32
+ : thumb_mode ? BFD_RELOC_ARM_THM_TLS_CALL : BFD_RELOC_ARM_TLS_CALL;
+ }
+ else
+ inst.reloc.type = (bfd_reloc_code_real_type) default_reloc;
+ inst.reloc.pc_rel = 1;
}
static void
-do_mov (void)
+do_branch (void)
{
- inst.instruction |= inst.operands[0].reg << 12;
- encode_arm_shifter_operand (1);
+#ifdef OBJ_ELF
+ if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4)
+ encode_branch (BFD_RELOC_ARM_PCREL_JUMP);
+ else
+#endif
+ encode_branch (BFD_RELOC_ARM_PCREL_BRANCH);
}
-/* ARM V6T2 16-bit immediate register load: MOV[WT]{cond} Rd, #<imm16>. */
static void
-do_mov16 (void)
+do_bl (void)
{
- bfd_vma imm;
- bfd_boolean top;
-
- top = (inst.instruction & 0x00400000) != 0;
- constraint (top && inst.reloc.type == BFD_RELOC_ARM_MOVW,
- _(":lower16: not allowed this instruction"));
- constraint (!top && inst.reloc.type == BFD_RELOC_ARM_MOVT,
- _(":upper16: not allowed instruction"));
- inst.instruction |= inst.operands[0].reg << 12;
- if (inst.reloc.type == BFD_RELOC_UNUSED)
+#ifdef OBJ_ELF
+ if (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4)
{
- imm = inst.reloc.exp.X_add_number;
- /* The value is in two pieces: 0:11, 16:19. */
- inst.instruction |= (imm & 0x00000fff);
- inst.instruction |= (imm & 0x0000f000) << 4;
+ if (inst.cond == COND_ALWAYS)
+ encode_branch (BFD_RELOC_ARM_PCREL_CALL);
+ else
+ encode_branch (BFD_RELOC_ARM_PCREL_JUMP);
}
+ else
+#endif
+ encode_branch (BFD_RELOC_ARM_PCREL_BRANCH);
}
-static void do_vfp_nsyn_opcode (const char *);
+/* ARM V5 branch-link-exchange instruction (argument parse)
+ BLX <target_addr> ie BLX(1)
+ BLX{<condition>} <Rm> ie BLX(2)
+ Unfortunately, there are two different opcodes for this mnemonic.
+ So, the insns[].value is not used, and the code here zaps values
+ into inst.instruction.
+ Also, the <target_addr> can be 25 bits, hence has its own reloc. */
-static int
-do_vfp_nsyn_mrs (void)
+static void
+do_blx (void)
{
- if (inst.operands[0].isvec)
+ if (inst.operands[0].isreg)
{
- if (inst.operands[1].reg != 1)
- 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");
- }
- else if (inst.operands[1].isvec)
- do_vfp_nsyn_opcode ("fmrx");
- else
- return FAIL;
-
- return SUCCESS;
-}
+ /* Arg is a register; the opcode provided by insns[] is correct.
+ It is not illegal to do "blx pc", just useless. */
+ if (inst.operands[0].reg == REG_PC)
+ as_tsktsk (_("use of r15 in blx in ARM mode is not really useful"));
-static int
-do_vfp_nsyn_msr (void)
-{
- if (inst.operands[0].isvec)
- do_vfp_nsyn_opcode ("fmxr");
+ inst.instruction |= inst.operands[0].reg;
+ }
else
- return FAIL;
-
- return SUCCESS;
+ {
+ /* Arg is an address; this instruction cannot be executed
+ conditionally, and the opcode must be adjusted.
+ We retain the BFD_RELOC_ARM_PCREL_BLX till the very end
+ where we generate out a BFD_RELOC_ARM_PCREL_CALL instead. */
+ constraint (inst.cond != COND_ALWAYS, BAD_COND);
+ inst.instruction = 0xfa000000;
+ encode_branch (BFD_RELOC_ARM_PCREL_BLX);
+ }
}
static void
-do_vmrs (void)
+do_bx (void)
{
- unsigned Rt = inst.operands[0].reg;
-
- if (thumb_mode && inst.operands[0].reg == REG_SP)
- {
- inst.error = BAD_SP;
- return;
- }
+ bfd_boolean want_reloc;
- /* APSR_ sets isvec. All other refs to PC are illegal. */
- if (!inst.operands[0].isvec && inst.operands[0].reg == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
+ if (inst.operands[0].reg == REG_PC)
+ as_tsktsk (_("use of r15 in bx in ARM mode is not really useful"));
- if (inst.operands[1].reg != 1)
- first_error (_("operand 1 must be FPSCR"));
+ inst.instruction |= inst.operands[0].reg;
+ /* Output R_ARM_V4BX relocations if is an EABI object that looks like
+ it is for ARMv4t or earlier. */
+ want_reloc = !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5);
+ if (object_arch && !ARM_CPU_HAS_FEATURE (*object_arch, arm_ext_v5))
+ want_reloc = TRUE;
- inst.instruction |= (Rt << 12);
+#ifdef OBJ_ELF
+ if (EF_ARM_EABI_VERSION (meabi_flags) < EF_ARM_EABI_VER4)
+#endif
+ want_reloc = FALSE;
+
+ if (want_reloc)
+ inst.reloc.type = BFD_RELOC_ARM_V4BX;
}
+
+/* ARM v5TEJ. Jump to Jazelle code. */
+
static void
-do_vmsr (void)
+do_bxj (void)
{
- unsigned Rt = inst.operands[1].reg;
-
- if (thumb_mode)
- reject_bad_reg (Rt);
- else if (Rt == REG_PC)
- {
- inst.error = BAD_PC;
- return;
- }
+ if (inst.operands[0].reg == REG_PC)
+ as_tsktsk (_("use of r15 in bxj is not really useful"));
- if (inst.operands[0].reg != 1)
- first_error (_("operand 0 must be FPSCR"));
+ inst.instruction |= inst.operands[0].reg;
+}
- inst.instruction |= (Rt << 12);
+/* Co-processor data operation:
+ CDP{cond} <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>{, <opcode_2>}
+ CDP2 <coproc>, <opcode_1>, <CRd>, <CRn>, <CRm>{, <opcode_2>} */
+static void
+do_cdp (void)
+{
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm << 20;
+ inst.instruction |= inst.operands[2].reg << 12;
+ inst.instruction |= inst.operands[3].reg << 16;
+ inst.instruction |= inst.operands[4].reg;
+ inst.instruction |= inst.operands[5].imm << 5;
}
static void
-do_mrs (void)
+do_cmp (void)
{
- unsigned br;
+ inst.instruction |= inst.operands[0].reg << 16;
+ encode_arm_shifter_operand (1);
+}
- if (do_vfp_nsyn_mrs () == SUCCESS)
- return;
+/* Transfer between coprocessor and ARM registers.
+ MRC{cond} <coproc>, <opcode_1>, <Rd>, <CRn>, <CRm>{, <opcode_2>}
+ MRC2
+ MCR{cond}
+ MCR2
- constraint (inst.operands[0].reg == REG_PC, BAD_PC);
- inst.instruction |= inst.operands[0].reg << 12;
+ No special properties. */
- if (inst.operands[1].isreg)
- {
- br = inst.operands[1].reg;
- if (((br & 0x200) == 0) && ((br & 0xf0000) != 0xf000))
- as_bad (_("bad register for mrs"));
- }
- else
- {
- /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all. */
- constraint ((inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f))
- != (PSR_c|PSR_f),
- _("'APSR', 'CPSR' or 'SPSR' expected"));
- br = (15<<16) | (inst.operands[1].imm & SPSR_BIT);
- }
+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;
+};
- inst.instruction |= br;
-}
+#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_CORE_LOW (ARM_EXT_V8), ARM_ARCH_NONE,
+ DEPR_ACCESS_V8, NULL},
+ {15, 0, 7, 10, 4, /* CP15DSB. */
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V8), ARM_ARCH_NONE,
+ DEPR_ACCESS_V8, NULL},
+ {15, 0, 7, 5, 4, /* CP15ISB. */
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V8), ARM_ARCH_NONE,
+ DEPR_ACCESS_V8, NULL},
+ {14, 6, 1, 0, 0, /* TEEHBR. */
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V8), ARM_ARCH_NONE,
+ DEPR_ACCESS_V8, NULL},
+ {14, 6, 0, 0, 0, /* TEECR. */
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V8), ARM_ARCH_NONE,
+ DEPR_ACCESS_V8, NULL},
+};
-/* Two possible forms:
- "{C|S}PSR_<field>, Rm",
- "{C|S}PSR_f, #expression". */
+#undef DEPR_ACCESS_V8
+
+static const size_t deprecated_coproc_reg_count =
+ sizeof (deprecated_coproc_regs) / sizeof (deprecated_coproc_regs[0]);
static void
-do_msr (void)
+do_co_reg (void)
{
- if (do_vfp_nsyn_msr () == SUCCESS)
- return;
+ unsigned Rd;
+ size_t i;
- inst.instruction |= inst.operands[0].imm;
- if (inst.operands[1].isreg)
- inst.instruction |= inst.operands[1].reg;
+ Rd = inst.operands[2].reg;
+ if (thumb_mode)
+ {
+ if (inst.instruction == 0xee000010
+ || inst.instruction == 0xfe000010)
+ /* MCR, MCR2 */
+ reject_bad_reg (Rd);
+ else
+ /* MRC, MRC2 */
+ constraint (Rd == REG_SP, BAD_SP);
+ }
else
{
- inst.instruction |= INST_IMMEDIATE;
- inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
- inst.reloc.pc_rel = 0;
+ /* MCR */
+ if (inst.instruction == 0xe000010)
+ constraint (Rd == REG_PC, BAD_PC);
}
-}
-
-static void
-do_mul (void)
-{
- constraint (inst.operands[2].reg == REG_PC, BAD_PC);
- if (!inst.operands[2].present)
- inst.operands[2].reg = inst.operands[0].reg;
- inst.instruction |= inst.operands[0].reg << 16;
- inst.instruction |= inst.operands[1].reg;
- inst.instruction |= inst.operands[2].reg << 8;
+ 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_tsktsk ("%s", r->dep_msg);
+ }
+ }
- if (inst.operands[0].reg == inst.operands[1].reg
- && !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6))
- as_tsktsk (_("Rd and Rm should be different in mul"));
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm << 21;
+ inst.instruction |= Rd << 12;
+ inst.instruction |= inst.operands[3].reg << 16;
+ inst.instruction |= inst.operands[4].reg;
+ inst.instruction |= inst.operands[5].imm << 5;
}
-/* Long Multiply Parser
- UMULL RdLo, RdHi, Rm, Rs
- SMULL RdLo, RdHi, Rm, Rs
- UMLAL RdLo, RdHi, Rm, Rs
- SMLAL RdLo, RdHi, Rm, Rs. */
-
-static void
-do_mull (void)
-{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.instruction |= inst.operands[2].reg;
- inst.instruction |= inst.operands[3].reg << 8;
-
- /* rdhi and rdlo must be different. */
- if (inst.operands[0].reg == inst.operands[1].reg)
- as_tsktsk (_("rdhi and rdlo must be different"));
+/* Transfer between coprocessor register and pair of ARM registers.
+ MCRR{cond} <coproc>, <opcode>, <Rd>, <Rn>, <CRm>.
+ MCRR2
+ MRRC{cond}
+ MRRC2
- /* rdhi, rdlo and rm must all be different before armv6. */
- if ((inst.operands[0].reg == inst.operands[2].reg
- || inst.operands[1].reg == inst.operands[2].reg)
- && !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6))
- as_tsktsk (_("rdhi, rdlo and rm must all be different"));
-}
+ Two XScale instructions are special cases of these:
-static void
-do_nop (void)
-{
- if (inst.operands[0].present
- || ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6k))
- {
- /* Architectural NOP hints are CPSR sets with no bits selected. */
- inst.instruction &= 0xf0000000;
- inst.instruction |= 0x0320f000;
- if (inst.operands[0].present)
- inst.instruction |= inst.operands[0].imm;
- }
-}
+ MAR{cond} acc0, <RdLo>, <RdHi> == MCRR{cond} p0, #0, <RdLo>, <RdHi>, c0
+ MRA{cond} acc0, <RdLo>, <RdHi> == MRRC{cond} p0, #0, <RdLo>, <RdHi>, c0
-/* ARM V6 Pack Halfword Bottom Top instruction (argument parse).
- PKHBT {<cond>} <Rd>, <Rn>, <Rm> {, LSL #<shift_imm>}
- Condition defaults to COND_ALWAYS.
- Error if Rd, Rn or Rm are R15. */
+ Result unpredictable if Rd or Rn is R15. */
static void
-do_pkhbt (void)
+do_co_reg2c (void)
{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.instruction |= inst.operands[2].reg;
- if (inst.operands[3].present)
- encode_arm_shift (3);
-}
+ unsigned Rd, Rn;
-/* ARM V6 PKHTB (Argument Parse). */
+ Rd = inst.operands[2].reg;
+ Rn = inst.operands[3].reg;
-static void
-do_pkhtb (void)
-{
- if (!inst.operands[3].present)
+ if (thumb_mode)
{
- /* If the shift specifier is omitted, turn the instruction
- into pkhbt rd, rm, rn. */
- inst.instruction &= 0xfff00010;
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg;
- inst.instruction |= inst.operands[2].reg << 16;
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
}
else
{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.instruction |= inst.operands[2].reg;
- encode_arm_shift (3);
+ constraint (Rd == REG_PC, BAD_PC);
+ constraint (Rn == REG_PC, BAD_PC);
}
-}
-
-/* ARMv5TE: Preload-Cache
- MP Extensions: Preload for write
-
- PLD(W) <addr_mode>
-
- Syntactically, like LDR with B=1, W=0, L=1. */
-static void
-do_pld (void)
-{
- constraint (!inst.operands[0].isreg,
- _("'[' expected after PLD mnemonic"));
- constraint (inst.operands[0].postind,
- _("post-indexed expression used in preload instruction"));
- constraint (inst.operands[0].writeback,
- _("writeback used in preload instruction"));
- constraint (!inst.operands[0].preind,
- _("unindexed addressing used in preload instruction"));
- encode_arm_addr_mode_2 (0, /*is_t=*/FALSE);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm << 4;
+ inst.instruction |= Rd << 12;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= inst.operands[4].reg;
}
-/* ARMv7: PLI <addr_mode> */
static void
-do_pli (void)
+do_cpsi (void)
{
- constraint (!inst.operands[0].isreg,
- _("'[' expected after PLI mnemonic"));
- constraint (inst.operands[0].postind,
- _("post-indexed expression used in preload instruction"));
- constraint (inst.operands[0].writeback,
- _("writeback used in preload instruction"));
- constraint (!inst.operands[0].preind,
- _("unindexed addressing used in preload instruction"));
- encode_arm_addr_mode_2 (0, /*is_t=*/FALSE);
- inst.instruction &= ~PRE_INDEX;
+ inst.instruction |= inst.operands[0].imm << 6;
+ if (inst.operands[1].present)
+ {
+ inst.instruction |= CPSI_MMOD;
+ inst.instruction |= inst.operands[1].imm;
+ }
}
static void
-do_push_pop (void)
+do_dbg (void)
{
- inst.operands[1] = inst.operands[0];
- memset (&inst.operands[0], 0, sizeof inst.operands[0]);
- inst.operands[0].isreg = 1;
- inst.operands[0].writeback = 1;
- inst.operands[0].reg = REG_SP;
- do_ldmstm ();
+ inst.instruction |= inst.operands[0].imm;
}
-/* ARM V6 RFE (Return from Exception) loads the PC and CPSR from the
- word at the specified address and the following word
- respectively.
- Unconditionally executed.
- Error if Rn is R15. */
-
static void
-do_rfe (void)
+do_div (void)
{
- inst.instruction |= inst.operands[0].reg << 16;
- if (inst.operands[0].writeback)
- inst.instruction |= WRITE_BACK;
-}
+ unsigned Rd, Rn, Rm;
-/* ARM V6 ssat (argument parse). */
+ Rd = inst.operands[0].reg;
+ Rn = (inst.operands[1].present
+ ? inst.operands[1].reg : Rd);
+ Rm = inst.operands[2].reg;
-static void
-do_ssat (void)
-{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= (inst.operands[1].imm - 1) << 16;
- inst.instruction |= inst.operands[2].reg;
+ constraint ((Rd == REG_PC), BAD_PC);
+ constraint ((Rn == REG_PC), BAD_PC);
+ constraint ((Rm == REG_PC), BAD_PC);
- if (inst.operands[3].present)
- encode_arm_shift (3);
+ inst.instruction |= Rd << 16;
+ inst.instruction |= Rn << 0;
+ inst.instruction |= Rm << 8;
}
-/* ARM V6 usat (argument parse). */
-
static void
-do_usat (void)
+do_it (void)
{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].imm << 16;
- inst.instruction |= inst.operands[2].reg;
+ /* There is no IT instruction in ARM mode. We
+ process it to do the validation as if in
+ thumb mode, just in case the code gets
+ assembled for thumb using the unified syntax. */
- if (inst.operands[3].present)
- encode_arm_shift (3);
+ inst.size = 0;
+ if (unified_syntax)
+ {
+ set_it_insn_type (IT_INSN);
+ now_it.mask = (inst.instruction & 0xf) | 0x10;
+ now_it.cc = inst.operands[0].imm;
+ }
}
-/* ARM V6 ssat16 (argument parse). */
-
-static void
-do_ssat16 (void)
+/* 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)
{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= ((inst.operands[1].imm - 1) << 16);
- inst.instruction |= inst.operands[2].reg;
+ int i = ffs (range) - 1;
+ return (i > 15 || range != (1 << i)) ? -1 : i;
}
static void
-do_usat16 (void)
+encode_ldmstm(int from_push_pop_mnem)
{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].imm << 16;
- inst.instruction |= inst.operands[2].reg;
-}
+ int base_reg = inst.operands[0].reg;
+ int range = inst.operands[1].imm;
+ int one_reg;
-/* ARM V6 SETEND (argument parse). Sets the E bit in the CPSR while
- preserving the other bits.
+ inst.instruction |= base_reg << 16;
+ inst.instruction |= range;
- setend <endian_specifier>, where <endian_specifier> is either
- BE or LE. */
+ if (inst.operands[1].writeback)
+ inst.instruction |= LDM_TYPE_2_OR_3;
-static void
-do_setend (void)
-{
- if (inst.operands[0].imm)
- inst.instruction |= 0x200;
-}
-
-static void
-do_shift (void)
-{
- unsigned int Rm = (inst.operands[1].present
- ? inst.operands[1].reg
- : inst.operands[0].reg);
-
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= Rm;
- if (inst.operands[2].isreg) /* Rd, {Rm,} Rs */
+ if (inst.operands[0].writeback)
{
- inst.instruction |= inst.operands[2].reg << 8;
- inst.instruction |= SHIFT_BY_REG;
+ inst.instruction |= WRITE_BACK;
+ /* Check for unpredictable uses of writeback. */
+ if (inst.instruction & LOAD_BIT)
+ {
+ /* Not allowed in LDM type 2. */
+ if ((inst.instruction & LDM_TYPE_2_OR_3)
+ && ((range & (1 << REG_PC)) == 0))
+ as_warn (_("writeback of base register is UNPREDICTABLE"));
+ /* Only allowed if base reg not in list for other types. */
+ else if (range & (1 << base_reg))
+ as_warn (_("writeback of base register when in register list is UNPREDICTABLE"));
+ }
+ else /* STM. */
+ {
+ /* Not allowed for type 2. */
+ if (inst.instruction & LDM_TYPE_2_OR_3)
+ as_warn (_("writeback of base register is UNPREDICTABLE"));
+ /* Only allowed if base reg not in list, or first in list. */
+ else if ((range & (1 << base_reg))
+ && (range & ((1 << base_reg) - 1)))
+ as_warn (_("if writeback register is in list, it must be the lowest reg in the list"));
+ }
}
- else
- inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
-}
-static void
-do_smc (void)
-{
- inst.reloc.type = BFD_RELOC_ARM_SMC;
- inst.reloc.pc_rel = 0;
-}
+ /* 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;
-static void
-do_hvc (void)
-{
- inst.reloc.type = BFD_RELOC_ARM_HVC;
- inst.reloc.pc_rel = 0;
+ inst.instruction &= A_COND_MASK;
+ inst.instruction |= is_push ? A2_OPCODE_PUSH : A2_OPCODE_POP;
+ inst.instruction |= one_reg << 12;
+ }
}
static void
-do_swi (void)
+do_ldmstm (void)
{
- inst.reloc.type = BFD_RELOC_ARM_SWI;
- inst.reloc.pc_rel = 0;
+ encode_ldmstm (/*from_push_pop_mnem=*/FALSE);
}
-/* ARM V5E (El Segundo) signed-multiply-accumulate (argument parse)
- SMLAxy{cond} Rd,Rm,Rs,Rn
- SMLAWy{cond} Rd,Rm,Rs,Rn
- Error if any register is R15. */
-
-static void
-do_smla (void)
-{
- inst.instruction |= inst.operands[0].reg << 16;
- inst.instruction |= inst.operands[1].reg;
- inst.instruction |= inst.operands[2].reg << 8;
- inst.instruction |= inst.operands[3].reg << 12;
-}
+/* ARMv5TE load-consecutive (argument parse)
+ Mode is like LDRH.
-/* ARM V5E (El Segundo) signed-multiply-accumulate-long (argument parse)
- SMLALxy{cond} Rdlo,Rdhi,Rm,Rs
- Error if any register is R15.
- Warning if Rdlo == Rdhi. */
+ LDRccD R, mode
+ STRccD R, mode. */
static void
-do_smlal (void)
+do_ldrd (void)
{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.instruction |= inst.operands[2].reg;
- inst.instruction |= inst.operands[3].reg << 8;
-
- if (inst.operands[0].reg == inst.operands[1].reg)
- as_tsktsk (_("rdhi and rdlo must be different"));
-}
-
-/* ARM V5E (El Segundo) signed-multiply (argument parse)
- SMULxy{cond} Rd,Rm,Rs
- Error if any register is R15. */
+ constraint (inst.operands[0].reg % 2 != 0,
+ _("first transfer register must be even"));
+ constraint (inst.operands[1].present
+ && inst.operands[1].reg != inst.operands[0].reg + 1,
+ _("can only transfer two consecutive registers"));
+ constraint (inst.operands[0].reg == REG_LR, _("r14 not allowed here"));
+ constraint (!inst.operands[2].isreg, _("'[' expected"));
-static void
-do_smul (void)
-{
- inst.instruction |= inst.operands[0].reg << 16;
- inst.instruction |= inst.operands[1].reg;
- inst.instruction |= inst.operands[2].reg << 8;
-}
+ if (!inst.operands[1].present)
+ inst.operands[1].reg = inst.operands[0].reg + 1;
-/* ARM V6 srs (argument parse). The variable fields in the encoding are
- the same for both ARM and Thumb-2. */
+ /* 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. */
-static void
-do_srs (void)
-{
- int reg;
+ 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.operands[0].present)
+ if (!(inst.instruction & V4_STR_BIT))
{
- reg = inst.operands[0].reg;
- constraint (reg != REG_SP, _("SRS base register must be r13"));
+ /* For an index-register load, the index register must not overlap the
+ 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"));
}
- else
- reg = REG_SP;
-
- inst.instruction |= reg << 16;
- inst.instruction |= inst.operands[1].imm;
- if (inst.operands[0].writeback || inst.operands[1].writeback)
- inst.instruction |= WRITE_BACK;
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_addr_mode_3 (2, /*is_t=*/FALSE);
}
-/* ARM V6 strex (argument parse). */
-
static void
-do_strex (void)
+do_ldrex (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
- /* See comment in do_ldrex(). */
- || (inst.operands[2].reg == REG_PC),
+ constraint (!inst.operands[1].isreg || !inst.operands[1].preind
+ || inst.operands[1].postind || inst.operands[1].writeback
+ || inst.operands[1].immisreg || inst.operands[1].shifted
+ || inst.operands[1].negative
+ /* This can arise if the programmer has written
+ strex rN, rM, foo
+ or if they have mistakenly used a register name as the last
+ operand, eg:
+ strex rN, rM, rX
+ It is very difficult to distinguish between these two cases
+ because "rX" might actually be a label. ie the register
+ name has been occluded by a symbol of the same name. So we
+ just generate a general 'bad addressing mode' type error
+ message and leave it up to the programmer to discover the
+ true cause and fix their mistake. */
+ || (inst.operands[1].reg == REG_PC),
BAD_ADDR_MODE);
- constraint (inst.operands[0].reg == inst.operands[1].reg
- || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
-
constraint (inst.reloc.exp.X_op != O_constant
|| inst.reloc.exp.X_add_number != 0,
_("offset must be zero in ARM encoding"));
+ constraint ((inst.operands[1].reg == REG_PC), BAD_PC);
+
inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg;
- inst.instruction |= inst.operands[2].reg << 16;
+ inst.instruction |= inst.operands[1].reg << 16;
inst.reloc.type = BFD_RELOC_UNUSED;
}
static void
-do_strexd (void)
+do_ldrexd (void)
{
- constraint (inst.operands[1].reg % 2 != 0,
+ constraint (inst.operands[0].reg % 2 != 0,
_("even register required"));
- constraint (inst.operands[2].present
- && inst.operands[2].reg != inst.operands[1].reg + 1,
- _("can only store two consecutive registers"));
- /* If op 2 were present and equal to PC, this function wouldn't
+ constraint (inst.operands[1].present
+ && inst.operands[1].reg != inst.operands[0].reg + 1,
+ _("can only load two consecutive registers"));
+ /* If op 1 were present and equal to PC, this function wouldn't
have been called in the first place. */
- constraint (inst.operands[1].reg == REG_LR, _("r14 not allowed here"));
-
- constraint (inst.operands[0].reg == inst.operands[1].reg
- || inst.operands[0].reg == inst.operands[1].reg + 1
- || inst.operands[0].reg == inst.operands[3].reg,
- BAD_OVERLAP);
+ constraint (inst.operands[0].reg == REG_LR, _("r14 not allowed here"));
inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg;
- inst.instruction |= inst.operands[3].reg << 16;
+ inst.instruction |= inst.operands[2].reg << 16;
}
-/* 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
- before extracting the 16-bit value.
- SXTAH{<cond>} <Rd>, <Rn>, <Rm>{, <rotation>}
- Condition defaults to COND_ALWAYS.
- Error if any register uses R15. */
-
+/* In both ARM and thumb state 'ldr pc, #imm' with an immediate
+ which is not a multiple of four is UNPREDICTABLE. */
static void
-do_sxtah (void)
+check_ldr_r15_aligned (void)
{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.instruction |= inst.operands[2].reg;
- inst.instruction |= inst.operands[3].imm << 10;
+ 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"));
}
-/* ARM V6 SXTH.
-
- SXTH {<cond>} <Rd>, <Rm>{, <rotation>}
- Condition defaults to COND_ALWAYS.
- Error if any register uses R15. */
-
static void
-do_sxth (void)
+do_ldst (void)
{
inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg;
- inst.instruction |= inst.operands[2].imm << 10;
+ if (!inst.operands[1].isreg)
+ if (move_or_literal_pool (0, CONST_ARM, /*mode_3=*/FALSE))
+ return;
+ encode_arm_addr_mode_2 (1, /*is_t=*/FALSE);
+ check_ldr_r15_aligned ();
}
-\f
-/* VFP instructions. In a logical order: SP variant first, monad
- before dyad, arithmetic then move then load/store. */
static void
-do_vfp_sp_monadic (void)
+do_ldstt (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sm);
-}
+ /* ldrt/strt always use post-indexed addressing. Turn [Rn] into [Rn]! and
+ reject [Rn,...]. */
+ if (inst.operands[1].preind)
+ {
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ _("this instruction requires a post-indexed address"));
-static void
-do_vfp_sp_dyadic (void)
-{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sn);
- encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Sm);
+ inst.operands[1].preind = 0;
+ inst.operands[1].postind = 1;
+ inst.operands[1].writeback = 1;
+ }
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_addr_mode_2 (1, /*is_t=*/TRUE);
}
-static void
-do_vfp_sp_compare_z (void)
-{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
-}
+/* Halfword and signed-byte load/store operations. */
static void
-do_vfp_dp_sp_cvt (void)
+do_ldstv4 (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sm);
+ constraint (inst.operands[0].reg == REG_PC, BAD_PC);
+ inst.instruction |= inst.operands[0].reg << 12;
+ if (!inst.operands[1].isreg)
+ if (move_or_literal_pool (0, CONST_ARM, /*mode_3=*/TRUE))
+ return;
+ encode_arm_addr_mode_3 (1, /*is_t=*/FALSE);
}
static void
-do_vfp_sp_dp_cvt (void)
+do_ldsttv4 (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dm);
-}
-
-static void
-do_vfp_reg_from_sp (void)
-{
- inst.instruction |= inst.operands[0].reg << 12;
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sn);
-}
+ /* ldrt/strt always use post-indexed addressing. Turn [Rn] into [Rn]! and
+ reject [Rn,...]. */
+ if (inst.operands[1].preind)
+ {
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ _("this instruction requires a post-indexed address"));
-static void
-do_vfp_reg2_from_sp2 (void)
-{
- constraint (inst.operands[2].imm != 2,
- _("only two consecutive VFP SP registers allowed here"));
+ inst.operands[1].preind = 0;
+ inst.operands[1].postind = 1;
+ inst.operands[1].writeback = 1;
+ }
inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Sm);
+ encode_arm_addr_mode_3 (1, /*is_t=*/TRUE);
}
+/* Co-processor register load/store.
+ Format: <LDC|STC>{cond}[L] CP#,CRd,<address> */
static void
-do_vfp_sp_from_reg (void)
+do_lstc (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sn);
+ inst.instruction |= inst.operands[0].reg << 8;
inst.instruction |= inst.operands[1].reg << 12;
+ encode_arm_cp_address (2, TRUE, TRUE, 0);
}
static void
-do_vfp_sp2_from_reg2 (void)
+do_mlas (void)
{
- constraint (inst.operands[0].imm != 2,
- _("only two consecutive VFP SP registers allowed here"));
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sm);
- inst.instruction |= inst.operands[1].reg << 12;
- inst.instruction |= inst.operands[2].reg << 16;
+ /* This restriction does not apply to mls (nor to mla in v6 or later). */
+ if (inst.operands[0].reg == inst.operands[1].reg
+ && !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6)
+ && !(inst.instruction & 0x00400000))
+ as_tsktsk (_("Rd and Rm should be different in mla"));
+
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 8;
+ inst.instruction |= inst.operands[3].reg << 12;
}
static void
-do_vfp_sp_ldst (void)
+do_mov (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
- encode_arm_cp_address (1, FALSE, TRUE, 0);
+ constraint (inst.reloc.type >= BFD_RELOC_ARM_THUMB_ALU_ABS_G0_NC
+ && inst.reloc.type <= BFD_RELOC_ARM_THUMB_ALU_ABS_G3_NC ,
+ THUMB1_RELOC_ONLY);
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_shifter_operand (1);
}
+/* ARM V6T2 16-bit immediate register load: MOV[WT]{cond} Rd, #<imm16>. */
static void
-do_vfp_dp_ldst (void)
+do_mov16 (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
- encode_arm_cp_address (1, FALSE, TRUE, 0);
+ bfd_vma imm;
+ bfd_boolean top;
+
+ top = (inst.instruction & 0x00400000) != 0;
+ constraint (top && inst.reloc.type == BFD_RELOC_ARM_MOVW,
+ _(":lower16: not allowed this instruction"));
+ constraint (!top && inst.reloc.type == BFD_RELOC_ARM_MOVT,
+ _(":upper16: not allowed instruction"));
+ inst.instruction |= inst.operands[0].reg << 12;
+ if (inst.reloc.type == BFD_RELOC_UNUSED)
+ {
+ imm = inst.reloc.exp.X_add_number;
+ /* The value is in two pieces: 0:11, 16:19. */
+ inst.instruction |= (imm & 0x00000fff);
+ inst.instruction |= (imm & 0x0000f000) << 4;
+ }
}
+static int
+do_vfp_nsyn_mrs (void)
+{
+ if (inst.operands[0].isvec)
+ {
+ if (inst.operands[1].reg != 1)
+ 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");
+ }
+ else if (inst.operands[1].isvec)
+ do_vfp_nsyn_opcode ("fmrx");
+ else
+ return FAIL;
+
+ return SUCCESS;
+}
-static void
-vfp_sp_ldstm (enum vfp_ldstm_type ldstm_type)
+static int
+do_vfp_nsyn_msr (void)
{
- if (inst.operands[0].writeback)
- inst.instruction |= WRITE_BACK;
+ if (inst.operands[0].isvec)
+ do_vfp_nsyn_opcode ("fmxr");
else
- constraint (ldstm_type != VFP_LDSTMIA,
- _("this addressing mode requires base-register writeback"));
- inst.instruction |= inst.operands[0].reg << 16;
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sd);
- inst.instruction |= inst.operands[1].imm;
+ return FAIL;
+
+ return SUCCESS;
}
static void
-vfp_dp_ldstm (enum vfp_ldstm_type ldstm_type)
+do_vmrs (void)
{
- int count;
-
- if (inst.operands[0].writeback)
- inst.instruction |= WRITE_BACK;
- else
- constraint (ldstm_type != VFP_LDSTMIA && ldstm_type != VFP_LDSTMIAX,
- _("this addressing mode requires base-register writeback"));
+ unsigned Rt = inst.operands[0].reg;
- inst.instruction |= inst.operands[0].reg << 16;
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dd);
+ if (thumb_mode && Rt == REG_SP)
+ {
+ inst.error = BAD_SP;
+ return;
+ }
- count = inst.operands[1].imm << 1;
- if (ldstm_type == VFP_LDSTMIAX || ldstm_type == VFP_LDSTMDBX)
- count += 1;
+ /* APSR_ sets isvec. All other refs to PC are illegal. */
+ if (!inst.operands[0].isvec && Rt == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
- inst.instruction |= count;
+ /* 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);
}
static void
-do_vfp_sp_ldstmia (void)
+do_vmsr (void)
{
- vfp_sp_ldstm (VFP_LDSTMIA);
+ unsigned Rt = inst.operands[1].reg;
+
+ if (thumb_mode)
+ reject_bad_reg (Rt);
+ else if (Rt == REG_PC)
+ {
+ inst.error = BAD_PC;
+ return;
+ }
+
+ /* 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);
}
static void
-do_vfp_sp_ldstmdb (void)
+do_mrs (void)
{
- vfp_sp_ldstm (VFP_LDSTMDB);
+ unsigned br;
+
+ if (do_vfp_nsyn_mrs () == SUCCESS)
+ return;
+
+ constraint (inst.operands[0].reg == REG_PC, BAD_PC);
+ inst.instruction |= inst.operands[0].reg << 12;
+
+ if (inst.operands[1].isreg)
+ {
+ br = inst.operands[1].reg;
+ if (((br & 0x200) == 0) && ((br & 0xf0000) != 0xf000))
+ as_bad (_("bad register for mrs"));
+ }
+ else
+ {
+ /* mrs only accepts CPSR/SPSR/CPSR_all/SPSR_all. */
+ constraint ((inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f))
+ != (PSR_c|PSR_f),
+ _("'APSR', 'CPSR' or 'SPSR' expected"));
+ br = (15<<16) | (inst.operands[1].imm & SPSR_BIT);
+ }
+
+ inst.instruction |= br;
}
+/* Two possible forms:
+ "{C|S}PSR_<field>, Rm",
+ "{C|S}PSR_f, #expression". */
+
static void
-do_vfp_dp_ldstmia (void)
+do_msr (void)
{
- vfp_dp_ldstm (VFP_LDSTMIA);
+ if (do_vfp_nsyn_msr () == SUCCESS)
+ return;
+
+ inst.instruction |= inst.operands[0].imm;
+ if (inst.operands[1].isreg)
+ inst.instruction |= inst.operands[1].reg;
+ else
+ {
+ inst.instruction |= INST_IMMEDIATE;
+ inst.reloc.type = BFD_RELOC_ARM_IMMEDIATE;
+ inst.reloc.pc_rel = 0;
+ }
}
static void
-do_vfp_dp_ldstmdb (void)
+do_mul (void)
{
- vfp_dp_ldstm (VFP_LDSTMDB);
+ constraint (inst.operands[2].reg == REG_PC, BAD_PC);
+
+ if (!inst.operands[2].present)
+ inst.operands[2].reg = inst.operands[0].reg;
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 8;
+
+ if (inst.operands[0].reg == inst.operands[1].reg
+ && !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6))
+ as_tsktsk (_("Rd and Rm should be different in mul"));
}
+/* Long Multiply Parser
+ UMULL RdLo, RdHi, Rm, Rs
+ SMULL RdLo, RdHi, Rm, Rs
+ UMLAL RdLo, RdHi, Rm, Rs
+ SMLAL RdLo, RdHi, Rm, Rs. */
+
static void
-do_vfp_xp_ldstmia (void)
+do_mull (void)
{
- vfp_dp_ldstm (VFP_LDSTMIAX);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].reg << 8;
+
+ /* rdhi and rdlo must be different. */
+ if (inst.operands[0].reg == inst.operands[1].reg)
+ as_tsktsk (_("rdhi and rdlo must be different"));
+
+ /* rdhi, rdlo and rm must all be different before armv6. */
+ if ((inst.operands[0].reg == inst.operands[2].reg
+ || inst.operands[1].reg == inst.operands[2].reg)
+ && !ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6))
+ as_tsktsk (_("rdhi, rdlo and rm must all be different"));
}
static void
-do_vfp_xp_ldstmdb (void)
+do_nop (void)
{
- vfp_dp_ldstm (VFP_LDSTMDBX);
+ if (inst.operands[0].present
+ || ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6k))
+ {
+ /* Architectural NOP hints are CPSR sets with no bits selected. */
+ inst.instruction &= 0xf0000000;
+ inst.instruction |= 0x0320f000;
+ if (inst.operands[0].present)
+ inst.instruction |= inst.operands[0].imm;
+ }
}
+/* ARM V6 Pack Halfword Bottom Top instruction (argument parse).
+ PKHBT {<cond>} <Rd>, <Rn>, <Rm> {, LSL #<shift_imm>}
+ Condition defaults to COND_ALWAYS.
+ Error if Rd, Rn or Rm are R15. */
+
static void
-do_vfp_dp_rd_rm (void)
+do_pkhbt (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dm);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ if (inst.operands[3].present)
+ encode_arm_shift (3);
}
-static void
-do_vfp_dp_rn_rd (void)
-{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dn);
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dd);
-}
+/* ARM V6 PKHTB (Argument Parse). */
static void
-do_vfp_dp_rd_rn (void)
+do_pkhtb (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dn);
+ if (!inst.operands[3].present)
+ {
+ /* If the shift specifier is omitted, turn the instruction
+ into pkhbt rd, rm, rn. */
+ inst.instruction &= 0xfff00010;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 16;
+ }
+ else
+ {
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ encode_arm_shift (3);
+ }
}
-static void
-do_vfp_dp_rd_rn_rm (void)
-{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dn);
- encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Dm);
-}
+/* ARMv5TE: Preload-Cache
+ MP Extensions: Preload for write
-static void
-do_vfp_dp_rd (void)
-{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
-}
+ PLD(W) <addr_mode>
+
+ Syntactically, like LDR with B=1, W=0, L=1. */
static void
-do_vfp_dp_rm_rd_rn (void)
+do_pld (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dm);
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dd);
- encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Dn);
+ constraint (!inst.operands[0].isreg,
+ _("'[' expected after PLD mnemonic"));
+ constraint (inst.operands[0].postind,
+ _("post-indexed expression used in preload instruction"));
+ constraint (inst.operands[0].writeback,
+ _("writeback used in preload instruction"));
+ constraint (!inst.operands[0].preind,
+ _("unindexed addressing used in preload instruction"));
+ encode_arm_addr_mode_2 (0, /*is_t=*/FALSE);
}
-/* VFPv3 instructions. */
+/* ARMv7: PLI <addr_mode> */
static void
-do_vfp_sp_const (void)
+do_pli (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
- inst.instruction |= (inst.operands[1].imm & 0xf0) << 12;
- inst.instruction |= (inst.operands[1].imm & 0x0f);
+ constraint (!inst.operands[0].isreg,
+ _("'[' expected after PLI mnemonic"));
+ constraint (inst.operands[0].postind,
+ _("post-indexed expression used in preload instruction"));
+ constraint (inst.operands[0].writeback,
+ _("writeback used in preload instruction"));
+ constraint (!inst.operands[0].preind,
+ _("unindexed addressing used in preload instruction"));
+ encode_arm_addr_mode_2 (0, /*is_t=*/FALSE);
+ inst.instruction &= ~PRE_INDEX;
}
static void
-do_vfp_dp_const (void)
+do_push_pop (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
- inst.instruction |= (inst.operands[1].imm & 0xf0) << 12;
- inst.instruction |= (inst.operands[1].imm & 0x0f);
+ constraint (inst.operands[0].writeback,
+ _("push/pop do not support {reglist}^"));
+ inst.operands[1] = inst.operands[0];
+ memset (&inst.operands[0], 0, sizeof inst.operands[0]);
+ inst.operands[0].isreg = 1;
+ inst.operands[0].writeback = 1;
+ inst.operands[0].reg = REG_SP;
+ encode_ldmstm (/*from_push_pop_mnem=*/TRUE);
}
+/* ARM V6 RFE (Return from Exception) loads the PC and CPSR from the
+ word at the specified address and the following word
+ respectively.
+ Unconditionally executed.
+ Error if Rn is R15. */
+
static void
-vfp_conv (int srcsize)
+do_rfe (void)
{
- unsigned immbits = srcsize - inst.operands[1].imm;
- inst.instruction |= (immbits & 1) << 5;
- inst.instruction |= (immbits >> 1);
+ inst.instruction |= inst.operands[0].reg << 16;
+ if (inst.operands[0].writeback)
+ inst.instruction |= WRITE_BACK;
}
+/* ARM V6 ssat (argument parse). */
+
static void
-do_vfp_sp_conv_16 (void)
+do_ssat (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
- vfp_conv (16);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= (inst.operands[1].imm - 1) << 16;
+ inst.instruction |= inst.operands[2].reg;
+
+ if (inst.operands[3].present)
+ encode_arm_shift (3);
}
+/* ARM V6 usat (argument parse). */
+
static void
-do_vfp_dp_conv_16 (void)
+do_usat (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
- vfp_conv (16);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].imm << 16;
+ inst.instruction |= inst.operands[2].reg;
+
+ if (inst.operands[3].present)
+ encode_arm_shift (3);
}
+/* ARM V6 ssat16 (argument parse). */
+
static void
-do_vfp_sp_conv_32 (void)
+do_ssat16 (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
- vfp_conv (32);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= ((inst.operands[1].imm - 1) << 16);
+ inst.instruction |= inst.operands[2].reg;
}
static void
-do_vfp_dp_conv_32 (void)
+do_usat16 (void)
{
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
- vfp_conv (32);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].imm << 16;
+ inst.instruction |= inst.operands[2].reg;
}
-\f
-/* FPA instructions. Also in a logical order. */
+
+/* ARM V6 SETEND (argument parse). Sets the E bit in the CPSR while
+ preserving the other bits.
+
+ setend <endian_specifier>, where <endian_specifier> is either
+ BE or LE. */
static void
-do_fpa_cmp (void)
+do_setend (void)
{
- inst.instruction |= inst.operands[0].reg << 16;
- inst.instruction |= inst.operands[1].reg;
+ if (warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v8))
+ as_tsktsk (_("setend use is deprecated for ARMv8"));
+
+ if (inst.operands[0].imm)
+ inst.instruction |= 0x200;
}
static void
-do_fpa_ldmstm (void)
+do_shift (void)
{
- inst.instruction |= inst.operands[0].reg << 12;
- switch (inst.operands[1].imm)
- {
- case 1: inst.instruction |= CP_T_X; break;
- case 2: inst.instruction |= CP_T_Y; break;
- case 3: inst.instruction |= CP_T_Y | CP_T_X; break;
- case 4: break;
- default: abort ();
- }
+ unsigned int Rm = (inst.operands[1].present
+ ? inst.operands[1].reg
+ : inst.operands[0].reg);
- if (inst.instruction & (PRE_INDEX | INDEX_UP))
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= Rm;
+ if (inst.operands[2].isreg) /* Rd, {Rm,} Rs */
{
- /* The instruction specified "ea" or "fd", so we can only accept
- [Rn]{!}. The instruction does not really support stacking or
- unstacking, so we have to emulate these by setting appropriate
- bits and offsets. */
- constraint (inst.reloc.exp.X_op != O_constant
- || inst.reloc.exp.X_add_number != 0,
- _("this instruction does not support indexing"));
-
- if ((inst.instruction & PRE_INDEX) || inst.operands[2].writeback)
- inst.reloc.exp.X_add_number = 12 * inst.operands[1].imm;
-
- if (!(inst.instruction & INDEX_UP))
- inst.reloc.exp.X_add_number = -inst.reloc.exp.X_add_number;
-
- if (!(inst.instruction & PRE_INDEX) && inst.operands[2].writeback)
- {
- inst.operands[2].preind = 0;
- inst.operands[2].postind = 1;
- }
+ 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"));
}
-
- encode_arm_cp_address (2, TRUE, TRUE, 0);
+ else
+ inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
}
-\f
-/* iWMMXt instructions: strictly in alphabetical order. */
static void
-do_iwmmxt_tandorc (void)
+do_smc (void)
{
- constraint (inst.operands[0].reg != REG_PC, _("only r15 allowed here"));
+ inst.reloc.type = BFD_RELOC_ARM_SMC;
+ inst.reloc.pc_rel = 0;
}
static void
-do_iwmmxt_textrc (void)
+do_hvc (void)
{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].imm;
+ inst.reloc.type = BFD_RELOC_ARM_HVC;
+ inst.reloc.pc_rel = 0;
}
static void
-do_iwmmxt_textrm (void)
+do_swi (void)
{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.instruction |= inst.operands[2].imm;
+ inst.reloc.type = BFD_RELOC_ARM_SWI;
+ inst.reloc.pc_rel = 0;
}
static void
-do_iwmmxt_tinsr (void)
+do_setpan (void)
{
- inst.instruction |= inst.operands[0].reg << 16;
- inst.instruction |= inst.operands[1].reg << 12;
- inst.instruction |= inst.operands[2].imm;
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_pan),
+ _("selected processor does not support SETPAN instruction"));
+
+ inst.instruction |= ((inst.operands[0].imm & 1) << 9);
}
static void
-do_iwmmxt_tmia (void)
+do_t_setpan (void)
{
- inst.instruction |= inst.operands[0].reg << 5;
- inst.instruction |= inst.operands[1].reg;
- inst.instruction |= inst.operands[2].reg << 12;
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_pan),
+ _("selected processor does not support SETPAN instruction"));
+
+ inst.instruction |= (inst.operands[0].imm << 3);
}
+/* ARM V5E (El Segundo) signed-multiply-accumulate (argument parse)
+ SMLAxy{cond} Rd,Rm,Rs,Rn
+ SMLAWy{cond} Rd,Rm,Rs,Rn
+ Error if any register is R15. */
+
static void
-do_iwmmxt_waligni (void)
+do_smla (void)
{
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.instruction |= inst.operands[2].reg;
- inst.instruction |= inst.operands[3].imm << 20;
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 8;
+ inst.instruction |= inst.operands[3].reg << 12;
}
+/* ARM V5E (El Segundo) signed-multiply-accumulate-long (argument parse)
+ SMLALxy{cond} Rdlo,Rdhi,Rm,Rs
+ Error if any register is R15.
+ Warning if Rdlo == Rdhi. */
+
static void
-do_iwmmxt_wmerge (void)
+do_smlal (void)
{
inst.instruction |= inst.operands[0].reg << 12;
inst.instruction |= inst.operands[1].reg << 16;
inst.instruction |= inst.operands[2].reg;
- inst.instruction |= inst.operands[3].imm << 21;
+ inst.instruction |= inst.operands[3].reg << 8;
+
+ if (inst.operands[0].reg == inst.operands[1].reg)
+ as_tsktsk (_("rdhi and rdlo must be different"));
}
-static void
-do_iwmmxt_wmov (void)
-{
- /* WMOV rD, rN is an alias for WOR rD, rN, rN. */
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.instruction |= inst.operands[1].reg;
-}
+/* ARM V5E (El Segundo) signed-multiply (argument parse)
+ SMULxy{cond} Rd,Rm,Rs
+ Error if any register is R15. */
static void
-do_iwmmxt_wldstbh (void)
+do_smul (void)
{
- int reloc;
- inst.instruction |= inst.operands[0].reg << 12;
- if (thumb_mode)
- reloc = BFD_RELOC_ARM_T32_CP_OFF_IMM_S2;
- else
- reloc = BFD_RELOC_ARM_CP_OFF_IMM_S2;
- encode_arm_cp_address (1, TRUE, FALSE, reloc);
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 8;
}
-static void
-do_iwmmxt_wldstw (void)
-{
- /* RIWR_RIWC clears .isreg for a control register. */
- if (!inst.operands[0].isreg)
- {
- constraint (inst.cond != COND_ALWAYS, BAD_COND);
- inst.instruction |= 0xf0000000;
- }
-
- inst.instruction |= inst.operands[0].reg << 12;
- encode_arm_cp_address (1, TRUE, TRUE, 0);
-}
+/* ARM V6 srs (argument parse). The variable fields in the encoding are
+ the same for both ARM and Thumb-2. */
static void
-do_iwmmxt_wldstd (void)
+do_srs (void)
{
- inst.instruction |= inst.operands[0].reg << 12;
- if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt2)
- && inst.operands[1].immisreg)
+ int reg;
+
+ if (inst.operands[0].present)
{
- inst.instruction &= ~0x1a000ff;
- inst.instruction |= (0xf << 28);
- if (inst.operands[1].preind)
- inst.instruction |= PRE_INDEX;
- if (!inst.operands[1].negative)
- inst.instruction |= INDEX_UP;
- if (inst.operands[1].writeback)
- inst.instruction |= WRITE_BACK;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.instruction |= inst.reloc.exp.X_add_number << 4;
- inst.instruction |= inst.operands[1].imm;
+ reg = inst.operands[0].reg;
+ constraint (reg != REG_SP, _("SRS base register must be r13"));
}
else
- encode_arm_cp_address (1, TRUE, FALSE, 0);
+ reg = REG_SP;
+
+ inst.instruction |= reg << 16;
+ inst.instruction |= inst.operands[1].imm;
+ if (inst.operands[0].writeback || inst.operands[1].writeback)
+ inst.instruction |= WRITE_BACK;
}
+/* ARM V6 strex (argument parse). */
+
static void
-do_iwmmxt_wshufh (void)
+do_strex (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
+ /* See comment in do_ldrex(). */
+ || (inst.operands[2].reg == REG_PC),
+ BAD_ADDR_MODE);
+
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
+
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ _("offset must be zero in ARM encoding"));
+
inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.instruction |= ((inst.operands[2].imm & 0xf0) << 16);
- inst.instruction |= (inst.operands[2].imm & 0x0f);
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 16;
+ inst.reloc.type = BFD_RELOC_UNUSED;
}
static void
-do_iwmmxt_wzero (void)
+do_t_strexbh (void)
{
- /* WZERO reg is an alias for WANDN reg, reg, reg. */
- inst.instruction |= inst.operands[0].reg;
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[0].reg << 16;
+ 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_iwmmxt_wrwrwr_or_imm5 (void)
+do_strexd (void)
{
- if (inst.operands[2].isreg)
- do_rd_rn_rm ();
- else {
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt2),
- _("immediate operand requires iWMMXt2"));
- do_rd_rn ();
- if (inst.operands[2].imm == 0)
- {
- switch ((inst.instruction >> 20) & 0xf)
- {
- case 4:
- case 5:
- case 6:
- case 7:
- /* w...h wrd, wrn, #0 -> wrorh wrd, wrn, #16. */
- inst.operands[2].imm = 16;
- inst.instruction = (inst.instruction & 0xff0fffff) | (0x7 << 20);
- break;
- case 8:
- case 9:
- case 10:
- case 11:
- /* w...w wrd, wrn, #0 -> wrorw wrd, wrn, #32. */
- inst.operands[2].imm = 32;
- inst.instruction = (inst.instruction & 0xff0fffff) | (0xb << 20);
- break;
- case 12:
- case 13:
- case 14:
- case 15:
- {
- /* w...d wrd, wrn, #0 -> wor wrd, wrn, wrn. */
- unsigned long wrn;
- wrn = (inst.instruction >> 16) & 0xf;
- inst.instruction &= 0xff0fff0f;
- inst.instruction |= wrn;
- /* Bail out here; the instruction is now assembled. */
- return;
- }
- }
- }
- /* Map 32 -> 0, etc. */
- inst.operands[2].imm &= 0x1f;
- inst.instruction |= (0xf << 28) | ((inst.operands[2].imm & 0x10) << 4) | (inst.operands[2].imm & 0xf);
- }
-}
-\f
-/* Cirrus Maverick instructions. Simple 2-, 3-, and 4-register
- operations first, then control, shift, and load/store. */
+ constraint (inst.operands[1].reg % 2 != 0,
+ _("even register required"));
+ constraint (inst.operands[2].present
+ && inst.operands[2].reg != inst.operands[1].reg + 1,
+ _("can only store two consecutive registers"));
+ /* If op 2 were present and equal to PC, this function wouldn't
+ have been called in the first place. */
+ constraint (inst.operands[1].reg == REG_LR, _("r14 not allowed here"));
-/* Insns like "foo X,Y,Z". */
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[1].reg + 1
+ || inst.operands[0].reg == inst.operands[3].reg,
+ BAD_OVERLAP);
-static void
-do_mav_triple (void)
-{
- inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[0].reg << 12;
inst.instruction |= inst.operands[1].reg;
- inst.instruction |= inst.operands[2].reg << 12;
+ inst.instruction |= inst.operands[3].reg << 16;
}
-/* Insns like "foo W,X,Y,Z".
- where W=MVAX[0:3] and X,Y,Z=MVFX[0:15]. */
-
+/* ARM V8 STRL. */
static void
-do_mav_quad (void)
+do_stlex (void)
{
- inst.instruction |= inst.operands[0].reg << 5;
- inst.instruction |= inst.operands[1].reg << 12;
- inst.instruction |= inst.operands[2].reg << 16;
- inst.instruction |= inst.operands[3].reg;
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
+
+ do_rd_rm_rn ();
}
-/* cfmvsc32<cond> DSPSC,MVDX[15:0]. */
static void
-do_mav_dspsc (void)
+do_t_stlex (void)
{
- inst.instruction |= inst.operands[1].reg << 12;
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg, BAD_OVERLAP);
+
+ do_rm_rd_rn ();
}
-/* Maverick shift immediate instructions.
- cfsh32<cond> MVFX[15:0],MVFX[15:0],Shift[6:0].
- cfsh64<cond> MVDX[15:0],MVDX[15:0],Shift[6:0]. */
+/* 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
+ before extracting the 16-bit value.
+ SXTAH{<cond>} <Rd>, <Rn>, <Rm>{, <rotation>}
+ Condition defaults to COND_ALWAYS.
+ Error if any register uses R15. */
static void
-do_mav_shift (void)
+do_sxtah (void)
{
- int imm = inst.operands[2].imm;
-
inst.instruction |= inst.operands[0].reg << 12;
inst.instruction |= inst.operands[1].reg << 16;
-
- /* Bits 0-3 of the insn should have bits 0-3 of the immediate.
- Bits 5-7 of the insn should have bits 4-6 of the immediate.
- Bit 4 should be 0. */
- imm = (imm & 0xf) | ((imm & 0x70) << 1);
-
- inst.instruction |= imm;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].imm << 10;
}
-\f
-/* XScale instructions. Also sorted arithmetic before move. */
-/* Xscale multiply-accumulate (argument parse)
- MIAcc acc0,Rm,Rs
- MIAPHcc acc0,Rm,Rs
- MIAxycc acc0,Rm,Rs. */
+/* ARM V6 SXTH.
+
+ SXTH {<cond>} <Rd>, <Rm>{, <rotation>}
+ Condition defaults to COND_ALWAYS.
+ Error if any register uses R15. */
static void
-do_xsc_mia (void)
+do_sxth (void)
{
+ inst.instruction |= inst.operands[0].reg << 12;
inst.instruction |= inst.operands[1].reg;
- inst.instruction |= inst.operands[2].reg << 12;
+ inst.instruction |= inst.operands[2].imm << 10;
}
-
-/* Xscale move-accumulator-register (argument parse)
-
- MARcc acc0,RdLo,RdHi. */
+\f
+/* VFP instructions. In a logical order: SP variant first, monad
+ before dyad, arithmetic then move then load/store. */
static void
-do_xsc_mar (void)
+do_vfp_sp_monadic (void)
{
- inst.instruction |= inst.operands[1].reg << 12;
- inst.instruction |= inst.operands[2].reg << 16;
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sm);
}
-/* Xscale move-register-accumulator (argument parse)
-
- MRAcc RdLo,RdHi,acc0. */
-
static void
-do_xsc_mra (void)
+do_vfp_sp_dyadic (void)
{
- constraint (inst.operands[0].reg == inst.operands[1].reg, BAD_OVERLAP);
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sn);
+ encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Sm);
}
-\f
-/* Encoding functions relevant only to Thumb. */
-/* inst.operands[i] is a shifted-register operand; encode
- it into inst.instruction in the format used by Thumb32. */
+static void
+do_vfp_sp_compare_z (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+}
static void
-encode_thumb32_shifted_operand (int i)
+do_vfp_dp_sp_cvt (void)
{
- unsigned int value = inst.reloc.exp.X_add_number;
- unsigned int shift = inst.operands[i].shift_kind;
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sm);
+}
- constraint (inst.operands[i].immisreg,
- _("shift by register not allowed in thumb mode"));
- inst.instruction |= inst.operands[i].reg;
- if (shift == SHIFT_RRX)
- inst.instruction |= SHIFT_ROR << 4;
+static void
+do_vfp_sp_dp_cvt (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dm);
+}
+
+static void
+do_vfp_reg_from_sp (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sn);
+}
+
+static void
+do_vfp_reg2_from_sp2 (void)
+{
+ constraint (inst.operands[2].imm != 2,
+ _("only two consecutive VFP SP registers allowed here"));
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Sm);
+}
+
+static void
+do_vfp_sp_from_reg (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sn);
+ inst.instruction |= inst.operands[1].reg << 12;
+}
+
+static void
+do_vfp_sp2_from_reg2 (void)
+{
+ constraint (inst.operands[0].imm != 2,
+ _("only two consecutive VFP SP registers allowed here"));
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sm);
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+}
+
+static void
+do_vfp_sp_ldst (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ encode_arm_cp_address (1, FALSE, TRUE, 0);
+}
+
+static void
+do_vfp_dp_ldst (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ encode_arm_cp_address (1, FALSE, TRUE, 0);
+}
+
+
+static void
+vfp_sp_ldstm (enum vfp_ldstm_type ldstm_type)
+{
+ if (inst.operands[0].writeback)
+ inst.instruction |= WRITE_BACK;
else
- {
- constraint (inst.reloc.exp.X_op != O_constant,
- _("expression too complex"));
+ constraint (ldstm_type != VFP_LDSTMIA,
+ _("this addressing mode requires base-register writeback"));
+ inst.instruction |= inst.operands[0].reg << 16;
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sd);
+ inst.instruction |= inst.operands[1].imm;
+}
- constraint (value > 32
- || (value == 32 && (shift == SHIFT_LSL
- || shift == SHIFT_ROR)),
- _("shift expression is too large"));
+static void
+vfp_dp_ldstm (enum vfp_ldstm_type ldstm_type)
+{
+ int count;
- if (value == 0)
- shift = SHIFT_LSL;
- else if (value == 32)
- value = 0;
+ if (inst.operands[0].writeback)
+ inst.instruction |= WRITE_BACK;
+ else
+ constraint (ldstm_type != VFP_LDSTMIA && ldstm_type != VFP_LDSTMIAX,
+ _("this addressing mode requires base-register writeback"));
- inst.instruction |= shift << 4;
- inst.instruction |= (value & 0x1c) << 10;
- inst.instruction |= (value & 0x03) << 6;
- }
+ inst.instruction |= inst.operands[0].reg << 16;
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dd);
+
+ count = inst.operands[1].imm << 1;
+ if (ldstm_type == VFP_LDSTMIAX || ldstm_type == VFP_LDSTMDBX)
+ count += 1;
+
+ inst.instruction |= count;
}
+static void
+do_vfp_sp_ldstmia (void)
+{
+ vfp_sp_ldstm (VFP_LDSTMIA);
+}
-/* inst.operands[i] was set up by parse_address. Encode it into a
- Thumb32 format load or store instruction. Reject forms that cannot
- be used with such instructions. If is_t is true, reject forms that
- cannot be used with a T instruction; if is_d is true, reject forms
- that cannot be used with a D instruction. If it is a store insn,
- reject PC in Rn. */
+static void
+do_vfp_sp_ldstmdb (void)
+{
+ vfp_sp_ldstm (VFP_LDSTMDB);
+}
static void
-encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
+do_vfp_dp_ldstmia (void)
{
- const bfd_boolean is_pc = (inst.operands[i].reg == REG_PC);
+ vfp_dp_ldstm (VFP_LDSTMIA);
+}
- constraint (!inst.operands[i].isreg,
- _("Instruction does not support =N addresses"));
+static void
+do_vfp_dp_ldstmdb (void)
+{
+ vfp_dp_ldstm (VFP_LDSTMDB);
+}
- inst.instruction |= inst.operands[i].reg << 16;
- if (inst.operands[i].immisreg)
- {
- constraint (is_pc, BAD_PC_ADDRESSING);
- constraint (is_t || is_d, _("cannot use register index with this instruction"));
- constraint (inst.operands[i].negative,
- _("Thumb does not support negative register indexing"));
- constraint (inst.operands[i].postind,
- _("Thumb does not support register post-indexing"));
- constraint (inst.operands[i].writeback,
- _("Thumb does not support register indexing with writeback"));
- constraint (inst.operands[i].shifted && inst.operands[i].shift_kind != SHIFT_LSL,
- _("Thumb supports only LSL in shifted register indexing"));
+static void
+do_vfp_xp_ldstmia (void)
+{
+ vfp_dp_ldstm (VFP_LDSTMIAX);
+}
- inst.instruction |= inst.operands[i].imm;
- if (inst.operands[i].shifted)
- {
- constraint (inst.reloc.exp.X_op != O_constant,
- _("expression too complex"));
- constraint (inst.reloc.exp.X_add_number < 0
- || inst.reloc.exp.X_add_number > 3,
- _("shift out of range"));
- inst.instruction |= inst.reloc.exp.X_add_number << 4;
- }
- inst.reloc.type = BFD_RELOC_UNUSED;
- }
- else if (inst.operands[i].preind)
- {
- 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);
+static void
+do_vfp_xp_ldstmdb (void)
+{
+ vfp_dp_ldstm (VFP_LDSTMDBX);
+}
- if (is_d)
- {
- inst.instruction |= 0x01000000;
- if (inst.operands[i].writeback)
- inst.instruction |= 0x00200000;
- }
- else
- {
- inst.instruction |= 0x00000c00;
- if (inst.operands[i].writeback)
- inst.instruction |= 0x00000100;
- }
- inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_IMM;
- }
- else if (inst.operands[i].postind)
- {
- gas_assert (inst.operands[i].writeback);
- constraint (is_pc, _("cannot use post-indexing with PC-relative addressing"));
- constraint (is_t, _("cannot use post-indexing with this instruction"));
+static void
+do_vfp_dp_rd_rm (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dm);
+}
- if (is_d)
- inst.instruction |= 0x00200000;
- else
- inst.instruction |= 0x00000900;
- inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_IMM;
- }
- else /* unindexed - only for coprocessor */
- inst.error = _("instruction does not accept unindexed addressing");
+static void
+do_vfp_dp_rn_rd (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dn);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dd);
}
-/* Table of Thumb instructions which exist in both 16- and 32-bit
- encodings (the latter only in post-V6T2 cores). The index is the
- value used in the insns table below. When there is more than one
- possible 16-bit encoding for the instruction, this table always
- holds variant (1).
- Also contains several pseudo-instructions used during relaxation. */
-#define T16_32_TAB \
- X(_adc, 4140, eb400000), \
- X(_adcs, 4140, eb500000), \
- X(_add, 1c00, eb000000), \
- X(_adds, 1c00, eb100000), \
- X(_addi, 0000, f1000000), \
- X(_addis, 0000, f1100000), \
- X(_add_pc,000f, f20f0000), \
- X(_add_sp,000d, f10d0000), \
- X(_adr, 000f, f20f0000), \
- X(_and, 4000, ea000000), \
- X(_ands, 4000, ea100000), \
- X(_asr, 1000, fa40f000), \
- X(_asrs, 1000, fa50f000), \
- X(_b, e000, f000b000), \
- X(_bcond, d000, f0008000), \
- X(_bic, 4380, ea200000), \
- X(_bics, 4380, ea300000), \
- X(_cmn, 42c0, eb100f00), \
- X(_cmp, 2800, ebb00f00), \
- X(_cpsie, b660, f3af8400), \
- X(_cpsid, b670, f3af8600), \
- X(_cpy, 4600, ea4f0000), \
- X(_dec_sp,80dd, f1ad0d00), \
- X(_eor, 4040, ea800000), \
- X(_eors, 4040, ea900000), \
- X(_inc_sp,00dd, f10d0d00), \
- X(_ldmia, c800, e8900000), \
- X(_ldr, 6800, f8500000), \
- X(_ldrb, 7800, f8100000), \
- X(_ldrh, 8800, f8300000), \
- X(_ldrsb, 5600, f9100000), \
- X(_ldrsh, 5e00, f9300000), \
- X(_ldr_pc,4800, f85f0000), \
- X(_ldr_pc2,4800, f85f0000), \
- X(_ldr_sp,9800, f85d0000), \
- X(_lsl, 0000, fa00f000), \
- X(_lsls, 0000, fa10f000), \
- X(_lsr, 0800, fa20f000), \
- X(_lsrs, 0800, fa30f000), \
- X(_mov, 2000, ea4f0000), \
- X(_movs, 2000, ea5f0000), \
- X(_mul, 4340, fb00f000), \
- X(_muls, 4340, ffffffff), /* no 32b muls */ \
- X(_mvn, 43c0, ea6f0000), \
- X(_mvns, 43c0, ea7f0000), \
- X(_neg, 4240, f1c00000), /* rsb #0 */ \
- X(_negs, 4240, f1d00000), /* rsbs #0 */ \
- X(_orr, 4300, ea400000), \
- X(_orrs, 4300, ea500000), \
- X(_pop, bc00, e8bd0000), /* ldmia sp!,... */ \
- X(_push, b400, e92d0000), /* stmdb sp!,... */ \
- X(_rev, ba00, fa90f080), \
- X(_rev16, ba40, fa90f090), \
- X(_revsh, bac0, fa90f0b0), \
- X(_ror, 41c0, fa60f000), \
- X(_rors, 41c0, fa70f000), \
- X(_sbc, 4180, eb600000), \
- X(_sbcs, 4180, eb700000), \
- X(_stmia, c000, e8800000), \
- X(_str, 6000, f8400000), \
- X(_strb, 7000, f8000000), \
- X(_strh, 8000, f8200000), \
- X(_str_sp,9000, f84d0000), \
- X(_sub, 1e00, eba00000), \
- X(_subs, 1e00, ebb00000), \
- X(_subi, 8000, f1a00000), \
- X(_subis, 8000, f1b00000), \
- X(_sxtb, b240, fa4ff080), \
- X(_sxth, b200, fa0ff080), \
- X(_tst, 4200, ea100f00), \
- X(_uxtb, b2c0, fa5ff080), \
- X(_uxth, b280, fa1ff080), \
- X(_nop, bf00, f3af8000), \
- X(_yield, bf10, f3af8001), \
- X(_wfe, bf20, f3af8002), \
- X(_wfi, bf30, f3af8003), \
- X(_sev, bf40, f3af8004),
+static void
+do_vfp_dp_rd_rn (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dn);
+}
-/* 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
- as 16-bit instructions. */
-#define X(a,b,c) T_MNEM##a
-enum t16_32_codes { T16_32_OFFSET = 0xF7FF, T16_32_TAB };
-#undef X
+static void
+do_vfp_dp_rd_rn_rm (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dn);
+ encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Dm);
+}
-#define X(a,b,c) 0x##b
-static const unsigned short thumb_op16[] = { T16_32_TAB };
-#define THUMB_OP16(n) (thumb_op16[(n) - (T16_32_OFFSET + 1)])
-#undef X
+static void
+do_vfp_dp_rd (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+}
-#define X(a,b,c) 0x##c
-static const unsigned int thumb_op32[] = { T16_32_TAB };
-#define THUMB_OP32(n) (thumb_op32[(n) - (T16_32_OFFSET + 1)])
-#define THUMB_SETS_FLAGS(n) (THUMB_OP32 (n) & 0x00100000)
-#undef X
-#undef T16_32_TAB
+static void
+do_vfp_dp_rm_rd_rn (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dm);
+ encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Dd);
+ encode_arm_vfp_reg (inst.operands[2].reg, VFP_REG_Dn);
+}
-/* Thumb instruction encoders, in alphabetical order. */
+/* VFPv3 instructions. */
+static void
+do_vfp_sp_const (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ inst.instruction |= (inst.operands[1].imm & 0xf0) << 12;
+ inst.instruction |= (inst.operands[1].imm & 0x0f);
+}
-/* ADDW or SUBW. */
+static void
+do_vfp_dp_const (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ inst.instruction |= (inst.operands[1].imm & 0xf0) << 12;
+ inst.instruction |= (inst.operands[1].imm & 0x0f);
+}
static void
-do_t_add_sub_w (void)
+vfp_conv (int srcsize)
{
- int Rd, Rn;
+ int immbits = srcsize - inst.operands[1].imm;
- Rd = inst.operands[0].reg;
- Rn = inst.operands[1].reg;
+ 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;
+ }
- /* If Rn is REG_PC, this is ADR; if Rn is REG_SP, then this
- is the SP-{plus,minus}-immediate form of the instruction. */
- if (Rn == REG_SP)
- constraint (Rd == REG_PC, BAD_PC);
- else
- reject_bad_reg (Rd);
+ inst.instruction |= (immbits & 1) << 5;
+ inst.instruction |= (immbits >> 1);
+}
- inst.instruction |= (Rn << 16) | (Rd << 8);
- inst.reloc.type = BFD_RELOC_ARM_T32_IMM12;
+static void
+do_vfp_sp_conv_16 (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ vfp_conv (16);
}
-/* Parse an add or subtract instruction. We get here with inst.instruction
- equalling any of THUMB_OPCODE_add, adds, sub, or subs. */
+static void
+do_vfp_dp_conv_16 (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ vfp_conv (16);
+}
static void
-do_t_add_sub (void)
+do_vfp_sp_conv_32 (void)
{
- int Rd, Rs, Rn;
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
+ vfp_conv (32);
+}
- Rd = inst.operands[0].reg;
- Rs = (inst.operands[1].present
- ? inst.operands[1].reg /* Rd, Rs, foo */
- : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
+static void
+do_vfp_dp_conv_32 (void)
+{
+ encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Dd);
+ vfp_conv (32);
+}
+\f
+/* FPA instructions. Also in a logical order. */
- if (Rd == REG_PC)
- set_it_insn_type_last ();
+static void
+do_fpa_cmp (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+}
- if (unified_syntax)
+static void
+do_fpa_ldmstm (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ switch (inst.operands[1].imm)
{
- bfd_boolean flags;
- bfd_boolean narrow;
- int opcode;
+ case 1: inst.instruction |= CP_T_X; break;
+ case 2: inst.instruction |= CP_T_Y; break;
+ case 3: inst.instruction |= CP_T_Y | CP_T_X; break;
+ case 4: break;
+ default: abort ();
+ }
- flags = (inst.instruction == T_MNEM_adds
- || inst.instruction == T_MNEM_subs);
- if (flags)
- narrow = !in_it_block ();
- else
- narrow = in_it_block ();
- if (!inst.operands[2].isreg)
+ if (inst.instruction & (PRE_INDEX | INDEX_UP))
+ {
+ /* The instruction specified "ea" or "fd", so we can only accept
+ [Rn]{!}. The instruction does not really support stacking or
+ unstacking, so we have to emulate these by setting appropriate
+ bits and offsets. */
+ constraint (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0,
+ _("this instruction does not support indexing"));
+
+ if ((inst.instruction & PRE_INDEX) || inst.operands[2].writeback)
+ inst.reloc.exp.X_add_number = 12 * inst.operands[1].imm;
+
+ if (!(inst.instruction & INDEX_UP))
+ inst.reloc.exp.X_add_number = -inst.reloc.exp.X_add_number;
+
+ if (!(inst.instruction & PRE_INDEX) && inst.operands[2].writeback)
{
- int add;
+ inst.operands[2].preind = 0;
+ inst.operands[2].postind = 1;
+ }
+ }
- constraint (Rd == REG_SP && Rs != REG_SP, BAD_SP);
+ encode_arm_cp_address (2, TRUE, TRUE, 0);
+}
+\f
+/* iWMMXt instructions: strictly in alphabetical order. */
- add = (inst.instruction == T_MNEM_add
- || inst.instruction == T_MNEM_adds);
- opcode = 0;
- if (inst.size_req != 4)
- {
- /* Attempt to use a narrow opcode, with relaxation if
- 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)
- opcode = T_MNEM_add_sp;
- else if (Rd <= 7 && Rs == REG_PC && add && !flags)
- opcode = T_MNEM_add_pc;
- else if (Rd <= 7 && Rs <= 7 && narrow)
- {
- if (flags)
- opcode = add ? T_MNEM_addis : T_MNEM_subis;
- else
- opcode = add ? T_MNEM_addi : T_MNEM_subi;
- }
- if (opcode)
- {
- inst.instruction = THUMB_OP16(opcode);
- inst.instruction |= (Rd << 4) | Rs;
- inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
- if (inst.size_req != 2)
- inst.relax = opcode;
- }
- else
- constraint (inst.size_req == 2, BAD_HIREG);
- }
- if (inst.size_req == 4
- || (inst.size_req != 2 && !opcode))
- {
- if (Rd == REG_PC)
- {
- constraint (add, BAD_PC);
- constraint (Rs != REG_LR || inst.instruction != T_MNEM_subs,
- _("only SUBS PC, LR, #const allowed"));
- constraint (inst.reloc.exp.X_op != O_constant,
- _("expression too complex"));
- constraint (inst.reloc.exp.X_add_number < 0
- || inst.reloc.exp.X_add_number > 0xff,
- _("immediate value out of range"));
- inst.instruction = T2_SUBS_PC_LR
- | inst.reloc.exp.X_add_number;
- inst.reloc.type = BFD_RELOC_UNUSED;
- return;
- }
- else if (Rs == REG_PC)
- {
- /* Always use addw/subw. */
- inst.instruction = add ? 0xf20f0000 : 0xf2af0000;
- inst.reloc.type = BFD_RELOC_ARM_T32_IMM12;
- }
- else
- {
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction = (inst.instruction & 0xe1ffffff)
- | 0x10000000;
- if (flags)
- inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
- else
- inst.reloc.type = BFD_RELOC_ARM_T32_ADD_IMM;
- }
- inst.instruction |= Rd << 8;
- inst.instruction |= Rs << 16;
- }
- }
- else
- {
- 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)
- {
- if (Rd > 7 || Rs > 7 || Rn > 7)
- narrow = FALSE;
-
- if (narrow)
- {
- inst.instruction = ((inst.instruction == T_MNEM_adds
- || inst.instruction == T_MNEM_add)
- ? T_OPCODE_ADD_R3
- : T_OPCODE_SUB_R3);
- inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
- return;
- }
-
- if (inst.instruction == T_MNEM_add && (Rd == Rs || Rd == Rn))
- {
- /* Thumb-1 cores (except v6-M) require at least one high
- register in a narrow non flag setting add. */
- if (Rd > 7 || Rn > 7
- || ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6t2)
- || ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_msr))
- {
- if (Rd == Rn)
- {
- Rn = Rs;
- Rs = Rd;
- }
- inst.instruction = T_OPCODE_ADD_HI;
- inst.instruction |= (Rd & 8) << 4;
- inst.instruction |= (Rd & 7);
- inst.instruction |= Rn << 3;
- return;
- }
- }
- }
+static void
+do_iwmmxt_tandorc (void)
+{
+ constraint (inst.operands[0].reg != REG_PC, _("only r15 allowed here"));
+}
- constraint (Rd == REG_PC, BAD_PC);
- constraint (Rd == REG_SP && Rs != REG_SP, BAD_SP);
- constraint (Rs == REG_PC, BAD_PC);
- reject_bad_reg (Rn);
+static void
+do_iwmmxt_textrc (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].imm;
+}
- /* If we get here, it can't be done in 16 bits. */
- constraint (inst.operands[2].shifted && inst.operands[2].immisreg,
- _("shift must be constant"));
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= Rd << 8;
- inst.instruction |= Rs << 16;
- encode_thumb32_shifted_operand (2);
- }
- }
- else
- {
- constraint (inst.instruction == T_MNEM_adds
- || inst.instruction == T_MNEM_subs,
- BAD_THUMB32);
+static void
+do_iwmmxt_textrm (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].imm;
+}
- if (!inst.operands[2].isreg) /* Rd, Rs, #imm */
- {
- constraint ((Rd > 7 && (Rd != REG_SP || Rs != REG_SP))
- || (Rs > 7 && Rs != REG_SP && Rs != REG_PC),
- BAD_HIREG);
+static void
+do_iwmmxt_tinsr (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].imm;
+}
- inst.instruction = (inst.instruction == T_MNEM_add
- ? 0x0000 : 0x8000);
- inst.instruction |= (Rd << 4) | Rs;
- inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
- return;
- }
+static void
+do_iwmmxt_tmia (void)
+{
+ inst.instruction |= inst.operands[0].reg << 5;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 12;
+}
- Rn = inst.operands[2].reg;
- constraint (inst.operands[2].shifted, _("unshifted register required"));
+static void
+do_iwmmxt_waligni (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].imm << 20;
+}
- /* We now have Rd, Rs, and Rn set to registers. */
- if (Rd > 7 || Rs > 7 || Rn > 7)
- {
- /* Can't do this for SUB. */
- constraint (inst.instruction == T_MNEM_sub, BAD_HIREG);
- inst.instruction = T_OPCODE_ADD_HI;
- inst.instruction |= (Rd & 8) << 4;
- inst.instruction |= (Rd & 7);
- if (Rs == Rd)
- inst.instruction |= Rn << 3;
- else if (Rn == Rd)
- inst.instruction |= Rs << 3;
- else
- constraint (1, _("dest must overlap one source register"));
- }
- else
- {
- inst.instruction = (inst.instruction == T_MNEM_add
- ? T_OPCODE_ADD_R3 : T_OPCODE_SUB_R3);
- inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
- }
- }
+static void
+do_iwmmxt_wmerge (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
+ inst.instruction |= inst.operands[3].imm << 21;
}
static void
-do_t_adr (void)
+do_iwmmxt_wmov (void)
{
- unsigned Rd;
+ /* WMOV rD, rN is an alias for WOR rD, rN, rN. */
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+}
- Rd = inst.operands[0].reg;
- reject_bad_reg (Rd);
+static void
+do_iwmmxt_wldstbh (void)
+{
+ int reloc;
+ inst.instruction |= inst.operands[0].reg << 12;
+ if (thumb_mode)
+ reloc = BFD_RELOC_ARM_T32_CP_OFF_IMM_S2;
+ else
+ reloc = BFD_RELOC_ARM_CP_OFF_IMM_S2;
+ encode_arm_cp_address (1, TRUE, FALSE, reloc);
+}
- if (unified_syntax && inst.size_req == 0 && Rd <= 7)
+static void
+do_iwmmxt_wldstw (void)
+{
+ /* RIWR_RIWC clears .isreg for a control register. */
+ if (!inst.operands[0].isreg)
{
- /* Defer to section relaxation. */
- inst.relax = inst.instruction;
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= Rd << 4;
+ constraint (inst.cond != COND_ALWAYS, BAD_COND);
+ inst.instruction |= 0xf0000000;
}
- else if (unified_syntax && inst.size_req != 2)
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_arm_cp_address (1, TRUE, TRUE, 0);
+}
+
+static void
+do_iwmmxt_wldstd (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt2)
+ && inst.operands[1].immisreg)
{
- /* Generate a 32-bit opcode. */
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= Rd << 8;
- inst.reloc.type = BFD_RELOC_ARM_T32_ADD_PC12;
- inst.reloc.pc_rel = 1;
+ inst.instruction &= ~0x1a000ff;
+ inst.instruction |= (0xfU << 28);
+ if (inst.operands[1].preind)
+ inst.instruction |= PRE_INDEX;
+ if (!inst.operands[1].negative)
+ inst.instruction |= INDEX_UP;
+ if (inst.operands[1].writeback)
+ inst.instruction |= WRITE_BACK;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.reloc.exp.X_add_number << 4;
+ inst.instruction |= inst.operands[1].imm;
}
else
- {
- /* Generate a 16-bit opcode. */
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
- inst.reloc.exp.X_add_number -= 4; /* PC relative adjust. */
- inst.reloc.pc_rel = 1;
-
- inst.instruction |= Rd << 4;
- }
+ encode_arm_cp_address (1, TRUE, FALSE, 0);
}
-/* Arithmetic instructions for which there is just one 16-bit
- instruction encoding, and it allows only two low registers.
- For maximal compatibility with ARM syntax, we allow three register
- operands even when Thumb-32 instructions are not available, as long
- as the first two are identical. For instance, both "sbc r0,r1" and
- "sbc r0,r0,r1" are allowed. */
static void
-do_t_arit3 (void)
+do_iwmmxt_wshufh (void)
{
- int Rd, Rs, Rn;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= ((inst.operands[2].imm & 0xf0) << 16);
+ inst.instruction |= (inst.operands[2].imm & 0x0f);
+}
- Rd = inst.operands[0].reg;
- Rs = (inst.operands[1].present
- ? inst.operands[1].reg /* Rd, Rs, foo */
- : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
- Rn = inst.operands[2].reg;
+static void
+do_iwmmxt_wzero (void)
+{
+ /* WZERO reg is an alias for WANDN reg, reg, reg. */
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[0].reg << 16;
+}
- reject_bad_reg (Rd);
- reject_bad_reg (Rs);
+static void
+do_iwmmxt_wrwrwr_or_imm5 (void)
+{
if (inst.operands[2].isreg)
- reject_bad_reg (Rn);
-
- if (unified_syntax)
- {
- if (!inst.operands[2].isreg)
- {
- /* For an immediate, we always generate a 32-bit opcode;
- section relaxation will shrink it later if possible. */
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
- inst.instruction |= Rd << 8;
- inst.instruction |= Rs << 16;
- inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
- }
- else
- {
- bfd_boolean narrow;
-
- /* See if we can do this with a 16-bit instruction. */
- if (THUMB_SETS_FLAGS (inst.instruction))
- narrow = !in_it_block ();
- else
- narrow = in_it_block ();
-
- if (Rd > 7 || Rn > 7 || Rs > 7)
- narrow = FALSE;
- if (inst.operands[2].shifted)
- narrow = FALSE;
- if (inst.size_req == 4)
- narrow = FALSE;
-
- if (narrow
- && Rd == Rs)
+ do_rd_rn_rm ();
+ else {
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_cext_iwmmxt2),
+ _("immediate operand requires iWMMXt2"));
+ do_rd_rn ();
+ if (inst.operands[2].imm == 0)
+ {
+ switch ((inst.instruction >> 20) & 0xf)
+ {
+ case 4:
+ case 5:
+ case 6:
+ case 7:
+ /* w...h wrd, wrn, #0 -> wrorh wrd, wrn, #16. */
+ inst.operands[2].imm = 16;
+ inst.instruction = (inst.instruction & 0xff0fffff) | (0x7 << 20);
+ break;
+ case 8:
+ case 9:
+ case 10:
+ case 11:
+ /* w...w wrd, wrn, #0 -> wrorw wrd, wrn, #32. */
+ inst.operands[2].imm = 32;
+ inst.instruction = (inst.instruction & 0xff0fffff) | (0xb << 20);
+ break;
+ case 12:
+ case 13:
+ case 14:
+ case 15:
{
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= Rd;
- inst.instruction |= Rn << 3;
+ /* w...d wrd, wrn, #0 -> wor wrd, wrn, wrn. */
+ unsigned long wrn;
+ wrn = (inst.instruction >> 16) & 0xf;
+ inst.instruction &= 0xff0fff0f;
+ inst.instruction |= wrn;
+ /* Bail out here; the instruction is now assembled. */
return;
}
+ }
+ }
+ /* Map 32 -> 0, etc. */
+ inst.operands[2].imm &= 0x1f;
+ inst.instruction |= (0xfU << 28) | ((inst.operands[2].imm & 0x10) << 4) | (inst.operands[2].imm & 0xf);
+ }
+}
+\f
+/* Cirrus Maverick instructions. Simple 2-, 3-, and 4-register
+ operations first, then control, shift, and load/store. */
- /* If we get here, it can't be done in 16 bits. */
- constraint (inst.operands[2].shifted
- && inst.operands[2].immisreg,
- _("shift must be constant"));
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= Rd << 8;
- inst.instruction |= Rs << 16;
- encode_thumb32_shifted_operand (2);
- }
- }
- else
- {
- /* On its face this is a lie - the instruction does set the
- flags. However, the only supported mnemonic in this mode
- says it doesn't. */
- constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
+/* Insns like "foo X,Y,Z". */
- constraint (!inst.operands[2].isreg || inst.operands[2].shifted,
- _("unshifted register required"));
- constraint (Rd > 7 || Rs > 7 || Rn > 7, BAD_HIREG);
- constraint (Rd != Rs,
- _("dest and source1 must be the same register"));
+static void
+do_mav_triple (void)
+{
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 12;
+}
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= Rd;
- inst.instruction |= Rn << 3;
- }
+/* Insns like "foo W,X,Y,Z".
+ where W=MVAX[0:3] and X,Y,Z=MVFX[0:15]. */
+
+static void
+do_mav_quad (void)
+{
+ inst.instruction |= inst.operands[0].reg << 5;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+ inst.instruction |= inst.operands[3].reg;
}
-/* Similarly, but for instructions where the arithmetic operation is
- commutative, so we can allow either of them to be different from
- the destination operand in a 16-bit instruction. For instance, all
- three of "adc r0,r1", "adc r0,r0,r1", and "adc r0,r1,r0" are
- accepted. */
+/* cfmvsc32<cond> DSPSC,MVDX[15:0]. */
static void
-do_t_arit3c (void)
+do_mav_dspsc (void)
{
- int Rd, Rs, Rn;
+ inst.instruction |= inst.operands[1].reg << 12;
+}
- Rd = inst.operands[0].reg;
- Rs = (inst.operands[1].present
- ? inst.operands[1].reg /* Rd, Rs, foo */
- : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
- Rn = inst.operands[2].reg;
+/* Maverick shift immediate instructions.
+ cfsh32<cond> MVFX[15:0],MVFX[15:0],Shift[6:0].
+ cfsh64<cond> MVDX[15:0],MVDX[15:0],Shift[6:0]. */
- reject_bad_reg (Rd);
- reject_bad_reg (Rs);
- if (inst.operands[2].isreg)
- reject_bad_reg (Rn);
+static void
+do_mav_shift (void)
+{
+ int imm = inst.operands[2].imm;
- if (unified_syntax)
- {
- if (!inst.operands[2].isreg)
- {
- /* For an immediate, we always generate a 32-bit opcode;
- section relaxation will shrink it later if possible. */
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
- inst.instruction |= Rd << 8;
- inst.instruction |= Rs << 16;
- inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
- }
- else
- {
- bfd_boolean narrow;
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
- /* See if we can do this with a 16-bit instruction. */
- if (THUMB_SETS_FLAGS (inst.instruction))
- narrow = !in_it_block ();
- else
- narrow = in_it_block ();
+ /* Bits 0-3 of the insn should have bits 0-3 of the immediate.
+ Bits 5-7 of the insn should have bits 4-6 of the immediate.
+ Bit 4 should be 0. */
+ imm = (imm & 0xf) | ((imm & 0x70) << 1);
- if (Rd > 7 || Rn > 7 || Rs > 7)
- narrow = FALSE;
- if (inst.operands[2].shifted)
- narrow = FALSE;
- if (inst.size_req == 4)
- narrow = FALSE;
+ inst.instruction |= imm;
+}
+\f
+/* XScale instructions. Also sorted arithmetic before move. */
- if (narrow)
- {
- if (Rd == Rs)
- {
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= Rd;
- inst.instruction |= Rn << 3;
- return;
- }
- if (Rd == Rn)
- {
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= Rd;
- inst.instruction |= Rs << 3;
- return;
- }
- }
+/* Xscale multiply-accumulate (argument parse)
+ MIAcc acc0,Rm,Rs
+ MIAPHcc acc0,Rm,Rs
+ MIAxycc acc0,Rm,Rs. */
- /* If we get here, it can't be done in 16 bits. */
- constraint (inst.operands[2].shifted
- && inst.operands[2].immisreg,
- _("shift must be constant"));
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= Rd << 8;
- inst.instruction |= Rs << 16;
- encode_thumb32_shifted_operand (2);
- }
- }
- else
- {
- /* On its face this is a lie - the instruction does set the
- flags. However, the only supported mnemonic in this mode
- says it doesn't. */
- constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
+static void
+do_xsc_mia (void)
+{
+ inst.instruction |= inst.operands[1].reg;
+ inst.instruction |= inst.operands[2].reg << 12;
+}
- constraint (!inst.operands[2].isreg || inst.operands[2].shifted,
- _("unshifted register required"));
- constraint (Rd > 7 || Rs > 7 || Rn > 7, BAD_HIREG);
+/* Xscale move-accumulator-register (argument parse)
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= Rd;
-
- if (Rd == Rs)
- inst.instruction |= Rn << 3;
- else if (Rd == Rn)
- inst.instruction |= Rs << 3;
- else
- constraint (1, _("dest must overlap one source register"));
- }
-}
+ MARcc acc0,RdLo,RdHi. */
static void
-do_t_barrier (void)
+do_xsc_mar (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;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
}
+/* Xscale move-register-accumulator (argument parse)
+
+ MRAcc RdLo,RdHi,acc0. */
+
static void
-do_t_bfc (void)
+do_xsc_mra (void)
{
- unsigned Rd;
- unsigned int msb = inst.operands[1].imm + inst.operands[2].imm;
- constraint (msb > 32, _("bit-field extends past end of register"));
- /* The instruction encoding stores the LSB and MSB,
- not the LSB and width. */
- Rd = inst.operands[0].reg;
- reject_bad_reg (Rd);
- inst.instruction |= Rd << 8;
- inst.instruction |= (inst.operands[1].imm & 0x1c) << 10;
- inst.instruction |= (inst.operands[1].imm & 0x03) << 6;
- inst.instruction |= msb - 1;
+ constraint (inst.operands[0].reg == inst.operands[1].reg, BAD_OVERLAP);
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
}
+\f
+/* Encoding functions relevant only to Thumb. */
+
+/* inst.operands[i] is a shifted-register operand; encode
+ it into inst.instruction in the format used by Thumb32. */
static void
-do_t_bfi (void)
+encode_thumb32_shifted_operand (int i)
{
- int Rd, Rn;
- unsigned int msb;
-
- Rd = inst.operands[0].reg;
- reject_bad_reg (Rd);
+ unsigned int value = inst.reloc.exp.X_add_number;
+ unsigned int shift = inst.operands[i].shift_kind;
- /* #0 in second position is alternative syntax for bfc, which is
- the same instruction but with REG_PC in the Rm field. */
- if (!inst.operands[1].isreg)
- Rn = REG_PC;
+ constraint (inst.operands[i].immisreg,
+ _("shift by register not allowed in thumb mode"));
+ inst.instruction |= inst.operands[i].reg;
+ if (shift == SHIFT_RRX)
+ inst.instruction |= SHIFT_ROR << 4;
else
{
- Rn = inst.operands[1].reg;
- reject_bad_reg (Rn);
- }
-
- msb = inst.operands[2].imm + inst.operands[3].imm;
- constraint (msb > 32, _("bit-field extends past end of register"));
- /* The instruction encoding stores the LSB and MSB,
- not the LSB and width. */
- inst.instruction |= Rd << 8;
- inst.instruction |= Rn << 16;
- inst.instruction |= (inst.operands[2].imm & 0x1c) << 10;
- inst.instruction |= (inst.operands[2].imm & 0x03) << 6;
- inst.instruction |= msb - 1;
-}
-
-static void
-do_t_bfx (void)
-{
- unsigned Rd, Rn;
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
- Rd = inst.operands[0].reg;
- Rn = inst.operands[1].reg;
+ constraint (value > 32
+ || (value == 32 && (shift == SHIFT_LSL
+ || shift == SHIFT_ROR)),
+ _("shift expression is too large"));
- reject_bad_reg (Rd);
- reject_bad_reg (Rn);
+ if (value == 0)
+ shift = SHIFT_LSL;
+ else if (value == 32)
+ value = 0;
- constraint (inst.operands[2].imm + inst.operands[3].imm > 32,
- _("bit-field extends past end of register"));
- inst.instruction |= Rd << 8;
- inst.instruction |= Rn << 16;
- inst.instruction |= (inst.operands[2].imm & 0x1c) << 10;
- inst.instruction |= (inst.operands[2].imm & 0x03) << 6;
- inst.instruction |= inst.operands[3].imm - 1;
+ inst.instruction |= shift << 4;
+ inst.instruction |= (value & 0x1c) << 10;
+ inst.instruction |= (value & 0x03) << 6;
+ }
}
-/* ARM V5 Thumb BLX (argument parse)
- BLX <target_addr> which is BLX(1)
- BLX <Rm> which is BLX(2)
- Unfortunately, there are two different opcodes for this mnemonic.
- So, the insns[].value is not used, and the code here zaps values
- into inst.instruction.
-
- ??? How to take advantage of the additional two bits of displacement
- available in Thumb32 mode? Need new relocation? */
-
-static void
-do_t_blx (void)
-{
- set_it_insn_type_last ();
- if (inst.operands[0].isreg)
- {
- constraint (inst.operands[0].reg == REG_PC, BAD_PC);
- /* We have a register, so this is BLX(2). */
- inst.instruction |= inst.operands[0].reg << 3;
- }
- else
- {
- /* No register. This must be BLX(1). */
- inst.instruction = 0xf000e800;
- encode_branch (BFD_RELOC_THUMB_PCREL_BLX);
- }
-}
+/* inst.operands[i] was set up by parse_address. Encode it into a
+ Thumb32 format load or store instruction. Reject forms that cannot
+ be used with such instructions. If is_t is true, reject forms that
+ cannot be used with a T instruction; if is_d is true, reject forms
+ that cannot be used with a D instruction. If it is a store insn,
+ reject PC in Rn. */
static void
-do_t_branch (void)
+encode_thumb32_addr_mode (int i, bfd_boolean is_t, bfd_boolean is_d)
{
- int opcode;
- int cond;
- int reloc;
+ const bfd_boolean is_pc = (inst.operands[i].reg == REG_PC);
- cond = inst.cond;
- set_it_insn_type (IF_INSIDE_IT_LAST_INSN);
+ constraint (!inst.operands[i].isreg,
+ _("Instruction does not support =N addresses"));
- if (in_it_block ())
+ inst.instruction |= inst.operands[i].reg << 16;
+ if (inst.operands[i].immisreg)
{
- /* Conditional branches inside IT blocks are encoded as unconditional
- branches. */
- cond = COND_ALWAYS;
- }
- else
- cond = inst.cond;
-
- if (cond != COND_ALWAYS)
- opcode = T_MNEM_bcond;
- else
- opcode = inst.instruction;
+ constraint (is_pc, BAD_PC_ADDRESSING);
+ constraint (is_t || is_d, _("cannot use register index with this instruction"));
+ constraint (inst.operands[i].negative,
+ _("Thumb does not support negative register indexing"));
+ constraint (inst.operands[i].postind,
+ _("Thumb does not support register post-indexing"));
+ constraint (inst.operands[i].writeback,
+ _("Thumb does not support register indexing with writeback"));
+ constraint (inst.operands[i].shifted && inst.operands[i].shift_kind != SHIFT_LSL,
+ _("Thumb supports only LSL in shifted register indexing"));
- if (unified_syntax
- && (inst.size_req == 4
- || (inst.size_req != 2 && inst.operands[0].hasreloc)))
- {
- inst.instruction = THUMB_OP32(opcode);
- if (cond == COND_ALWAYS)
- reloc = BFD_RELOC_THUMB_PCREL_BRANCH25;
- else
+ inst.instruction |= inst.operands[i].imm;
+ if (inst.operands[i].shifted)
{
- gas_assert (cond != 0xF);
- inst.instruction |= cond << 22;
- reloc = BFD_RELOC_THUMB_PCREL_BRANCH20;
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+ constraint (inst.reloc.exp.X_add_number < 0
+ || inst.reloc.exp.X_add_number > 3,
+ _("shift out of range"));
+ inst.instruction |= inst.reloc.exp.X_add_number << 4;
}
+ inst.reloc.type = BFD_RELOC_UNUSED;
}
- else
+ else if (inst.operands[i].preind)
{
- inst.instruction = THUMB_OP16(opcode);
- if (cond == COND_ALWAYS)
- reloc = BFD_RELOC_THUMB_PCREL_BRANCH12;
+ 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),
+ BAD_PC_ADDRESSING);
+
+ if (is_d)
+ {
+ inst.instruction |= 0x01000000;
+ if (inst.operands[i].writeback)
+ inst.instruction |= 0x00200000;
+ }
else
{
- inst.instruction |= cond << 8;
- reloc = BFD_RELOC_THUMB_PCREL_BRANCH9;
+ inst.instruction |= 0x00000c00;
+ if (inst.operands[i].writeback)
+ inst.instruction |= 0x00000100;
}
- /* Allow section relaxation. */
- if (unified_syntax && inst.size_req != 2)
- inst.relax = opcode;
+ inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_IMM;
}
- inst.reloc.type = reloc;
- inst.reloc.pc_rel = 1;
-}
-
-static void
-do_t_bkpt (void)
-{
- constraint (inst.cond != COND_ALWAYS,
- _("instruction is always unconditional"));
- if (inst.operands[0].present)
+ else if (inst.operands[i].postind)
{
- constraint (inst.operands[0].imm > 255,
- _("immediate value out of range"));
- inst.instruction |= inst.operands[0].imm;
- set_it_insn_type (NEUTRAL_IT_INSN);
+ gas_assert (inst.operands[i].writeback);
+ constraint (is_pc, _("cannot use post-indexing with PC-relative addressing"));
+ constraint (is_t, _("cannot use post-indexing with this instruction"));
+
+ if (is_d)
+ inst.instruction |= 0x00200000;
+ else
+ inst.instruction |= 0x00000900;
+ inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_IMM;
}
+ else /* unindexed - only for coprocessor */
+ inst.error = _("instruction does not accept unindexed addressing");
}
-static void
-do_t_branch23 (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.
- So if we see a PLT reloc now, put it back to how it used to be to
- keep the preexisting behaviour. */
- if (inst.reloc.type == BFD_RELOC_ARM_PLT32)
- inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23;
-
-#if defined(OBJ_COFF)
- /* If the destination of the branch is a defined symbol which does not have
- the THUMB_FUNC attribute, then we must be calling a function which has
- the (interfacearm) attribute. We look for the Thumb entry point to that
- function and change the branch to refer to that function instead. */
- if ( inst.reloc.exp.X_op == O_symbol
- && inst.reloc.exp.X_add_symbol != NULL
- && S_IS_DEFINED (inst.reloc.exp.X_add_symbol)
- && ! THUMB_IS_FUNC (inst.reloc.exp.X_add_symbol))
- inst.reloc.exp.X_add_symbol =
- find_real_start (inst.reloc.exp.X_add_symbol);
-#endif
-}
-
-static void
-do_t_bx (void)
-{
- set_it_insn_type_last ();
- inst.instruction |= inst.operands[0].reg << 3;
- /* ??? FIXME: Should add a hacky reloc here if reg is REG_PC. The reloc
- should cause the alignment to be checked once it is known. This is
- because BX PC only works if the instruction is word aligned. */
-}
-
-static void
-do_t_bxj (void)
-{
- int Rm;
-
- set_it_insn_type_last ();
- Rm = inst.operands[0].reg;
- reject_bad_reg (Rm);
- inst.instruction |= Rm << 16;
-}
+/* Table of Thumb instructions which exist in both 16- and 32-bit
+ encodings (the latter only in post-V6T2 cores). The index is the
+ value used in the insns table below. When there is more than one
+ possible 16-bit encoding for the instruction, this table always
+ holds variant (1).
+ Also contains several pseudo-instructions used during relaxation. */
+#define T16_32_TAB \
+ X(_adc, 4140, eb400000), \
+ X(_adcs, 4140, eb500000), \
+ X(_add, 1c00, eb000000), \
+ X(_adds, 1c00, eb100000), \
+ X(_addi, 0000, f1000000), \
+ X(_addis, 0000, f1100000), \
+ X(_add_pc,000f, f20f0000), \
+ X(_add_sp,000d, f10d0000), \
+ X(_adr, 000f, f20f0000), \
+ X(_and, 4000, ea000000), \
+ X(_ands, 4000, ea100000), \
+ X(_asr, 1000, fa40f000), \
+ X(_asrs, 1000, fa50f000), \
+ X(_b, e000, f000b000), \
+ X(_bcond, d000, f0008000), \
+ X(_bic, 4380, ea200000), \
+ X(_bics, 4380, ea300000), \
+ X(_cmn, 42c0, eb100f00), \
+ X(_cmp, 2800, ebb00f00), \
+ X(_cpsie, b660, f3af8400), \
+ X(_cpsid, b670, f3af8600), \
+ X(_cpy, 4600, ea4f0000), \
+ X(_dec_sp,80dd, f1ad0d00), \
+ X(_eor, 4040, ea800000), \
+ X(_eors, 4040, ea900000), \
+ X(_inc_sp,00dd, f10d0d00), \
+ X(_ldmia, c800, e8900000), \
+ X(_ldr, 6800, f8500000), \
+ X(_ldrb, 7800, f8100000), \
+ X(_ldrh, 8800, f8300000), \
+ X(_ldrsb, 5600, f9100000), \
+ X(_ldrsh, 5e00, f9300000), \
+ X(_ldr_pc,4800, f85f0000), \
+ X(_ldr_pc2,4800, f85f0000), \
+ X(_ldr_sp,9800, f85d0000), \
+ X(_lsl, 0000, fa00f000), \
+ X(_lsls, 0000, fa10f000), \
+ X(_lsr, 0800, fa20f000), \
+ X(_lsrs, 0800, fa30f000), \
+ X(_mov, 2000, ea4f0000), \
+ X(_movs, 2000, ea5f0000), \
+ X(_mul, 4340, fb00f000), \
+ X(_muls, 4340, ffffffff), /* no 32b muls */ \
+ X(_mvn, 43c0, ea6f0000), \
+ X(_mvns, 43c0, ea7f0000), \
+ X(_neg, 4240, f1c00000), /* rsb #0 */ \
+ X(_negs, 4240, f1d00000), /* rsbs #0 */ \
+ X(_orr, 4300, ea400000), \
+ X(_orrs, 4300, ea500000), \
+ X(_pop, bc00, e8bd0000), /* ldmia sp!,... */ \
+ X(_push, b400, e92d0000), /* stmdb sp!,... */ \
+ X(_rev, ba00, fa90f080), \
+ X(_rev16, ba40, fa90f090), \
+ X(_revsh, bac0, fa90f0b0), \
+ X(_ror, 41c0, fa60f000), \
+ X(_rors, 41c0, fa70f000), \
+ X(_sbc, 4180, eb600000), \
+ X(_sbcs, 4180, eb700000), \
+ X(_stmia, c000, e8800000), \
+ X(_str, 6000, f8400000), \
+ X(_strb, 7000, f8000000), \
+ X(_strh, 8000, f8200000), \
+ X(_str_sp,9000, f84d0000), \
+ X(_sub, 1e00, eba00000), \
+ X(_subs, 1e00, ebb00000), \
+ X(_subi, 8000, f1a00000), \
+ X(_subis, 8000, f1b00000), \
+ X(_sxtb, b240, fa4ff080), \
+ X(_sxth, b200, fa0ff080), \
+ X(_tst, 4200, ea100f00), \
+ X(_uxtb, b2c0, fa5ff080), \
+ X(_uxth, b280, fa1ff080), \
+ X(_nop, bf00, f3af8000), \
+ X(_yield, bf10, f3af8001), \
+ X(_wfe, bf20, f3af8002), \
+ X(_wfi, bf30, f3af8003), \
+ X(_sev, bf40, f3af8004), \
+ X(_sevl, bf50, f3af8005), \
+ X(_udf, de00, f7f0a000)
-static void
-do_t_clz (void)
-{
- unsigned Rd;
- unsigned Rm;
+/* 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
+ as 16-bit instructions. */
+#define X(a,b,c) T_MNEM##a
+enum t16_32_codes { T16_32_OFFSET = 0xF7FF, T16_32_TAB };
+#undef X
- Rd = inst.operands[0].reg;
- Rm = inst.operands[1].reg;
+#define X(a,b,c) 0x##b
+static const unsigned short thumb_op16[] = { T16_32_TAB };
+#define THUMB_OP16(n) (thumb_op16[(n) - (T16_32_OFFSET + 1)])
+#undef X
- reject_bad_reg (Rd);
- reject_bad_reg (Rm);
+#define X(a,b,c) 0x##c
+static const unsigned int thumb_op32[] = { T16_32_TAB };
+#define THUMB_OP32(n) (thumb_op32[(n) - (T16_32_OFFSET + 1)])
+#define THUMB_SETS_FLAGS(n) (THUMB_OP32 (n) & 0x00100000)
+#undef X
+#undef T16_32_TAB
- inst.instruction |= Rd << 8;
- inst.instruction |= Rm << 16;
- inst.instruction |= Rm;
-}
+/* Thumb instruction encoders, in alphabetical order. */
-static void
-do_t_cps (void)
-{
- set_it_insn_type (OUTSIDE_IT_INSN);
- inst.instruction |= inst.operands[0].imm;
-}
+/* ADDW or SUBW. */
static void
-do_t_cpsi (void)
+do_t_add_sub_w (void)
{
- set_it_insn_type (OUTSIDE_IT_INSN);
- if (unified_syntax
- && (inst.operands[1].present || inst.size_req == 4)
- && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6_notm))
- {
- unsigned int imod = (inst.instruction & 0x0030) >> 4;
- inst.instruction = 0xf3af8000;
- inst.instruction |= imod << 9;
- inst.instruction |= inst.operands[0].imm << 5;
- if (inst.operands[1].present)
- inst.instruction |= 0x100 | inst.operands[1].imm;
- }
- else
- {
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1)
- && (inst.operands[0].imm & 4),
- _("selected processor does not support 'A' form "
- "of this instruction"));
- constraint (inst.operands[1].present || inst.size_req == 4,
- _("Thumb does not support the 2-argument "
- "form of this instruction"));
- inst.instruction |= inst.operands[0].imm;
- }
-}
+ int Rd, Rn;
-/* THUMB CPY instruction (argument parse). */
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
-static void
-do_t_cpy (void)
-{
- if (inst.size_req == 4)
- {
- inst.instruction = THUMB_OP32 (T_MNEM_mov);
- inst.instruction |= inst.operands[0].reg << 8;
- inst.instruction |= inst.operands[1].reg;
- }
+ /* If Rn is REG_PC, this is ADR; if Rn is REG_SP, then this
+ is the SP-{plus,minus}-immediate form of the instruction. */
+ if (Rn == REG_SP)
+ constraint (Rd == REG_PC, BAD_PC);
else
- {
- inst.instruction |= (inst.operands[0].reg & 0x8) << 4;
- inst.instruction |= (inst.operands[0].reg & 0x7);
- inst.instruction |= inst.operands[1].reg << 3;
- }
-}
+ reject_bad_reg (Rd);
-static void
-do_t_cbz (void)
-{
- set_it_insn_type (OUTSIDE_IT_INSN);
- constraint (inst.operands[0].reg > 7, BAD_HIREG);
- inst.instruction |= inst.operands[0].reg;
- inst.reloc.pc_rel = 1;
- inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH7;
+ inst.instruction |= (Rn << 16) | (Rd << 8);
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMM12;
}
-static void
-do_t_dbg (void)
-{
- inst.instruction |= inst.operands[0].imm;
-}
+/* Parse an add or subtract instruction. We get here with inst.instruction
+ equalling any of THUMB_OPCODE_add, adds, sub, or subs. */
static void
-do_t_div (void)
+do_t_add_sub (void)
{
- unsigned Rd, Rn, Rm;
+ int Rd, Rs, Rn;
Rd = inst.operands[0].reg;
- Rn = (inst.operands[1].present
- ? inst.operands[1].reg : Rd);
- Rm = inst.operands[2].reg;
+ Rs = (inst.operands[1].present
+ ? inst.operands[1].reg /* Rd, Rs, foo */
+ : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
- reject_bad_reg (Rd);
- reject_bad_reg (Rn);
- reject_bad_reg (Rm);
+ if (Rd == REG_PC)
+ set_it_insn_type_last ();
- inst.instruction |= Rd << 8;
- inst.instruction |= Rn << 16;
- inst.instruction |= Rm;
-}
+ if (unified_syntax)
+ {
+ bfd_boolean flags;
+ bfd_boolean narrow;
+ int opcode;
-static void
-do_t_hint (void)
-{
- if (unified_syntax && inst.size_req == 4)
- inst.instruction = THUMB_OP32 (inst.instruction);
- else
- inst.instruction = THUMB_OP16 (inst.instruction);
-}
+ flags = (inst.instruction == T_MNEM_adds
+ || inst.instruction == T_MNEM_subs);
+ if (flags)
+ narrow = !in_it_block ();
+ else
+ narrow = in_it_block ();
+ if (!inst.operands[2].isreg)
+ {
+ int add;
-static void
-do_t_it (void)
-{
- unsigned int cond = inst.operands[0].imm;
-
- set_it_insn_type (IT_INSN);
- now_it.mask = (inst.instruction & 0xf) | 0x10;
- now_it.cc = cond;
-
- /* If the condition is a negative condition, invert the mask. */
- if ((cond & 0x1) == 0x0)
- {
- unsigned int mask = inst.instruction & 0x000f;
+ constraint (Rd == REG_SP && Rs != REG_SP, BAD_SP);
- if ((mask & 0x7) == 0)
- /* no conversion needed */;
- else if ((mask & 0x3) == 0)
- mask ^= 0x8;
- else if ((mask & 0x1) == 0)
- mask ^= 0xC;
+ add = (inst.instruction == T_MNEM_add
+ || inst.instruction == T_MNEM_adds);
+ opcode = 0;
+ if (inst.size_req != 4)
+ {
+ /* Attempt to use a narrow opcode, with relaxation if
+ 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)
+ opcode = T_MNEM_add_sp;
+ else if (Rd <= 7 && Rs == REG_PC && add && !flags)
+ opcode = T_MNEM_add_pc;
+ else if (Rd <= 7 && Rs <= 7 && narrow)
+ {
+ if (flags)
+ opcode = add ? T_MNEM_addis : T_MNEM_subis;
+ else
+ opcode = add ? T_MNEM_addi : T_MNEM_subi;
+ }
+ if (opcode)
+ {
+ inst.instruction = THUMB_OP16(opcode);
+ inst.instruction |= (Rd << 4) | Rs;
+ if (inst.reloc.type < BFD_RELOC_ARM_THUMB_ALU_ABS_G0_NC
+ || inst.reloc.type > BFD_RELOC_ARM_THUMB_ALU_ABS_G3_NC)
+ {
+ if (inst.size_req == 2)
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ else
+ inst.relax = opcode;
+ }
+ }
+ else
+ constraint (inst.size_req == 2, BAD_HIREG);
+ }
+ if (inst.size_req == 4
+ || (inst.size_req != 2 && !opcode))
+ {
+ constraint (inst.reloc.type >= BFD_RELOC_ARM_THUMB_ALU_ABS_G0_NC
+ && inst.reloc.type <= BFD_RELOC_ARM_THUMB_ALU_ABS_G3_NC ,
+ THUMB1_RELOC_ONLY);
+ if (Rd == REG_PC)
+ {
+ constraint (add, BAD_PC);
+ constraint (Rs != REG_LR || inst.instruction != T_MNEM_subs,
+ _("only SUBS PC, LR, #const allowed"));
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+ constraint (inst.reloc.exp.X_add_number < 0
+ || inst.reloc.exp.X_add_number > 0xff,
+ _("immediate value out of range"));
+ inst.instruction = T2_SUBS_PC_LR
+ | inst.reloc.exp.X_add_number;
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ return;
+ }
+ else if (Rs == REG_PC)
+ {
+ /* Always use addw/subw. */
+ inst.instruction = add ? 0xf20f0000 : 0xf2af0000;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMM12;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff)
+ | 0x10000000;
+ if (flags)
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ else
+ inst.reloc.type = BFD_RELOC_ARM_T32_ADD_IMM;
+ }
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ }
+ }
else
- mask ^= 0xE;
-
- inst.instruction &= 0xfff0;
- inst.instruction |= mask;
- }
-
- inst.instruction |= cond << 4;
-}
+ {
+ unsigned int value = inst.reloc.exp.X_add_number;
+ unsigned int shift = inst.operands[2].shift_kind;
-/* Helper function used for both push/pop and ldm/stm. */
-static void
-encode_thumb2_ldmstm (int base, unsigned mask, bfd_boolean writeback)
-{
- bfd_boolean load;
+ 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)
+ {
+ if (Rd > 7 || Rs > 7 || Rn > 7)
+ narrow = FALSE;
- load = (inst.instruction & (1 << 20)) != 0;
+ if (narrow)
+ {
+ inst.instruction = ((inst.instruction == T_MNEM_adds
+ || inst.instruction == T_MNEM_add)
+ ? T_OPCODE_ADD_R3
+ : T_OPCODE_SUB_R3);
+ inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
+ return;
+ }
- if (mask & (1 << 13))
- inst.error = _("SP not allowed in register list");
+ if (inst.instruction == T_MNEM_add && (Rd == Rs || Rd == Rn))
+ {
+ /* Thumb-1 cores (except v6-M) require at least one high
+ register in a narrow non flag setting add. */
+ if (Rd > 7 || Rn > 7
+ || ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6t2)
+ || ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_msr))
+ {
+ if (Rd == Rn)
+ {
+ Rn = Rs;
+ Rs = Rd;
+ }
+ inst.instruction = T_OPCODE_ADD_HI;
+ inst.instruction |= (Rd & 8) << 4;
+ inst.instruction |= (Rd & 7);
+ inst.instruction |= Rn << 3;
+ return;
+ }
+ }
+ }
- if ((mask & (1 << base)) != 0
- && writeback)
- inst.error = _("having the base register in the register list when "
- "using write back is UNPREDICTABLE");
+ constraint (Rd == REG_PC, BAD_PC);
+ constraint (Rd == REG_SP && Rs != REG_SP, BAD_SP);
+ constraint (Rs == REG_PC, BAD_PC);
+ reject_bad_reg (Rn);
- 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 we get here, it can't be done in 16 bits. */
+ constraint (inst.operands[2].shifted && inst.operands[2].immisreg,
+ _("shift must be constant"));
+ 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);
+ }
}
else
{
- if (mask & (1 << 15))
- inst.error = _("PC not allowed in register list");
- }
+ constraint (inst.instruction == T_MNEM_adds
+ || inst.instruction == T_MNEM_subs,
+ BAD_THUMB32);
- if ((mask & (mask - 1)) == 0)
- {
- /* Single register transfers implemented as str/ldr. */
- if (writeback)
+ if (!inst.operands[2].isreg) /* Rd, Rs, #imm */
{
- if (inst.instruction & (1 << 23))
- inst.instruction = 0x00000b04; /* ia! -> [base], #4 */
+ constraint ((Rd > 7 && (Rd != REG_SP || Rs != REG_SP))
+ || (Rs > 7 && Rs != REG_SP && Rs != REG_PC),
+ BAD_HIREG);
+
+ inst.instruction = (inst.instruction == T_MNEM_add
+ ? 0x0000 : 0x8000);
+ inst.instruction |= (Rd << 4) | Rs;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ return;
+ }
+
+ Rn = inst.operands[2].reg;
+ constraint (inst.operands[2].shifted, _("unshifted register required"));
+
+ /* We now have Rd, Rs, and Rn set to registers. */
+ if (Rd > 7 || Rs > 7 || Rn > 7)
+ {
+ /* Can't do this for SUB. */
+ constraint (inst.instruction == T_MNEM_sub, BAD_HIREG);
+ inst.instruction = T_OPCODE_ADD_HI;
+ inst.instruction |= (Rd & 8) << 4;
+ inst.instruction |= (Rd & 7);
+ if (Rs == Rd)
+ inst.instruction |= Rn << 3;
+ else if (Rn == Rd)
+ inst.instruction |= Rs << 3;
else
- inst.instruction = 0x00000d04; /* db! -> [base, #-4]! */
+ constraint (1, _("dest must overlap one source register"));
}
else
{
- if (inst.instruction & (1 << 23))
- inst.instruction = 0x00800000; /* ia -> [base] */
- else
- inst.instruction = 0x00000c04; /* db -> [base, #-4] */
+ inst.instruction = (inst.instruction == T_MNEM_add
+ ? T_OPCODE_ADD_R3 : T_OPCODE_SUB_R3);
+ inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
}
-
- inst.instruction |= 0xf8400000;
- if (load)
- inst.instruction |= 0x00100000;
-
- mask = ffs (mask) - 1;
- mask <<= 12;
}
- else if (writeback)
- inst.instruction |= WRITE_BACK;
-
- inst.instruction |= mask;
- inst.instruction |= base << 16;
}
static void
-do_t_ldmstm (void)
+do_t_adr (void)
{
- /* This really doesn't seem worth it. */
- constraint (inst.reloc.type != BFD_RELOC_UNUSED,
- _("expression too complex"));
- constraint (inst.operands[1].writeback,
- _("Thumb load/store multiple does not support {reglist}^"));
+ unsigned Rd;
- if (unified_syntax)
+ Rd = inst.operands[0].reg;
+ reject_bad_reg (Rd);
+
+ if (unified_syntax && inst.size_req == 0 && Rd <= 7)
{
- bfd_boolean narrow;
- unsigned mask;
+ /* Defer to section relaxation. */
+ inst.relax = inst.instruction;
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd << 4;
+ }
+ else if (unified_syntax && inst.size_req != 2)
+ {
+ /* Generate a 32-bit opcode. */
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.reloc.type = BFD_RELOC_ARM_T32_ADD_PC12;
+ inst.reloc.pc_rel = 1;
+ }
+ else
+ {
+ /* Generate a 16-bit opcode. */
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ inst.reloc.exp.X_add_number -= 4; /* PC relative adjust. */
+ inst.reloc.pc_rel = 1;
- narrow = FALSE;
- /* See if we can use a 16-bit instruction. */
- if (inst.instruction < 0xffff /* not ldmdb/stmdb */
- && inst.size_req != 4
- && !(inst.operands[1].imm & ~0xff))
- {
- mask = 1 << inst.operands[0].reg;
+ inst.instruction |= Rd << 4;
+ }
+}
- if (inst.operands[0].reg <= 7)
- {
- if (inst.instruction == T_MNEM_stmia
- ? 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)))
- as_warn (_("value stored for r%d is UNKNOWN"),
- inst.operands[0].reg);
+/* Arithmetic instructions for which there is just one 16-bit
+ instruction encoding, and it allows only two low registers.
+ For maximal compatibility with ARM syntax, we allow three register
+ operands even when Thumb-32 instructions are not available, as long
+ as the first two are identical. For instance, both "sbc r0,r1" and
+ "sbc r0,r0,r1" are allowed. */
+static void
+do_t_arit3 (void)
+{
+ int Rd, Rs, Rn;
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= inst.operands[0].reg << 8;
- inst.instruction |= inst.operands[1].imm;
- narrow = TRUE;
- }
- else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0)
- {
- /* 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.
- 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
- str or ldr. The SP cases are handled below. */
- unsigned long opcode;
- /* First, record an error for Case 3. */
- if (inst.operands[1].imm & mask
- && inst.operands[0].writeback)
- 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
- : T_MNEM_ldr);
- inst.instruction = THUMB_OP16 (opcode);
- inst.instruction |= inst.operands[0].reg << 3;
- inst.instruction |= (ffs (inst.operands[1].imm)-1);
- narrow = TRUE;
- }
- }
- else if (inst.operands[0] .reg == REG_SP)
- {
- if (inst.operands[0].writeback)
- {
- inst.instruction =
- THUMB_OP16 (inst.instruction == T_MNEM_stmia
- ? T_MNEM_push : T_MNEM_pop);
- inst.instruction |= inst.operands[1].imm;
- narrow = TRUE;
- }
- else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0)
- {
- inst.instruction =
- THUMB_OP16 (inst.instruction == T_MNEM_stmia
- ? T_MNEM_str_sp : T_MNEM_ldr_sp);
- inst.instruction |= ((ffs (inst.operands[1].imm)-1) << 8);
- narrow = TRUE;
- }
- }
- }
+ Rd = inst.operands[0].reg;
+ Rs = (inst.operands[1].present
+ ? inst.operands[1].reg /* Rd, Rs, foo */
+ : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
+ Rn = inst.operands[2].reg;
- if (!narrow)
- {
- if (inst.instruction < 0xffff)
- inst.instruction = THUMB_OP32 (inst.instruction);
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rs);
+ if (inst.operands[2].isreg)
+ reject_bad_reg (Rn);
- encode_thumb2_ldmstm (inst.operands[0].reg, inst.operands[1].imm,
- inst.operands[0].writeback);
- }
- }
- else
+ if (unified_syntax)
{
- constraint (inst.operands[0].reg > 7
- || (inst.operands[1].imm & ~0xff), BAD_HIREG);
- constraint (inst.instruction != T_MNEM_ldmia
- && inst.instruction != T_MNEM_stmia,
- _("Thumb-2 instruction only valid in unified syntax"));
- if (inst.instruction == T_MNEM_stmia)
+ if (!inst.operands[2].isreg)
{
- if (!inst.operands[0].writeback)
- as_warn (_("this instruction will write back the base register"));
- if ((inst.operands[1].imm & (1 << inst.operands[0].reg))
- && (inst.operands[1].imm & ((1 << inst.operands[0].reg) - 1)))
- as_warn (_("value stored for r%d is UNKNOWN"),
- inst.operands[0].reg);
+ /* For an immediate, we always generate a 32-bit opcode;
+ section relaxation will shrink it later if possible. */
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
}
else
{
- if (!inst.operands[0].writeback
- && !(inst.operands[1].imm & (1 << inst.operands[0].reg)))
- as_warn (_("this instruction will write back the base register"));
- else if (inst.operands[0].writeback
- && (inst.operands[1].imm & (1 << inst.operands[0].reg)))
- as_warn (_("this instruction will not write back the base register"));
- }
-
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= inst.operands[0].reg << 8;
- inst.instruction |= inst.operands[1].imm;
- }
-}
+ bfd_boolean narrow;
-static void
-do_t_ldrex (void)
-{
- constraint (!inst.operands[1].isreg || !inst.operands[1].preind
- || inst.operands[1].postind || inst.operands[1].writeback
- || inst.operands[1].immisreg || inst.operands[1].shifted
- || inst.operands[1].negative,
- BAD_ADDR_MODE);
+ /* See if we can do this with a 16-bit instruction. */
+ if (THUMB_SETS_FLAGS (inst.instruction))
+ narrow = !in_it_block ();
+ else
+ narrow = in_it_block ();
- constraint ((inst.operands[1].reg == REG_PC), BAD_PC);
+ if (Rd > 7 || Rn > 7 || Rs > 7)
+ narrow = FALSE;
+ if (inst.operands[2].shifted)
+ narrow = FALSE;
+ if (inst.size_req == 4)
+ narrow = FALSE;
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_U8;
-}
+ if (narrow
+ && Rd == Rs)
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rn << 3;
+ return;
+ }
-static void
-do_t_ldrexd (void)
-{
- if (!inst.operands[1].present)
- {
- constraint (inst.operands[0].reg == REG_LR,
- _("r14 not allowed as first register "
- "when second register is omitted"));
- inst.operands[1].reg = inst.operands[0].reg + 1;
+ /* If we get here, it can't be done in 16 bits. */
+ constraint (inst.operands[2].shifted
+ && inst.operands[2].immisreg,
+ _("shift must be constant"));
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ encode_thumb32_shifted_operand (2);
+ }
}
- constraint (inst.operands[0].reg == inst.operands[1].reg,
- BAD_OVERLAP);
+ else
+ {
+ /* On its face this is a lie - the instruction does set the
+ flags. However, the only supported mnemonic in this mode
+ says it doesn't. */
+ constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 8;
- inst.instruction |= inst.operands[2].reg << 16;
+ constraint (!inst.operands[2].isreg || inst.operands[2].shifted,
+ _("unshifted register required"));
+ constraint (Rd > 7 || Rs > 7 || Rn > 7, BAD_HIREG);
+ constraint (Rd != Rs,
+ _("dest and source1 must be the same register"));
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rn << 3;
+ }
}
+/* Similarly, but for instructions where the arithmetic operation is
+ commutative, so we can allow either of them to be different from
+ the destination operand in a 16-bit instruction. For instance, all
+ three of "adc r0,r1", "adc r0,r0,r1", and "adc r0,r1,r0" are
+ accepted. */
static void
-do_t_ldst (void)
+do_t_arit3c (void)
{
- unsigned long opcode;
- int Rn;
+ int Rd, Rs, Rn;
- if (inst.operands[0].isreg
- && !inst.operands[0].preind
- && inst.operands[0].reg == REG_PC)
- set_it_insn_type_last ();
+ Rd = inst.operands[0].reg;
+ Rs = (inst.operands[1].present
+ ? inst.operands[1].reg /* Rd, Rs, foo */
+ : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
+ Rn = inst.operands[2].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rs);
+ if (inst.operands[2].isreg)
+ reject_bad_reg (Rn);
- opcode = inst.instruction;
if (unified_syntax)
{
- if (!inst.operands[1].isreg)
+ if (!inst.operands[2].isreg)
{
- if (opcode <= 0xffff)
- inst.instruction = THUMB_OP32 (opcode);
- if (move_or_literal_pool (0, /*thumb_p=*/TRUE, /*mode_3=*/FALSE))
- return;
+ /* For an immediate, we always generate a 32-bit opcode;
+ section relaxation will shrink it later if possible. */
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
}
- if (inst.operands[1].isreg
- && !inst.operands[1].writeback
- && !inst.operands[1].shifted && !inst.operands[1].postind
- && !inst.operands[1].negative && inst.operands[0].reg <= 7
- && opcode <= 0xffff
- && inst.size_req != 4)
+ else
{
- /* Insn may have a 16-bit form. */
- Rn = inst.operands[1].reg;
- if (inst.operands[1].immisreg)
+ bfd_boolean narrow;
+
+ /* See if we can do this with a 16-bit instruction. */
+ if (THUMB_SETS_FLAGS (inst.instruction))
+ narrow = !in_it_block ();
+ else
+ narrow = in_it_block ();
+
+ if (Rd > 7 || Rn > 7 || Rs > 7)
+ narrow = FALSE;
+ if (inst.operands[2].shifted)
+ narrow = FALSE;
+ if (inst.size_req == 4)
+ narrow = FALSE;
+
+ if (narrow)
{
- inst.instruction = THUMB_OP16 (opcode);
- /* [Rn, Rik] */
- if (Rn <= 7 && inst.operands[1].imm <= 7)
- goto op16;
- else if (opcode != T_MNEM_ldr && opcode != T_MNEM_str)
- reject_bad_reg (inst.operands[1].imm);
- }
- else if ((Rn <= 7 && opcode != T_MNEM_ldrsh
- && opcode != T_MNEM_ldrsb)
- || ((Rn == REG_PC || Rn == REG_SP) && opcode == T_MNEM_ldr)
- || (Rn == REG_SP && opcode == T_MNEM_str))
- {
- /* [Rn, #const] */
- if (Rn > 7)
+ if (Rd == Rs)
{
- if (Rn == REG_PC)
- {
- if (inst.reloc.pc_rel)
- opcode = T_MNEM_ldr_pc2;
- else
- opcode = T_MNEM_ldr_pc;
- }
- else
- {
- if (opcode == T_MNEM_ldr)
- opcode = T_MNEM_ldr_sp;
- else
- opcode = T_MNEM_str_sp;
- }
- inst.instruction = inst.operands[0].reg << 8;
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rn << 3;
+ return;
}
- else
+ if (Rd == Rn)
{
- inst.instruction = inst.operands[0].reg;
- inst.instruction |= inst.operands[1].reg << 3;
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rs << 3;
+ return;
}
- inst.instruction |= THUMB_OP16 (opcode);
- if (inst.size_req == 2)
- inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
- else
- inst.relax = opcode;
- return;
}
- }
- /* Definitely a 32-bit variant. */
-
- /* Do some validations regarding addressing modes. */
- if (inst.operands[1].immisreg && opcode != T_MNEM_ldr
- && opcode != T_MNEM_str)
- reject_bad_reg (inst.operands[1].imm);
- inst.instruction = THUMB_OP32 (opcode);
- inst.instruction |= inst.operands[0].reg << 12;
- encode_thumb32_addr_mode (1, /*is_t=*/FALSE, /*is_d=*/FALSE);
- return;
+ /* If we get here, it can't be done in 16 bits. */
+ constraint (inst.operands[2].shifted
+ && inst.operands[2].immisreg,
+ _("shift must be constant"));
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ encode_thumb32_shifted_operand (2);
+ }
}
-
- constraint (inst.operands[0].reg > 7, BAD_HIREG);
-
- if (inst.instruction == T_MNEM_ldrsh || inst.instruction == T_MNEM_ldrsb)
+ else
{
- /* Only [Rn,Rm] is acceptable. */
- constraint (inst.operands[1].reg > 7 || inst.operands[1].imm > 7, BAD_HIREG);
- constraint (!inst.operands[1].isreg || !inst.operands[1].immisreg
- || inst.operands[1].postind || inst.operands[1].shifted
- || inst.operands[1].negative,
- _("Thumb does not support this addressing mode"));
- inst.instruction = THUMB_OP16 (inst.instruction);
- goto op16;
- }
+ /* On its face this is a lie - the instruction does set the
+ flags. However, the only supported mnemonic in this mode
+ says it doesn't. */
+ constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
- inst.instruction = THUMB_OP16 (inst.instruction);
- if (!inst.operands[1].isreg)
- if (move_or_literal_pool (0, /*thumb_p=*/TRUE, /*mode_3=*/FALSE))
- return;
+ constraint (!inst.operands[2].isreg || inst.operands[2].shifted,
+ _("unshifted register required"));
+ constraint (Rd > 7 || Rs > 7 || Rn > 7, BAD_HIREG);
- constraint (!inst.operands[1].preind
- || inst.operands[1].shifted
- || inst.operands[1].writeback,
- _("Thumb does not support this addressing mode"));
- if (inst.operands[1].reg == REG_PC || inst.operands[1].reg == REG_SP)
- {
- constraint (inst.instruction & 0x0600,
- _("byte or halfword not valid for base register"));
- constraint (inst.operands[1].reg == REG_PC
- && !(inst.instruction & THUMB_LOAD_BIT),
- _("r15 based store not allowed"));
- constraint (inst.operands[1].immisreg,
- _("invalid base register for register offset"));
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
- if (inst.operands[1].reg == REG_PC)
- inst.instruction = T_OPCODE_LDR_PC;
- else if (inst.instruction & THUMB_LOAD_BIT)
- inst.instruction = T_OPCODE_LDR_SP;
+ if (Rd == Rs)
+ inst.instruction |= Rn << 3;
+ else if (Rd == Rn)
+ inst.instruction |= Rs << 3;
else
- inst.instruction = T_OPCODE_STR_SP;
-
- inst.instruction |= inst.operands[0].reg << 8;
- inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
- return;
- }
-
- constraint (inst.operands[1].reg > 7, BAD_HIREG);
- if (!inst.operands[1].immisreg)
- {
- /* Immediate offset. */
- inst.instruction |= inst.operands[0].reg;
- inst.instruction |= inst.operands[1].reg << 3;
- inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
- return;
- }
-
- /* Register offset. */
- constraint (inst.operands[1].imm > 7, BAD_HIREG);
- constraint (inst.operands[1].negative,
- _("Thumb does not support this addressing mode"));
-
- op16:
- switch (inst.instruction)
- {
- case T_OPCODE_STR_IW: inst.instruction = T_OPCODE_STR_RW; break;
- case T_OPCODE_STR_IH: inst.instruction = T_OPCODE_STR_RH; break;
- case T_OPCODE_STR_IB: inst.instruction = T_OPCODE_STR_RB; break;
- case T_OPCODE_LDR_IW: inst.instruction = T_OPCODE_LDR_RW; break;
- case T_OPCODE_LDR_IH: inst.instruction = T_OPCODE_LDR_RH; break;
- case T_OPCODE_LDR_IB: inst.instruction = T_OPCODE_LDR_RB; break;
- case 0x5600 /* ldrsb */:
- case 0x5e00 /* ldrsh */: break;
- default: abort ();
+ constraint (1, _("dest must overlap one source register"));
}
-
- inst.instruction |= inst.operands[0].reg;
- inst.instruction |= inst.operands[1].reg << 3;
- inst.instruction |= inst.operands[1].imm << 6;
}
static void
-do_t_ldstd (void)
+do_t_bfc (void)
{
- if (!inst.operands[1].present)
- {
- inst.operands[1].reg = inst.operands[0].reg + 1;
- constraint (inst.operands[0].reg == REG_LR,
- _("r14 not allowed here"));
- }
- 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);
+ unsigned Rd;
+ unsigned int msb = inst.operands[1].imm + inst.operands[2].imm;
+ constraint (msb > 32, _("bit-field extends past end of register"));
+ /* The instruction encoding stores the LSB and MSB,
+ not the LSB and width. */
+ Rd = inst.operands[0].reg;
+ reject_bad_reg (Rd);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= (inst.operands[1].imm & 0x1c) << 10;
+ inst.instruction |= (inst.operands[1].imm & 0x03) << 6;
+ inst.instruction |= msb - 1;
}
static void
-do_t_ldstt (void)
+do_t_bfi (void)
{
- inst.instruction |= inst.operands[0].reg << 12;
- encode_thumb32_addr_mode (1, /*is_t=*/TRUE, /*is_d=*/FALSE);
+ int Rd, Rn;
+ unsigned int msb;
+
+ Rd = inst.operands[0].reg;
+ reject_bad_reg (Rd);
+
+ /* #0 in second position is alternative syntax for bfc, which is
+ the same instruction but with REG_PC in the Rm field. */
+ if (!inst.operands[1].isreg)
+ Rn = REG_PC;
+ else
+ {
+ Rn = inst.operands[1].reg;
+ reject_bad_reg (Rn);
+ }
+
+ msb = inst.operands[2].imm + inst.operands[3].imm;
+ constraint (msb > 32, _("bit-field extends past end of register"));
+ /* The instruction encoding stores the LSB and MSB,
+ not the LSB and width. */
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= (inst.operands[2].imm & 0x1c) << 10;
+ inst.instruction |= (inst.operands[2].imm & 0x03) << 6;
+ inst.instruction |= msb - 1;
}
static void
-do_t_mla (void)
+do_t_bfx (void)
{
- unsigned Rd, Rn, Rm, Ra;
+ unsigned Rd, Rn;
Rd = inst.operands[0].reg;
Rn = inst.operands[1].reg;
- Rm = inst.operands[2].reg;
- Ra = inst.operands[3].reg;
reject_bad_reg (Rd);
reject_bad_reg (Rn);
- reject_bad_reg (Rm);
- reject_bad_reg (Ra);
+ constraint (inst.operands[2].imm + inst.operands[3].imm > 32,
+ _("bit-field extends past end of register"));
inst.instruction |= Rd << 8;
inst.instruction |= Rn << 16;
- inst.instruction |= Rm;
- inst.instruction |= Ra << 12;
+ inst.instruction |= (inst.operands[2].imm & 0x1c) << 10;
+ inst.instruction |= (inst.operands[2].imm & 0x03) << 6;
+ inst.instruction |= inst.operands[3].imm - 1;
}
-static void
-do_t_mlal (void)
-{
- unsigned RdLo, RdHi, Rn, Rm;
+/* ARM V5 Thumb BLX (argument parse)
+ BLX <target_addr> which is BLX(1)
+ BLX <Rm> which is BLX(2)
+ Unfortunately, there are two different opcodes for this mnemonic.
+ So, the insns[].value is not used, and the code here zaps values
+ into inst.instruction.
- RdLo = inst.operands[0].reg;
- RdHi = inst.operands[1].reg;
- Rn = inst.operands[2].reg;
- Rm = inst.operands[3].reg;
+ ??? How to take advantage of the additional two bits of displacement
+ available in Thumb32 mode? Need new relocation? */
- reject_bad_reg (RdLo);
- reject_bad_reg (RdHi);
- reject_bad_reg (Rn);
- reject_bad_reg (Rm);
+static void
+do_t_blx (void)
+{
+ set_it_insn_type_last ();
- inst.instruction |= RdLo << 12;
- inst.instruction |= RdHi << 8;
- inst.instruction |= Rn << 16;
- inst.instruction |= Rm;
+ if (inst.operands[0].isreg)
+ {
+ constraint (inst.operands[0].reg == REG_PC, BAD_PC);
+ /* We have a register, so this is BLX(2). */
+ inst.instruction |= inst.operands[0].reg << 3;
+ }
+ else
+ {
+ /* No register. This must be BLX(1). */
+ inst.instruction = 0xf000e800;
+ encode_branch (BFD_RELOC_THUMB_PCREL_BLX);
+ }
}
static void
-do_t_mov_cmp (void)
+do_t_branch (void)
{
- unsigned Rn, Rm;
+ int opcode;
+ int cond;
+ bfd_reloc_code_real_type reloc;
- Rn = inst.operands[0].reg;
+ cond = inst.cond;
+ set_it_insn_type (IF_INSIDE_IT_LAST_INSN);
+
+ if (in_it_block ())
+ {
+ /* Conditional branches inside IT blocks are encoded as unconditional
+ branches. */
+ cond = COND_ALWAYS;
+ }
+ else
+ cond = inst.cond;
+
+ if (cond != COND_ALWAYS)
+ opcode = T_MNEM_bcond;
+ else
+ opcode = inst.instruction;
+
+ if (unified_syntax
+ && (inst.size_req == 4
+ || (inst.size_req != 2
+ && (inst.operands[0].hasreloc
+ || inst.reloc.exp.X_op == O_constant))))
+ {
+ inst.instruction = THUMB_OP32(opcode);
+ if (cond == COND_ALWAYS)
+ reloc = BFD_RELOC_THUMB_PCREL_BRANCH25;
+ else
+ {
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6t2),
+ _("selected architecture does not support "
+ "wide conditional branch instruction"));
+
+ gas_assert (cond != 0xF);
+ inst.instruction |= cond << 22;
+ reloc = BFD_RELOC_THUMB_PCREL_BRANCH20;
+ }
+ }
+ else
+ {
+ inst.instruction = THUMB_OP16(opcode);
+ if (cond == COND_ALWAYS)
+ reloc = BFD_RELOC_THUMB_PCREL_BRANCH12;
+ else
+ {
+ inst.instruction |= cond << 8;
+ reloc = BFD_RELOC_THUMB_PCREL_BRANCH9;
+ }
+ /* Allow section relaxation. */
+ if (unified_syntax && inst.size_req != 2)
+ inst.relax = opcode;
+ }
+ inst.reloc.type = reloc;
+ 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_hlt1 (int range)
+{
+ constraint (inst.cond != COND_ALWAYS,
+ _("instruction is always unconditional"));
+ if (inst.operands[0].present)
+ {
+ constraint (inst.operands[0].imm > range,
+ _("immediate value out of range"));
+ inst.instruction |= inst.operands[0].imm;
+ }
+
+ 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
+do_t_branch23 (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.
+ So if we see a PLT reloc now, put it back to how it used to be to
+ keep the preexisting behaviour. */
+ if (inst.reloc.type == BFD_RELOC_ARM_PLT32)
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23;
+
+#if defined(OBJ_COFF)
+ /* If the destination of the branch is a defined symbol which does not have
+ the THUMB_FUNC attribute, then we must be calling a function which has
+ the (interfacearm) attribute. We look for the Thumb entry point to that
+ function and change the branch to refer to that function instead. */
+ if ( inst.reloc.exp.X_op == O_symbol
+ && inst.reloc.exp.X_add_symbol != NULL
+ && S_IS_DEFINED (inst.reloc.exp.X_add_symbol)
+ && ! THUMB_IS_FUNC (inst.reloc.exp.X_add_symbol))
+ inst.reloc.exp.X_add_symbol =
+ find_real_start (inst.reloc.exp.X_add_symbol);
+#endif
+}
+
+static void
+do_t_bx (void)
+{
+ set_it_insn_type_last ();
+ inst.instruction |= inst.operands[0].reg << 3;
+ /* ??? FIXME: Should add a hacky reloc here if reg is REG_PC. The reloc
+ should cause the alignment to be checked once it is known. This is
+ because BX PC only works if the instruction is word aligned. */
+}
+
+static void
+do_t_bxj (void)
+{
+ int Rm;
+
+ set_it_insn_type_last ();
+ Rm = inst.operands[0].reg;
+ reject_bad_reg (Rm);
+ inst.instruction |= Rm << 16;
+}
+
+static void
+do_t_clz (void)
+{
+ unsigned Rd;
+ unsigned Rm;
+
+ Rd = inst.operands[0].reg;
Rm = inst.operands[1].reg;
- if (Rn == REG_PC)
- set_it_insn_type_last ();
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rm << 16;
+ inst.instruction |= Rm;
+}
+
+static void
+do_t_cps (void)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+ inst.instruction |= inst.operands[0].imm;
+}
+
+static void
+do_t_cpsi (void)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+ if (unified_syntax
+ && (inst.operands[1].present || inst.size_req == 4)
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6_notm))
+ {
+ unsigned int imod = (inst.instruction & 0x0030) >> 4;
+ inst.instruction = 0xf3af8000;
+ inst.instruction |= imod << 9;
+ inst.instruction |= inst.operands[0].imm << 5;
+ if (inst.operands[1].present)
+ inst.instruction |= 0x100 | inst.operands[1].imm;
+ }
+ else
+ {
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1)
+ && (inst.operands[0].imm & 4),
+ _("selected processor does not support 'A' form "
+ "of this instruction"));
+ constraint (inst.operands[1].present || inst.size_req == 4,
+ _("Thumb does not support the 2-argument "
+ "form of this instruction"));
+ inst.instruction |= inst.operands[0].imm;
+ }
+}
+
+/* THUMB CPY instruction (argument parse). */
+
+static void
+do_t_cpy (void)
+{
+ if (inst.size_req == 4)
+ {
+ inst.instruction = THUMB_OP32 (T_MNEM_mov);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg;
+ }
+ else
+ {
+ inst.instruction |= (inst.operands[0].reg & 0x8) << 4;
+ inst.instruction |= (inst.operands[0].reg & 0x7);
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+}
+
+static void
+do_t_cbz (void)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
+ constraint (inst.operands[0].reg > 7, BAD_HIREG);
+ inst.instruction |= inst.operands[0].reg;
+ inst.reloc.pc_rel = 1;
+ inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH7;
+}
+
+static void
+do_t_dbg (void)
+{
+ inst.instruction |= inst.operands[0].imm;
+}
+
+static void
+do_t_div (void)
+{
+ unsigned Rd, Rn, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rn = (inst.operands[1].present
+ ? inst.operands[1].reg : Rd);
+ Rm = inst.operands[2].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+}
+
+static void
+do_t_hint (void)
+{
+ if (unified_syntax && inst.size_req == 4)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ else
+ inst.instruction = THUMB_OP16 (inst.instruction);
+}
+
+static void
+do_t_it (void)
+{
+ unsigned int cond = inst.operands[0].imm;
+
+ 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. */
+ now_it.block_length = 1;
+ }
+ else if ((mask & 0x3) == 0)
+ {
+ mask ^= 0x8;
+ now_it.block_length = 2;
+ }
+ else if ((mask & 0x1) == 0)
+ {
+ mask ^= 0xC;
+ now_it.block_length = 3;
+ }
+ else
+ {
+ mask ^= 0xE;
+ now_it.block_length = 4;
+ }
+
+ inst.instruction &= 0xfff0;
+ inst.instruction |= mask;
+ }
+
+ inst.instruction |= cond << 4;
+}
+
+/* Helper function used for both push/pop and ldm/stm. */
+static void
+encode_thumb2_ldmstm (int base, unsigned mask, bfd_boolean writeback)
+{
+ bfd_boolean load;
+
+ load = (inst.instruction & (1 << 20)) != 0;
+
+ if (mask & (1 << 13))
+ inst.error = _("SP not allowed in register list");
+
+ if ((mask & (1 << base)) != 0
+ && writeback)
+ inst.error = _("having the base register in the register list when "
+ "using write back is UNPREDICTABLE");
+
+ 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 ();
+ }
+ }
+ else
+ {
+ if (mask & (1 << 15))
+ inst.error = _("PC not allowed in register list");
+ }
+
+ if ((mask & (mask - 1)) == 0)
+ {
+ /* Single register transfers implemented as str/ldr. */
+ if (writeback)
+ {
+ if (inst.instruction & (1 << 23))
+ inst.instruction = 0x00000b04; /* ia! -> [base], #4 */
+ else
+ inst.instruction = 0x00000d04; /* db! -> [base, #-4]! */
+ }
+ else
+ {
+ if (inst.instruction & (1 << 23))
+ inst.instruction = 0x00800000; /* ia -> [base] */
+ else
+ inst.instruction = 0x00000c04; /* db -> [base, #-4] */
+ }
+
+ inst.instruction |= 0xf8400000;
+ if (load)
+ inst.instruction |= 0x00100000;
+
+ mask = ffs (mask) - 1;
+ mask <<= 12;
+ }
+ else if (writeback)
+ inst.instruction |= WRITE_BACK;
+
+ inst.instruction |= mask;
+ inst.instruction |= base << 16;
+}
+
+static void
+do_t_ldmstm (void)
+{
+ /* This really doesn't seem worth it. */
+ constraint (inst.reloc.type != BFD_RELOC_UNUSED,
+ _("expression too complex"));
+ constraint (inst.operands[1].writeback,
+ _("Thumb load/store multiple does not support {reglist}^"));
+
+ if (unified_syntax)
+ {
+ bfd_boolean narrow;
+ unsigned mask;
+
+ narrow = FALSE;
+ /* See if we can use a 16-bit instruction. */
+ if (inst.instruction < 0xffff /* not ldmdb/stmdb */
+ && inst.size_req != 4
+ && !(inst.operands[1].imm & ~0xff))
+ {
+ mask = 1 << inst.operands[0].reg;
+
+ if (inst.operands[0].reg <= 7)
+ {
+ if (inst.instruction == T_MNEM_stmia
+ ? 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)))
+ as_warn (_("value stored for r%d is UNKNOWN"),
+ inst.operands[0].reg);
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm;
+ narrow = TRUE;
+ }
+ else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0)
+ {
+ /* 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.
+ 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
+ str or ldr. The SP cases are handled below. */
+ unsigned long opcode;
+ /* First, record an error for Case 3. */
+ if (inst.operands[1].imm & mask
+ && inst.operands[0].writeback)
+ 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
+ : T_MNEM_ldr);
+ inst.instruction = THUMB_OP16 (opcode);
+ inst.instruction |= inst.operands[0].reg << 3;
+ inst.instruction |= (ffs (inst.operands[1].imm)-1);
+ narrow = TRUE;
+ }
+ }
+ else if (inst.operands[0] .reg == REG_SP)
+ {
+ if (inst.operands[0].writeback)
+ {
+ inst.instruction =
+ THUMB_OP16 (inst.instruction == T_MNEM_stmia
+ ? T_MNEM_push : T_MNEM_pop);
+ inst.instruction |= inst.operands[1].imm;
+ narrow = TRUE;
+ }
+ else if ((inst.operands[1].imm & (inst.operands[1].imm-1)) == 0)
+ {
+ inst.instruction =
+ THUMB_OP16 (inst.instruction == T_MNEM_stmia
+ ? T_MNEM_str_sp : T_MNEM_ldr_sp);
+ inst.instruction |= ((ffs (inst.operands[1].imm)-1) << 8);
+ narrow = TRUE;
+ }
+ }
+ }
+
+ if (!narrow)
+ {
+ if (inst.instruction < 0xffff)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+
+ encode_thumb2_ldmstm (inst.operands[0].reg, inst.operands[1].imm,
+ inst.operands[0].writeback);
+ }
+ }
+ else
+ {
+ constraint (inst.operands[0].reg > 7
+ || (inst.operands[1].imm & ~0xff), BAD_HIREG);
+ constraint (inst.instruction != T_MNEM_ldmia
+ && inst.instruction != T_MNEM_stmia,
+ _("Thumb-2 instruction only valid in unified syntax"));
+ if (inst.instruction == T_MNEM_stmia)
+ {
+ if (!inst.operands[0].writeback)
+ as_warn (_("this instruction will write back the base register"));
+ if ((inst.operands[1].imm & (1 << inst.operands[0].reg))
+ && (inst.operands[1].imm & ((1 << inst.operands[0].reg) - 1)))
+ as_warn (_("value stored for r%d is UNKNOWN"),
+ inst.operands[0].reg);
+ }
+ else
+ {
+ if (!inst.operands[0].writeback
+ && !(inst.operands[1].imm & (1 << inst.operands[0].reg)))
+ as_warn (_("this instruction will write back the base register"));
+ else if (inst.operands[0].writeback
+ && (inst.operands[1].imm & (1 << inst.operands[0].reg)))
+ as_warn (_("this instruction will not write back the base register"));
+ }
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].imm;
+ }
+}
+
+static void
+do_t_ldrex (void)
+{
+ constraint (!inst.operands[1].isreg || !inst.operands[1].preind
+ || inst.operands[1].postind || inst.operands[1].writeback
+ || inst.operands[1].immisreg || inst.operands[1].shifted
+ || inst.operands[1].negative,
+ BAD_ADDR_MODE);
+
+ constraint ((inst.operands[1].reg == REG_PC), BAD_PC);
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_U8;
+}
+
+static void
+do_t_ldrexd (void)
+{
+ if (!inst.operands[1].present)
+ {
+ constraint (inst.operands[0].reg == REG_LR,
+ _("r14 not allowed as first register "
+ "when second register is omitted"));
+ inst.operands[1].reg = inst.operands[0].reg + 1;
+ }
+ constraint (inst.operands[0].reg == inst.operands[1].reg,
+ BAD_OVERLAP);
+
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 8;
+ inst.instruction |= inst.operands[2].reg << 16;
+}
+
+static void
+do_t_ldst (void)
+{
+ unsigned long opcode;
+ int Rn;
+
+ if (inst.operands[0].isreg
+ && !inst.operands[0].preind
+ && inst.operands[0].reg == REG_PC)
+ set_it_insn_type_last ();
+
+ opcode = inst.instruction;
+ if (unified_syntax)
+ {
+ if (!inst.operands[1].isreg)
+ {
+ if (opcode <= 0xffff)
+ inst.instruction = THUMB_OP32 (opcode);
+ if (move_or_literal_pool (0, CONST_THUMB, /*mode_3=*/FALSE))
+ return;
+ }
+ if (inst.operands[1].isreg
+ && !inst.operands[1].writeback
+ && !inst.operands[1].shifted && !inst.operands[1].postind
+ && !inst.operands[1].negative && inst.operands[0].reg <= 7
+ && opcode <= 0xffff
+ && inst.size_req != 4)
+ {
+ /* Insn may have a 16-bit form. */
+ Rn = inst.operands[1].reg;
+ if (inst.operands[1].immisreg)
+ {
+ inst.instruction = THUMB_OP16 (opcode);
+ /* [Rn, Rik] */
+ if (Rn <= 7 && inst.operands[1].imm <= 7)
+ goto op16;
+ else if (opcode != T_MNEM_ldr && opcode != T_MNEM_str)
+ reject_bad_reg (inst.operands[1].imm);
+ }
+ else if ((Rn <= 7 && opcode != T_MNEM_ldrsh
+ && opcode != T_MNEM_ldrsb)
+ || ((Rn == REG_PC || Rn == REG_SP) && opcode == T_MNEM_ldr)
+ || (Rn == REG_SP && opcode == T_MNEM_str))
+ {
+ /* [Rn, #const] */
+ if (Rn > 7)
+ {
+ if (Rn == REG_PC)
+ {
+ if (inst.reloc.pc_rel)
+ opcode = T_MNEM_ldr_pc2;
+ else
+ opcode = T_MNEM_ldr_pc;
+ }
+ else
+ {
+ if (opcode == T_MNEM_ldr)
+ opcode = T_MNEM_ldr_sp;
+ else
+ opcode = T_MNEM_str_sp;
+ }
+ inst.instruction = inst.operands[0].reg << 8;
+ }
+ else
+ {
+ inst.instruction = inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+ inst.instruction |= THUMB_OP16 (opcode);
+ if (inst.size_req == 2)
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ else
+ inst.relax = opcode;
+ return;
+ }
+ }
+ /* Definitely a 32-bit variant. */
+
+ /* Warning for Erratum 752419. */
+ if (opcode == T_MNEM_ldr
+ && inst.operands[0].reg == REG_SP
+ && inst.operands[1].writeback == 1
+ && !inst.operands[1].immisreg)
+ {
+ 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)))
+ 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)
+ 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;
+ }
+
+ constraint (inst.operands[0].reg > 7, BAD_HIREG);
+
+ if (inst.instruction == T_MNEM_ldrsh || inst.instruction == T_MNEM_ldrsb)
+ {
+ /* Only [Rn,Rm] is acceptable. */
+ constraint (inst.operands[1].reg > 7 || inst.operands[1].imm > 7, BAD_HIREG);
+ constraint (!inst.operands[1].isreg || !inst.operands[1].immisreg
+ || inst.operands[1].postind || inst.operands[1].shifted
+ || inst.operands[1].negative,
+ _("Thumb does not support this addressing mode"));
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ goto op16;
+ }
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ if (!inst.operands[1].isreg)
+ if (move_or_literal_pool (0, CONST_THUMB, /*mode_3=*/FALSE))
+ return;
+
+ constraint (!inst.operands[1].preind
+ || inst.operands[1].shifted
+ || inst.operands[1].writeback,
+ _("Thumb does not support this addressing mode"));
+ if (inst.operands[1].reg == REG_PC || inst.operands[1].reg == REG_SP)
+ {
+ constraint (inst.instruction & 0x0600,
+ _("byte or halfword not valid for base register"));
+ constraint (inst.operands[1].reg == REG_PC
+ && !(inst.instruction & THUMB_LOAD_BIT),
+ _("r15 based store not allowed"));
+ constraint (inst.operands[1].immisreg,
+ _("invalid base register for register offset"));
+
+ if (inst.operands[1].reg == REG_PC)
+ inst.instruction = T_OPCODE_LDR_PC;
+ else if (inst.instruction & THUMB_LOAD_BIT)
+ inst.instruction = T_OPCODE_LDR_SP;
+ else
+ inst.instruction = T_OPCODE_STR_SP;
+
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ return;
+ }
+
+ constraint (inst.operands[1].reg > 7, BAD_HIREG);
+ if (!inst.operands[1].immisreg)
+ {
+ /* Immediate offset. */
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ return;
+ }
+
+ /* Register offset. */
+ constraint (inst.operands[1].imm > 7, BAD_HIREG);
+ constraint (inst.operands[1].negative,
+ _("Thumb does not support this addressing mode"));
+
+ op16:
+ switch (inst.instruction)
+ {
+ case T_OPCODE_STR_IW: inst.instruction = T_OPCODE_STR_RW; break;
+ case T_OPCODE_STR_IH: inst.instruction = T_OPCODE_STR_RH; break;
+ case T_OPCODE_STR_IB: inst.instruction = T_OPCODE_STR_RB; break;
+ case T_OPCODE_LDR_IW: inst.instruction = T_OPCODE_LDR_RW; break;
+ case T_OPCODE_LDR_IH: inst.instruction = T_OPCODE_LDR_RH; break;
+ case T_OPCODE_LDR_IB: inst.instruction = T_OPCODE_LDR_RB; break;
+ case 0x5600 /* ldrsb */:
+ case 0x5e00 /* ldrsh */: break;
+ default: abort ();
+ }
+
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ inst.instruction |= inst.operands[1].imm << 6;
+}
+
+static void
+do_t_ldstd (void)
+{
+ if (!inst.operands[1].present)
+ {
+ 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);
+}
+
+static void
+do_t_ldstt (void)
+{
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_thumb32_addr_mode (1, /*is_t=*/TRUE, /*is_d=*/FALSE);
+}
+
+static void
+do_t_mla (void)
+{
+ unsigned Rd, Rn, Rm, Ra;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
+ Rm = inst.operands[2].reg;
+ Ra = inst.operands[3].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+ reject_bad_reg (Ra);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+ inst.instruction |= Ra << 12;
+}
+
+static void
+do_t_mlal (void)
+{
+ unsigned RdLo, RdHi, Rn, Rm;
+
+ RdLo = inst.operands[0].reg;
+ RdHi = inst.operands[1].reg;
+ Rn = inst.operands[2].reg;
+ Rm = inst.operands[3].reg;
+
+ reject_bad_reg (RdLo);
+ reject_bad_reg (RdHi);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= RdLo << 12;
+ inst.instruction |= RdHi << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+}
+
+static void
+do_t_mov_cmp (void)
+{
+ unsigned Rn, Rm;
+
+ Rn = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
+
+ if (Rn == REG_PC)
+ set_it_insn_type_last ();
+
+ if (unified_syntax)
+ {
+ int r0off = (inst.instruction == T_MNEM_mov
+ || inst.instruction == T_MNEM_movs) ? 8 : 16;
+ unsigned long opcode;
+ bfd_boolean narrow;
+ bfd_boolean low_regs;
+
+ low_regs = (Rn <= 7 && Rm <= 7);
+ opcode = inst.instruction;
+ if (in_it_block ())
+ narrow = opcode != T_MNEM_movs;
+ else
+ narrow = opcode != T_MNEM_movs || low_regs;
+ if (inst.size_req == 4
+ || inst.operands[1].shifted)
+ narrow = FALSE;
+
+ /* MOVS PC, LR is encoded as SUBS PC, LR, #0. */
+ if (opcode == T_MNEM_movs && inst.operands[1].isreg
+ && !inst.operands[1].shifted
+ && Rn == REG_PC
+ && Rm == REG_LR)
+ {
+ inst.instruction = T2_SUBS_PC_LR;
+ return;
+ }
+
+ if (opcode == T_MNEM_cmp)
+ {
+ constraint (Rn == REG_PC, BAD_PC);
+ if (narrow)
+ {
+ /* In the Thumb-2 ISA, use of R13 as Rm is deprecated,
+ but valid. */
+ warn_deprecated_sp (Rm);
+ /* R15 was documented as a valid choice for Rm in ARMv6,
+ but as UNPREDICTABLE in ARMv7. ARM's proprietary
+ tools reject R15, so we do too. */
+ constraint (Rm == REG_PC, BAD_PC);
+ }
+ else
+ reject_bad_reg (Rm);
+ }
+ else if (opcode == T_MNEM_mov
+ || opcode == T_MNEM_movs)
+ {
+ if (inst.operands[1].isreg)
+ {
+ if (opcode == T_MNEM_movs)
+ {
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+ }
+ else if (narrow)
+ {
+ /* This is mov.n. */
+ if ((Rn == REG_SP || Rn == REG_PC)
+ && (Rm == REG_SP || Rm == REG_PC))
+ {
+ as_tsktsk (_("Use of r%u as a source register is "
+ "deprecated when r%u is the destination "
+ "register."), Rm, Rn);
+ }
+ }
+ else
+ {
+ /* This is mov.w. */
+ constraint (Rn == REG_PC, BAD_PC);
+ constraint (Rm == REG_PC, BAD_PC);
+ constraint (Rn == REG_SP && Rm == REG_SP, BAD_SP);
+ }
+ }
+ else
+ reject_bad_reg (Rn);
+ }
+
+ if (!inst.operands[1].isreg)
+ {
+ /* Immediate operand. */
+ if (!in_it_block () && opcode == T_MNEM_mov)
+ narrow = 0;
+ if (low_regs && narrow)
+ {
+ inst.instruction = THUMB_OP16 (opcode);
+ inst.instruction |= Rn << 8;
+ if (inst.reloc.type < BFD_RELOC_ARM_THUMB_ALU_ABS_G0_NC
+ || inst.reloc.type > BFD_RELOC_ARM_THUMB_ALU_ABS_G3_NC)
+ {
+ if (inst.size_req == 2)
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
+ else
+ inst.relax = opcode;
+ }
+ }
+ else
+ {
+ constraint (inst.reloc.type >= BFD_RELOC_ARM_THUMB_ALU_ABS_G0_NC
+ && inst.reloc.type <= BFD_RELOC_ARM_THUMB_ALU_ABS_G3_NC ,
+ THUMB1_RELOC_ONLY);
+
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= Rn << r0off;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ }
+ else if (inst.operands[1].shifted && inst.operands[1].immisreg
+ && (inst.instruction == T_MNEM_mov
+ || inst.instruction == T_MNEM_movs))
+ {
+ /* Register shifts are encoded as separate shift instructions. */
+ bfd_boolean flags = (inst.instruction == T_MNEM_movs);
+
+ if (in_it_block ())
+ narrow = !flags;
+ else
+ narrow = flags;
+
+ if (inst.size_req == 4)
+ narrow = FALSE;
+
+ if (!low_regs || inst.operands[1].imm > 7)
+ narrow = FALSE;
+
+ if (Rn != Rm)
+ narrow = FALSE;
+
+ switch (inst.operands[1].shift_kind)
+ {
+ case SHIFT_LSL:
+ opcode = narrow ? T_OPCODE_LSL_R : THUMB_OP32 (T_MNEM_lsl);
+ break;
+ case SHIFT_ASR:
+ opcode = narrow ? T_OPCODE_ASR_R : THUMB_OP32 (T_MNEM_asr);
+ break;
+ case SHIFT_LSR:
+ opcode = narrow ? T_OPCODE_LSR_R : THUMB_OP32 (T_MNEM_lsr);
+ break;
+ case SHIFT_ROR:
+ opcode = narrow ? T_OPCODE_ROR_R : THUMB_OP32 (T_MNEM_ror);
+ break;
+ default:
+ abort ();
+ }
+
+ inst.instruction = opcode;
+ if (narrow)
+ {
+ inst.instruction |= Rn;
+ inst.instruction |= inst.operands[1].imm << 3;
+ }
+ else
+ {
+ if (flags)
+ inst.instruction |= CONDS_BIT;
+
+ inst.instruction |= Rn << 8;
+ inst.instruction |= Rm << 16;
+ inst.instruction |= inst.operands[1].imm;
+ }
+ }
+ else if (!narrow)
+ {
+ /* Some mov with immediate shift have narrow variants.
+ Register shifts are handled above. */
+ if (low_regs && inst.operands[1].shifted
+ && (inst.instruction == T_MNEM_mov
+ || inst.instruction == T_MNEM_movs))
+ {
+ if (in_it_block ())
+ narrow = (inst.instruction == T_MNEM_mov);
+ else
+ narrow = (inst.instruction == T_MNEM_movs);
+ }
+
+ if (narrow)
+ {
+ switch (inst.operands[1].shift_kind)
+ {
+ case SHIFT_LSL: inst.instruction = T_OPCODE_LSL_I; break;
+ case SHIFT_LSR: inst.instruction = T_OPCODE_LSR_I; break;
+ case SHIFT_ASR: inst.instruction = T_OPCODE_ASR_I; break;
+ default: narrow = FALSE; break;
+ }
+ }
+
+ if (narrow)
+ {
+ inst.instruction |= Rn;
+ inst.instruction |= Rm << 3;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rn << r0off;
+ encode_thumb32_shifted_operand (1);
+ }
+ }
+ else
+ 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.instruction |= Rm << 3;
+ break;
+
+ case T_MNEM_movs:
+ /* We know we have low registers at this point.
+ Generate LSLS Rd, Rs, #0. */
+ inst.instruction = T_OPCODE_LSL_I;
+ inst.instruction |= Rn;
+ inst.instruction |= Rm << 3;
+ break;
+
+ case T_MNEM_cmp:
+ if (low_regs)
+ {
+ inst.instruction = T_OPCODE_CMP_LR;
+ inst.instruction |= Rn;
+ inst.instruction |= Rm << 3;
+ }
+ else
+ {
+ inst.instruction = T_OPCODE_CMP_HR;
+ inst.instruction |= (Rn & 0x8) << 4;
+ inst.instruction |= (Rn & 0x7);
+ inst.instruction |= Rm << 3;
+ }
+ break;
+ }
+ return;
+ }
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+
+ /* PR 10443: Do not silently ignore shifted operands. */
+ constraint (inst.operands[1].shifted,
+ _("shifts in CMP/MOV instructions are only supported in unified syntax"));
+
+ if (inst.operands[1].isreg)
+ {
+ if (Rn < 8 && Rm < 8)
+ {
+ /* A move of two lowregs is encoded as ADD Rd, Rs, #0
+ since a MOV instruction produces unpredictable results. */
+ if (inst.instruction == T_OPCODE_MOV_I8)
+ inst.instruction = T_OPCODE_ADD_I3;
+ else
+ inst.instruction = T_OPCODE_CMP_LR;
+
+ inst.instruction |= Rn;
+ inst.instruction |= Rm << 3;
+ }
+ else
+ {
+ if (inst.instruction == T_OPCODE_MOV_I8)
+ inst.instruction = T_OPCODE_MOV_HR;
+ else
+ inst.instruction = T_OPCODE_CMP_HR;
+ do_t_cpy ();
+ }
+ }
+ else
+ {
+ constraint (Rn > 7,
+ _("only lo regs allowed with immediate"));
+ inst.instruction |= Rn << 8;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
+ }
+}
+
+static void
+do_t_mov16 (void)
+{
+ unsigned Rd;
+ bfd_vma imm;
+ bfd_boolean top;
+
+ top = (inst.instruction & 0x00800000) != 0;
+ if (inst.reloc.type == BFD_RELOC_ARM_MOVW)
+ {
+ constraint (top, _(":lower16: not allowed this instruction"));
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_MOVW;
+ }
+ else if (inst.reloc.type == BFD_RELOC_ARM_MOVT)
+ {
+ constraint (!top, _(":upper16: not allowed this instruction"));
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_MOVT;
+ }
+
+ Rd = inst.operands[0].reg;
+ reject_bad_reg (Rd);
+
+ inst.instruction |= Rd << 8;
+ if (inst.reloc.type == BFD_RELOC_UNUSED)
+ {
+ imm = inst.reloc.exp.X_add_number;
+ inst.instruction |= (imm & 0xf000) << 4;
+ inst.instruction |= (imm & 0x0800) << 15;
+ inst.instruction |= (imm & 0x0700) << 4;
+ inst.instruction |= (imm & 0x00ff);
+ }
+}
+
+static void
+do_t_mvn_tst (void)
+{
+ unsigned Rn, Rm;
+
+ Rn = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
+
+ if (inst.instruction == T_MNEM_cmp
+ || inst.instruction == T_MNEM_cmn)
+ constraint (Rn == REG_PC, BAD_PC);
+ else
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+
+ if (unified_syntax)
+ {
+ int r0off = (inst.instruction == T_MNEM_mvn
+ || inst.instruction == T_MNEM_mvns) ? 8 : 16;
+ bfd_boolean narrow;
+
+ if (inst.size_req == 4
+ || inst.instruction > 0xffff
+ || inst.operands[1].shifted
+ || Rn > 7 || Rm > 7)
+ narrow = FALSE;
+ 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 ();
+ else
+ narrow = in_it_block ();
+
+ if (!inst.operands[1].isreg)
+ {
+ /* For an immediate, we always generate a 32-bit opcode;
+ section relaxation will shrink it later if possible. */
+ if (inst.instruction < 0xffff)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.instruction |= Rn << r0off;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ {
+ /* See if we can do this with a 16-bit instruction. */
+ if (narrow)
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rn;
+ inst.instruction |= Rm << 3;
+ }
+ else
+ {
+ constraint (inst.operands[1].shifted
+ && inst.operands[1].immisreg,
+ _("shift must be constant"));
+ if (inst.instruction < 0xffff)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rn << r0off;
+ encode_thumb32_shifted_operand (1);
+ }
+ }
+ }
+ else
+ {
+ constraint (inst.instruction > 0xffff
+ || inst.instruction == T_MNEM_mvns, BAD_THUMB32);
+ constraint (!inst.operands[1].isreg || inst.operands[1].shifted,
+ _("unshifted register required"));
+ constraint (Rn > 7 || Rm > 7,
+ BAD_HIREG);
+
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rn;
+ inst.instruction |= Rm << 3;
+ }
+}
+
+static void
+do_t_mrs (void)
+{
+ unsigned Rd;
+
+ if (do_vfp_nsyn_mrs () == SUCCESS)
+ return;
+
+ Rd = inst.operands[0].reg;
+ reject_bad_reg (Rd);
+ inst.instruction |= Rd << 8;
+
+ if (inst.operands[1].isreg)
+ {
+ unsigned br = inst.operands[1].reg;
+ if (((br & 0x200) == 0) && ((br & 0xf000) != 0xf000))
+ as_bad (_("bad register for mrs"));
+
+ inst.instruction |= br & (0xf << 16);
+ inst.instruction |= (br & 0x300) >> 4;
+ inst.instruction |= (br & SPSR_BIT) >> 2;
+ }
+ else
+ {
+ 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))
+ {
+ /* 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 =
+ !ARM_FEATURE_CORE_EQUAL (selected_cpu, arm_arch_any);
+ 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). */
+ constraint ((flags & ~SPSR_BIT) != (PSR_c|PSR_f),
+ _("'APSR', 'CPSR' or 'SPSR' expected"));
+
+ inst.instruction |= (flags & SPSR_BIT) >> 2;
+ inst.instruction |= inst.operands[1].imm & 0xff;
+ inst.instruction |= 0xf0000;
+ }
+}
+
+static void
+do_t_msr (void)
+{
+ int flags;
+ unsigned Rn;
+
+ if (do_vfp_nsyn_msr () == SUCCESS)
+ return;
+
+ constraint (!inst.operands[1].isreg,
+ _("Thumb encoding does not support an immediate here"));
+
+ if (inst.operands[0].isreg)
+ flags = (int)(inst.operands[0].reg);
+ else
+ flags = inst.operands[0].imm;
+
+ if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m))
+ {
+ int bits = inst.operands[0].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT);
+
+ /* 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 =
+ !ARM_FEATURE_CORE_EQUAL (selected_cpu, arm_arch_any);
+ 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 "
+ "requested special purpose register"));
+
+ Rn = inst.operands[1].reg;
+ reject_bad_reg (Rn);
+
+ inst.instruction |= (flags & SPSR_BIT) >> 2;
+ inst.instruction |= (flags & 0xf0000) >> 8;
+ inst.instruction |= (flags & 0x300) >> 4;
+ inst.instruction |= (flags & 0xff);
+ inst.instruction |= Rn << 16;
+}
+
+static void
+do_t_mul (void)
+{
+ bfd_boolean narrow;
+ unsigned Rd, Rn, Rm;
+
+ if (!inst.operands[2].present)
+ inst.operands[2].reg = inst.operands[0].reg;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
+ Rm = inst.operands[2].reg;
+
+ if (unified_syntax)
+ {
+ if (inst.size_req == 4
+ || (Rd != Rn
+ && Rd != Rm)
+ || Rn > 7
+ || Rm > 7)
+ narrow = FALSE;
+ else if (inst.instruction == T_MNEM_muls)
+ narrow = !in_it_block ();
+ else
+ narrow = in_it_block ();
+ }
+ else
+ {
+ constraint (inst.instruction == T_MNEM_muls, BAD_THUMB32);
+ constraint (Rn > 7 || Rm > 7,
+ BAD_HIREG);
+ narrow = TRUE;
+ }
+
+ if (narrow)
+ {
+ /* 16-bit MULS/Conditional MUL. */
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+
+ if (Rd == Rn)
+ inst.instruction |= Rm << 3;
+ else if (Rd == Rm)
+ inst.instruction |= Rn << 3;
+ else
+ constraint (1, _("dest must overlap one source register"));
+ }
+ else
+ {
+ constraint (inst.instruction != T_MNEM_mul,
+ _("Thumb-2 MUL must not set flags"));
+ /* 32-bit MUL. */
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm << 0;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+ }
+}
+
+static void
+do_t_mull (void)
+{
+ unsigned RdLo, RdHi, Rn, Rm;
+
+ RdLo = inst.operands[0].reg;
+ RdHi = inst.operands[1].reg;
+ Rn = inst.operands[2].reg;
+ Rm = inst.operands[3].reg;
+
+ reject_bad_reg (RdLo);
+ reject_bad_reg (RdHi);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= RdLo << 12;
+ inst.instruction |= RdHi << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+
+ if (RdLo == RdHi)
+ as_tsktsk (_("rdhi and rdlo must be different"));
+}
+
+static void
+do_t_nop (void)
+{
+ set_it_insn_type (NEUTRAL_IT_INSN);
+
+ if (unified_syntax)
+ {
+ if (inst.size_req == 4 || inst.operands[0].imm > 15)
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].imm;
+ }
+ else
+ {
+ /* PR9722: Check for Thumb2 availability before
+ generating a thumb2 nop instruction. */
+ if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6t2))
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].imm << 4;
+ }
+ else
+ inst.instruction = 0x46c0;
+ }
+ }
+ else
+ {
+ constraint (inst.operands[0].present,
+ _("Thumb does not support NOP with hints"));
+ inst.instruction = 0x46c0;
+ }
+}
+static void
+do_t_neg (void)
+{
if (unified_syntax)
{
- int r0off = (inst.instruction == T_MNEM_mov
- || inst.instruction == T_MNEM_movs) ? 8 : 16;
- unsigned long opcode;
bfd_boolean narrow;
- bfd_boolean low_regs;
- low_regs = (Rn <= 7 && Rm <= 7);
- opcode = inst.instruction;
- if (in_it_block ())
- narrow = opcode != T_MNEM_movs;
+ if (THUMB_SETS_FLAGS (inst.instruction))
+ narrow = !in_it_block ();
else
- narrow = opcode != T_MNEM_movs || low_regs;
- if (inst.size_req == 4
- || inst.operands[1].shifted)
+ narrow = in_it_block ();
+ if (inst.operands[0].reg > 7 || inst.operands[1].reg > 7)
+ narrow = FALSE;
+ if (inst.size_req == 4)
narrow = FALSE;
- /* MOVS PC, LR is encoded as SUBS PC, LR, #0. */
- if (opcode == T_MNEM_movs && inst.operands[1].isreg
- && !inst.operands[1].shifted
- && Rn == REG_PC
- && Rm == REG_LR)
- {
- inst.instruction = T2_SUBS_PC_LR;
- return;
- }
-
- if (opcode == T_MNEM_cmp)
+ if (!narrow)
{
- constraint (Rn == REG_PC, BAD_PC);
- if (narrow)
- {
- /* In the Thumb-2 ISA, use of R13 as Rm is deprecated,
- but valid. */
- warn_deprecated_sp (Rm);
- /* R15 was documented as a valid choice for Rm in ARMv6,
- but as UNPREDICTABLE in ARMv7. ARM's proprietary
- tools reject R15, so we do too. */
- constraint (Rm == REG_PC, BAD_PC);
- }
- else
- reject_bad_reg (Rm);
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
}
- else if (opcode == T_MNEM_mov
- || opcode == T_MNEM_movs)
+ else
{
- if (inst.operands[1].isreg)
- {
- if (opcode == T_MNEM_movs)
- {
- reject_bad_reg (Rn);
- reject_bad_reg (Rm);
- }
- else if (narrow)
- {
- /* This is mov.n. */
- if ((Rn == REG_SP || Rn == REG_PC)
- && (Rm == REG_SP || Rm == REG_PC))
- {
- as_warn (_("Use of r%u as a source register is "
- "deprecated when r%u is the destination "
- "register."), Rm, Rn);
- }
- }
- else
- {
- /* This is mov.w. */
- constraint (Rn == REG_PC, BAD_PC);
- constraint (Rm == REG_PC, BAD_PC);
- constraint (Rn == REG_SP && Rm == REG_SP, BAD_SP);
- }
- }
- else
- reject_bad_reg (Rn);
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
}
+ }
+ else
+ {
+ constraint (inst.operands[0].reg > 7 || inst.operands[1].reg > 7,
+ BAD_HIREG);
+ constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
- if (!inst.operands[1].isreg)
- {
- /* Immediate operand. */
- if (!in_it_block () && opcode == T_MNEM_mov)
- narrow = 0;
- if (low_regs && narrow)
- {
- inst.instruction = THUMB_OP16 (opcode);
- inst.instruction |= Rn << 8;
- if (inst.size_req == 2)
- inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
- else
- inst.relax = opcode;
- }
- else
- {
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
- inst.instruction |= Rn << r0off;
- inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
- }
- }
- else if (inst.operands[1].shifted && inst.operands[1].immisreg
- && (inst.instruction == T_MNEM_mov
- || inst.instruction == T_MNEM_movs))
- {
- /* Register shifts are encoded as separate shift instructions. */
- bfd_boolean flags = (inst.instruction == T_MNEM_movs);
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
+}
+
+static void
+do_t_orn (void)
+{
+ unsigned Rd, Rn;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].present ? inst.operands[1].reg : Rd;
+
+ reject_bad_reg (Rd);
+ /* Rn == REG_SP is unpredictable; Rn == REG_PC is MVN. */
+ reject_bad_reg (Rn);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+
+ if (!inst.operands[2].isreg)
+ {
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
+ else
+ {
+ unsigned Rm;
+
+ Rm = inst.operands[2].reg;
+ reject_bad_reg (Rm);
+
+ constraint (inst.operands[2].shifted
+ && inst.operands[2].immisreg,
+ _("shift must be constant"));
+ encode_thumb32_shifted_operand (2);
+ }
+}
+
+static void
+do_t_pkhbt (void)
+{
+ unsigned Rd, Rn, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
+ Rm = inst.operands[2].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+ if (inst.operands[3].present)
+ {
+ unsigned int val = inst.reloc.exp.X_add_number;
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+ inst.instruction |= (val & 0x1c) << 10;
+ inst.instruction |= (val & 0x03) << 6;
+ }
+}
+
+static void
+do_t_pkhtb (void)
+{
+ if (!inst.operands[3].present)
+ {
+ unsigned Rtmp;
+
+ inst.instruction &= ~0x00000020;
+
+ /* PR 10168. Swap the Rm and Rn registers. */
+ Rtmp = inst.operands[1].reg;
+ inst.operands[1].reg = inst.operands[2].reg;
+ inst.operands[2].reg = Rtmp;
+ }
+ do_t_pkhbt ();
+}
+
+static void
+do_t_pld (void)
+{
+ if (inst.operands[0].immisreg)
+ reject_bad_reg (inst.operands[0].imm);
+
+ encode_thumb32_addr_mode (0, /*is_t=*/FALSE, /*is_d=*/FALSE);
+}
+
+static void
+do_t_push_pop (void)
+{
+ unsigned mask;
+
+ constraint (inst.operands[0].writeback,
+ _("push/pop do not support {reglist}^"));
+ constraint (inst.reloc.type != BFD_RELOC_UNUSED,
+ _("expression too complex"));
+
+ mask = inst.operands[0].imm;
+ if (inst.size_req != 4 && (mask & ~0xff) == 0)
+ inst.instruction = THUMB_OP16 (inst.instruction) | mask;
+ else if (inst.size_req != 4
+ && (mask & ~0xff) == (1U << (inst.instruction == T_MNEM_push
+ ? REG_LR : REG_PC)))
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= THUMB_PP_PC_LR;
+ inst.instruction |= mask & 0xff;
+ }
+ else if (unified_syntax)
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ encode_thumb2_ldmstm (13, mask, TRUE);
+ }
+ else
+ {
+ inst.error = _("invalid register list to push/pop instruction");
+ return;
+ }
+}
+
+static void
+do_t_rbit (void)
+{
+ unsigned Rd, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rm);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rm << 16;
+ inst.instruction |= Rm;
+}
+
+static void
+do_t_rev (void)
+{
+ unsigned Rd, Rm;
+
+ Rd = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
- if (in_it_block ())
- narrow = !flags;
- else
- narrow = flags;
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rm);
- if (inst.size_req == 4)
- narrow = FALSE;
+ if (Rd <= 7 && Rm <= 7
+ && inst.size_req != 4)
+ {
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rm << 3;
+ }
+ else if (unified_syntax)
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rm << 16;
+ inst.instruction |= Rm;
+ }
+ else
+ inst.error = BAD_HIREG;
+}
- if (!low_regs || inst.operands[1].imm > 7)
- narrow = FALSE;
+static void
+do_t_rrx (void)
+{
+ unsigned Rd, Rm;
- if (Rn != Rm)
- narrow = FALSE;
+ Rd = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
- switch (inst.operands[1].shift_kind)
- {
- case SHIFT_LSL:
- opcode = narrow ? T_OPCODE_LSL_R : THUMB_OP32 (T_MNEM_lsl);
- break;
- case SHIFT_ASR:
- opcode = narrow ? T_OPCODE_ASR_R : THUMB_OP32 (T_MNEM_asr);
- break;
- case SHIFT_LSR:
- opcode = narrow ? T_OPCODE_LSR_R : THUMB_OP32 (T_MNEM_lsr);
- break;
- case SHIFT_ROR:
- opcode = narrow ? T_OPCODE_ROR_R : THUMB_OP32 (T_MNEM_ror);
- break;
- default:
- abort ();
- }
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rm);
- inst.instruction = opcode;
- if (narrow)
- {
- inst.instruction |= Rn;
- inst.instruction |= inst.operands[1].imm << 3;
- }
- else
- {
- if (flags)
- inst.instruction |= CONDS_BIT;
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rm;
+}
- inst.instruction |= Rn << 8;
- inst.instruction |= Rm << 16;
- inst.instruction |= inst.operands[1].imm;
- }
- }
- else if (!narrow)
- {
- /* Some mov with immediate shift have narrow variants.
- Register shifts are handled above. */
- if (low_regs && inst.operands[1].shifted
- && (inst.instruction == T_MNEM_mov
- || inst.instruction == T_MNEM_movs))
- {
- if (in_it_block ())
- narrow = (inst.instruction == T_MNEM_mov);
- else
- narrow = (inst.instruction == T_MNEM_movs);
- }
+static void
+do_t_rsb (void)
+{
+ unsigned Rd, Rs;
- if (narrow)
- {
- switch (inst.operands[1].shift_kind)
- {
- case SHIFT_LSL: inst.instruction = T_OPCODE_LSL_I; break;
- case SHIFT_LSR: inst.instruction = T_OPCODE_LSR_I; break;
- case SHIFT_ASR: inst.instruction = T_OPCODE_ASR_I; break;
- default: narrow = FALSE; break;
- }
- }
+ Rd = inst.operands[0].reg;
+ Rs = (inst.operands[1].present
+ ? inst.operands[1].reg /* Rd, Rs, foo */
+ : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
- if (narrow)
- {
- inst.instruction |= Rn;
- inst.instruction |= Rm << 3;
- inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
- }
- else
- {
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= Rn << r0off;
- encode_thumb32_shifted_operand (1);
- }
- }
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rs);
+ if (inst.operands[2].isreg)
+ reject_bad_reg (inst.operands[2].reg);
+
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rs << 16;
+ if (!inst.operands[2].isreg)
+ {
+ bfd_boolean narrow;
+
+ if ((inst.instruction & 0x00100000) != 0)
+ narrow = !in_it_block ();
else
- switch (inst.instruction)
- {
- case T_MNEM_mov:
- inst.instruction = T_OPCODE_MOV_HR;
- inst.instruction |= (Rn & 0x8) << 4;
- inst.instruction |= (Rn & 0x7);
- inst.instruction |= Rm << 3;
- break;
+ narrow = in_it_block ();
- case T_MNEM_movs:
- /* We know we have low registers at this point.
- Generate LSLS Rd, Rs, #0. */
- inst.instruction = T_OPCODE_LSL_I;
- inst.instruction |= Rn;
- inst.instruction |= Rm << 3;
- break;
+ if (Rd > 7 || Rs > 7)
+ narrow = FALSE;
- case T_MNEM_cmp:
- if (low_regs)
- {
- inst.instruction = T_OPCODE_CMP_LR;
- inst.instruction |= Rn;
- inst.instruction |= Rm << 3;
- }
- else
- {
- inst.instruction = T_OPCODE_CMP_HR;
- inst.instruction |= (Rn & 0x8) << 4;
- inst.instruction |= (Rn & 0x7);
- inst.instruction |= Rm << 3;
- }
- break;
- }
- return;
+ if (inst.size_req == 4 || !unified_syntax)
+ narrow = FALSE;
+
+ if (inst.reloc.exp.X_op != O_constant
+ || inst.reloc.exp.X_add_number != 0)
+ narrow = FALSE;
+
+ /* Turn rsb #0 into 16-bit neg. We should probably do this via
+ relaxation, but it doesn't seem worth the hassle. */
+ if (narrow)
+ {
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ inst.instruction = THUMB_OP16 (T_MNEM_negs);
+ inst.instruction |= Rs << 3;
+ inst.instruction |= Rd;
+ }
+ else
+ {
+ inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
+ inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ }
}
+ else
+ encode_thumb32_shifted_operand (2);
+}
- inst.instruction = THUMB_OP16 (inst.instruction);
+static void
+do_t_setend (void)
+{
+ if (warn_on_deprecated
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v8))
+ as_tsktsk (_("setend use is deprecated for ARMv8"));
- /* PR 10443: Do not silently ignore shifted operands. */
- constraint (inst.operands[1].shifted,
- _("shifts in CMP/MOV instructions are only supported in unified syntax"));
+ set_it_insn_type (OUTSIDE_IT_INSN);
+ if (inst.operands[0].imm)
+ inst.instruction |= 0x8;
+}
- if (inst.operands[1].isreg)
+static void
+do_t_shift (void)
+{
+ if (!inst.operands[1].present)
+ inst.operands[1].reg = inst.operands[0].reg;
+
+ if (unified_syntax)
{
- if (Rn < 8 && Rm < 8)
+ bfd_boolean narrow;
+ int shift_kind;
+
+ switch (inst.instruction)
{
- /* A move of two lowregs is encoded as ADD Rd, Rs, #0
- since a MOV instruction produces unpredictable results. */
- if (inst.instruction == T_OPCODE_MOV_I8)
- inst.instruction = T_OPCODE_ADD_I3;
- else
- inst.instruction = T_OPCODE_CMP_LR;
+ case T_MNEM_asr:
+ case T_MNEM_asrs: shift_kind = SHIFT_ASR; break;
+ case T_MNEM_lsl:
+ case T_MNEM_lsls: shift_kind = SHIFT_LSL; break;
+ case T_MNEM_lsr:
+ case T_MNEM_lsrs: shift_kind = SHIFT_LSR; break;
+ case T_MNEM_ror:
+ case T_MNEM_rors: shift_kind = SHIFT_ROR; break;
+ default: abort ();
+ }
+
+ if (THUMB_SETS_FLAGS (inst.instruction))
+ narrow = !in_it_block ();
+ else
+ narrow = in_it_block ();
+ if (inst.operands[0].reg > 7 || inst.operands[1].reg > 7)
+ narrow = FALSE;
+ if (!inst.operands[2].isreg && shift_kind == SHIFT_ROR)
+ narrow = FALSE;
+ if (inst.operands[2].isreg
+ && (inst.operands[1].reg != inst.operands[0].reg
+ || inst.operands[2].reg > 7))
+ narrow = FALSE;
+ if (inst.size_req == 4)
+ narrow = FALSE;
+
+ reject_bad_reg (inst.operands[0].reg);
+ reject_bad_reg (inst.operands[1].reg);
+
+ if (!narrow)
+ {
+ if (inst.operands[2].isreg)
+ {
+ reject_bad_reg (inst.operands[2].reg);
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[2].reg;
- inst.instruction |= Rn;
- inst.instruction |= Rm << 3;
+ /* PR 12854: Error on extraneous shifts. */
+ constraint (inst.operands[2].shifted,
+ _("extraneous shift as part of operand to shift insn"));
+ }
+ else
+ {
+ inst.operands[1].shifted = 1;
+ inst.operands[1].shift_kind = shift_kind;
+ inst.instruction = THUMB_OP32 (THUMB_SETS_FLAGS (inst.instruction)
+ ? T_MNEM_movs : T_MNEM_mov);
+ inst.instruction |= inst.operands[0].reg << 8;
+ encode_thumb32_shifted_operand (1);
+ /* Prevent the incorrect generation of an ARM_IMMEDIATE fixup. */
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ }
}
else
{
- if (inst.instruction == T_OPCODE_MOV_I8)
- inst.instruction = T_OPCODE_MOV_HR;
+ if (inst.operands[2].isreg)
+ {
+ switch (shift_kind)
+ {
+ case SHIFT_ASR: inst.instruction = T_OPCODE_ASR_R; break;
+ case SHIFT_LSL: inst.instruction = T_OPCODE_LSL_R; break;
+ case SHIFT_LSR: inst.instruction = T_OPCODE_LSR_R; break;
+ case SHIFT_ROR: inst.instruction = T_OPCODE_ROR_R; break;
+ default: abort ();
+ }
+
+ 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 = T_OPCODE_CMP_HR;
- do_t_cpy ();
+ {
+ switch (shift_kind)
+ {
+ case SHIFT_ASR: inst.instruction = T_OPCODE_ASR_I; break;
+ case SHIFT_LSL: inst.instruction = T_OPCODE_LSL_I; break;
+ case SHIFT_LSR: inst.instruction = T_OPCODE_LSR_I; break;
+ default: abort ();
+ }
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
}
}
else
{
- constraint (Rn > 7,
- _("only lo regs allowed with immediate"));
- inst.instruction |= Rn << 8;
- inst.reloc.type = BFD_RELOC_ARM_THUMB_IMM;
+ constraint (inst.operands[0].reg > 7
+ || inst.operands[1].reg > 7, BAD_HIREG);
+ constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
+
+ if (inst.operands[2].isreg) /* Rd, {Rs,} Rn */
+ {
+ constraint (inst.operands[2].reg > 7, BAD_HIREG);
+ constraint (inst.operands[0].reg != inst.operands[1].reg,
+ _("source1 and dest must be same register"));
+
+ switch (inst.instruction)
+ {
+ case T_MNEM_asr: inst.instruction = T_OPCODE_ASR_R; break;
+ case T_MNEM_lsl: inst.instruction = T_OPCODE_LSL_R; break;
+ case T_MNEM_lsr: inst.instruction = T_OPCODE_LSR_R; break;
+ case T_MNEM_ror: inst.instruction = T_OPCODE_ROR_R; break;
+ default: abort ();
+ }
+
+ 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
+ {
+ switch (inst.instruction)
+ {
+ case T_MNEM_asr: inst.instruction = T_OPCODE_ASR_I; break;
+ case T_MNEM_lsl: inst.instruction = T_OPCODE_LSL_I; break;
+ case T_MNEM_lsr: inst.instruction = T_OPCODE_LSR_I; break;
+ case T_MNEM_ror: inst.error = _("ror #imm not supported"); return;
+ default: abort ();
+ }
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ }
}
}
static void
-do_t_mov16 (void)
+do_t_simd (void)
{
- unsigned Rd;
- bfd_vma imm;
- bfd_boolean top;
-
- top = (inst.instruction & 0x00800000) != 0;
- if (inst.reloc.type == BFD_RELOC_ARM_MOVW)
- {
- constraint (top, _(":lower16: not allowed this instruction"));
- inst.reloc.type = BFD_RELOC_ARM_THUMB_MOVW;
- }
- else if (inst.reloc.type == BFD_RELOC_ARM_MOVT)
- {
- constraint (!top, _(":upper16: not allowed this instruction"));
- inst.reloc.type = BFD_RELOC_ARM_THUMB_MOVT;
- }
+ unsigned Rd, Rn, Rm;
Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
+ Rm = inst.operands[2].reg;
+
reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+ reject_bad_reg (Rm);
inst.instruction |= Rd << 8;
- if (inst.reloc.type == BFD_RELOC_UNUSED)
- {
- imm = inst.reloc.exp.X_add_number;
- inst.instruction |= (imm & 0xf000) << 4;
- inst.instruction |= (imm & 0x0800) << 15;
- inst.instruction |= (imm & 0x0700) << 4;
- inst.instruction |= (imm & 0x00ff);
- }
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
}
static void
-do_t_mvn_tst (void)
+do_t_simd2 (void)
{
- unsigned Rn, Rm;
+ unsigned Rd, Rn, Rm;
- Rn = inst.operands[0].reg;
+ Rd = inst.operands[0].reg;
Rm = inst.operands[1].reg;
+ Rn = inst.operands[2].reg;
- if (inst.instruction == T_MNEM_cmp
- || inst.instruction == T_MNEM_cmn)
- constraint (Rn == REG_PC, BAD_PC);
- else
- reject_bad_reg (Rn);
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
reject_bad_reg (Rm);
- if (unified_syntax)
- {
- int r0off = (inst.instruction == T_MNEM_mvn
- || inst.instruction == T_MNEM_mvns) ? 8 : 16;
- bfd_boolean narrow;
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rn << 16;
+ inst.instruction |= Rm;
+}
- if (inst.size_req == 4
- || inst.instruction > 0xffff
- || inst.operands[1].shifted
- || Rn > 7 || Rm > 7)
- narrow = FALSE;
- else if (inst.instruction == T_MNEM_cmn)
- narrow = TRUE;
- else if (THUMB_SETS_FLAGS (inst.instruction))
- narrow = !in_it_block ();
- else
- narrow = in_it_block ();
+static void
+do_t_smc (void)
+{
+ unsigned int value = inst.reloc.exp.X_add_number;
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7a),
+ _("SMC is not permitted on this architecture"));
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ 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 ();
+}
- if (!inst.operands[1].isreg)
- {
- /* For an immediate, we always generate a 32-bit opcode;
- section relaxation will shrink it later if possible. */
- if (inst.instruction < 0xffff)
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
- inst.instruction |= Rn << r0off;
- inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
- }
- else
- {
- /* See if we can do this with a 16-bit instruction. */
- if (narrow)
- {
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= Rn;
- inst.instruction |= Rm << 3;
- }
- else
- {
- constraint (inst.operands[1].shifted
- && inst.operands[1].immisreg,
- _("shift must be constant"));
- if (inst.instruction < 0xffff)
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= Rn << r0off;
- encode_thumb32_shifted_operand (1);
- }
- }
- }
- else
- {
- constraint (inst.instruction > 0xffff
- || inst.instruction == T_MNEM_mvns, BAD_THUMB32);
- constraint (!inst.operands[1].isreg || inst.operands[1].shifted,
- _("unshifted register required"));
- constraint (Rn > 7 || Rm > 7,
- BAD_HIREG);
+static void
+do_t_hvc (void)
+{
+ unsigned int value = inst.reloc.exp.X_add_number;
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= Rn;
- inst.instruction |= Rm << 3;
- }
+ inst.reloc.type = BFD_RELOC_UNUSED;
+ inst.instruction |= (value & 0x0fff);
+ inst.instruction |= (value & 0xf000) << 4;
}
static void
-do_t_mrs (void)
+do_t_ssat_usat (int bias)
{
- unsigned Rd;
-
- if (do_vfp_nsyn_mrs () == SUCCESS)
- return;
+ unsigned Rd, Rn;
Rd = inst.operands[0].reg;
+ Rn = inst.operands[2].reg;
+
reject_bad_reg (Rd);
+ reject_bad_reg (Rn);
+
inst.instruction |= Rd << 8;
+ inst.instruction |= inst.operands[1].imm - bias;
+ inst.instruction |= Rn << 16;
- if (inst.operands[1].isreg)
+ if (inst.operands[3].present)
{
- unsigned br = inst.operands[1].reg;
- if (((br & 0x200) == 0) && ((br & 0xf000) != 0xf000))
- as_bad (_("bad register for mrs"));
+ offsetT shift_amount = inst.reloc.exp.X_add_number;
- inst.instruction |= br & (0xf << 16);
- inst.instruction |= (br & 0x300) >> 4;
- inst.instruction |= (br & SPSR_BIT) >> 2;
- }
- else
- {
- int flags = inst.operands[1].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT);
+ inst.reloc.type = BFD_RELOC_UNUSED;
- if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m))
- constraint (flags != 0, _("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). */
- constraint ((flags & ~SPSR_BIT) != (PSR_c|PSR_f),
- _("'APSR', 'CPSR' or 'SPSR' expected"));
+ constraint (inst.reloc.exp.X_op != O_constant,
+ _("expression too complex"));
+
+ if (shift_amount != 0)
+ {
+ constraint (shift_amount > 31,
+ _("shift expression is too large"));
- inst.instruction |= (flags & SPSR_BIT) >> 2;
- inst.instruction |= inst.operands[1].imm & 0xff;
- inst.instruction |= 0xf0000;
+ if (inst.operands[3].shift_kind == SHIFT_ASR)
+ inst.instruction |= 0x00200000; /* sh bit. */
+
+ inst.instruction |= (shift_amount & 0x1c) << 10;
+ inst.instruction |= (shift_amount & 0x03) << 6;
+ }
}
}
static void
-do_t_msr (void)
+do_t_ssat (void)
{
- int flags;
- unsigned Rn;
-
- if (do_vfp_nsyn_msr () == SUCCESS)
- return;
-
- constraint (!inst.operands[1].isreg,
- _("Thumb encoding does not support an immediate here"));
-
- if (inst.operands[0].isreg)
- flags = (int)(inst.operands[0].reg);
- else
- flags = inst.operands[0].imm;
+ do_t_ssat_usat (1);
+}
- if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_m))
- {
- int bits = inst.operands[0].imm & (PSR_c|PSR_x|PSR_s|PSR_f|SPSR_BIT);
+static void
+do_t_ssat16 (void)
+{
+ unsigned Rd, Rn;
- 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"));
- }
- else
- constraint ((flags & 0xff) != 0, _("selected processor does not support "
- "requested special purpose register"));
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[2].reg;
- Rn = inst.operands[1].reg;
+ reject_bad_reg (Rd);
reject_bad_reg (Rn);
- inst.instruction |= (flags & SPSR_BIT) >> 2;
- inst.instruction |= (flags & 0xf0000) >> 8;
- inst.instruction |= (flags & 0x300) >> 4;
- inst.instruction |= (flags & 0xff);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= inst.operands[1].imm - 1;
inst.instruction |= Rn << 16;
}
static void
-do_t_mul (void)
+do_t_strex (void)
{
- bfd_boolean narrow;
- unsigned Rd, Rn, Rm;
-
- if (!inst.operands[2].present)
- inst.operands[2].reg = inst.operands[0].reg;
+ 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);
- Rd = inst.operands[0].reg;
- Rn = inst.operands[1].reg;
- Rm = inst.operands[2].reg;
+ constraint (inst.operands[2].reg == REG_PC, BAD_PC);
- if (unified_syntax)
- {
- if (inst.size_req == 4
- || (Rd != Rn
- && Rd != Rm)
- || Rn > 7
- || Rm > 7)
- narrow = FALSE;
- else if (inst.instruction == T_MNEM_muls)
- narrow = !in_it_block ();
- else
- narrow = in_it_block ();
- }
- else
- {
- constraint (inst.instruction == T_MNEM_muls, BAD_THUMB32);
- constraint (Rn > 7 || Rm > 7,
- BAD_HIREG);
- narrow = TRUE;
- }
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+ inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_U8;
+}
- if (narrow)
- {
- /* 16-bit MULS/Conditional MUL. */
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= Rd;
+static void
+do_t_strexd (void)
+{
+ if (!inst.operands[2].present)
+ inst.operands[2].reg = inst.operands[1].reg + 1;
- if (Rd == Rn)
- inst.instruction |= Rm << 3;
- else if (Rd == Rm)
- inst.instruction |= Rn << 3;
- else
- constraint (1, _("dest must overlap one source register"));
- }
- else
- {
- constraint (inst.instruction != T_MNEM_mul,
- _("Thumb-2 MUL must not set flags"));
- /* 32-bit MUL. */
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= Rd << 8;
- inst.instruction |= Rn << 16;
- inst.instruction |= Rm << 0;
+ constraint (inst.operands[0].reg == inst.operands[1].reg
+ || inst.operands[0].reg == inst.operands[2].reg
+ || inst.operands[0].reg == inst.operands[3].reg,
+ BAD_OVERLAP);
- reject_bad_reg (Rd);
- reject_bad_reg (Rn);
- reject_bad_reg (Rm);
- }
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 8;
+ inst.instruction |= inst.operands[3].reg << 16;
}
static void
-do_t_mull (void)
+do_t_sxtah (void)
{
- unsigned RdLo, RdHi, Rn, Rm;
+ unsigned Rd, Rn, Rm;
- RdLo = inst.operands[0].reg;
- RdHi = inst.operands[1].reg;
- Rn = inst.operands[2].reg;
- Rm = inst.operands[3].reg;
+ Rd = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
+ Rm = inst.operands[2].reg;
- reject_bad_reg (RdLo);
- reject_bad_reg (RdHi);
+ reject_bad_reg (Rd);
reject_bad_reg (Rn);
reject_bad_reg (Rm);
- inst.instruction |= RdLo << 12;
- inst.instruction |= RdHi << 8;
+ inst.instruction |= Rd << 8;
inst.instruction |= Rn << 16;
inst.instruction |= Rm;
-
- if (RdLo == RdHi)
- as_tsktsk (_("rdhi and rdlo must be different"));
+ inst.instruction |= inst.operands[3].imm << 4;
}
static void
-do_t_nop (void)
+do_t_sxth (void)
{
- set_it_insn_type (NEUTRAL_IT_INSN);
+ unsigned Rd, Rm;
- if (unified_syntax)
+ Rd = inst.operands[0].reg;
+ Rm = inst.operands[1].reg;
+
+ reject_bad_reg (Rd);
+ reject_bad_reg (Rm);
+
+ if (inst.instruction <= 0xffff
+ && inst.size_req != 4
+ && Rd <= 7 && Rm <= 7
+ && (!inst.operands[2].present || inst.operands[2].imm == 0))
{
- if (inst.size_req == 4 || inst.operands[0].imm > 15)
- {
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= inst.operands[0].imm;
- }
- else
- {
- /* PR9722: Check for Thumb2 availability before
- generating a thumb2 nop instruction. */
- if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6t2))
- {
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= inst.operands[0].imm << 4;
- }
- else
- inst.instruction = 0x46c0;
- }
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= Rd;
+ inst.instruction |= Rm << 3;
+ }
+ else if (unified_syntax)
+ {
+ if (inst.instruction <= 0xffff)
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= Rd << 8;
+ inst.instruction |= Rm;
+ inst.instruction |= inst.operands[2].imm << 4;
}
else
{
- constraint (inst.operands[0].present,
- _("Thumb does not support NOP with hints"));
- inst.instruction = 0x46c0;
+ constraint (inst.operands[2].present && inst.operands[2].imm != 0,
+ _("Thumb encoding does not support rotation"));
+ constraint (1, BAD_HIREG);
}
}
static void
-do_t_neg (void)
+do_t_swi (void)
{
- if (unified_syntax)
+ /* We have to do the following check manually as ARM_EXT_OS only applies
+ to ARM_EXT_V6M. */
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6m))
{
- bfd_boolean narrow;
+ if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_os)
+ /* This only applies to the v6m howver, not later architectures. */
+ && ! ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7))
+ as_bad (_("SVC is not permitted on this architecture"));
+ ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, arm_ext_os);
+ }
- if (THUMB_SETS_FLAGS (inst.instruction))
- narrow = !in_it_block ();
- else
- narrow = in_it_block ();
- if (inst.operands[0].reg > 7 || inst.operands[1].reg > 7)
- narrow = FALSE;
- if (inst.size_req == 4)
- narrow = FALSE;
+ inst.reloc.type = BFD_RELOC_ARM_SWI;
+}
- if (!narrow)
- {
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= inst.operands[0].reg << 8;
- inst.instruction |= inst.operands[1].reg << 16;
- }
- else
- {
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= inst.operands[0].reg;
- inst.instruction |= inst.operands[1].reg << 3;
- }
- }
- else
- {
- constraint (inst.operands[0].reg > 7 || inst.operands[1].reg > 7,
- BAD_HIREG);
- constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
+static void
+do_t_tb (void)
+{
+ unsigned Rn, Rm;
+ int half;
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= inst.operands[0].reg;
- inst.instruction |= inst.operands[1].reg << 3;
- }
+ half = (inst.instruction & 0x10) != 0;
+ set_it_insn_type_last ();
+ constraint (inst.operands[0].immisreg,
+ _("instruction requires register index"));
+
+ Rn = inst.operands[0].reg;
+ Rm = inst.operands[0].imm;
+
+ constraint (Rn == REG_SP, BAD_SP);
+ reject_bad_reg (Rm);
+
+ constraint (!half && inst.operands[0].shifted,
+ _("instruction does not allow shifted index"));
+ inst.instruction |= (Rn << 16) | Rm;
}
static void
-do_t_orn (void)
+do_t_udf (void)
{
- unsigned Rd, Rn;
-
- Rd = inst.operands[0].reg;
- Rn = inst.operands[1].present ? inst.operands[1].reg : Rd;
-
- reject_bad_reg (Rd);
- /* Rn == REG_SP is unpredictable; Rn == REG_PC is MVN. */
- reject_bad_reg (Rn);
-
- inst.instruction |= Rd << 8;
- inst.instruction |= Rn << 16;
+ if (!inst.operands[0].present)
+ inst.operands[0].imm = 0;
- if (!inst.operands[2].isreg)
+ if ((unsigned int) inst.operands[0].imm > 255 || inst.size_req == 4)
{
- inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
- inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ 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
{
- unsigned Rm;
+ inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction |= inst.operands[0].imm;
+ }
- Rm = inst.operands[2].reg;
- reject_bad_reg (Rm);
+ set_it_insn_type (NEUTRAL_IT_INSN);
+}
- constraint (inst.operands[2].shifted
- && inst.operands[2].immisreg,
- _("shift must be constant"));
- encode_thumb32_shifted_operand (2);
- }
+
+static void
+do_t_usat (void)
+{
+ do_t_ssat_usat (0);
}
static void
-do_t_pkhbt (void)
+do_t_usat16 (void)
{
- unsigned Rd, Rn, Rm;
+ unsigned Rd, Rn;
Rd = inst.operands[0].reg;
- Rn = inst.operands[1].reg;
- Rm = inst.operands[2].reg;
+ Rn = inst.operands[2].reg;
reject_bad_reg (Rd);
reject_bad_reg (Rn);
- reject_bad_reg (Rm);
inst.instruction |= Rd << 8;
+ inst.instruction |= inst.operands[1].imm;
inst.instruction |= Rn << 16;
- inst.instruction |= Rm;
- if (inst.operands[3].present)
- {
- unsigned int val = inst.reloc.exp.X_add_number;
- constraint (inst.reloc.exp.X_op != O_constant,
- _("expression too complex"));
- inst.instruction |= (val & 0x1c) << 10;
- inst.instruction |= (val & 0x03) << 6;
- }
}
-static void
-do_t_pkhtb (void)
-{
- if (!inst.operands[3].present)
- {
- unsigned Rtmp;
+/* Neon instruction encoder helpers. */
- inst.instruction &= ~0x00000020;
+/* Encodings for the different types for various Neon opcodes. */
- /* PR 10168. Swap the Rm and Rn registers. */
- Rtmp = inst.operands[1].reg;
- inst.operands[1].reg = inst.operands[2].reg;
- inst.operands[2].reg = Rtmp;
- }
- do_t_pkhbt ();
-}
+/* An "invalid" code for the following tables. */
+#define N_INV -1u
-static void
-do_t_pld (void)
+struct neon_tab_entry
{
- if (inst.operands[0].immisreg)
- reject_bad_reg (inst.operands[0].imm);
+ unsigned integer;
+ unsigned float_or_poly;
+ unsigned scalar_or_imm;
+};
- encode_thumb32_addr_mode (0, /*is_t=*/FALSE, /*is_d=*/FALSE);
-}
+/* Map overloaded Neon opcodes to their respective encodings. */
+#define NEON_ENC_TAB \
+ X(vabd, 0x0000700, 0x1200d00, N_INV), \
+ X(vmax, 0x0000600, 0x0000f00, N_INV), \
+ X(vmin, 0x0000610, 0x0200f00, N_INV), \
+ X(vpadd, 0x0000b10, 0x1000d00, N_INV), \
+ X(vpmax, 0x0000a00, 0x1000f00, N_INV), \
+ X(vpmin, 0x0000a10, 0x1200f00, N_INV), \
+ X(vadd, 0x0000800, 0x0000d00, N_INV), \
+ X(vsub, 0x1000800, 0x0200d00, N_INV), \
+ X(vceq, 0x1000810, 0x0000e00, 0x1b10100), \
+ X(vcge, 0x0000310, 0x1000e00, 0x1b10080), \
+ X(vcgt, 0x0000300, 0x1200e00, 0x1b10000), \
+ /* Register variants of the following two instructions are encoded as
+ vcge / vcgt with the operands reversed. */ \
+ X(vclt, 0x0000300, 0x1200e00, 0x1b10200), \
+ X(vcle, 0x0000310, 0x1000e00, 0x1b10180), \
+ X(vfma, N_INV, 0x0000c10, N_INV), \
+ X(vfms, N_INV, 0x0200c10, N_INV), \
+ X(vmla, 0x0000900, 0x0000d10, 0x0800040), \
+ X(vmls, 0x1000900, 0x0200d10, 0x0800440), \
+ X(vmul, 0x0000910, 0x1000d10, 0x0800840), \
+ X(vmull, 0x0800c00, 0x0800e00, 0x0800a40), /* polynomial not float. */ \
+ X(vmlal, 0x0800800, N_INV, 0x0800240), \
+ X(vmlsl, 0x0800a00, N_INV, 0x0800640), \
+ X(vqdmlal, 0x0800900, N_INV, 0x0800340), \
+ X(vqdmlsl, 0x0800b00, N_INV, 0x0800740), \
+ X(vqdmull, 0x0800d00, N_INV, 0x0800b40), \
+ X(vqdmulh, 0x0000b00, N_INV, 0x0800c40), \
+ X(vqrdmulh, 0x1000b00, N_INV, 0x0800d40), \
+ X(vqrdmlah, 0x3000b10, N_INV, 0x0800e40), \
+ X(vqrdmlsh, 0x3000c10, N_INV, 0x0800f40), \
+ X(vshl, 0x0000400, N_INV, 0x0800510), \
+ X(vqshl, 0x0000410, N_INV, 0x0800710), \
+ X(vand, 0x0000110, N_INV, 0x0800030), \
+ X(vbic, 0x0100110, N_INV, 0x0800030), \
+ X(veor, 0x1000110, N_INV, N_INV), \
+ X(vorn, 0x0300110, N_INV, 0x0800010), \
+ X(vorr, 0x0200110, N_INV, 0x0800010), \
+ X(vmvn, 0x1b00580, N_INV, 0x0800030), \
+ X(vshll, 0x1b20300, N_INV, 0x0800a10), /* max shift, immediate. */ \
+ X(vcvt, 0x1b30600, N_INV, 0x0800e10), /* integer, fixed-point. */ \
+ X(vdup, 0xe800b10, N_INV, 0x1b00c00), /* arm, scalar. */ \
+ X(vld1, 0x0200000, 0x0a00000, 0x0a00c00), /* interlv, lane, dup. */ \
+ X(vst1, 0x0000000, 0x0800000, N_INV), \
+ X(vld2, 0x0200100, 0x0a00100, 0x0a00d00), \
+ X(vst2, 0x0000100, 0x0800100, N_INV), \
+ X(vld3, 0x0200200, 0x0a00200, 0x0a00e00), \
+ X(vst3, 0x0000200, 0x0800200, N_INV), \
+ X(vld4, 0x0200300, 0x0a00300, 0x0a00f00), \
+ X(vst4, 0x0000300, 0x0800300, N_INV), \
+ X(vmovn, 0x1b20200, N_INV, N_INV), \
+ X(vtrn, 0x1b20080, N_INV, N_INV), \
+ X(vqmovn, 0x1b20200, N_INV, N_INV), \
+ X(vqmovun, 0x1b20240, N_INV, N_INV), \
+ X(vnmul, 0xe200a40, 0xe200b40, N_INV), \
+ X(vnmla, 0xe100a40, 0xe100b40, N_INV), \
+ X(vnmls, 0xe100a00, 0xe100b00, N_INV), \
+ X(vfnma, 0xe900a40, 0xe900b40, N_INV), \
+ X(vfnms, 0xe900a00, 0xe900b00, N_INV), \
+ 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(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)
-static void
-do_t_push_pop (void)
+enum neon_opc
{
- unsigned mask;
+#define X(OPC,I,F,S) N_MNEM_##OPC
+NEON_ENC_TAB
+#undef X
+};
- constraint (inst.operands[0].writeback,
- _("push/pop do not support {reglist}^"));
- constraint (inst.reloc.type != BFD_RELOC_UNUSED,
- _("expression too complex"));
+static const struct neon_tab_entry neon_enc_tab[] =
+{
+#define X(OPC,I,F,S) { (I), (F), (S) }
+NEON_ENC_TAB
+#undef X
+};
- mask = inst.operands[0].imm;
- if ((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))
- {
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= THUMB_PP_PC_LR;
- inst.instruction |= mask & 0xff;
- }
- else if (unified_syntax)
- {
- inst.instruction = THUMB_OP32 (inst.instruction);
- encode_thumb2_ldmstm (13, mask, TRUE);
- }
- else
- {
- inst.error = _("invalid register list to push/pop instruction");
- return;
- }
-}
+/* Do not use these macros; instead, use NEON_ENCODE defined below. */
+#define NEON_ENC_INTEGER_(X) (neon_enc_tab[(X) & 0x0fffffff].integer)
+#define NEON_ENC_ARMREG_(X) (neon_enc_tab[(X) & 0x0fffffff].integer)
+#define NEON_ENC_POLY_(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly)
+#define NEON_ENC_FLOAT_(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly)
+#define NEON_ENC_SCALAR_(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm)
+#define NEON_ENC_IMMED_(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm)
+#define NEON_ENC_INTERLV_(X) (neon_enc_tab[(X) & 0x0fffffff].integer)
+#define NEON_ENC_LANE_(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly)
+#define NEON_ENC_DUP_(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm)
+#define NEON_ENC_SINGLE_(X) \
+ ((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))
-static void
-do_t_rbit (void)
-{
- unsigned Rd, Rm;
+#define NEON_ENCODE(type, inst) \
+ do \
+ { \
+ inst.instruction = NEON_ENC_##type##_ (inst.instruction); \
+ inst.is_neon = 1; \
+ } \
+ while (0)
- Rd = inst.operands[0].reg;
- Rm = inst.operands[1].reg;
+#define check_neon_suffixes \
+ do \
+ { \
+ if (!inst.error && inst.vectype.elems > 0 && !inst.is_neon) \
+ { \
+ as_bad (_("invalid neon suffix for non neon instruction")); \
+ return; \
+ } \
+ } \
+ while (0)
+
+/* Define shapes for instruction operands. The following mnemonic characters
+ are used in this table:
+
+ F - VFP S<n> register
+ D - Neon D<n> register
+ Q - Neon Q<n> register
+ I - Immediate
+ S - Scalar
+ R - ARM register
+ L - D<n> register list
+
+ This table is used to generate various data:
+ - enumerations of the form NS_DDR to be used as arguments to
+ neon_select_shape.
+ - a table classifying shapes into single, double, quad, mixed.
+ - a table used to drive neon_select_shape. */
+
+#define NEON_SHAPE_DEF \
+ X(3, (D, D, D), DOUBLE), \
+ X(3, (Q, Q, Q), QUAD), \
+ X(3, (D, D, I), DOUBLE), \
+ X(3, (Q, Q, I), QUAD), \
+ X(3, (D, D, S), DOUBLE), \
+ X(3, (Q, Q, S), QUAD), \
+ X(2, (D, D), DOUBLE), \
+ X(2, (Q, Q), QUAD), \
+ X(2, (D, S), DOUBLE), \
+ X(2, (Q, S), QUAD), \
+ X(2, (D, R), DOUBLE), \
+ X(2, (Q, R), QUAD), \
+ X(2, (D, I), DOUBLE), \
+ X(2, (Q, I), QUAD), \
+ X(3, (D, L, D), DOUBLE), \
+ X(2, (D, Q), MIXED), \
+ X(2, (Q, D), MIXED), \
+ X(3, (D, Q, I), MIXED), \
+ X(3, (Q, D, I), MIXED), \
+ X(3, (Q, D, D), MIXED), \
+ X(3, (D, Q, Q), MIXED), \
+ X(3, (Q, Q, D), MIXED), \
+ X(3, (Q, D, S), MIXED), \
+ X(3, (D, Q, S), MIXED), \
+ X(4, (D, D, D, I), DOUBLE), \
+ X(4, (Q, Q, Q, I), QUAD), \
+ X(2, (F, F), SINGLE), \
+ X(3, (F, F, F), SINGLE), \
+ X(2, (F, I), SINGLE), \
+ X(2, (F, D), MIXED), \
+ X(2, (D, F), MIXED), \
+ X(3, (F, F, I), MIXED), \
+ X(4, (R, R, F, F), SINGLE), \
+ X(4, (F, F, R, R), SINGLE), \
+ X(3, (D, R, R), DOUBLE), \
+ X(3, (R, R, D), DOUBLE), \
+ X(2, (S, R), SINGLE), \
+ X(2, (R, S), SINGLE), \
+ X(2, (F, R), SINGLE), \
+ X(2, (R, F), SINGLE), \
+/* Half float shape supported so far. */\
+ X (2, (H, D), MIXED), \
+ X (2, (D, H), MIXED), \
+ X (2, (H, F), MIXED), \
+ X (2, (F, H), MIXED), \
+ X (2, (H, H), HALF), \
+ X (2, (H, R), HALF), \
+ X (2, (R, H), HALF), \
+ X (2, (H, I), HALF), \
+ X (3, (H, H, H), HALF), \
+ X (3, (H, F, I), MIXED), \
+ X (3, (F, H, I), MIXED)
- reject_bad_reg (Rd);
- reject_bad_reg (Rm);
+#define S2(A,B) NS_##A##B
+#define S3(A,B,C) NS_##A##B##C
+#define S4(A,B,C,D) NS_##A##B##C##D
- inst.instruction |= Rd << 8;
- inst.instruction |= Rm << 16;
- inst.instruction |= Rm;
-}
+#define X(N, L, C) S##N L
-static void
-do_t_rev (void)
+enum neon_shape
{
- unsigned Rd, Rm;
-
- Rd = inst.operands[0].reg;
- Rm = inst.operands[1].reg;
-
- reject_bad_reg (Rd);
- reject_bad_reg (Rm);
+ NEON_SHAPE_DEF,
+ NS_NULL
+};
- if (Rd <= 7 && Rm <= 7
- && inst.size_req != 4)
- {
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= Rd;
- inst.instruction |= Rm << 3;
- }
- else if (unified_syntax)
- {
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= Rd << 8;
- inst.instruction |= Rm << 16;
- inst.instruction |= Rm;
- }
- else
- inst.error = BAD_HIREG;
-}
+#undef X
+#undef S2
+#undef S3
+#undef S4
-static void
-do_t_rrx (void)
+enum neon_shape_class
{
- unsigned Rd, Rm;
-
- Rd = inst.operands[0].reg;
- Rm = inst.operands[1].reg;
-
- reject_bad_reg (Rd);
- reject_bad_reg (Rm);
+ SC_HALF,
+ SC_SINGLE,
+ SC_DOUBLE,
+ SC_QUAD,
+ SC_MIXED
+};
- inst.instruction |= Rd << 8;
- inst.instruction |= Rm;
-}
+#define X(N, L, C) SC_##C
-static void
-do_t_rsb (void)
+static enum neon_shape_class neon_shape_class[] =
{
- unsigned Rd, Rs;
+ NEON_SHAPE_DEF
+};
- Rd = inst.operands[0].reg;
- Rs = (inst.operands[1].present
- ? inst.operands[1].reg /* Rd, Rs, foo */
- : inst.operands[0].reg); /* Rd, foo -> Rd, Rd, foo */
+#undef X
- reject_bad_reg (Rd);
- reject_bad_reg (Rs);
- if (inst.operands[2].isreg)
- reject_bad_reg (inst.operands[2].reg);
+enum neon_shape_el
+{
+ SE_H,
+ SE_F,
+ SE_D,
+ SE_Q,
+ SE_I,
+ SE_S,
+ SE_R,
+ SE_L
+};
- inst.instruction |= Rd << 8;
- inst.instruction |= Rs << 16;
- if (!inst.operands[2].isreg)
- {
- bfd_boolean narrow;
+/* Register widths of above. */
+static unsigned neon_shape_el_size[] =
+{
+ 16,
+ 32,
+ 64,
+ 128,
+ 0,
+ 32,
+ 32,
+ 0
+};
- if ((inst.instruction & 0x00100000) != 0)
- narrow = !in_it_block ();
- else
- narrow = in_it_block ();
+struct neon_shape_info
+{
+ unsigned els;
+ enum neon_shape_el el[NEON_MAX_TYPE_ELS];
+};
- if (Rd > 7 || Rs > 7)
- narrow = FALSE;
+#define S2(A,B) { SE_##A, SE_##B }
+#define S3(A,B,C) { SE_##A, SE_##B, SE_##C }
+#define S4(A,B,C,D) { SE_##A, SE_##B, SE_##C, SE_##D }
- if (inst.size_req == 4 || !unified_syntax)
- narrow = FALSE;
+#define X(N, L, C) { N, S##N L }
- if (inst.reloc.exp.X_op != O_constant
- || inst.reloc.exp.X_add_number != 0)
- narrow = FALSE;
+static struct neon_shape_info neon_shape_tab[] =
+{
+ NEON_SHAPE_DEF
+};
- /* Turn rsb #0 into 16-bit neg. We should probably do this via
- relaxation, but it doesn't seem worth the hassle. */
- if (narrow)
- {
- inst.reloc.type = BFD_RELOC_UNUSED;
- inst.instruction = THUMB_OP16 (T_MNEM_negs);
- inst.instruction |= Rs << 3;
- inst.instruction |= Rd;
- }
- else
- {
- inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
- inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
- }
- }
- else
- encode_thumb32_shifted_operand (2);
-}
+#undef X
+#undef S2
+#undef S3
+#undef S4
-static void
-do_t_setend (void)
-{
- set_it_insn_type (OUTSIDE_IT_INSN);
- if (inst.operands[0].imm)
- inst.instruction |= 0x8;
-}
+/* Bit masks used in type checking given instructions.
+ 'N_EQK' means the type must be the same as (or based on in some way) the key
+ type, which itself is marked with the 'N_KEY' bit. If the 'N_EQK' bit is
+ set, various other bits can be set as well in order to modify the meaning of
+ the type constraint. */
-static void
-do_t_shift (void)
+enum neon_type_mask
{
- if (!inst.operands[1].present)
- inst.operands[1].reg = inst.operands[0].reg;
-
- if (unified_syntax)
- {
- bfd_boolean narrow;
- int shift_kind;
+ N_S8 = 0x0000001,
+ N_S16 = 0x0000002,
+ N_S32 = 0x0000004,
+ N_S64 = 0x0000008,
+ N_U8 = 0x0000010,
+ N_U16 = 0x0000020,
+ N_U32 = 0x0000040,
+ N_U64 = 0x0000080,
+ N_I8 = 0x0000100,
+ N_I16 = 0x0000200,
+ N_I32 = 0x0000400,
+ N_I64 = 0x0000800,
+ N_8 = 0x0001000,
+ N_16 = 0x0002000,
+ N_32 = 0x0004000,
+ N_64 = 0x0008000,
+ N_P8 = 0x0010000,
+ N_P16 = 0x0020000,
+ 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_UNS = 0x0000008, /* If N_EQK, this operand is forced to be unsigned. */
+ N_INT = 0x0000010, /* If N_EQK, this operand is forced to be integer. */
+ 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_P64
+};
- switch (inst.instruction)
- {
- case T_MNEM_asr:
- case T_MNEM_asrs: shift_kind = SHIFT_ASR; break;
- case T_MNEM_lsl:
- case T_MNEM_lsls: shift_kind = SHIFT_LSL; break;
- case T_MNEM_lsr:
- case T_MNEM_lsrs: shift_kind = SHIFT_LSR; break;
- case T_MNEM_ror:
- case T_MNEM_rors: shift_kind = SHIFT_ROR; break;
- default: abort ();
- }
+#define N_ALLMODS (N_DBL | N_HLF | N_SGN | N_UNS | N_INT | N_FLT | N_SIZ)
- if (THUMB_SETS_FLAGS (inst.instruction))
- narrow = !in_it_block ();
- else
- narrow = in_it_block ();
- if (inst.operands[0].reg > 7 || inst.operands[1].reg > 7)
- narrow = FALSE;
- if (!inst.operands[2].isreg && shift_kind == SHIFT_ROR)
- narrow = FALSE;
- if (inst.operands[2].isreg
- && (inst.operands[1].reg != inst.operands[0].reg
- || inst.operands[2].reg > 7))
- narrow = FALSE;
- if (inst.size_req == 4)
- narrow = FALSE;
+#define N_SU_ALL (N_S8 | N_S16 | N_S32 | N_S64 | N_U8 | N_U16 | N_U32 | N_U64)
+#define N_SU_32 (N_S8 | N_S16 | N_S32 | N_U8 | N_U16 | N_U32)
+#define N_SU_16_64 (N_S16 | N_S32 | N_S64 | N_U16 | N_U32 | N_U64)
+#define N_S_32 (N_S8 | N_S16 | N_S32)
+#define N_F_16_32 (N_F16 | N_F32)
+#define N_SUF_32 (N_SU_32 | N_F_16_32)
+#define N_I_ALL (N_I8 | N_I16 | N_I32 | N_I64)
+#define N_IF_32 (N_I8 | N_I16 | N_I32 | N_F16 | N_F32)
+#define N_F_ALL (N_F16 | N_F32 | N_F64)
- reject_bad_reg (inst.operands[0].reg);
- reject_bad_reg (inst.operands[1].reg);
+/* Pass this as the first type argument to neon_check_type to ignore types
+ altogether. */
+#define N_IGNORE_TYPE (N_KEY | N_EQK)
- if (!narrow)
- {
- if (inst.operands[2].isreg)
- {
- reject_bad_reg (inst.operands[2].reg);
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= inst.operands[0].reg << 8;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.instruction |= inst.operands[2].reg;
- }
- else
- {
- inst.operands[1].shifted = 1;
- inst.operands[1].shift_kind = shift_kind;
- inst.instruction = THUMB_OP32 (THUMB_SETS_FLAGS (inst.instruction)
- ? T_MNEM_movs : T_MNEM_mov);
- inst.instruction |= inst.operands[0].reg << 8;
- encode_thumb32_shifted_operand (1);
- /* Prevent the incorrect generation of an ARM_IMMEDIATE fixup. */
- inst.reloc.type = BFD_RELOC_UNUSED;
- }
- }
- else
- {
- if (inst.operands[2].isreg)
- {
- switch (shift_kind)
- {
- case SHIFT_ASR: inst.instruction = T_OPCODE_ASR_R; break;
- case SHIFT_LSL: inst.instruction = T_OPCODE_LSL_R; break;
- case SHIFT_LSR: inst.instruction = T_OPCODE_LSR_R; break;
- case SHIFT_ROR: inst.instruction = T_OPCODE_ROR_R; break;
- default: abort ();
- }
+/* Select a "shape" for the current instruction (describing register types or
+ sizes) from a list of alternatives. Return NS_NULL if the current instruction
+ doesn't fit. For non-polymorphic shapes, checking is usually done as a
+ function of operand parsing, so this function doesn't need to be called.
+ Shapes should be listed in order of decreasing length. */
- inst.instruction |= inst.operands[0].reg;
- inst.instruction |= inst.operands[2].reg << 3;
- }
- else
- {
- switch (shift_kind)
- {
- case SHIFT_ASR: inst.instruction = T_OPCODE_ASR_I; break;
- case SHIFT_LSL: inst.instruction = T_OPCODE_LSL_I; break;
- case SHIFT_LSR: inst.instruction = T_OPCODE_LSR_I; break;
- default: abort ();
- }
- inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
- inst.instruction |= inst.operands[0].reg;
- inst.instruction |= inst.operands[1].reg << 3;
- }
- }
- }
- else
+static enum neon_shape
+neon_select_shape (enum neon_shape shape, ...)
+{
+ va_list ap;
+ enum neon_shape first_shape = shape;
+
+ /* Fix missing optional operands. FIXME: we don't know at this point how
+ many arguments we should have, so this makes the assumption that we have
+ > 1. This is true of all current Neon opcodes, I think, but may not be
+ true in the future. */
+ if (!inst.operands[1].present)
+ inst.operands[1] = inst.operands[0];
+
+ va_start (ap, shape);
+
+ for (; shape != NS_NULL; shape = (enum neon_shape) va_arg (ap, int))
{
- constraint (inst.operands[0].reg > 7
- || inst.operands[1].reg > 7, BAD_HIREG);
- constraint (THUMB_SETS_FLAGS (inst.instruction), BAD_THUMB32);
+ unsigned j;
+ int matches = 1;
- if (inst.operands[2].isreg) /* Rd, {Rs,} Rn */
+ for (j = 0; j < neon_shape_tab[shape].els; j++)
{
- constraint (inst.operands[2].reg > 7, BAD_HIREG);
- constraint (inst.operands[0].reg != inst.operands[1].reg,
- _("source1 and dest must be same register"));
-
- switch (inst.instruction)
+ if (!inst.operands[j].present)
{
- case T_MNEM_asr: inst.instruction = T_OPCODE_ASR_R; break;
- case T_MNEM_lsl: inst.instruction = T_OPCODE_LSL_R; break;
- case T_MNEM_lsr: inst.instruction = T_OPCODE_LSR_R; break;
- case T_MNEM_ror: inst.instruction = T_OPCODE_ROR_R; break;
- default: abort ();
+ matches = 0;
+ break;
}
- inst.instruction |= inst.operands[0].reg;
- inst.instruction |= inst.operands[2].reg << 3;
- }
- else
- {
- switch (inst.instruction)
+ switch (neon_shape_tab[shape].el[j])
{
- case T_MNEM_asr: inst.instruction = T_OPCODE_ASR_I; break;
- case T_MNEM_lsl: inst.instruction = T_OPCODE_LSL_I; break;
- case T_MNEM_lsr: inst.instruction = T_OPCODE_LSR_I; break;
- case T_MNEM_ror: inst.error = _("ror #imm not supported"); return;
- default: abort ();
- }
- inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
- inst.instruction |= inst.operands[0].reg;
- inst.instruction |= inst.operands[1].reg << 3;
- }
- }
-}
+ /* If a .f16, .16, .u16, .s16 type specifier is given over
+ a VFP single precision register operand, it's essentially
+ means only half of the register is used.
+
+ If the type specifier is given after the mnemonics, the
+ information is stored in inst.vectype. If the type specifier
+ is given after register operand, the information is stored
+ in inst.operands[].vectype.
+
+ When there is only one type specifier, and all the register
+ operands are the same type of hardware register, the type
+ specifier applies to all register operands.
+
+ If no type specifier is given, the shape is inferred from
+ operand information.
+
+ for example:
+ vadd.f16 s0, s1, s2: NS_HHH
+ vabs.f16 s0, s1: NS_HH
+ vmov.f16 s0, r1: NS_HR
+ vmov.f16 r0, s1: NS_RH
+ vcvt.f16 r0, s1: NS_RH
+ vcvt.f16.s32 s2, s2, #29: NS_HFI
+ vcvt.f16.s32 s2, s2: NS_HF
+ */
+ case SE_H:
+ if (!(inst.operands[j].isreg
+ && inst.operands[j].isvec
+ && inst.operands[j].issingle
+ && !inst.operands[j].isquad
+ && ((inst.vectype.elems == 1
+ && inst.vectype.el[0].size == 16)
+ || (inst.vectype.elems > 1
+ && inst.vectype.el[j].size == 16)
+ || (inst.vectype.elems == 0
+ && inst.operands[j].vectype.type != NT_invtype
+ && inst.operands[j].vectype.size == 16))))
+ matches = 0;
+ break;
-static void
-do_t_simd (void)
-{
- unsigned Rd, Rn, Rm;
+ case SE_F:
+ if (!(inst.operands[j].isreg
+ && inst.operands[j].isvec
+ && inst.operands[j].issingle
+ && !inst.operands[j].isquad
+ && ((inst.vectype.elems == 1 && inst.vectype.el[0].size == 32)
+ || (inst.vectype.elems > 1 && inst.vectype.el[j].size == 32)
+ || (inst.vectype.elems == 0
+ && (inst.operands[j].vectype.size == 32
+ || inst.operands[j].vectype.type == NT_invtype)))))
+ matches = 0;
+ break;
- Rd = inst.operands[0].reg;
- Rn = inst.operands[1].reg;
- Rm = inst.operands[2].reg;
+ case SE_D:
+ if (!(inst.operands[j].isreg
+ && inst.operands[j].isvec
+ && !inst.operands[j].isquad
+ && !inst.operands[j].issingle))
+ matches = 0;
+ break;
- reject_bad_reg (Rd);
- reject_bad_reg (Rn);
- reject_bad_reg (Rm);
+ case SE_R:
+ if (!(inst.operands[j].isreg
+ && !inst.operands[j].isvec))
+ matches = 0;
+ break;
- inst.instruction |= Rd << 8;
- inst.instruction |= Rn << 16;
- inst.instruction |= Rm;
-}
+ case SE_Q:
+ if (!(inst.operands[j].isreg
+ && inst.operands[j].isvec
+ && inst.operands[j].isquad
+ && !inst.operands[j].issingle))
+ matches = 0;
+ break;
-static void
-do_t_simd2 (void)
-{
- unsigned Rd, Rn, Rm;
+ case SE_I:
+ if (!(!inst.operands[j].isreg
+ && !inst.operands[j].isscalar))
+ matches = 0;
+ break;
- Rd = inst.operands[0].reg;
- Rm = inst.operands[1].reg;
- Rn = inst.operands[2].reg;
+ case SE_S:
+ if (!(!inst.operands[j].isreg
+ && inst.operands[j].isscalar))
+ matches = 0;
+ break;
- reject_bad_reg (Rd);
- reject_bad_reg (Rn);
- reject_bad_reg (Rm);
+ 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;
+ }
- inst.instruction |= Rd << 8;
- inst.instruction |= Rn << 16;
- inst.instruction |= Rm;
+ va_end (ap);
+
+ if (shape == NS_NULL && first_shape != NS_NULL)
+ first_error (_("invalid instruction shape"));
+
+ return shape;
}
-static void
-do_t_smc (void)
+/* True if SHAPE is predominantly a quadword operation (most of the time, this
+ means the Q bit should be set). */
+
+static int
+neon_quad (enum neon_shape shape)
{
- unsigned int value = inst.reloc.exp.X_add_number;
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7a),
- _("SMC is not permitted on this architecture"));
- constraint (inst.reloc.exp.X_op != O_constant,
- _("expression too complex"));
- inst.reloc.type = BFD_RELOC_UNUSED;
- inst.instruction |= (value & 0xf000) >> 12;
- inst.instruction |= (value & 0x0ff0);
- inst.instruction |= (value & 0x000f) << 16;
+ return neon_shape_class[shape] == SC_QUAD;
}
static void
-do_t_hvc (void)
+neon_modify_type_size (unsigned typebits, enum neon_el_type *g_type,
+ unsigned *g_size)
{
- unsigned int value = inst.reloc.exp.X_add_number;
-
- inst.reloc.type = BFD_RELOC_UNUSED;
- inst.instruction |= (value & 0x0fff);
- inst.instruction |= (value & 0xf000) << 4;
+ /* 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_EQK) != 0)
+ {
+ if ((typebits & N_HLF) != 0)
+ *g_size /= 2;
+ else if ((typebits & N_DBL) != 0)
+ *g_size *= 2;
+ if ((typebits & N_SGN) != 0)
+ *g_type = NT_signed;
+ else if ((typebits & N_UNS) != 0)
+ *g_type = NT_unsigned;
+ else if ((typebits & N_INT) != 0)
+ *g_type = NT_integer;
+ else if ((typebits & N_FLT) != 0)
+ *g_type = NT_float;
+ else if ((typebits & N_SIZ) != 0)
+ *g_type = NT_untyped;
+ }
}
-static void
-do_t_ssat_usat (int bias)
+/* Return operand OPNO promoted by bits set in THISARG. KEY should be the "key"
+ operand type, i.e. the single type specified in a Neon instruction when it
+ is the only one given. */
+
+static struct neon_type_el
+neon_type_promote (struct neon_type_el *key, unsigned thisarg)
{
- unsigned Rd, Rn;
+ struct neon_type_el dest = *key;
- Rd = inst.operands[0].reg;
- Rn = inst.operands[2].reg;
+ gas_assert ((thisarg & N_EQK) != 0);
- reject_bad_reg (Rd);
- reject_bad_reg (Rn);
+ neon_modify_type_size (thisarg, &dest.type, &dest.size);
- inst.instruction |= Rd << 8;
- inst.instruction |= inst.operands[1].imm - bias;
- inst.instruction |= Rn << 16;
+ return dest;
+}
- if (inst.operands[3].present)
+/* Convert Neon type and size into compact bitmask representation. */
+
+static enum neon_type_mask
+type_chk_of_el_type (enum neon_el_type type, unsigned size)
+{
+ switch (type)
{
- offsetT shift_amount = inst.reloc.exp.X_add_number;
+ 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: ;
+ }
+ break;
- inst.reloc.type = BFD_RELOC_UNUSED;
+ 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: ;
+ }
+ break;
- constraint (inst.reloc.exp.X_op != O_constant,
- _("expression too complex"));
+ case NT_float:
+ switch (size)
+ {
+ case 16: return N_F16;
+ case 32: return N_F32;
+ case 64: return N_F64;
+ default: ;
+ }
+ break;
- if (shift_amount != 0)
+ case NT_poly:
+ switch (size)
{
- constraint (shift_amount > 31,
- _("shift expression is too large"));
+ case 8: return N_P8;
+ case 16: return N_P16;
+ case 64: return N_P64;
+ default: ;
+ }
+ break;
- if (inst.operands[3].shift_kind == SHIFT_ASR)
- inst.instruction |= 0x00200000; /* sh bit. */
+ 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: ;
+ }
+ break;
- inst.instruction |= (shift_amount & 0x1c) << 10;
- inst.instruction |= (shift_amount & 0x03) << 6;
+ 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: ;
}
+ break;
+
+ default: ;
}
-}
-static void
-do_t_ssat (void)
-{
- do_t_ssat_usat (1);
+ return N_UTYP;
}
-static void
-do_t_ssat16 (void)
-{
- unsigned Rd, Rn;
-
- Rd = inst.operands[0].reg;
- Rn = inst.operands[2].reg;
-
- reject_bad_reg (Rd);
- reject_bad_reg (Rn);
-
- inst.instruction |= Rd << 8;
- inst.instruction |= inst.operands[1].imm - 1;
- inst.instruction |= Rn << 16;
-}
+/* Convert compact Neon bitmask type representation to a type and size. Only
+ handles the case where a single bit is set in the mask. */
-static void
-do_t_strex (void)
+static int
+el_type_of_type_chk (enum neon_el_type *type, unsigned *size,
+ enum neon_type_mask mask)
{
- 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[2].reg == REG_PC, BAD_PC);
-
- inst.instruction |= inst.operands[0].reg << 8;
- inst.instruction |= inst.operands[1].reg << 12;
- inst.instruction |= inst.operands[2].reg << 16;
- inst.reloc.type = BFD_RELOC_ARM_T32_OFFSET_U8;
-}
+ if ((mask & N_EQK) != 0)
+ return FAIL;
-static void
-do_t_strexd (void)
-{
- if (!inst.operands[2].present)
- inst.operands[2].reg = inst.operands[1].reg + 1;
+ 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_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 | N_P64)) != 0)
+ *size = 64;
+ else
+ return FAIL;
- constraint (inst.operands[0].reg == inst.operands[1].reg
- || inst.operands[0].reg == inst.operands[2].reg
- || inst.operands[0].reg == inst.operands[3].reg,
- BAD_OVERLAP);
+ if ((mask & (N_S8 | N_S16 | N_S32 | N_S64)) != 0)
+ *type = NT_signed;
+ else if ((mask & (N_U8 | N_U16 | N_U32 | N_U64)) != 0)
+ *type = NT_unsigned;
+ else if ((mask & (N_I8 | N_I16 | N_I32 | N_I64)) != 0)
+ *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 | N_P64)) != 0)
+ *type = NT_poly;
+ else if ((mask & (N_F_ALL)) != 0)
+ *type = NT_float;
+ else
+ return FAIL;
- inst.instruction |= inst.operands[0].reg;
- inst.instruction |= inst.operands[1].reg << 12;
- inst.instruction |= inst.operands[2].reg << 8;
- inst.instruction |= inst.operands[3].reg << 16;
+ return SUCCESS;
}
-static void
-do_t_sxtah (void)
+/* Modify a bitmask of allowed types. This is only needed for type
+ relaxation. */
+
+static unsigned
+modify_types_allowed (unsigned allowed, unsigned mods)
{
- unsigned Rd, Rn, Rm;
+ unsigned size;
+ enum neon_el_type type;
+ unsigned destmask;
+ int i;
- Rd = inst.operands[0].reg;
- Rn = inst.operands[1].reg;
- Rm = inst.operands[2].reg;
+ destmask = 0;
- reject_bad_reg (Rd);
- reject_bad_reg (Rn);
- reject_bad_reg (Rm);
+ 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);
+ }
+ }
- inst.instruction |= Rd << 8;
- inst.instruction |= Rn << 16;
- inst.instruction |= Rm;
- inst.instruction |= inst.operands[3].imm << 4;
+ return destmask;
}
-static void
-do_t_sxth (void)
-{
- unsigned Rd, Rm;
+/* Check type and return type classification.
+ The manual states (paraphrase): If one datatype is given, it indicates the
+ type given in:
+ - the second operand, if there is one
+ - the operand, if there is no second operand
+ - the result, if there are no operands.
+ This isn't quite good enough though, so we use a concept of a "key" datatype
+ which is set on a per-instruction basis, which is the one which matters when
+ only one data type is written.
+ Note: this function has side-effects (e.g. filling in missing operands). All
+ Neon instructions should call it before performing bit encoding. */
- Rd = inst.operands[0].reg;
- Rm = inst.operands[1].reg;
+static struct neon_type_el
+neon_check_type (unsigned els, enum neon_shape ns, ...)
+{
+ va_list ap;
+ unsigned i, pass, key_el = 0;
+ unsigned types[NEON_MAX_TYPE_ELS];
+ enum neon_el_type k_type = NT_invtype;
+ unsigned k_size = -1u;
+ struct neon_type_el badtype = {NT_invtype, -1};
+ unsigned key_allowed = 0;
- reject_bad_reg (Rd);
- reject_bad_reg (Rm);
+ /* Optional registers in Neon instructions are always (not) in operand 1.
+ Fill in the missing operand here, if it was omitted. */
+ if (els > 1 && !inst.operands[1].present)
+ inst.operands[1] = inst.operands[0];
- if (inst.instruction <= 0xffff
- && inst.size_req != 4
- && Rd <= 7 && Rm <= 7
- && (!inst.operands[2].present || inst.operands[2].imm == 0))
+ /* Suck up all the varargs. */
+ va_start (ap, ns);
+ for (i = 0; i < els; i++)
{
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= Rd;
- inst.instruction |= Rm << 3;
+ unsigned thisarg = va_arg (ap, unsigned);
+ if (thisarg == N_IGNORE_TYPE)
+ {
+ va_end (ap);
+ return badtype;
+ }
+ types[i] = thisarg;
+ if ((thisarg & N_KEY) != 0)
+ key_el = i;
}
- else if (unified_syntax)
+ 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;
+ }
+
+ /* Duplicate inst.vectype elements here as necessary.
+ FIXME: No idea if this is exactly the same as the ARM assembler,
+ particularly when an insn takes one register and one non-register
+ operand. */
+ if (inst.vectype.elems == 1 && els > 1)
{
- if (inst.instruction <= 0xffff)
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= Rd << 8;
- inst.instruction |= Rm;
- inst.instruction |= inst.operands[2].imm << 4;
+ unsigned j;
+ 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]);
}
- else
+ else if (inst.vectype.elems == 0 && els > 0)
{
- constraint (inst.operands[2].present && inst.operands[2].imm != 0,
- _("Thumb encoding does not support rotation"));
- constraint (1, BAD_HIREG);
- }
-}
+ 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. */
+ for (j = 0; j < els; j++)
+ if (inst.operands[j].vectype.type != NT_invtype)
+ inst.vectype.el[j] = inst.operands[j].vectype;
-static void
-do_t_swi (void)
-{
- /* We have to do the following check manually as ARM_EXT_OS only applies
- to ARM_EXT_V6M. */
- if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6m))
+ 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]);
+ }
+ else
+ {
+ first_error (_("operand types can't be inferred"));
+ return badtype;
+ }
+ }
+ else if (inst.vectype.elems != els)
{
- if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_os)
- /* This only applies to the v6m howver, not later architectures. */
- && ! ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v7))
- as_bad (_("SVC is not permitted on this architecture"));
- ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used, arm_ext_os);
+ first_error (_("type specifier has the wrong number of parts"));
+ return badtype;
}
- inst.reloc.type = BFD_RELOC_ARM_SWI;
-}
-
-static void
-do_t_tb (void)
-{
- unsigned Rn, Rm;
- int half;
+ 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;
- half = (inst.instruction & 0x10) != 0;
- set_it_insn_type_last ();
- constraint (inst.operands[0].immisreg,
- _("instruction requires register index"));
+ /* 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)
+ && (types_allowed & N_SU_ALL) == 0)
+ g_type = NT_integer;
- Rn = inst.operands[0].reg;
- Rm = inst.operands[0].imm;
+ /* 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 (((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;
- constraint (Rn == REG_SP, BAD_SP);
- reject_bad_reg (Rm);
+ if (pass == 0)
+ {
+ if ((thisarg & N_KEY) != 0)
+ {
+ k_type = g_type;
+ k_size = g_size;
+ key_allowed = thisarg & ~N_KEY;
+
+ /* Check architecture constraint on FP16 extension. */
+ if (k_size == 16
+ && k_type == NT_float
+ && ! ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_fp16))
+ {
+ inst.error = _(BAD_FP16);
+ return badtype;
+ }
+ }
+ }
+ else
+ {
+ if ((thisarg & N_VFP) != 0)
+ {
+ enum neon_shape_el regshape;
+ unsigned regwidth, match;
- constraint (!half && inst.operands[0].shifted,
- _("instruction does not allow shifted index"));
- inst.instruction |= (Rn << 16) | Rm;
-}
+ /* 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;
-static void
-do_t_usat (void)
-{
- do_t_ssat_usat (0);
-}
+ /* FP16 will use a single precision register. */
+ if (regwidth == 32 && match == 16)
+ {
+ if (ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_fp16))
+ match = regwidth;
+ else
+ {
+ inst.error = _(BAD_FP16);
+ return badtype;
+ }
+ }
-static void
-do_t_usat16 (void)
-{
- unsigned Rd, Rn;
+ if (regwidth != match)
+ {
+ first_error (_("operand size must match register width"));
+ return badtype;
+ }
+ }
- Rd = inst.operands[0].reg;
- Rn = inst.operands[2].reg;
+ if ((thisarg & N_EQK) == 0)
+ {
+ unsigned given_type = type_chk_of_el_type (g_type, g_size);
- reject_bad_reg (Rd);
- reject_bad_reg (Rn);
+ 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;
+ }
+ }
+ }
+ }
+ }
- inst.instruction |= Rd << 8;
- inst.instruction |= inst.operands[1].imm;
- inst.instruction |= Rn << 16;
+ return inst.vectype.el[key_el];
}
-/* Neon instruction encoder helpers. */
-
-/* Encodings for the different types for various Neon opcodes. */
+/* Neon-style VFP instruction forwarding. */
-/* An "invalid" code for the following tables. */
-#define N_INV -1u
+/* Thumb VFP instructions have 0xE in the condition field. */
-struct neon_tab_entry
+static void
+do_vfp_cond_or_thumb (void)
{
- unsigned integer;
- unsigned float_or_poly;
- unsigned scalar_or_imm;
-};
+ inst.is_neon = 1;
-/* Map overloaded Neon opcodes to their respective encodings. */
-#define NEON_ENC_TAB \
- X(vabd, 0x0000700, 0x1200d00, N_INV), \
- X(vmax, 0x0000600, 0x0000f00, N_INV), \
- X(vmin, 0x0000610, 0x0200f00, N_INV), \
- X(vpadd, 0x0000b10, 0x1000d00, N_INV), \
- X(vpmax, 0x0000a00, 0x1000f00, N_INV), \
- X(vpmin, 0x0000a10, 0x1200f00, N_INV), \
- X(vadd, 0x0000800, 0x0000d00, N_INV), \
- X(vsub, 0x1000800, 0x0200d00, N_INV), \
- X(vceq, 0x1000810, 0x0000e00, 0x1b10100), \
- X(vcge, 0x0000310, 0x1000e00, 0x1b10080), \
- X(vcgt, 0x0000300, 0x1200e00, 0x1b10000), \
- /* Register variants of the following two instructions are encoded as
- vcge / vcgt with the operands reversed. */ \
- X(vclt, 0x0000300, 0x1200e00, 0x1b10200), \
- X(vcle, 0x0000310, 0x1000e00, 0x1b10180), \
- X(vfma, N_INV, 0x0000c10, N_INV), \
- X(vfms, N_INV, 0x0200c10, N_INV), \
- X(vmla, 0x0000900, 0x0000d10, 0x0800040), \
- X(vmls, 0x1000900, 0x0200d10, 0x0800440), \
- X(vmul, 0x0000910, 0x1000d10, 0x0800840), \
- X(vmull, 0x0800c00, 0x0800e00, 0x0800a40), /* polynomial not float. */ \
- X(vmlal, 0x0800800, N_INV, 0x0800240), \
- X(vmlsl, 0x0800a00, N_INV, 0x0800640), \
- X(vqdmlal, 0x0800900, N_INV, 0x0800340), \
- X(vqdmlsl, 0x0800b00, N_INV, 0x0800740), \
- X(vqdmull, 0x0800d00, N_INV, 0x0800b40), \
- X(vqdmulh, 0x0000b00, N_INV, 0x0800c40), \
- X(vqrdmulh, 0x1000b00, N_INV, 0x0800d40), \
- X(vshl, 0x0000400, N_INV, 0x0800510), \
- X(vqshl, 0x0000410, N_INV, 0x0800710), \
- X(vand, 0x0000110, N_INV, 0x0800030), \
- X(vbic, 0x0100110, N_INV, 0x0800030), \
- X(veor, 0x1000110, N_INV, N_INV), \
- X(vorn, 0x0300110, N_INV, 0x0800010), \
- X(vorr, 0x0200110, N_INV, 0x0800010), \
- X(vmvn, 0x1b00580, N_INV, 0x0800030), \
- X(vshll, 0x1b20300, N_INV, 0x0800a10), /* max shift, immediate. */ \
- X(vcvt, 0x1b30600, N_INV, 0x0800e10), /* integer, fixed-point. */ \
- X(vdup, 0xe800b10, N_INV, 0x1b00c00), /* arm, scalar. */ \
- X(vld1, 0x0200000, 0x0a00000, 0x0a00c00), /* interlv, lane, dup. */ \
- X(vst1, 0x0000000, 0x0800000, N_INV), \
- X(vld2, 0x0200100, 0x0a00100, 0x0a00d00), \
- X(vst2, 0x0000100, 0x0800100, N_INV), \
- X(vld3, 0x0200200, 0x0a00200, 0x0a00e00), \
- X(vst3, 0x0000200, 0x0800200, N_INV), \
- X(vld4, 0x0200300, 0x0a00300, 0x0a00f00), \
- X(vst4, 0x0000300, 0x0800300, N_INV), \
- X(vmovn, 0x1b20200, N_INV, N_INV), \
- X(vtrn, 0x1b20080, N_INV, N_INV), \
- X(vqmovn, 0x1b20200, N_INV, N_INV), \
- X(vqmovun, 0x1b20240, N_INV, N_INV), \
- X(vnmul, 0xe200a40, 0xe200b40, N_INV), \
- X(vnmla, 0xe100a40, 0xe100b40, N_INV), \
- X(vnmls, 0xe100a00, 0xe100b00, N_INV), \
- X(vfnma, 0xe900a40, 0xe900b40, N_INV), \
- X(vfnms, 0xe900a00, 0xe900b00, N_INV), \
- X(vcmp, 0xeb40a40, 0xeb40b40, N_INV), \
- X(vcmpz, 0xeb50a40, 0xeb50b40, N_INV), \
- X(vcmpe, 0xeb40ac0, 0xeb40bc0, N_INV), \
- X(vcmpez, 0xeb50ac0, 0xeb50bc0, N_INV)
+ if (thumb_mode)
+ inst.instruction |= 0xe0000000;
+ else
+ inst.instruction |= inst.cond << 28;
+}
-enum neon_opc
-{
-#define X(OPC,I,F,S) N_MNEM_##OPC
-NEON_ENC_TAB
-#undef X
-};
+/* Look up and encode a simple mnemonic, for use as a helper function for the
+ Neon-style VFP syntax. This avoids duplication of bits of the insns table,
+ etc. It is assumed that operand parsing has already been done, and that the
+ operands are in the form expected by the given opcode (this isn't necessarily
+ the same as the form in which they were parsed, hence some massaging must
+ take place before this function is called).
+ Checks current arch version against that in the looked-up opcode. */
-static const struct neon_tab_entry neon_enc_tab[] =
+static void
+do_vfp_nsyn_opcode (const char *opname)
{
-#define X(OPC,I,F,S) { (I), (F), (S) }
-NEON_ENC_TAB
-#undef X
-};
-
-/* Do not use these macros; instead, use NEON_ENCODE defined below. */
-#define NEON_ENC_INTEGER_(X) (neon_enc_tab[(X) & 0x0fffffff].integer)
-#define NEON_ENC_ARMREG_(X) (neon_enc_tab[(X) & 0x0fffffff].integer)
-#define NEON_ENC_POLY_(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly)
-#define NEON_ENC_FLOAT_(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly)
-#define NEON_ENC_SCALAR_(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm)
-#define NEON_ENC_IMMED_(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm)
-#define NEON_ENC_INTERLV_(X) (neon_enc_tab[(X) & 0x0fffffff].integer)
-#define NEON_ENC_LANE_(X) (neon_enc_tab[(X) & 0x0fffffff].float_or_poly)
-#define NEON_ENC_DUP_(X) (neon_enc_tab[(X) & 0x0fffffff].scalar_or_imm)
-#define NEON_ENC_SINGLE_(X) \
- ((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_ENCODE(type, inst) \
- do \
- { \
- inst.instruction = NEON_ENC_##type##_ (inst.instruction); \
- inst.is_neon = 1; \
- } \
- while (0)
+ const struct asm_opcode *opcode;
-#define check_neon_suffixes \
- do \
- { \
- if (!inst.error && inst.vectype.elems > 0 && !inst.is_neon) \
- { \
- as_bad (_("invalid neon suffix for non neon instruction")); \
- return; \
- } \
- } \
- while (0)
+ opcode = (const struct asm_opcode *) hash_find (arm_ops_hsh, opname);
-/* Define shapes for instruction operands. The following mnemonic characters
- are used in this table:
+ if (!opcode)
+ abort ();
- F - VFP S<n> register
- D - Neon D<n> register
- Q - Neon Q<n> register
- I - Immediate
- S - Scalar
- R - ARM register
- L - D<n> register list
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant,
+ thumb_mode ? *opcode->tvariant : *opcode->avariant),
+ _(BAD_FPU));
- This table is used to generate various data:
- - enumerations of the form NS_DDR to be used as arguments to
- neon_select_shape.
- - a table classifying shapes into single, double, quad, mixed.
- - a table used to drive neon_select_shape. */
+ inst.is_neon = 1;
-#define NEON_SHAPE_DEF \
- X(3, (D, D, D), DOUBLE), \
- X(3, (Q, Q, Q), QUAD), \
- X(3, (D, D, I), DOUBLE), \
- X(3, (Q, Q, I), QUAD), \
- X(3, (D, D, S), DOUBLE), \
- X(3, (Q, Q, S), QUAD), \
- X(2, (D, D), DOUBLE), \
- X(2, (Q, Q), QUAD), \
- X(2, (D, S), DOUBLE), \
- X(2, (Q, S), QUAD), \
- X(2, (D, R), DOUBLE), \
- X(2, (Q, R), QUAD), \
- X(2, (D, I), DOUBLE), \
- X(2, (Q, I), QUAD), \
- X(3, (D, L, D), DOUBLE), \
- X(2, (D, Q), MIXED), \
- X(2, (Q, D), MIXED), \
- X(3, (D, Q, I), MIXED), \
- X(3, (Q, D, I), MIXED), \
- X(3, (Q, D, D), MIXED), \
- X(3, (D, Q, Q), MIXED), \
- X(3, (Q, Q, D), MIXED), \
- X(3, (Q, D, S), MIXED), \
- X(3, (D, Q, S), MIXED), \
- X(4, (D, D, D, I), DOUBLE), \
- X(4, (Q, Q, Q, I), QUAD), \
- X(2, (F, F), SINGLE), \
- X(3, (F, F, F), SINGLE), \
- X(2, (F, I), SINGLE), \
- X(2, (F, D), MIXED), \
- X(2, (D, F), MIXED), \
- X(3, (F, F, I), MIXED), \
- X(4, (R, R, F, F), SINGLE), \
- X(4, (F, F, R, R), SINGLE), \
- X(3, (D, R, R), DOUBLE), \
- X(3, (R, R, D), DOUBLE), \
- X(2, (S, R), SINGLE), \
- X(2, (R, S), SINGLE), \
- X(2, (F, R), SINGLE), \
- X(2, (R, F), SINGLE)
+ if (thumb_mode)
+ {
+ inst.instruction = opcode->tvalue;
+ opcode->tencode ();
+ }
+ else
+ {
+ inst.instruction = (inst.cond << 28) | opcode->avalue;
+ opcode->aencode ();
+ }
+}
-#define S2(A,B) NS_##A##B
-#define S3(A,B,C) NS_##A##B##C
-#define S4(A,B,C,D) NS_##A##B##C##D
+static void
+do_vfp_nsyn_add_sub (enum neon_shape rs)
+{
+ int is_add = (inst.instruction & 0x0fffffff) == N_MNEM_vadd;
-#define X(N, L, C) S##N L
+ if (rs == NS_FFF || rs == NS_HHH)
+ {
+ if (is_add)
+ do_vfp_nsyn_opcode ("fadds");
+ else
+ do_vfp_nsyn_opcode ("fsubs");
-enum neon_shape
-{
- NEON_SHAPE_DEF,
- NS_NULL
-};
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
+ }
+ else
+ {
+ if (is_add)
+ do_vfp_nsyn_opcode ("faddd");
+ else
+ do_vfp_nsyn_opcode ("fsubd");
+ }
+}
-#undef X
-#undef S2
-#undef S3
-#undef S4
+/* Check operand types to see if this is a VFP instruction, and if so call
+ PFN (). */
-enum neon_shape_class
+static int
+try_vfp_nsyn (int args, void (*pfn) (enum neon_shape))
{
- SC_SINGLE,
- SC_DOUBLE,
- SC_QUAD,
- SC_MIXED
-};
+ enum neon_shape rs;
+ struct neon_type_el et;
-#define X(N, L, C) SC_##C
+ switch (args)
+ {
+ case 2:
+ rs = neon_select_shape (NS_HH, NS_FF, NS_DD, NS_NULL);
+ et = neon_check_type (2, rs, N_EQK | N_VFP, N_F_ALL | N_KEY | N_VFP);
+ break;
-static enum neon_shape_class neon_shape_class[] =
-{
- NEON_SHAPE_DEF
-};
+ case 3:
+ rs = neon_select_shape (NS_HHH, NS_FFF, NS_DDD, NS_NULL);
+ et = neon_check_type (3, rs, N_EQK | N_VFP, N_EQK | N_VFP,
+ N_F_ALL | N_KEY | N_VFP);
+ break;
-#undef X
+ default:
+ abort ();
+ }
-enum neon_shape_el
-{
- SE_F,
- SE_D,
- SE_Q,
- SE_I,
- SE_S,
- SE_R,
- SE_L
-};
+ if (et.type != NT_invtype)
+ {
+ pfn (rs);
+ return SUCCESS;
+ }
-/* Register widths of above. */
-static unsigned neon_shape_el_size[] =
+ inst.error = NULL;
+ return FAIL;
+}
+
+static void
+do_vfp_nsyn_mla_mls (enum neon_shape rs)
{
- 32,
- 64,
- 128,
- 0,
- 32,
- 32,
- 0
-};
+ int is_mla = (inst.instruction & 0x0fffffff) == N_MNEM_vmla;
-struct neon_shape_info
+ if (rs == NS_FFF || rs == NS_HHH)
+ {
+ if (is_mla)
+ do_vfp_nsyn_opcode ("fmacs");
+ else
+ do_vfp_nsyn_opcode ("fnmacs");
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
+ }
+ else
+ {
+ if (is_mla)
+ do_vfp_nsyn_opcode ("fmacd");
+ else
+ do_vfp_nsyn_opcode ("fnmacd");
+ }
+}
+
+static void
+do_vfp_nsyn_fma_fms (enum neon_shape rs)
{
- unsigned els;
- enum neon_shape_el el[NEON_MAX_TYPE_ELS];
-};
+ int is_fma = (inst.instruction & 0x0fffffff) == N_MNEM_vfma;
-#define S2(A,B) { SE_##A, SE_##B }
-#define S3(A,B,C) { SE_##A, SE_##B, SE_##C }
-#define S4(A,B,C,D) { SE_##A, SE_##B, SE_##C, SE_##D }
+ if (rs == NS_FFF || rs == NS_HHH)
+ {
+ if (is_fma)
+ do_vfp_nsyn_opcode ("ffmas");
+ else
+ do_vfp_nsyn_opcode ("ffnmas");
-#define X(N, L, C) { N, S##N L }
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
+ }
+ else
+ {
+ if (is_fma)
+ do_vfp_nsyn_opcode ("ffmad");
+ else
+ do_vfp_nsyn_opcode ("ffnmad");
+ }
+}
-static struct neon_shape_info neon_shape_tab[] =
+static void
+do_vfp_nsyn_mul (enum neon_shape rs)
{
- NEON_SHAPE_DEF
-};
+ if (rs == NS_FFF || rs == NS_HHH)
+ {
+ do_vfp_nsyn_opcode ("fmuls");
-#undef X
-#undef S2
-#undef S3
-#undef S4
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
+ }
+ else
+ do_vfp_nsyn_opcode ("fmuld");
+}
-/* Bit masks used in type checking given instructions.
- 'N_EQK' means the type must be the same as (or based on in some way) the key
- type, which itself is marked with the 'N_KEY' bit. If the 'N_EQK' bit is
- set, various other bits can be set as well in order to modify the meaning of
- the type constraint. */
+static void
+do_vfp_nsyn_abs_neg (enum neon_shape rs)
+{
+ int is_neg = (inst.instruction & 0x80) != 0;
+ neon_check_type (2, rs, N_EQK | N_VFP, N_F_ALL | N_VFP | N_KEY);
-enum neon_type_mask
+ if (rs == NS_FF || rs == NS_HH)
+ {
+ if (is_neg)
+ do_vfp_nsyn_opcode ("fnegs");
+ else
+ do_vfp_nsyn_opcode ("fabss");
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HH)
+ do_scalar_fp16_v82_encode ();
+ }
+ else
+ {
+ if (is_neg)
+ do_vfp_nsyn_opcode ("fnegd");
+ else
+ do_vfp_nsyn_opcode ("fabsd");
+ }
+}
+
+/* Encode single-precision (only!) VFP fldm/fstm instructions. Double precision
+ insns belong to Neon, and are handled elsewhere. */
+
+static void
+do_vfp_nsyn_ldm_stm (int is_dbmode)
{
- N_S8 = 0x0000001,
- N_S16 = 0x0000002,
- N_S32 = 0x0000004,
- N_S64 = 0x0000008,
- N_U8 = 0x0000010,
- N_U16 = 0x0000020,
- N_U32 = 0x0000040,
- N_U64 = 0x0000080,
- N_I8 = 0x0000100,
- N_I16 = 0x0000200,
- N_I32 = 0x0000400,
- N_I64 = 0x0000800,
- N_8 = 0x0001000,
- N_16 = 0x0002000,
- N_32 = 0x0004000,
- N_64 = 0x0008000,
- N_P8 = 0x0010000,
- N_P16 = 0x0020000,
- N_F16 = 0x0040000,
- N_F32 = 0x0080000,
- N_F64 = 0x0100000,
- 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_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_UNS = 0x0000008, /* If N_EQK, this operand is forced to be unsigned. */
- N_INT = 0x0000010, /* If N_EQK, this operand is forced to be integer. */
- 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
-};
+ int is_ldm = (inst.instruction & (1 << 20)) != 0;
+ if (is_ldm)
+ {
+ if (is_dbmode)
+ do_vfp_nsyn_opcode ("fldmdbs");
+ else
+ do_vfp_nsyn_opcode ("fldmias");
+ }
+ else
+ {
+ if (is_dbmode)
+ do_vfp_nsyn_opcode ("fstmdbs");
+ else
+ do_vfp_nsyn_opcode ("fstmias");
+ }
+}
-#define N_ALLMODS (N_DBL | N_HLF | N_SGN | N_UNS | N_INT | N_FLT | N_SIZ)
+static void
+do_vfp_nsyn_sqrt (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_HH, NS_FF, NS_DD, NS_NULL);
+ neon_check_type (2, rs, N_EQK | N_VFP, N_F_ALL | N_KEY | N_VFP);
-#define N_SU_ALL (N_S8 | N_S16 | N_S32 | N_S64 | N_U8 | N_U16 | N_U32 | N_U64)
-#define N_SU_32 (N_S8 | N_S16 | N_S32 | N_U8 | N_U16 | N_U32)
-#define N_SU_16_64 (N_S16 | N_S32 | N_S64 | N_U16 | N_U32 | N_U64)
-#define N_SUF_32 (N_SU_32 | N_F32)
-#define N_I_ALL (N_I8 | N_I16 | N_I32 | N_I64)
-#define N_IF_32 (N_I8 | N_I16 | N_I32 | N_F32)
+ if (rs == NS_FF || rs == NS_HH)
+ {
+ do_vfp_nsyn_opcode ("fsqrts");
-/* Pass this as the first type argument to neon_check_type to ignore types
- altogether. */
-#define N_IGNORE_TYPE (N_KEY | N_EQK)
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HH)
+ do_scalar_fp16_v82_encode ();
+ }
+ else
+ do_vfp_nsyn_opcode ("fsqrtd");
+}
-/* Select a "shape" for the current instruction (describing register types or
- sizes) from a list of alternatives. Return NS_NULL if the current instruction
- doesn't fit. For non-polymorphic shapes, checking is usually done as a
- function of operand parsing, so this function doesn't need to be called.
- Shapes should be listed in order of decreasing length. */
+static void
+do_vfp_nsyn_div (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_HHH, NS_FFF, NS_DDD, NS_NULL);
+ neon_check_type (3, rs, N_EQK | N_VFP, N_EQK | N_VFP,
+ N_F_ALL | N_KEY | N_VFP);
-static enum neon_shape
-neon_select_shape (enum neon_shape shape, ...)
+ if (rs == NS_FFF || rs == NS_HHH)
+ {
+ do_vfp_nsyn_opcode ("fdivs");
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
+ }
+ else
+ do_vfp_nsyn_opcode ("fdivd");
+}
+
+static void
+do_vfp_nsyn_nmul (void)
{
- va_list ap;
- enum neon_shape first_shape = shape;
+ enum neon_shape rs = neon_select_shape (NS_HHH, NS_FFF, NS_DDD, NS_NULL);
+ neon_check_type (3, rs, N_EQK | N_VFP, N_EQK | N_VFP,
+ N_F_ALL | N_KEY | N_VFP);
- /* Fix missing optional operands. FIXME: we don't know at this point how
- many arguments we should have, so this makes the assumption that we have
- > 1. This is true of all current Neon opcodes, I think, but may not be
- true in the future. */
- if (!inst.operands[1].present)
- inst.operands[1] = inst.operands[0];
+ if (rs == NS_FFF || rs == NS_HHH)
+ {
+ NEON_ENCODE (SINGLE, inst);
+ do_vfp_sp_dyadic ();
- va_start (ap, shape);
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
+ }
+ else
+ {
+ NEON_ENCODE (DOUBLE, inst);
+ do_vfp_dp_rd_rn_rm ();
+ }
+ do_vfp_cond_or_thumb ();
- for (; shape != NS_NULL; shape = (enum neon_shape) va_arg (ap, int))
+}
+
+static void
+do_vfp_nsyn_cmp (void)
+{
+ enum neon_shape rs;
+ if (inst.operands[1].isreg)
{
- unsigned j;
- int matches = 1;
+ rs = neon_select_shape (NS_HH, NS_FF, NS_DD, NS_NULL);
+ neon_check_type (2, rs, N_EQK | N_VFP, N_F_ALL | N_KEY | N_VFP);
- 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 (rs == NS_FF || rs == NS_HH)
+ {
+ NEON_ENCODE (SINGLE, inst);
+ do_vfp_sp_monadic ();
+ }
+ else
+ {
+ NEON_ENCODE (DOUBLE, inst);
+ do_vfp_dp_rd_rm ();
+ }
}
+ else
+ {
+ rs = neon_select_shape (NS_HI, NS_FI, NS_DI, NS_NULL);
+ neon_check_type (2, rs, N_F_ALL | N_KEY | N_VFP, N_EQK);
- va_end (ap);
+ 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 ();
+ }
- if (shape == NS_NULL && first_shape != NS_NULL)
- first_error (_("invalid instruction shape"));
+ if (rs == NS_FI || rs == NS_HI)
+ {
+ NEON_ENCODE (SINGLE, inst);
+ do_vfp_sp_compare_z ();
+ }
+ else
+ {
+ NEON_ENCODE (DOUBLE, inst);
+ do_vfp_dp_rd ();
+ }
+ }
+ do_vfp_cond_or_thumb ();
- return shape;
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HI || rs == NS_HH)
+ do_scalar_fp16_v82_encode ();
}
-/* True if SHAPE is predominantly a quadword operation (most of the time, this
- means the Q bit should be set). */
+static void
+nsyn_insert_sp (void)
+{
+ inst.operands[1] = inst.operands[0];
+ memset (&inst.operands[0], '\0', sizeof (inst.operands[0]));
+ inst.operands[0].reg = REG_SP;
+ inst.operands[0].isreg = 1;
+ inst.operands[0].writeback = 1;
+ inst.operands[0].present = 1;
+}
-static int
-neon_quad (enum neon_shape shape)
+static void
+do_vfp_nsyn_push (void)
{
- return neon_shape_class[shape] == SC_QUAD;
+ nsyn_insert_sp ();
+ if (inst.operands[1].issingle)
+ do_vfp_nsyn_opcode ("fstmdbs");
+ else
+ do_vfp_nsyn_opcode ("fstmdbd");
}
static void
-neon_modify_type_size (unsigned typebits, enum neon_el_type *g_type,
- unsigned *g_size)
+do_vfp_nsyn_pop (void)
{
- /* 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_EQK) != 0)
- {
- if ((typebits & N_HLF) != 0)
- *g_size /= 2;
- else if ((typebits & N_DBL) != 0)
- *g_size *= 2;
- if ((typebits & N_SGN) != 0)
- *g_type = NT_signed;
- else if ((typebits & N_UNS) != 0)
- *g_type = NT_unsigned;
- else if ((typebits & N_INT) != 0)
- *g_type = NT_integer;
- else if ((typebits & N_FLT) != 0)
- *g_type = NT_float;
- else if ((typebits & N_SIZ) != 0)
- *g_type = NT_untyped;
- }
+ nsyn_insert_sp ();
+ if (inst.operands[1].issingle)
+ do_vfp_nsyn_opcode ("fldmias");
+ else
+ do_vfp_nsyn_opcode ("fldmiad");
}
-/* Return operand OPNO promoted by bits set in THISARG. KEY should be the "key"
- operand type, i.e. the single type specified in a Neon instruction when it
- is the only one given. */
+/* Fix up Neon data-processing instructions, ORing in the correct bits for
+ ARM mode or Thumb mode and moving the encoded bit 24 to bit 28. */
-static struct neon_type_el
-neon_type_promote (struct neon_type_el *key, unsigned thisarg)
+static void
+neon_dp_fixup (struct arm_it* insn)
{
- struct neon_type_el dest = *key;
+ unsigned int i = insn->instruction;
+ insn->is_neon = 1;
- gas_assert ((thisarg & N_EQK) != 0);
+ if (thumb_mode)
+ {
+ /* The U bit is at bit 24 by default. Move to bit 28 in Thumb mode. */
+ if (i & (1 << 24))
+ i |= 1 << 28;
- neon_modify_type_size (thisarg, &dest.type, &dest.size);
+ i &= ~(1 << 24);
- return dest;
+ i |= 0xef000000;
+ }
+ else
+ i |= 0xf2000000;
+
+ insn->instruction = i;
}
-/* Convert Neon type and size into compact bitmask representation. */
+/* Turn a size (8, 16, 32, 64) into the respective bit number minus 3
+ (0, 1, 2, 3). */
-static enum neon_type_mask
-type_chk_of_el_type (enum neon_el_type type, unsigned size)
+static unsigned
+neon_logbits (unsigned x)
{
- switch (type)
- {
- 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: ;
- }
- 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: ;
- }
- break;
+ return ffs (x) - 4;
+}
- case NT_float:
- switch (size)
- {
- case 16: return N_F16;
- case 32: return N_F32;
- case 64: return N_F64;
- default: ;
- }
- break;
+#define LOW4(R) ((R) & 0xf)
+#define HI1(R) (((R) >> 4) & 1)
- case NT_poly:
- switch (size)
- {
- case 8: return N_P8;
- case 16: return N_P16;
- default: ;
- }
- break;
+/* Encode insns with bit pattern:
- 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: ;
- }
- break;
+ |28/24|23|22 |21 20|19 16|15 12|11 8|7|6|5|4|3 0|
+ | U |x |D |size | Rn | Rd |x x x x|N|Q|M|x| Rm |
- 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: ;
- }
- break;
+ SIZE is passed in bits. -1 means size field isn't changed, in case it has a
+ different meaning for some instruction. */
- default: ;
- }
+static void
+neon_three_same (int isquad, int ubit, int size)
+{
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
+ inst.instruction |= HI1 (inst.operands[1].reg) << 7;
+ inst.instruction |= LOW4 (inst.operands[2].reg);
+ inst.instruction |= HI1 (inst.operands[2].reg) << 5;
+ inst.instruction |= (isquad != 0) << 6;
+ inst.instruction |= (ubit != 0) << 24;
+ if (size != -1)
+ inst.instruction |= neon_logbits (size) << 20;
- return N_UTYP;
+ neon_dp_fixup (&inst);
}
-/* Convert compact Neon bitmask type representation to a type and size. Only
- handles the case where a single bit is set in the mask. */
+/* Encode instructions of the form:
-static int
-el_type_of_type_chk (enum neon_el_type *type, unsigned *size,
- enum neon_type_mask mask)
-{
- if ((mask & N_EQK) != 0)
- return FAIL;
+ |28/24|23|22|21 20|19 18|17 16|15 12|11 7|6|5|4|3 0|
+ | U |x |D |x x |size |x x | Rd |x x x x x|Q|M|x| Rm |
- 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)
- *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)
- *size = 64;
- else
- return FAIL;
+ Don't write size if SIZE == -1. */
- if ((mask & (N_S8 | N_S16 | N_S32 | N_S64)) != 0)
- *type = NT_signed;
- else if ((mask & (N_U8 | N_U16 | N_U32 | N_U64)) != 0)
- *type = NT_unsigned;
- else if ((mask & (N_I8 | N_I16 | N_I32 | N_I64)) != 0)
- *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)
- *type = NT_poly;
- else if ((mask & (N_F32 | N_F64)) != 0)
- *type = NT_float;
- else
- return FAIL;
+static void
+neon_two_same (int qbit, int ubit, int size)
+{
+ 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 |= (qbit != 0) << 6;
+ inst.instruction |= (ubit != 0) << 24;
- return SUCCESS;
+ if (size != -1)
+ inst.instruction |= neon_logbits (size) << 18;
+
+ neon_dp_fixup (&inst);
}
-/* Modify a bitmask of allowed types. This is only needed for type
- relaxation. */
+/* Neon instruction encoders, in approximate order of appearance. */
-static unsigned
-modify_types_allowed (unsigned allowed, unsigned mods)
+static void
+do_neon_dyadic_i_su (void)
{
- unsigned size;
- enum neon_el_type type;
- unsigned destmask;
- int i;
-
- destmask = 0;
-
- 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);
- }
- }
-
- return destmask;
+ 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_SU_32 | N_KEY);
+ neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
}
-/* Check type and return type classification.
- The manual states (paraphrase): If one datatype is given, it indicates the
- type given in:
- - the second operand, if there is one
- - the operand, if there is no second operand
- - the result, if there are no operands.
- This isn't quite good enough though, so we use a concept of a "key" datatype
- which is set on a per-instruction basis, which is the one which matters when
- only one data type is written.
- Note: this function has side-effects (e.g. filling in missing operands). All
- Neon instructions should call it before performing bit encoding. */
+static void
+do_neon_dyadic_i64_su (void)
+{
+ 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_SU_ALL | N_KEY);
+ neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
+}
-static struct neon_type_el
-neon_check_type (unsigned els, enum neon_shape ns, ...)
+static void
+neon_imm_shift (int write_ubit, int uval, int isquad, struct neon_type_el et,
+ unsigned immbits)
{
- va_list ap;
- unsigned i, pass, key_el = 0;
- unsigned types[NEON_MAX_TYPE_ELS];
- enum neon_el_type k_type = NT_invtype;
- unsigned k_size = -1u;
- struct neon_type_el badtype = {NT_invtype, -1};
- unsigned key_allowed = 0;
+ unsigned size = et.size >> 3;
+ 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 |= (isquad != 0) << 6;
+ inst.instruction |= immbits << 16;
+ inst.instruction |= (size >> 3) << 7;
+ inst.instruction |= (size & 0x7) << 19;
+ if (write_ubit)
+ inst.instruction |= (uval != 0) << 24;
- /* Optional registers in Neon instructions are always (not) in operand 1.
- Fill in the missing operand here, if it was omitted. */
- if (els > 1 && !inst.operands[1].present)
- inst.operands[1] = inst.operands[0];
+ neon_dp_fixup (&inst);
+}
- /* Suck up all the varargs. */
- va_start (ap, ns);
- for (i = 0; i < els; i++)
+static void
+do_neon_shl_imm (void)
+{
+ if (!inst.operands[2].isreg)
{
- unsigned thisarg = va_arg (ap, unsigned);
- if (thisarg == N_IGNORE_TYPE)
- {
- va_end (ap);
- return badtype;
- }
- types[i] = thisarg;
- if ((thisarg & N_KEY) != 0)
- 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;
- }
+ 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_KEY | N_I_ALL);
+ int imm = inst.operands[2].imm;
- /* Duplicate inst.vectype elements here as necessary.
- FIXME: No idea if this is exactly the same as the ARM assembler,
- particularly when an insn takes one register and one non-register
- operand. */
- if (inst.vectype.elems == 1 && els > 1)
- {
- unsigned j;
- 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]);
+ constraint (imm < 0 || (unsigned)imm >= et.size,
+ _("immediate out of range for shift"));
+ NEON_ENCODE (IMMED, inst);
+ neon_imm_shift (FALSE, 0, neon_quad (rs), et, imm);
}
- else if (inst.vectype.elems == 0 && els > 0)
+ else
{
- 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. */
- for (j = 0; j < els; j++)
- if (inst.operands[j].vectype.type != NT_invtype)
- inst.vectype.el[j] = inst.operands[j].vectype;
+ 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);
+ unsigned int tmp;
- 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]);
- }
- else
- {
- first_error (_("operand types can't be inferred"));
- return badtype;
- }
- }
- else if (inst.vectype.elems != els)
- {
- first_error (_("type specifier has the wrong number of parts"));
- return badtype;
+ /* 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. */
+ tmp = inst.operands[2].reg;
+ inst.operands[2].reg = inst.operands[1].reg;
+ inst.operands[1].reg = tmp;
+ NEON_ENCODE (INTEGER, inst);
+ neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
}
+}
- for (pass = 0; pass < 2; pass++)
+static void
+do_neon_qshl_imm (void)
+{
+ if (!inst.operands[2].isreg)
{
- 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
- integer types if sign-specific variants are unavailable. */
- 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
- 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))
- 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;
+ 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_SU_ALL | N_KEY);
+ int imm = inst.operands[2].imm;
- /* 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;
- }
- }
- }
- }
+ constraint (imm < 0 || (unsigned)imm >= et.size,
+ _("immediate out of range for shift"));
+ NEON_ENCODE (IMMED, inst);
+ neon_imm_shift (TRUE, et.type == NT_unsigned, neon_quad (rs), et, 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);
+ unsigned int tmp;
- return inst.vectype.el[key_el];
+ /* See note in do_neon_shl_imm. */
+ tmp = inst.operands[2].reg;
+ inst.operands[2].reg = inst.operands[1].reg;
+ inst.operands[1].reg = tmp;
+ NEON_ENCODE (INTEGER, inst);
+ neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
+ }
}
-/* Neon-style VFP instruction forwarding. */
-
-/* Thumb VFP instructions have 0xE in the condition field. */
-
static void
-do_vfp_cond_or_thumb (void)
+do_neon_rshl (void)
{
- inst.is_neon = 1;
+ 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_SU_ALL | N_KEY);
+ unsigned int tmp;
- if (thumb_mode)
- inst.instruction |= 0xe0000000;
- else
- inst.instruction |= inst.cond << 28;
+ tmp = inst.operands[2].reg;
+ inst.operands[2].reg = inst.operands[1].reg;
+ inst.operands[1].reg = tmp;
+ neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
}
-/* Look up and encode a simple mnemonic, for use as a helper function for the
- Neon-style VFP syntax. This avoids duplication of bits of the insns table,
- etc. It is assumed that operand parsing has already been done, and that the
- operands are in the form expected by the given opcode (this isn't necessarily
- the same as the form in which they were parsed, hence some massaging must
- take place before this function is called).
- Checks current arch version against that in the looked-up opcode. */
-
-static void
-do_vfp_nsyn_opcode (const char *opname)
+static int
+neon_cmode_for_logic_imm (unsigned immediate, unsigned *immbits, int size)
{
- const struct asm_opcode *opcode;
-
- opcode = (const struct asm_opcode *) hash_find (arm_ops_hsh, opname);
-
- if (!opcode)
- abort ();
-
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant,
- thumb_mode ? *opcode->tvariant : *opcode->avariant),
- _(BAD_FPU));
+ /* Handle .I8 pseudo-instructions. */
+ 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. */
+ immediate |= immediate << 8;
+ size = 16;
+ }
- inst.is_neon = 1;
+ if (size >= 32)
+ {
+ if (immediate == (immediate & 0x000000ff))
+ {
+ *immbits = immediate;
+ return 0x1;
+ }
+ else if (immediate == (immediate & 0x0000ff00))
+ {
+ *immbits = immediate >> 8;
+ return 0x3;
+ }
+ else if (immediate == (immediate & 0x00ff0000))
+ {
+ *immbits = immediate >> 16;
+ return 0x5;
+ }
+ else if (immediate == (immediate & 0xff000000))
+ {
+ *immbits = immediate >> 24;
+ return 0x7;
+ }
+ if ((immediate & 0xffff) != (immediate >> 16))
+ goto bad_immediate;
+ immediate &= 0xffff;
+ }
- if (thumb_mode)
+ if (immediate == (immediate & 0x000000ff))
{
- inst.instruction = opcode->tvalue;
- opcode->tencode ();
+ *immbits = immediate;
+ return 0x9;
}
- else
+ else if (immediate == (immediate & 0x0000ff00))
{
- inst.instruction = (inst.cond << 28) | opcode->avalue;
- opcode->aencode ();
+ *immbits = immediate >> 8;
+ return 0xb;
}
+
+ bad_immediate:
+ first_error (_("immediate value out of range"));
+ return FAIL;
}
static void
-do_vfp_nsyn_add_sub (enum neon_shape rs)
+do_neon_logic (void)
{
- int is_add = (inst.instruction & 0x0fffffff) == N_MNEM_vadd;
+ if (inst.operands[2].present && inst.operands[2].isreg)
+ {
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ neon_check_type (3, rs, N_IGNORE_TYPE);
+ /* U bit and size field were set as part of the bitmask. */
+ NEON_ENCODE (INTEGER, inst);
+ neon_three_same (neon_quad (rs), 0, -1);
+ }
+ else
+ {
+ const int three_ops_form = (inst.operands[2].present
+ && !inst.operands[2].isreg);
+ const int immoperand = (three_ops_form ? 2 : 1);
+ enum neon_shape rs = (three_ops_form
+ ? 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);
+ enum neon_opc opcode = (enum neon_opc) inst.instruction & 0x0fffffff;
+ unsigned immbits;
+ int cmode;
+
+ if (et.type == NT_invtype)
+ return;
+
+ if (three_ops_form)
+ constraint (inst.operands[0].reg != inst.operands[1].reg,
+ _("first and second operands shall be the same register"));
+
+ NEON_ENCODE (IMMED, inst);
+
+ immbits = inst.operands[immoperand].imm;
+ if (et.size == 64)
+ {
+ /* .i64 is a pseudo-op, so the immediate must be a repeating
+ pattern. */
+ if (immbits != (inst.operands[immoperand].regisimm ?
+ inst.operands[immoperand].reg : 0))
+ {
+ /* Set immbits to an invalid constant. */
+ immbits = 0xdeadbeef;
+ }
+ }
+
+ switch (opcode)
+ {
+ case N_MNEM_vbic:
+ cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
+ break;
- if (rs == NS_FFF)
- {
- if (is_add)
- do_vfp_nsyn_opcode ("fadds");
- else
- do_vfp_nsyn_opcode ("fsubs");
- }
- else
- {
- if (is_add)
- do_vfp_nsyn_opcode ("faddd");
- else
- do_vfp_nsyn_opcode ("fsubd");
- }
-}
+ case N_MNEM_vorr:
+ cmode = neon_cmode_for_logic_imm (immbits, &immbits, et.size);
+ break;
-/* Check operand types to see if this is a VFP instruction, and if so call
- PFN (). */
+ 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;
-static int
-try_vfp_nsyn (int args, void (*pfn) (enum neon_shape))
-{
- enum neon_shape rs;
- struct neon_type_el et;
+ 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;
- switch (args)
- {
- 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);
- break;
+ default:
+ abort ();
+ }
- 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);
- break;
+ if (cmode == FAIL)
+ return;
- default:
- abort ();
- }
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= cmode << 8;
+ neon_write_immbits (immbits);
- if (et.type != NT_invtype)
- {
- pfn (rs);
- return SUCCESS;
+ neon_dp_fixup (&inst);
}
-
- inst.error = NULL;
- return FAIL;
}
static void
-do_vfp_nsyn_mla_mls (enum neon_shape rs)
+do_neon_bitfield (void)
{
- int is_mla = (inst.instruction & 0x0fffffff) == N_MNEM_vmla;
-
- if (rs == NS_FFF)
- {
- if (is_mla)
- do_vfp_nsyn_opcode ("fmacs");
- else
- do_vfp_nsyn_opcode ("fnmacs");
- }
- else
- {
- if (is_mla)
- do_vfp_nsyn_opcode ("fmacd");
- else
- do_vfp_nsyn_opcode ("fnmacd");
- }
+ enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
+ neon_check_type (3, rs, N_IGNORE_TYPE);
+ neon_three_same (neon_quad (rs), 0, -1);
}
static void
-do_vfp_nsyn_fma_fms (enum neon_shape rs)
+neon_dyadic_misc (enum neon_el_type ubit_meaning, unsigned types,
+ unsigned destbits)
{
- int is_fma = (inst.instruction & 0x0fffffff) == N_MNEM_vfma;
-
- if (rs == NS_FFF)
+ 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);
+ if (et.type == NT_float)
{
- if (is_fma)
- do_vfp_nsyn_opcode ("ffmas");
- else
- do_vfp_nsyn_opcode ("ffnmas");
+ NEON_ENCODE (FLOAT, inst);
+ neon_three_same (neon_quad (rs), 0, et.size == 16 ? (int) et.size : -1);
}
else
{
- if (is_fma)
- do_vfp_nsyn_opcode ("ffmad");
- else
- do_vfp_nsyn_opcode ("ffnmad");
+ NEON_ENCODE (INTEGER, inst);
+ neon_three_same (neon_quad (rs), et.type == ubit_meaning, et.size);
}
}
static void
-do_vfp_nsyn_mul (enum neon_shape rs)
+do_neon_dyadic_if_su (void)
{
- if (rs == NS_FFF)
- do_vfp_nsyn_opcode ("fmuls");
- else
- do_vfp_nsyn_opcode ("fmuld");
+ neon_dyadic_misc (NT_unsigned, N_SUF_32, 0);
}
static void
-do_vfp_nsyn_abs_neg (enum neon_shape rs)
+do_neon_dyadic_if_su_d (void)
{
- int is_neg = (inst.instruction & 0x80) != 0;
- neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_VFP | N_KEY);
-
- if (rs == NS_FF)
- {
- if (is_neg)
- do_vfp_nsyn_opcode ("fnegs");
- else
- do_vfp_nsyn_opcode ("fabss");
- }
- else
- {
- if (is_neg)
- do_vfp_nsyn_opcode ("fnegd");
- else
- do_vfp_nsyn_opcode ("fabsd");
- }
+ /* This version only allow D registers, but that constraint is enforced during
+ operand parsing so we don't need to do anything extra here. */
+ neon_dyadic_misc (NT_unsigned, N_SUF_32, 0);
}
-/* Encode single-precision (only!) VFP fldm/fstm instructions. Double precision
- insns belong to Neon, and are handled elsewhere. */
-
static void
-do_vfp_nsyn_ldm_stm (int is_dbmode)
+do_neon_dyadic_if_i_d (void)
{
- int is_ldm = (inst.instruction & (1 << 20)) != 0;
- if (is_ldm)
- {
- if (is_dbmode)
- do_vfp_nsyn_opcode ("fldmdbs");
- else
- do_vfp_nsyn_opcode ("fldmias");
- }
- else
- {
- if (is_dbmode)
- do_vfp_nsyn_opcode ("fstmdbs");
- else
- do_vfp_nsyn_opcode ("fstmias");
- }
+ /* The "untyped" case can't happen. Do this to stop the "U" bit being
+ affected if we specify unsigned args. */
+ neon_dyadic_misc (NT_untyped, N_IF_32, 0);
}
-static void
-do_vfp_nsyn_sqrt (void)
+enum vfp_or_neon_is_neon_bits
{
- enum neon_shape rs = neon_select_shape (NS_FF, NS_DD, NS_NULL);
- neon_check_type (2, rs, N_EQK | N_VFP, N_F32 | N_F64 | N_KEY | N_VFP);
+ NEON_CHECK_CC = 1,
+ NEON_CHECK_ARCH = 2,
+ NEON_CHECK_ARCH8 = 4
+};
- if (rs == NS_FF)
- do_vfp_nsyn_opcode ("fsqrts");
- else
- do_vfp_nsyn_opcode ("fsqrtd");
-}
+/* Call this function if an instruction which may have belonged to the VFP or
+ Neon instruction sets, but turned out to be a Neon instruction (due to the
+ operand types involved, etc.). We have to check and/or fix-up a couple of
+ things:
-static void
-do_vfp_nsyn_div (void)
-{
- enum neon_shape rs = neon_select_shape (NS_FFF, NS_DDD, NS_NULL);
- neon_check_type (3, rs, N_EQK | N_VFP, N_EQK | N_VFP,
- N_F32 | N_F64 | N_KEY | N_VFP);
+ - Make sure the user hasn't attempted to make a Neon instruction
+ conditional.
+ - Alter the value in the condition code field if necessary.
+ - Make sure that the arch supports Neon instructions.
- if (rs == NS_FFF)
- do_vfp_nsyn_opcode ("fdivs");
- else
- do_vfp_nsyn_opcode ("fdivd");
-}
+ Which of these operations take place depends on bits from enum
+ vfp_or_neon_is_neon_bits.
-static void
-do_vfp_nsyn_nmul (void)
-{
- enum neon_shape rs = neon_select_shape (NS_FFF, NS_DDD, NS_NULL);
- neon_check_type (3, rs, N_EQK | N_VFP, N_EQK | N_VFP,
- N_F32 | N_F64 | N_KEY | N_VFP);
+ WARNING: This function has side effects! If NEON_CHECK_CC is used and the
+ current instruction's condition is COND_ALWAYS, the condition field is
+ changed to inst.uncond_value. This is necessary because instructions shared
+ between VFP and Neon may be conditional for the VFP variants only, and the
+ unconditional Neon version must have, e.g., 0xF in the condition field. */
- if (rs == NS_FFF)
+static int
+vfp_or_neon_is_neon (unsigned check)
+{
+ /* Conditions are always legal in Thumb mode (IT blocks). */
+ if (!thumb_mode && (check & NEON_CHECK_CC))
{
- NEON_ENCODE (SINGLE, inst);
- do_vfp_sp_dyadic ();
+ if (inst.cond != COND_ALWAYS)
+ {
+ first_error (_(BAD_COND));
+ return FAIL;
+ }
+ if (inst.uncond_value != -1)
+ inst.instruction |= inst.uncond_value << 28;
}
- else
+
+ if ((check & NEON_CHECK_ARCH)
+ && !mark_feature_used (&fpu_neon_ext_v1))
{
- NEON_ENCODE (DOUBLE, inst);
- do_vfp_dp_rd_rn_rm ();
+ first_error (_(BAD_FPU));
+ return FAIL;
}
- do_vfp_cond_or_thumb ();
-}
-static void
-do_vfp_nsyn_cmp (void)
-{
- if (inst.operands[1].isreg)
+ if ((check & NEON_CHECK_ARCH8)
+ && !mark_feature_used (&fpu_neon_ext_armv8))
{
- enum neon_shape rs = neon_select_shape (NS_FF, NS_DD, NS_NULL);
- 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 ();
- }
- else
- {
- NEON_ENCODE (DOUBLE, inst);
- do_vfp_dp_rd_rm ();
- }
+ first_error (_(BAD_FPU));
+ return FAIL;
}
- else
- {
- enum neon_shape rs = neon_select_shape (NS_FI, NS_DI, NS_NULL);
- 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 ();
- }
-
- if (rs == NS_FI)
- {
- NEON_ENCODE (SINGLE, inst);
- do_vfp_sp_compare_z ();
- }
- else
- {
- NEON_ENCODE (DOUBLE, inst);
- do_vfp_dp_rd ();
- }
- }
- do_vfp_cond_or_thumb ();
+ return SUCCESS;
}
static void
-nsyn_insert_sp (void)
+do_neon_addsub_if_i (void)
{
- inst.operands[1] = inst.operands[0];
- memset (&inst.operands[0], '\0', sizeof (inst.operands[0]));
- inst.operands[0].reg = REG_SP;
- inst.operands[0].isreg = 1;
- inst.operands[0].writeback = 1;
- inst.operands[0].present = 1;
-}
+ if (try_vfp_nsyn (3, do_vfp_nsyn_add_sub) == SUCCESS)
+ return;
-static void
-do_vfp_nsyn_push (void)
-{
- nsyn_insert_sp ();
- if (inst.operands[1].issingle)
- do_vfp_nsyn_opcode ("fstmdbs");
- else
- do_vfp_nsyn_opcode ("fstmdbd");
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+
+ /* The "untyped" case can't happen. Do this to stop the "U" bit being
+ affected if we specify unsigned args. */
+ neon_dyadic_misc (NT_untyped, N_IF_32 | N_I64, 0);
}
+/* Swaps operands 1 and 2. If operand 1 (optional arg) was omitted, we want the
+ result to be:
+ V<op> A,B (A is operand 0, B is operand 2)
+ to mean:
+ V<op> A,B,A
+ not:
+ V<op> A,B,B
+ so handle that case specially. */
+
static void
-do_vfp_nsyn_pop (void)
+neon_exchange_operands (void)
{
- nsyn_insert_sp ();
- if (inst.operands[1].issingle)
- do_vfp_nsyn_opcode ("fldmias");
+ if (inst.operands[1].present)
+ {
+ void *scratch = xmalloc (sizeof (inst.operands[0]));
+
+ /* Swap operands[1] and operands[2]. */
+ memcpy (scratch, &inst.operands[1], sizeof (inst.operands[0]));
+ inst.operands[1] = inst.operands[2];
+ memcpy (&inst.operands[2], scratch, sizeof (inst.operands[0]));
+ free (scratch);
+ }
else
- do_vfp_nsyn_opcode ("fldmiad");
+ {
+ inst.operands[1] = inst.operands[2];
+ inst.operands[2] = inst.operands[0];
+ }
}
-/* Fix up Neon data-processing instructions, ORing in the correct bits for
- ARM mode or Thumb mode and moving the encoded bit 24 to bit 28. */
-
static void
-neon_dp_fixup (struct arm_it* insn)
+neon_compare (unsigned regtypes, unsigned immtypes, int invert)
{
- unsigned int i = insn->instruction;
- insn->is_neon = 1;
-
- if (thumb_mode)
+ if (inst.operands[2].isreg)
+ {
+ if (invert)
+ neon_exchange_operands ();
+ neon_dyadic_misc (NT_unsigned, regtypes, N_SIZ);
+ }
+ else
{
- /* The U bit is at bit 24 by default. Move to bit 28 in Thumb mode. */
- if (i & (1 << 24))
- i |= 1 << 28;
+ 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);
- i &= ~(1 << 24);
+ NEON_ENCODE (IMMED, 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;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= (et.type == NT_float) << 10;
+ inst.instruction |= neon_logbits (et.size) << 18;
- i |= 0xef000000;
+ neon_dp_fixup (&inst);
}
- else
- i |= 0xf2000000;
-
- insn->instruction = i;
}
-/* Turn a size (8, 16, 32, 64) into the respective bit number minus 3
- (0, 1, 2, 3). */
-
-static unsigned
-neon_logbits (unsigned x)
+static void
+do_neon_cmp (void)
{
- return ffs (x) - 4;
+ neon_compare (N_SUF_32, N_S_32 | N_F_16_32, FALSE);
}
-#define LOW4(R) ((R) & 0xf)
-#define HI1(R) (((R) >> 4) & 1)
-
-/* Encode insns with bit pattern:
-
- |28/24|23|22 |21 20|19 16|15 12|11 8|7|6|5|4|3 0|
- | U |x |D |size | Rn | Rd |x x x x|N|Q|M|x| Rm |
-
- SIZE is passed in bits. -1 means size field isn't changed, in case it has a
- different meaning for some instruction. */
-
static void
-neon_three_same (int isquad, int ubit, int size)
+do_neon_cmp_inv (void)
{
- inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
- inst.instruction |= HI1 (inst.operands[0].reg) << 22;
- inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
- inst.instruction |= HI1 (inst.operands[1].reg) << 7;
- inst.instruction |= LOW4 (inst.operands[2].reg);
- inst.instruction |= HI1 (inst.operands[2].reg) << 5;
- inst.instruction |= (isquad != 0) << 6;
- inst.instruction |= (ubit != 0) << 24;
- if (size != -1)
- inst.instruction |= neon_logbits (size) << 20;
+ neon_compare (N_SUF_32, N_S_32 | N_F_16_32, TRUE);
+}
- neon_dp_fixup (&inst);
+static void
+do_neon_ceq (void)
+{
+ neon_compare (N_IF_32, N_IF_32, FALSE);
}
-/* Encode instructions of the form:
+/* For multiply instructions, we have the possibility of 16-bit or 32-bit
+ scalars, which are encoded in 5 bits, M : Rm.
+ For 16-bit scalars, the register is encoded in Rm[2:0] and the index in
+ M:Rm[3], and for 32-bit scalars, the register is encoded in Rm[3:0] and the
+ index in M. */
- |28/24|23|22|21 20|19 18|17 16|15 12|11 7|6|5|4|3 0|
- | U |x |D |x x |size |x x | Rd |x x x x x|Q|M|x| Rm |
+static unsigned
+neon_scalar_for_mul (unsigned scalar, unsigned elsize)
+{
+ unsigned regno = NEON_SCALAR_REG (scalar);
+ unsigned elno = NEON_SCALAR_INDEX (scalar);
- Don't write size if SIZE == -1. */
+ switch (elsize)
+ {
+ case 16:
+ if (regno > 7 || elno > 3)
+ goto bad_scalar;
+ return regno | (elno << 3);
-static void
-neon_two_same (int qbit, int ubit, int size)
-{
- 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 |= (qbit != 0) << 6;
- inst.instruction |= (ubit != 0) << 24;
+ case 32:
+ if (regno > 15 || elno > 1)
+ goto bad_scalar;
+ return regno | (elno << 4);
- if (size != -1)
- inst.instruction |= neon_logbits (size) << 18;
+ default:
+ bad_scalar:
+ first_error (_("scalar out of range for multiply instruction"));
+ }
- neon_dp_fixup (&inst);
+ return 0;
}
-/* Neon instruction encoders, in approximate order of appearance. */
+/* Encode multiply / multiply-accumulate scalar instructions. */
static void
-do_neon_dyadic_i_su (void)
+neon_mul_mac (struct neon_type_el et, int ubit)
{
- 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_SU_32 | N_KEY);
- neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
-}
+ unsigned scalar;
-static void
-do_neon_dyadic_i64_su (void)
-{
- 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_SU_ALL | N_KEY);
- neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
-}
+ /* Give a more helpful error message if we have an invalid type. */
+ if (et.type == NT_invtype)
+ return;
-static void
-neon_imm_shift (int write_ubit, int uval, int isquad, struct neon_type_el et,
- unsigned immbits)
-{
- unsigned size = et.size >> 3;
+ scalar = neon_scalar_for_mul (inst.operands[2].reg, et.size);
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 |= (isquad != 0) << 6;
- inst.instruction |= immbits << 16;
- inst.instruction |= (size >> 3) << 7;
- inst.instruction |= (size & 0x7) << 19;
- if (write_ubit)
- inst.instruction |= (uval != 0) << 24;
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
+ inst.instruction |= HI1 (inst.operands[1].reg) << 7;
+ inst.instruction |= LOW4 (scalar);
+ inst.instruction |= HI1 (scalar) << 5;
+ inst.instruction |= (et.type == NT_float) << 8;
+ inst.instruction |= neon_logbits (et.size) << 20;
+ inst.instruction |= (ubit != 0) << 24;
neon_dp_fixup (&inst);
}
static void
-do_neon_shl_imm (void)
+do_neon_mac_maybe_scalar (void)
{
- if (!inst.operands[2].isreg)
+ if (try_vfp_nsyn (3, do_vfp_nsyn_mla_mls) == SUCCESS)
+ return;
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+
+ if (inst.operands[2].isscalar)
{
- 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_KEY | N_I_ALL);
- NEON_ENCODE (IMMED, inst);
- neon_imm_shift (FALSE, 0, neon_quad (rs), et, inst.operands[2].imm);
+ 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_F_16_32 | N_KEY);
+ NEON_ENCODE (SCALAR, inst);
+ neon_mul_mac (et, neon_quad (rs));
}
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);
- 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. */
- tmp = inst.operands[2].reg;
- inst.operands[2].reg = inst.operands[1].reg;
- inst.operands[1].reg = tmp;
- NEON_ENCODE (INTEGER, inst);
- neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
+ /* The "untyped" case can't happen. Do this to stop the "U" bit being
+ affected if we specify unsigned args. */
+ neon_dyadic_misc (NT_untyped, N_IF_32, 0);
}
}
static void
-do_neon_qshl_imm (void)
+do_neon_fmac (void)
{
- if (!inst.operands[2].isreg)
- {
- 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_SU_ALL | N_KEY);
+ if (try_vfp_nsyn (3, do_vfp_nsyn_fma_fms) == SUCCESS)
+ return;
- NEON_ENCODE (IMMED, inst);
- neon_imm_shift (TRUE, et.type == NT_unsigned, neon_quad (rs), et,
- 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);
- unsigned int tmp;
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
- /* See note in do_neon_shl_imm. */
- tmp = inst.operands[2].reg;
- inst.operands[2].reg = inst.operands[1].reg;
- inst.operands[1].reg = tmp;
- NEON_ENCODE (INTEGER, inst);
- neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
- }
+ neon_dyadic_misc (NT_untyped, N_IF_32, 0);
}
static void
-do_neon_rshl (void)
+do_neon_tst (void)
{
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_SU_ALL | N_KEY);
- unsigned int tmp;
-
- tmp = inst.operands[2].reg;
- inst.operands[2].reg = inst.operands[1].reg;
- inst.operands[1].reg = tmp;
- neon_three_same (neon_quad (rs), et.type == NT_unsigned, et.size);
+ N_EQK, N_EQK, N_8 | N_16 | N_32 | N_KEY);
+ neon_three_same (neon_quad (rs), 0, et.size);
}
-static int
-neon_cmode_for_logic_imm (unsigned immediate, unsigned *immbits, int size)
+/* VMUL with 3 registers allows the P8 type. The scalar version supports the
+ same types as the MAC equivalents. The polynomial type for this instruction
+ is encoded the same as the integer type. */
+
+static void
+do_neon_mul (void)
{
- /* Handle .I8 pseudo-instructions. */
- 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. */
- immediate |= immediate << 8;
- size = 16;
- }
+ if (try_vfp_nsyn (3, do_vfp_nsyn_mul) == SUCCESS)
+ return;
- if (size >= 32)
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+
+ if (inst.operands[2].isscalar)
+ do_neon_mac_maybe_scalar ();
+ else
+ neon_dyadic_misc (NT_poly, N_I8 | N_I16 | N_I32 | N_F16 | N_F32 | N_P8, 0);
+}
+
+static void
+do_neon_qdmulh (void)
+{
+ if (inst.operands[2].isscalar)
{
- if (immediate == (immediate & 0x000000ff))
- {
- *immbits = immediate;
- return 0x1;
- }
- else if (immediate == (immediate & 0x0000ff00))
- {
- *immbits = immediate >> 8;
- return 0x3;
- }
- else if (immediate == (immediate & 0x00ff0000))
- {
- *immbits = immediate >> 16;
- return 0x5;
- }
- else if (immediate == (immediate & 0xff000000))
- {
- *immbits = immediate >> 24;
- return 0x7;
- }
- if ((immediate & 0xffff) != (immediate >> 16))
- goto bad_immediate;
- immediate &= 0xffff;
+ 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);
+ NEON_ENCODE (SCALAR, inst);
+ neon_mul_mac (et, neon_quad (rs));
}
-
- if (immediate == (immediate & 0x000000ff))
+ else
{
- *immbits = immediate;
- return 0x9;
+ 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);
+ NEON_ENCODE (INTEGER, inst);
+ /* The U bit (rounding) comes from bit mask. */
+ neon_three_same (neon_quad (rs), 0, et.size);
}
- else if (immediate == (immediate & 0x0000ff00))
+}
+
+static void
+do_neon_qrdmlah (void)
+{
+ /* Check we're on the correct architecture. */
+ if (!mark_feature_used (&fpu_neon_ext_armv8))
+ inst.error =
+ _("instruction form not available on this architecture.");
+ else if (!mark_feature_used (&fpu_neon_ext_v8_1))
{
- *immbits = immediate >> 8;
- return 0xb;
+ as_warn (_("this instruction implies use of ARMv8.1 AdvSIMD."));
+ record_feature_use (&fpu_neon_ext_v8_1);
}
- bad_immediate:
- first_error (_("immediate value out of range"));
- return FAIL;
+ if (inst.operands[2].isscalar)
+ {
+ 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);
+ NEON_ENCODE (SCALAR, inst);
+ neon_mul_mac (et, neon_quad (rs));
+ }
+ 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_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);
+ }
}
-/* True if IMM has form 0bAAAAAAAABBBBBBBBCCCCCCCCDDDDDDDD for bits
- A, B, C, D. */
-
-static int
-neon_bits_same_in_bytes (unsigned imm)
+static void
+do_neon_fcmp_absolute (void)
{
- 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);
+ 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_F_16_32 | N_KEY);
+ /* Size field comes from bit mask. */
+ neon_three_same (neon_quad (rs), 1, et.size == 16 ? (int) et.size : -1);
}
-/* For immediate of above form, return 0bABCD. */
-
-static unsigned
-neon_squash_bits (unsigned imm)
+static void
+do_neon_fcmp_absolute_inv (void)
{
- return (imm & 0x01) | ((imm & 0x0100) >> 7) | ((imm & 0x010000) >> 14)
- | ((imm & 0x01000000) >> 21);
+ neon_exchange_operands ();
+ do_neon_fcmp_absolute ();
}
-/* Compress quarter-float representation to 0b...000 abcdefgh. */
-
-static unsigned
-neon_qfloat_bits (unsigned imm)
+static void
+do_neon_step (void)
{
- return ((imm >> 19) & 0x7f) | ((imm >> 24) & 0x80);
+ 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_F_16_32 | N_KEY);
+ neon_three_same (neon_quad (rs), 0, et.size == 16 ? (int) et.size : -1);
}
-/* Returns CMODE. IMMBITS [7:0] is set to bits suitable for inserting into
- the instruction. *OP is passed as the initial value of the op field, and
- may be set to a different value depending on the constant (i.e.
- "MOV I64, 0bAAAAAAAABBBB..." which uses OP = 1 despite being MOV not
- MVN). If the immediate looks like a repeated pattern then also
- try smaller element sizes. */
-
-static int
-neon_cmode_for_move_imm (unsigned immlo, unsigned immhi, int float_p,
- unsigned *immbits, int *op, int size,
- enum neon_el_type type)
+static void
+do_neon_abs_neg (void)
{
- /* Only permit float immediates (including 0.0/-0.0) if the operand type is
- float. */
- if (type == NT_float && !float_p)
- return FAIL;
+ enum neon_shape rs;
+ struct neon_type_el et;
- if (type == NT_float && is_quarter_float (immlo) && immhi == 0)
- {
- if (size != 32 || *op == 1)
- return FAIL;
- *immbits = neon_qfloat_bits (immlo);
- return 0xf;
- }
+ if (try_vfp_nsyn (2, do_vfp_nsyn_abs_neg) == SUCCESS)
+ return;
- if (size == 64)
- {
- if (neon_bits_same_in_bytes (immhi)
- && neon_bits_same_in_bytes (immlo))
- {
- if (*op == 1)
- return FAIL;
- *immbits = (neon_squash_bits (immhi) << 4)
- | neon_squash_bits (immlo);
- *op = 1;
- return 0xe;
- }
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
- if (immhi != immlo)
- return FAIL;
- }
+ rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ et = neon_check_type (2, rs, N_EQK, N_S_32 | N_F_16_32 | N_KEY);
- if (size >= 32)
- {
- if (immlo == (immlo & 0x000000ff))
- {
- *immbits = immlo;
- return 0x0;
- }
- else if (immlo == (immlo & 0x0000ff00))
- {
- *immbits = immlo >> 8;
- return 0x2;
- }
- else if (immlo == (immlo & 0x00ff0000))
- {
- *immbits = immlo >> 16;
- return 0x4;
- }
- else if (immlo == (immlo & 0xff000000))
- {
- *immbits = immlo >> 24;
- return 0x6;
- }
- else if (immlo == ((immlo & 0x0000ff00) | 0x000000ff))
- {
- *immbits = (immlo >> 8) & 0xff;
- return 0xc;
- }
- else if (immlo == ((immlo & 0x00ff0000) | 0x0000ffff))
- {
- *immbits = (immlo >> 16) & 0xff;
- return 0xd;
- }
+ 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 |= (et.type == NT_float) << 10;
+ inst.instruction |= neon_logbits (et.size) << 18;
- if ((immlo & 0xffff) != (immlo >> 16))
- return FAIL;
- immlo &= 0xffff;
- }
+ neon_dp_fixup (&inst);
+}
- if (size >= 16)
- {
- if (immlo == (immlo & 0x000000ff))
- {
- *immbits = immlo;
- return 0x8;
- }
- else if (immlo == (immlo & 0x0000ff00))
- {
- *immbits = immlo >> 8;
- return 0xa;
- }
+static void
+do_neon_sli (void)
+{
+ 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_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"));
+ neon_imm_shift (FALSE, 0, neon_quad (rs), et, imm);
+}
- if ((immlo & 0xff) != (immlo >> 8))
- return FAIL;
- immlo &= 0xff;
- }
+static void
+do_neon_sri (void)
+{
+ 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_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"));
+ neon_imm_shift (FALSE, 0, neon_quad (rs), et, et.size - imm);
+}
- if (immlo == (immlo & 0x000000ff))
+static void
+do_neon_qshlu_imm (void)
+{
+ 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_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"));
+ /* 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. */
+ inst.instruction |= (et.type == NT_unsigned) << 8;
+ /* The rest of the bits are the same as other immediate shifts. */
+ neon_imm_shift (FALSE, 0, neon_quad (rs), et, imm);
+}
+
+static void
+do_neon_qmovn (void)
+{
+ struct neon_type_el et = neon_check_type (2, NS_DQ,
+ N_EQK | N_HLF, N_SU_16_64 | N_KEY);
+ /* Saturating move where operands can be signed or unsigned, and the
+ destination has the same signedness. */
+ NEON_ENCODE (INTEGER, inst);
+ if (et.type == NT_unsigned)
+ inst.instruction |= 0xc0;
+ else
+ inst.instruction |= 0x80;
+ neon_two_same (0, 1, et.size / 2);
+}
+
+static void
+do_neon_qmovun (void)
+{
+ struct neon_type_el et = neon_check_type (2, NS_DQ,
+ N_EQK | N_HLF | N_UNS, N_S16 | N_S32 | N_S64 | N_KEY);
+ /* Saturating move with unsigned results. Operands must be signed. */
+ NEON_ENCODE (INTEGER, inst);
+ neon_two_same (0, 1, et.size / 2);
+}
+
+static void
+do_neon_rshift_sat_narrow (void)
+{
+ /* FIXME: Types for narrowing. If operands are signed, results can be signed
+ or unsigned. If operands are unsigned, results must also be unsigned. */
+ struct neon_type_el et = neon_check_type (2, NS_DQI,
+ N_EQK | N_HLF, N_SU_16_64 | N_KEY);
+ int imm = inst.operands[2].imm;
+ /* This gets the bounds check, size encoding and immediate bits calculation
+ right. */
+ et.size /= 2;
+
+ /* VQ{R}SHRN.I<size> <Dd>, <Qm>, #0 is a synonym for
+ VQMOVN.I<size> <Dd>, <Qm>. */
+ if (imm == 0)
{
- /* Don't allow MVN with 8-bit immediate. */
- if (*op == 1)
- return FAIL;
- *immbits = immlo;
- return 0xe;
+ inst.operands[2].present = 0;
+ inst.instruction = N_MNEM_vqmovn;
+ do_neon_qmovn ();
+ return;
}
- return FAIL;
+ constraint (imm < 1 || (unsigned)imm > et.size,
+ _("immediate out of range"));
+ neon_imm_shift (TRUE, et.type == NT_unsigned, 0, et, et.size - imm);
}
-/* Write immediate bits [7:0] to the following locations:
+static void
+do_neon_rshift_sat_narrow_u (void)
+{
+ /* FIXME: Types for narrowing. If operands are signed, results can be signed
+ or unsigned. If operands are unsigned, results must also be unsigned. */
+ struct neon_type_el et = neon_check_type (2, NS_DQI,
+ N_EQK | N_HLF | N_UNS, N_S16 | N_S32 | N_S64 | N_KEY);
+ int imm = inst.operands[2].imm;
+ /* This gets the bounds check, size encoding and immediate bits calculation
+ right. */
+ et.size /= 2;
- |28/24|23 19|18 16|15 4|3 0|
- | a |x x x x x|b c d|x x x x x x x x x x x x|e f g h|
+ /* VQSHRUN.I<size> <Dd>, <Qm>, #0 is a synonym for
+ VQMOVUN.I<size> <Dd>, <Qm>. */
+ if (imm == 0)
+ {
+ inst.operands[2].present = 0;
+ inst.instruction = N_MNEM_vqmovun;
+ do_neon_qmovun ();
+ return;
+ }
- This function is used by VMOV/VMVN/VORR/VBIC. */
+ constraint (imm < 1 || (unsigned)imm > et.size,
+ _("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. */
+ neon_imm_shift (TRUE, 1, 0, et, et.size - imm);
+}
static void
-neon_write_immbits (unsigned immbits)
+do_neon_movn (void)
{
- inst.instruction |= immbits & 0xf;
- inst.instruction |= ((immbits >> 4) & 0x7) << 16;
- inst.instruction |= ((immbits >> 7) & 0x1) << 24;
+ struct neon_type_el et = neon_check_type (2, NS_DQ,
+ N_EQK | N_HLF, N_I16 | N_I32 | N_I64 | N_KEY);
+ NEON_ENCODE (INTEGER, inst);
+ neon_two_same (0, 1, et.size / 2);
}
-/* Invert low-order SIZE bits of XHI:XLO. */
-
static void
-neon_invert_size (unsigned *xlo, unsigned *xhi, int size)
+do_neon_rshift_narrow (void)
{
- unsigned immlo = xlo ? *xlo : 0;
- unsigned immhi = xhi ? *xhi : 0;
+ struct neon_type_el et = neon_check_type (2, NS_DQI,
+ N_EQK | N_HLF, N_I16 | N_I32 | N_I64 | N_KEY);
+ int imm = inst.operands[2].imm;
+ /* This gets the bounds check, size encoding and immediate bits calculation
+ right. */
+ et.size /= 2;
- switch (size)
+ /* If immediate is zero then we are a pseudo-instruction for
+ VMOVN.I<size> <Dd>, <Qm> */
+ if (imm == 0)
{
- case 8:
- immlo = (~immlo) & 0xff;
- break;
-
- case 16:
- immlo = (~immlo) & 0xffff;
- break;
-
- case 64:
- immhi = (~immhi) & 0xffffffff;
- /* fall through. */
-
- case 32:
- immlo = (~immlo) & 0xffffffff;
- break;
-
- default:
- abort ();
+ inst.operands[2].present = 0;
+ inst.instruction = N_MNEM_vmovn;
+ do_neon_movn ();
+ return;
}
- if (xlo)
- *xlo = immlo;
-
- if (xhi)
- *xhi = immhi;
+ constraint (imm < 1 || (unsigned)imm > et.size,
+ _("immediate out of range for narrowing operation"));
+ neon_imm_shift (FALSE, 0, 0, et, et.size - imm);
}
static void
-do_neon_logic (void)
+do_neon_shll (void)
{
- if (inst.operands[2].present && inst.operands[2].isreg)
+ /* FIXME: Type checking when lengthening. */
+ struct neon_type_el et = neon_check_type (2, NS_QDI,
+ N_EQK | N_DBL, N_I8 | N_I16 | N_I32 | N_KEY);
+ unsigned imm = inst.operands[2].imm;
+
+ if (imm == et.size)
{
- enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
- neon_check_type (3, rs, N_IGNORE_TYPE);
- /* U bit and size field were set as part of the bitmask. */
+ /* Maximum shift variant. */
NEON_ENCODE (INTEGER, inst);
- neon_three_same (neon_quad (rs), 0, -1);
+ 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_logbits (et.size) << 18;
+
+ neon_dp_fixup (&inst);
}
else
{
- const int three_ops_form = (inst.operands[2].present
- && !inst.operands[2].isreg);
- const int immoperand = (three_ops_form ? 2 : 1);
- enum neon_shape rs = (three_ops_form
- ? 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);
- enum neon_opc opcode = (enum neon_opc) inst.instruction & 0x0fffffff;
- unsigned immbits;
- int cmode;
-
- if (et.type == NT_invtype)
- return;
+ /* 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);
+ NEON_ENCODE (IMMED, inst);
+ neon_imm_shift (TRUE, et.type == NT_unsigned, 0, et, imm);
+ }
+}
- if (three_ops_form)
- constraint (inst.operands[0].reg != inst.operands[1].reg,
- _("first and second operands shall be the same register"));
+/* Check the various types for the VCVT instruction, and return which version
+ the current instruction is. */
- NEON_ENCODE (IMMED, inst);
+#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 (s16_f16, N_S16, N_F16 | N_KEY, whole_reg, NULL, NULL, NULL) \
+ CVT_VAR (u16_f16, N_U16, N_F16 | N_KEY, whole_reg, NULL, NULL, NULL) \
+ CVT_VAR (f16_s16, N_F16 | N_KEY, N_S16, whole_reg, NULL, NULL, NULL) \
+ CVT_VAR (f16_u16, N_F16 | N_KEY, N_U16, whole_reg, NULL, NULL, NULL) \
+ 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) \
+ /* New VCVT instructions introduced by ARMv8.2 fp16 extension. \
+ Compared with single/double precision variants, only the co-processor \
+ field is different, so the encoding flow is reused here. */ \
+ CVT_VAR (f16_s32, N_F16 | N_KEY, N_S32, N_VFP, "fsltos", "fsitos", NULL) \
+ CVT_VAR (f16_u32, N_F16 | N_KEY, N_U32, N_VFP, "fultos", "fuitos", NULL) \
+ CVT_VAR (u32_f16, N_U32, N_F16 | N_KEY, N_VFP, "ftouls", "ftouis", "ftouizs")\
+ CVT_VAR (s32_f16, N_S32, N_F16 | N_KEY, N_VFP, "ftosls", "ftosis", "ftosizs")\
+ /* 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
+};
- immbits = inst.operands[immoperand].imm;
- if (et.size == 64)
- {
- /* .i64 is a pseudo-op, so the immediate must be a repeating
- pattern. */
- if (immbits != (inst.operands[immoperand].regisimm ?
- inst.operands[immoperand].reg : 0))
- {
- /* Set immbits to an invalid constant. */
- immbits = 0xdeadbeef;
- }
- }
+#undef CVT_VAR
- 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 ();
- }
+static enum neon_cvt_flavour
+get_neon_cvt_flavour (enum neon_shape rs)
+{
+#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); \
+ }
- if (cmode == FAIL)
- return;
+ struct neon_type_el et;
+ unsigned whole_reg = (rs == NS_FFI || rs == NS_FD || rs == NS_DF
+ || 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;
- inst.instruction |= neon_quad (rs) << 6;
- inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
- inst.instruction |= HI1 (inst.operands[0].reg) << 22;
- inst.instruction |= cmode << 8;
- neon_write_immbits (immbits);
+ CVT_FLAVOUR_VAR;
- neon_dp_fixup (&inst);
- }
+ return neon_cvt_flavour_invalid;
+#undef CVT_VAR
}
-static void
-do_neon_bitfield (void)
+enum neon_cvt_mode
{
- enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
- neon_check_type (3, rs, N_IGNORE_TYPE);
- neon_three_same (neon_quad (rs), 0, -1);
-}
+ 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
-neon_dyadic_misc (enum neon_el_type ubit_meaning, unsigned types,
- unsigned destbits)
+do_vfp_nsyn_cvt (enum neon_shape rs, enum neon_cvt_flavour flavour)
{
- 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);
- if (et.type == NT_float)
+ const char *opname = 0;
+
+ if (rs == NS_DDI || rs == NS_QQI || rs == NS_FFI
+ || rs == NS_FHI || rs == NS_HFI)
{
- NEON_ENCODE (FLOAT, inst);
- neon_three_same (neon_quad (rs), 0, -1);
+ /* Conversions with immediate bitshift. */
+ const char *enc[] =
+ {
+#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
{
- NEON_ENCODE (INTEGER, inst);
- neon_three_same (neon_quad (rs), et.type == ubit_meaning, et.size);
+ /* Conversions without bitshift. */
+ const char *enc[] =
+ {
+#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];
}
-}
-static void
-do_neon_dyadic_if_su (void)
-{
- neon_dyadic_misc (NT_unsigned, N_SUF_32, 0);
-}
+ if (opname)
+ do_vfp_nsyn_opcode (opname);
-static void
-do_neon_dyadic_if_su_d (void)
-{
- /* This version only allow D registers, but that constraint is enforced during
- operand parsing so we don't need to do anything extra here. */
- neon_dyadic_misc (NT_unsigned, N_SUF_32, 0);
+ /* ARMv8.2 fp16 VCVT instruction. */
+ if (flavour == neon_cvt_flavour_s32_f16
+ || flavour == neon_cvt_flavour_u32_f16
+ || flavour == neon_cvt_flavour_f16_u32
+ || flavour == neon_cvt_flavour_f16_s32)
+ do_scalar_fp16_v82_encode ();
}
static void
-do_neon_dyadic_if_i_d (void)
+do_vfp_nsyn_cvtz (void)
{
- /* The "untyped" case can't happen. Do this to stop the "U" bit being
- affected if we specify unsigned args. */
- neon_dyadic_misc (NT_untyped, N_IF_32, 0);
+ enum neon_shape rs = neon_select_shape (NS_FH, NS_FF, NS_FD, NS_NULL);
+ enum neon_cvt_flavour flavour = get_neon_cvt_flavour (rs);
+ const char *enc[] =
+ {
+#define CVT_VAR(C,A,B,R,BSN,CN,ZN) ZN,
+ CVT_FLAVOUR_VAR
+ NULL
+#undef CVT_VAR
+ };
+
+ if (flavour < (int) ARRAY_SIZE (enc) && enc[flavour])
+ do_vfp_nsyn_opcode (enc[flavour]);
}
-enum vfp_or_neon_is_neon_bits
+static void
+do_vfp_nsyn_cvt_fpv8 (enum neon_cvt_flavour flavour,
+ enum neon_cvt_mode mode)
{
- NEON_CHECK_CC = 1,
- NEON_CHECK_ARCH = 2
-};
-
-/* Call this function if an instruction which may have belonged to the VFP or
- Neon instruction sets, but turned out to be a Neon instruction (due to the
- operand types involved, etc.). We have to check and/or fix-up a couple of
- things:
+ int sz, op;
+ int rm;
- - Make sure the user hasn't attempted to make a Neon instruction
- conditional.
- - Alter the value in the condition code field if necessary.
- - Make sure that the arch supports Neon instructions.
+ /* Targets like FPv5-SP-D16 don't support FP v8 instructions with
+ D register operands. */
+ if (flavour == neon_cvt_flavour_s32_f64
+ || flavour == neon_cvt_flavour_u32_f64)
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_armv8),
+ _(BAD_FPU));
- Which of these operations take place depends on bits from enum
- vfp_or_neon_is_neon_bits.
+ if (flavour == neon_cvt_flavour_s32_f16
+ || flavour == neon_cvt_flavour_u32_f16)
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_fp16),
+ _(BAD_FP16));
- WARNING: This function has side effects! If NEON_CHECK_CC is used and the
- current instruction's condition is COND_ALWAYS, the condition field is
- changed to inst.uncond_value. This is necessary because instructions shared
- between VFP and Neon may be conditional for the VFP variants only, and the
- unconditional Neon version must have, e.g., 0xF in the condition field. */
+ set_it_insn_type (OUTSIDE_IT_INSN);
-static int
-vfp_or_neon_is_neon (unsigned check)
-{
- /* Conditions are always legal in Thumb mode (IT blocks). */
- if (!thumb_mode && (check & NEON_CHECK_CC))
+ switch (flavour)
{
- if (inst.cond != COND_ALWAYS)
- {
- first_error (_(BAD_COND));
- return FAIL;
- }
- if (inst.uncond_value != -1)
- inst.instruction |= inst.uncond_value << 28;
+ case neon_cvt_flavour_s32_f64:
+ sz = 1;
+ op = 1;
+ break;
+ case neon_cvt_flavour_s32_f32:
+ sz = 0;
+ op = 1;
+ break;
+ case neon_cvt_flavour_s32_f16:
+ 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;
+ case neon_cvt_flavour_u32_f16:
+ sz = 0;
+ op = 0;
+ break;
+ default:
+ first_error (_("invalid instruction shape"));
+ return;
}
- if ((check & NEON_CHECK_ARCH)
- && !ARM_CPU_HAS_FEATURE (cpu_variant, fpu_neon_ext_v1))
+ switch (mode)
{
- first_error (_(BAD_FPU));
- return FAIL;
+ 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;
}
- return SUCCESS;
+ 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;
+
+ /* ARMv8.2 fp16 VCVT instruction. */
+ if (flavour == neon_cvt_flavour_s32_f16
+ ||flavour == neon_cvt_flavour_u32_f16)
+ do_scalar_fp16_v82_encode ();
+ inst.instruction |= op << 7;
+ inst.instruction |= rm << 16;
+ inst.instruction |= 0xf0000000;
+ inst.is_neon = TRUE;
}
static void
-do_neon_addsub_if_i (void)
+do_neon_cvt_1 (enum neon_cvt_mode mode)
{
- if (try_vfp_nsyn (3, do_vfp_nsyn_add_sub) == SUCCESS)
- return;
+ 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_FH, NS_HF, NS_FHI, NS_HFI,
+ NS_NULL);
+ enum neon_cvt_flavour flavour = get_neon_cvt_flavour (rs);
- if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ if (flavour == neon_cvt_flavour_invalid)
return;
- /* The "untyped" case can't happen. Do this to stop the "U" bit being
- affected if we specify unsigned args. */
- neon_dyadic_misc (NT_untyped, N_IF_32 | N_I64, 0);
-}
-
-/* Swaps operands 1 and 2. If operand 1 (optional arg) was omitted, we want the
- result to be:
- V<op> A,B (A is operand 0, B is operand 2)
- to mean:
- V<op> A,B,A
- not:
- V<op> A,B,B
- so handle that case specially. */
-
-static void
-neon_exchange_operands (void)
-{
- void *scratch = alloca (sizeof (inst.operands[0]));
- if (inst.operands[1].present)
+ /* PR11109: Handle round-to-zero for VCVT conversions. */
+ if (mode == neon_cvt_mode_z
+ && ARM_CPU_HAS_FEATURE (cpu_variant, fpu_arch_vfp_v2)
+ && (flavour == neon_cvt_flavour_s16_f16
+ || flavour == neon_cvt_flavour_u16_f16
+ || 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))
{
- /* Swap operands[1] and operands[2]. */
- memcpy (scratch, &inst.operands[1], sizeof (inst.operands[0]));
- inst.operands[1] = inst.operands[2];
- memcpy (&inst.operands[2], scratch, sizeof (inst.operands[0]));
+ do_vfp_nsyn_cvtz ();
+ return;
}
- else
+
+ /* ARMv8.2 fp16 VCVT conversions. */
+ if (mode == neon_cvt_mode_z
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_fp16)
+ && (flavour == neon_cvt_flavour_s32_f16
+ || flavour == neon_cvt_flavour_u32_f16)
+ && (rs == NS_FH))
{
- inst.operands[1] = inst.operands[2];
- inst.operands[2] = inst.operands[0];
+ do_vfp_nsyn_cvtz ();
+ do_scalar_fp16_v82_encode ();
+ return;
}
-}
-static void
-neon_compare (unsigned regtypes, unsigned immtypes, int invert)
-{
- if (inst.operands[2].isreg)
+ /* VFP rather than Neon conversions. */
+ if (flavour >= neon_cvt_flavour_first_fp)
{
- if (invert)
- neon_exchange_operands ();
- neon_dyadic_misc (NT_unsigned, regtypes, N_SIZ);
+ 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;
}
- else
+
+ switch (rs)
{
- 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);
+ case NS_DDI:
+ case NS_QQI:
+ {
+ unsigned immbits;
+ unsigned enctab[] = {0x0000100, 0x1000100, 0x0, 0x1000000,
+ 0x0000100, 0x1000100, 0x0, 0x1000000};
+
+ 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;
+ 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;
+ if (flavour < neon_cvt_flavour_s16_f16)
+ {
+ inst.instruction |= 1 << 21;
+ immbits = 32 - inst.operands[2].imm;
+ inst.instruction |= immbits << 16;
+ }
+ else
+ {
+ inst.instruction |= 3 << 20;
+ immbits = 16 - inst.operands[2].imm;
+ inst.instruction |= immbits << 16;
+ inst.instruction &= ~(1 << 9);
+ }
+
+ 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_u16_f16
+ || flavour == neon_cvt_flavour_u32_f32) << 7;
+ inst.instruction |= mode << 8;
+ if (flavour == neon_cvt_flavour_u16_f16
+ || flavour == neon_cvt_flavour_s16_f16)
+ /* Mask off the original size bits and reencode them. */
+ inst.instruction = ((inst.instruction & 0xfff3ffff) | (1 << 18));
+
+ if (thumb_mode)
+ inst.instruction |= 0xfc000000;
+ else
+ inst.instruction |= 0xf0000000;
+ }
+ else
+ {
+ int_encode:
+ {
+ unsigned enctab[] = { 0x100, 0x180, 0x0, 0x080,
+ 0x100, 0x180, 0x0, 0x080};
+
+ NEON_ENCODE (INTEGER, inst);
+
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+
+ 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;
+ if (flavour >= neon_cvt_flavour_s16_f16
+ && flavour <= neon_cvt_flavour_f16_u16)
+ /* Half precision. */
+ inst.instruction |= 1 << 18;
+ else
+ inst.instruction |= 2 << 18;
+
+ neon_dp_fixup (&inst);
+ }
+ }
+ break;
+
+ /* Half-precision conversions for Advanced SIMD -- neon. */
+ case NS_QD:
+ case NS_DQ:
+
+ if ((rs == NS_DQ)
+ && (inst.vectype.el[0].size != 16 || inst.vectype.el[1].size != 32))
+ {
+ as_bad (_("operand size must match register width"));
+ break;
+ }
+
+ if ((rs == NS_QD)
+ && ((inst.vectype.el[0].size != 32 || inst.vectype.el[1].size != 16)))
+ {
+ as_bad (_("operand size must match register width"));
+ break;
+ }
+
+ if (rs == NS_DQ)
+ inst.instruction = 0x3b60600;
+ else
+ inst.instruction = 0x3b60700;
- NEON_ENCODE (IMMED, 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;
- inst.instruction |= neon_quad (rs) << 6;
- inst.instruction |= (et.type == NT_float) << 10;
- inst.instruction |= neon_logbits (et.size) << 18;
-
neon_dp_fixup (&inst);
+ break;
+
+ default:
+ /* Some VFP conversions go here (s32 <-> f32, u32 <-> f32). */
+ 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_cmp (void)
+do_neon_cvtr (void)
{
- neon_compare (N_SUF_32, N_S8 | N_S16 | N_S32 | N_F32, FALSE);
+ do_neon_cvt_1 (neon_cvt_mode_x);
}
static void
-do_neon_cmp_inv (void)
+do_neon_cvt (void)
{
- neon_compare (N_SUF_32, N_S8 | N_S16 | N_S32 | N_F32, TRUE);
+ do_neon_cvt_1 (neon_cvt_mode_z);
}
static void
-do_neon_ceq (void)
-{
- neon_compare (N_IF_32, N_IF_32, FALSE);
-}
-
-/* For multiply instructions, we have the possibility of 16-bit or 32-bit
- scalars, which are encoded in 5 bits, M : Rm.
- For 16-bit scalars, the register is encoded in Rm[2:0] and the index in
- M:Rm[3], and for 32-bit scalars, the register is encoded in Rm[3:0] and the
- index in M. */
-
-static unsigned
-neon_scalar_for_mul (unsigned scalar, unsigned elsize)
+do_neon_cvta (void)
{
- unsigned regno = NEON_SCALAR_REG (scalar);
- unsigned elno = NEON_SCALAR_INDEX (scalar);
-
- switch (elsize)
- {
- case 16:
- if (regno > 7 || elno > 3)
- goto bad_scalar;
- return regno | (elno << 3);
-
- case 32:
- if (regno > 15 || elno > 1)
- goto bad_scalar;
- return regno | (elno << 4);
-
- default:
- bad_scalar:
- first_error (_("scalar out of range for multiply instruction"));
- }
-
- return 0;
+ do_neon_cvt_1 (neon_cvt_mode_a);
}
-/* Encode multiply / multiply-accumulate scalar instructions. */
-
static void
-neon_mul_mac (struct neon_type_el et, int ubit)
+do_neon_cvtn (void)
{
- unsigned scalar;
-
- /* Give a more helpful error message if we have an invalid type. */
- if (et.type == NT_invtype)
- return;
-
- scalar = neon_scalar_for_mul (inst.operands[2].reg, et.size);
- inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
- inst.instruction |= HI1 (inst.operands[0].reg) << 22;
- inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
- inst.instruction |= HI1 (inst.operands[1].reg) << 7;
- inst.instruction |= LOW4 (scalar);
- inst.instruction |= HI1 (scalar) << 5;
- inst.instruction |= (et.type == NT_float) << 8;
- inst.instruction |= neon_logbits (et.size) << 20;
- inst.instruction |= (ubit != 0) << 24;
-
- neon_dp_fixup (&inst);
+ do_neon_cvt_1 (neon_cvt_mode_n);
}
static void
-do_neon_mac_maybe_scalar (void)
+do_neon_cvtp (void)
{
- if (try_vfp_nsyn (3, do_vfp_nsyn_mla_mls) == SUCCESS)
- return;
-
- if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
- return;
-
- if (inst.operands[2].isscalar)
- {
- 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);
- NEON_ENCODE (SCALAR, inst);
- neon_mul_mac (et, neon_quad (rs));
- }
- else
- {
- /* The "untyped" case can't happen. Do this to stop the "U" bit being
- affected if we specify unsigned args. */
- neon_dyadic_misc (NT_untyped, N_IF_32, 0);
- }
+ do_neon_cvt_1 (neon_cvt_mode_p);
}
static void
-do_neon_fmac (void)
+do_neon_cvtm (void)
{
- if (try_vfp_nsyn (3, do_vfp_nsyn_fma_fms) == SUCCESS)
- return;
-
- if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
- return;
-
- neon_dyadic_misc (NT_untyped, N_IF_32, 0);
+ do_neon_cvt_1 (neon_cvt_mode_m);
}
static void
-do_neon_tst (void)
+do_neon_cvttb_2 (bfd_boolean t, bfd_boolean to, bfd_boolean is_double)
{
- 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_8 | N_16 | N_32 | N_KEY);
- neon_three_same (neon_quad (rs), 0, et.size);
-}
+ if (is_double)
+ mark_feature_used (&fpu_vfp_ext_armv8);
-/* VMUL with 3 registers allows the P8 type. The scalar version supports the
- same types as the MAC equivalents. The polynomial type for this instruction
- is encoded the same as the integer type. */
+ 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_mul (void)
+do_neon_cvttb_1 (bfd_boolean t)
{
- if (try_vfp_nsyn (3, do_vfp_nsyn_mul) == SUCCESS)
- return;
+ enum neon_shape rs = neon_select_shape (NS_HF, NS_HD, NS_FH, NS_FF, NS_FD,
+ NS_DF, NS_DH, NS_NULL);
- if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ if (rs == NS_NULL)
return;
-
- if (inst.operands[2].isscalar)
- do_neon_mac_maybe_scalar ();
- else
- neon_dyadic_misc (NT_poly, N_I8 | N_I16 | N_I32 | N_F32 | N_P8, 0);
-}
-
-static void
-do_neon_qdmulh (void)
-{
- if (inst.operands[2].isscalar)
+ else if (neon_check_type (2, rs, N_F16, N_F32 | N_VFP).type != NT_invtype)
{
- 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);
- NEON_ENCODE (SCALAR, inst);
- neon_mul_mac (et, neon_quad (rs));
+ 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)
+ {
+ /* The VCVTB and VCVTT instructions with D-register operands
+ don't work for SP only targets. */
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_armv8),
+ _(BAD_FPU));
+
+ inst.error = NULL;
+ do_neon_cvttb_2 (t, /*to=*/TRUE, /*is_double=*/TRUE);
}
- else
+ else if (neon_check_type (2, rs, N_F64 | N_VFP, N_F16).type != NT_invtype)
{
- 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);
- NEON_ENCODE (INTEGER, inst);
- /* The U bit (rounding) comes from bit mask. */
- neon_three_same (neon_quad (rs), 0, et.size);
+ /* The VCVTB and VCVTT instructions with D-register operands
+ don't work for SP only targets. */
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_armv8),
+ _(BAD_FPU));
+
+ inst.error = NULL;
+ do_neon_cvttb_2 (t, /*to=*/FALSE, /*is_double=*/TRUE);
}
+ else
+ return;
}
static void
-do_neon_fcmp_absolute (void)
+do_neon_cvtb (void)
{
- enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
- neon_check_type (3, rs, N_EQK, N_EQK, N_F32 | N_KEY);
- /* Size field comes from bit mask. */
- neon_three_same (neon_quad (rs), 1, -1);
+ do_neon_cvttb_1 (FALSE);
}
-static void
-do_neon_fcmp_absolute_inv (void)
-{
- neon_exchange_operands ();
- do_neon_fcmp_absolute ();
-}
static void
-do_neon_step (void)
+do_neon_cvtt (void)
{
- enum neon_shape rs = neon_select_shape (NS_DDD, NS_QQQ, NS_NULL);
- neon_check_type (3, rs, N_EQK, N_EQK, N_F32 | N_KEY);
- neon_three_same (neon_quad (rs), 0, -1);
+ do_neon_cvttb_1 (TRUE);
}
static void
-do_neon_abs_neg (void)
+neon_move_immediate (void)
{
- enum neon_shape rs;
- struct neon_type_el et;
+ enum neon_shape rs = 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);
+ unsigned immlo, immhi = 0, immbits;
+ int op, cmode, float_p;
- if (try_vfp_nsyn (2, do_vfp_nsyn_abs_neg) == SUCCESS)
- return;
+ constraint (et.type == NT_invtype,
+ _("operand size must be specified for immediate VMOV"));
- if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
- return;
+ /* We start out as an MVN instruction if OP = 1, MOV otherwise. */
+ op = (inst.instruction & (1 << 5)) != 0;
- rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
- et = neon_check_type (2, rs, N_EQK, N_S8 | N_S16 | N_S32 | N_F32 | N_KEY);
+ immlo = inst.operands[1].imm;
+ if (inst.operands[1].regisimm)
+ immhi = inst.operands[1].reg;
+
+ constraint (et.size < 32 && (immlo & ~((1 << et.size) - 1)) != 0,
+ _("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)
+ {
+ /* 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. */
+ 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;
+ }
+ }
+
+ inst.instruction &= ~(1 << 5);
+ inst.instruction |= op << 5;
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 |= (et.type == NT_float) << 10;
- inst.instruction |= neon_logbits (et.size) << 18;
+ inst.instruction |= cmode << 8;
+
+ neon_write_immbits (immbits);
+}
+
+static void
+do_neon_mvn (void)
+{
+ if (inst.operands[1].isreg)
+ {
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_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;
+ inst.instruction |= neon_quad (rs) << 6;
+ }
+ else
+ {
+ NEON_ENCODE (IMMED, inst);
+ neon_move_immediate ();
+ }
neon_dp_fixup (&inst);
}
+/* Encode instructions of form:
+
+ |28/24|23|22|21 20|19 16|15 12|11 8|7|6|5|4|3 0|
+ | U |x |D |size | Rn | Rd |x x x x|N|x|M|x| Rm | */
+
static void
-do_neon_sli (void)
+neon_mixed_length (struct neon_type_el et, unsigned size)
{
- 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_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"));
- neon_imm_shift (FALSE, 0, neon_quad (rs), et, imm);
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
+ inst.instruction |= HI1 (inst.operands[1].reg) << 7;
+ inst.instruction |= LOW4 (inst.operands[2].reg);
+ inst.instruction |= HI1 (inst.operands[2].reg) << 5;
+ inst.instruction |= (et.type == NT_unsigned) << 24;
+ inst.instruction |= neon_logbits (size) << 20;
+
+ neon_dp_fixup (&inst);
}
static void
-do_neon_sri (void)
+do_neon_dyadic_long (void)
{
- 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_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"));
- neon_imm_shift (FALSE, 0, neon_quad (rs), et, et.size - imm);
+ /* FIXME: Type checking for lengthening op. */
+ struct neon_type_el et = neon_check_type (3, NS_QDD,
+ N_EQK | N_DBL, N_EQK, N_SU_32 | N_KEY);
+ neon_mixed_length (et, et.size);
}
static void
-do_neon_qshlu_imm (void)
+do_neon_abal (void)
{
- 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_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"));
- /* 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. */
- inst.instruction |= (et.type == NT_unsigned) << 8;
- /* The rest of the bits are the same as other immediate shifts. */
- neon_imm_shift (FALSE, 0, neon_quad (rs), et, imm);
+ struct neon_type_el et = neon_check_type (3, NS_QDD,
+ N_EQK | N_INT | N_DBL, N_EQK, N_SU_32 | N_KEY);
+ neon_mixed_length (et, et.size);
}
static void
-do_neon_qmovn (void)
+neon_mac_reg_scalar_long (unsigned regtypes, unsigned scalartypes)
{
- struct neon_type_el et = neon_check_type (2, NS_DQ,
- N_EQK | N_HLF, N_SU_16_64 | N_KEY);
- /* Saturating move where operands can be signed or unsigned, and the
- destination has the same signedness. */
- NEON_ENCODE (INTEGER, inst);
- if (et.type == NT_unsigned)
- inst.instruction |= 0xc0;
+ 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);
+ NEON_ENCODE (SCALAR, inst);
+ neon_mul_mac (et, et.type == NT_unsigned);
+ }
else
- inst.instruction |= 0x80;
- neon_two_same (0, 1, et.size / 2);
+ {
+ struct neon_type_el et = neon_check_type (3, NS_QDD,
+ N_EQK | N_DBL, N_EQK, scalartypes | N_KEY);
+ NEON_ENCODE (INTEGER, inst);
+ neon_mixed_length (et, et.size);
+ }
}
static void
-do_neon_qmovun (void)
+do_neon_mac_maybe_scalar_long (void)
{
- struct neon_type_el et = neon_check_type (2, NS_DQ,
- N_EQK | N_HLF | N_UNS, N_S16 | N_S32 | N_S64 | N_KEY);
- /* Saturating move with unsigned results. Operands must be signed. */
- NEON_ENCODE (INTEGER, inst);
- neon_two_same (0, 1, et.size / 2);
+ neon_mac_reg_scalar_long (N_S16 | N_S32 | N_U16 | N_U32, N_SU_32);
}
static void
-do_neon_rshift_sat_narrow (void)
+do_neon_dyadic_wide (void)
{
- /* FIXME: Types for narrowing. If operands are signed, results can be signed
- or unsigned. If operands are unsigned, results must also be unsigned. */
- struct neon_type_el et = neon_check_type (2, NS_DQI,
- N_EQK | N_HLF, N_SU_16_64 | N_KEY);
- int imm = inst.operands[2].imm;
- /* This gets the bounds check, size encoding and immediate bits calculation
- right. */
- et.size /= 2;
-
- /* VQ{R}SHRN.I<size> <Dd>, <Qm>, #0 is a synonym for
- VQMOVN.I<size> <Dd>, <Qm>. */
- if (imm == 0)
- {
- inst.operands[2].present = 0;
- inst.instruction = N_MNEM_vqmovn;
- do_neon_qmovn ();
- return;
- }
-
- constraint (imm < 1 || (unsigned)imm > et.size,
- _("immediate out of range"));
- neon_imm_shift (TRUE, et.type == NT_unsigned, 0, et, et.size - imm);
+ struct neon_type_el et = neon_check_type (3, NS_QQD,
+ N_EQK | N_DBL, N_EQK | N_DBL, N_SU_32 | N_KEY);
+ neon_mixed_length (et, et.size);
}
static void
-do_neon_rshift_sat_narrow_u (void)
+do_neon_dyadic_narrow (void)
{
- /* FIXME: Types for narrowing. If operands are signed, results can be signed
- or unsigned. If operands are unsigned, results must also be unsigned. */
- struct neon_type_el et = neon_check_type (2, NS_DQI,
- N_EQK | N_HLF | N_UNS, N_S16 | N_S32 | N_S64 | N_KEY);
- int imm = inst.operands[2].imm;
- /* This gets the bounds check, size encoding and immediate bits calculation
- right. */
- et.size /= 2;
-
- /* VQSHRUN.I<size> <Dd>, <Qm>, #0 is a synonym for
- VQMOVUN.I<size> <Dd>, <Qm>. */
- if (imm == 0)
- {
- inst.operands[2].present = 0;
- inst.instruction = N_MNEM_vqmovun;
- do_neon_qmovun ();
- return;
- }
+ struct neon_type_el et = neon_check_type (3, NS_QDD,
+ N_EQK | N_DBL, N_EQK, N_I16 | N_I32 | N_I64 | N_KEY);
+ /* Operand sign is unimportant, and the U bit is part of the opcode,
+ so force the operand type to integer. */
+ et.type = NT_integer;
+ neon_mixed_length (et, et.size / 2);
+}
- constraint (imm < 1 || (unsigned)imm > et.size,
- _("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. */
- neon_imm_shift (TRUE, 1, 0, et, et.size - imm);
+static void
+do_neon_mul_sat_scalar_long (void)
+{
+ neon_mac_reg_scalar_long (N_S16 | N_S32, N_S16 | N_S32);
}
static void
-do_neon_movn (void)
+do_neon_vmull (void)
{
- struct neon_type_el et = neon_check_type (2, NS_DQ,
- N_EQK | N_HLF, N_I16 | N_I32 | N_I64 | N_KEY);
- NEON_ENCODE (INTEGER, inst);
- neon_two_same (0, 1, et.size / 2);
+ if (inst.operands[2].isscalar)
+ do_neon_mac_maybe_scalar_long ();
+ else
+ {
+ struct neon_type_el et = neon_check_type (3, NS_QDD,
+ N_EQK | N_DBL, N_EQK, N_SU_32 | N_P8 | N_P64 | N_KEY);
+
+ if (et.type == NT_poly)
+ NEON_ENCODE (POLY, inst);
+ else
+ 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);
+ }
}
static void
-do_neon_rshift_narrow (void)
+do_neon_ext (void)
{
- struct neon_type_el et = neon_check_type (2, NS_DQI,
- N_EQK | N_HLF, N_I16 | N_I32 | N_I64 | N_KEY);
- int imm = inst.operands[2].imm;
- /* This gets the bounds check, size encoding and immediate bits calculation
- right. */
- et.size /= 2;
+ enum neon_shape rs = neon_select_shape (NS_DDDI, NS_QQQI, NS_NULL);
+ struct neon_type_el et = neon_check_type (3, rs,
+ N_EQK, N_EQK, N_8 | N_16 | N_32 | N_64 | N_KEY);
+ unsigned imm = (inst.operands[3].imm * et.size) / 8;
- /* If immediate is zero then we are a pseudo-instruction for
- VMOVN.I<size> <Dd>, <Qm> */
- if (imm == 0)
- {
- inst.operands[2].present = 0;
- inst.instruction = N_MNEM_vmovn;
- do_neon_movn ();
- return;
- }
+ constraint (imm >= (unsigned) (neon_quad (rs) ? 16 : 8),
+ _("shift out of range"));
+ inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[0].reg) << 22;
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
+ inst.instruction |= HI1 (inst.operands[1].reg) << 7;
+ inst.instruction |= LOW4 (inst.operands[2].reg);
+ inst.instruction |= HI1 (inst.operands[2].reg) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= imm << 8;
- constraint (imm < 1 || (unsigned)imm > et.size,
- _("immediate out of range for narrowing operation"));
- neon_imm_shift (FALSE, 0, 0, et, et.size - imm);
+ neon_dp_fixup (&inst);
}
static void
-do_neon_shll (void)
+do_neon_rev (void)
{
- /* FIXME: Type checking when lengthening. */
- struct neon_type_el et = neon_check_type (2, NS_QDI,
- N_EQK | N_DBL, N_I8 | N_I16 | N_I32 | N_KEY);
- unsigned imm = inst.operands[2].imm;
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_8 | N_16 | N_32 | N_KEY);
+ unsigned op = (inst.instruction >> 7) & 3;
+ /* N (width of reversed regions) is encoded as part of the bitmask. We
+ extract it here to check the elements to be reversed are smaller.
+ Otherwise we'd get a reserved instruction. */
+ 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"));
+ neon_two_same (neon_quad (rs), 1, et.size);
+}
- if (imm == et.size)
+static void
+do_neon_dup (void)
+{
+ if (inst.operands[1].isscalar)
{
- /* Maximum shift variant. */
- NEON_ENCODE (INTEGER, inst);
+ 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);
+ 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;
+
+ NEON_ENCODE (SCALAR, 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;
- inst.instruction |= neon_logbits (et.size) << 18;
+ inst.instruction |= LOW4 (dm);
+ inst.instruction |= HI1 (dm) << 5;
+ inst.instruction |= neon_quad (rs) << 6;
+ inst.instruction |= x << 17;
+ inst.instruction |= sizebits << 16;
neon_dp_fixup (&inst);
}
else
{
- /* 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);
- NEON_ENCODE (IMMED, inst);
- neon_imm_shift (TRUE, et.type == NT_unsigned, 0, et, imm);
+ 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);
+ /* 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;
+ }
+ 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. */
+ do_vfp_cond_or_thumb ();
}
}
-/* Check the various types for the VCVT instruction, and return which version
- the current instruction is. */
+/* VMOV has particularly many variations. It can be one of:
+ 0. VMOV<c><q> <Qd>, <Qm>
+ 1. VMOV<c><q> <Dd>, <Dm>
+ (Register operations, which are VORR with Rm = Rn.)
+ 2. VMOV<c><q>.<dt> <Qd>, #<imm>
+ 3. VMOV<c><q>.<dt> <Dd>, #<imm>
+ (Immediate loads.)
+ 4. VMOV<c><q>.<size> <Dn[x]>, <Rd>
+ (ARM register to scalar.)
+ 5. VMOV<c><q> <Dm>, <Rd>, <Rn>
+ (Two ARM registers to vector.)
+ 6. VMOV<c><q>.<dt> <Rd>, <Dn[x]>
+ (Scalar to ARM register.)
+ 7. VMOV<c><q> <Rd>, <Rn>, <Dm>
+ (Vector to two ARM registers.)
+ 8. VMOV.F32 <Sd>, <Sm>
+ 9. VMOV.F64 <Dd>, <Dm>
+ (VFP register moves.)
+ 10. VMOV.F32 <Sd>, #imm
+ 11. VMOV.F64 <Dd>, #imm
+ (VFP float immediate load.)
+ 12. VMOV <Rd>, <Sm>
+ (VFP single to ARM reg.)
+ 13. VMOV <Sd>, <Rm>
+ (ARM reg to VFP single.)
+ 14. VMOV <Rd>, <Re>, <Sn>, <Sm>
+ (Two ARM regs to two VFP singles.)
+ 15. VMOV <Sd>, <Se>, <Rn>, <Rm>
+ (Two VFP singles to two ARM regs.)
-static int
-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); \
- }
- struct neon_type_el et;
- unsigned whole_reg = (rs == NS_FFI || rs == NS_FD || rs == NS_DF
- || 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;
+ These cases can be disambiguated using neon_select_shape, except cases 1/9
+ and 3/11 which depend on the operand type too.
- 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);
+ All the encoded bits are hardcoded by this function.
- return -1;
-#undef CVT_VAR
-}
+ Cases 4, 6 may be used with VFPv1 and above (only 32-bit transfers!).
+ Cases 5, 7 may be used with VFPv2 and above.
-/* Neon-syntax VFP conversions. */
+ FIXME: Some of the checking may be a bit sloppy (in a couple of cases you
+ can specify a type where it doesn't make sense to, and is ignored). */
static void
-do_vfp_nsyn_cvt (enum neon_shape rs, int flavour)
+do_neon_mov (void)
{
- const char *opname = 0;
+ enum neon_shape rs = neon_select_shape (NS_RRFF, NS_FFRR, NS_DRR, NS_RRD,
+ NS_QQ, NS_DD, NS_QI, NS_DI, NS_SR,
+ NS_RS, NS_FF, NS_FI, NS_RF, NS_FR,
+ NS_HR, NS_RH, NS_HI, NS_NULL);
+ struct neon_type_el et;
+ const char *ldconst = 0;
- if (rs == NS_DDI || rs == NS_QQI || rs == NS_FFI)
- {
- /* 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]));
- }
- }
- else
+ switch (rs)
{
- /* 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];
- }
+ case NS_DD: /* case 1/9. */
+ et = neon_check_type (2, rs, N_EQK, N_F64 | N_KEY);
+ /* 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;
+ }
+ /* fall through. */
- if (opname)
- do_vfp_nsyn_opcode (opname);
-}
+ 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);
+ }
+ break;
-static void
-do_vfp_nsyn_cvtz (void)
-{
- enum neon_shape rs = neon_select_shape (NS_FF, NS_FD, NS_NULL);
- int flavour = neon_cvt_flavour (rs);
- const char *enc[] =
- {
- "ftosizs",
- "ftouizs",
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- NULL,
- "ftosizd",
- "ftouizd"
- };
+ case NS_DI: /* case 3/11. */
+ 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;
+ }
+ /* fall through. */
- if (flavour >= 0 && flavour < (int) ARRAY_SIZE (enc) && enc[flavour])
- do_vfp_nsyn_opcode (enc[flavour]);
-}
+ case NS_QI: /* case 2/3. */
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
+ return;
+ inst.instruction = 0x0800010;
+ neon_move_immediate ();
+ neon_dp_fixup (&inst);
+ break;
-static void
-do_neon_cvt_1 (bfd_boolean round_to_zero ATTRIBUTE_UNUSED)
-{
- 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);
+ 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);
+
+ /* .<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;
+ }
- /* PR11109: Handle round-to-zero for VCVT conversions. */
- if (round_to_zero
- && ARM_CPU_HAS_FEATURE (cpu_variant, fpu_arch_vfp_v2)
- && (flavour == 0 || flavour == 1 || flavour == 8 || flavour == 9)
- && (rs == NS_FD || rs == NS_FF))
- {
- do_vfp_nsyn_cvtz ();
- return;
- }
+ et = neon_check_type (2, NS_NULL, N_8 | N_16 | N_32 | N_KEY, N_EQK);
+ logsize = neon_logbits (et.size);
- /* VFP rather than Neon conversions. */
- if (flavour >= 6)
- {
- do_vfp_nsyn_cvt (rs, flavour);
- return;
- }
+ 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 (rs)
- {
- case NS_DDI:
- case NS_QQI:
- {
- unsigned immbits;
- unsigned enctab[] = { 0x0000100, 0x1000100, 0x0, 0x1000000 };
-
- 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;
- 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);
+ 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_DD:
- case NS_QQ:
- int_encode:
+ case NS_DRR: /* case 5 (fmdrr). */
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v2),
+ _(BAD_FPU));
+
+ inst.instruction = 0xc400b10;
+ do_vfp_cond_or_thumb ();
+ inst.instruction |= LOW4 (inst.operands[0].reg);
+ inst.instruction |= HI1 (inst.operands[0].reg) << 5;
+ inst.instruction |= inst.operands[1].reg << 12;
+ inst.instruction |= inst.operands[2].reg << 16;
+ break;
+
+ case NS_RS: /* case 6. */
{
- unsigned enctab[] = { 0x100, 0x180, 0x0, 0x080 };
+ 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;
+ }
+ break;
- NEON_ENCODE (INTEGER, inst);
+ case NS_RRD: /* case 7 (fmrrd). */
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v2),
+ _(BAD_FPU));
- if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
- return;
+ inst.instruction = 0xc500b10;
+ do_vfp_cond_or_thumb ();
+ inst.instruction |= inst.operands[0].reg << 12;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= LOW4 (inst.operands[2].reg);
+ inst.instruction |= HI1 (inst.operands[2].reg) << 5;
+ break;
- if (flavour != -1)
- inst.instruction |= enctab[flavour];
+ case NS_FF: /* case 8 (fcpys). */
+ do_vfp_nsyn_opcode ("fcpys");
+ break;
- 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;
+ case NS_HI:
+ case NS_FI: /* case 10 (fconsts). */
+ 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);
- neon_dp_fixup (&inst);
- }
- break;
+ /* ARMv8.2 fp16 vmov.f16 instruction. */
+ if (rs == NS_HI)
+ do_scalar_fp16_v82_encode ();
+ }
+ else
+ first_error (_("immediate out of range"));
+ break;
- /* Half-precision conversions for Advanced SIMD -- neon. */
- case NS_QD:
- case NS_DQ:
+ case NS_RH:
+ case NS_RF: /* case 12 (fmrs). */
+ do_vfp_nsyn_opcode ("fmrs");
+ /* ARMv8.2 fp16 vmov.f16 instruction. */
+ if (rs == NS_RH)
+ do_scalar_fp16_v82_encode ();
+ break;
- if ((rs == NS_DQ)
- && (inst.vectype.el[0].size != 16 || inst.vectype.el[1].size != 32))
- {
- as_bad (_("operand size must match register width"));
- break;
- }
+ case NS_HR:
+ case NS_FR: /* case 13 (fmsr). */
+ do_vfp_nsyn_opcode ("fmsr");
+ /* ARMv8.2 fp16 vmov.f16 instruction. */
+ if (rs == NS_HR)
+ do_scalar_fp16_v82_encode ();
+ break;
- if ((rs == NS_QD)
- && ((inst.vectype.el[0].size != 32 || inst.vectype.el[1].size != 16)))
- {
- as_bad (_("operand size must match register width"));
- break;
- }
+ /* The encoders for the fmrrs and fmsrr instructions expect three operands
+ (one of which is a list), but we have parsed four. Do some fiddling to
+ make the operands what do_vfp_reg2_from_sp2 and do_vfp_sp2_from_reg2
+ expect. */
+ case NS_RRFF: /* case 14 (fmrrs). */
+ constraint (inst.operands[3].reg != inst.operands[2].reg + 1,
+ _("VFP registers must be adjacent"));
+ inst.operands[2].imm = 2;
+ memset (&inst.operands[3], '\0', sizeof (inst.operands[3]));
+ do_vfp_nsyn_opcode ("fmrrs");
+ break;
- if (rs == NS_DQ)
- inst.instruction = 0x3b60600;
- else
- inst.instruction = 0x3b60700;
+ case NS_FFRR: /* case 15 (fmsrr). */
+ constraint (inst.operands[1].reg != inst.operands[0].reg + 1,
+ _("VFP registers must be adjacent"));
+ inst.operands[1] = inst.operands[2];
+ inst.operands[2] = inst.operands[3];
+ inst.operands[0].imm = 2;
+ memset (&inst.operands[3], '\0', sizeof (inst.operands[3]));
+ do_vfp_nsyn_opcode ("fmsrr");
+ break;
- 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;
- neon_dp_fixup (&inst);
+ case NS_NULL:
+ /* neon_select_shape has determined that the instruction
+ shape is wrong and has already set the error message. */
break;
default:
- /* Some VFP conversions go here (s32 <-> f32, u32 <-> f32). */
- do_vfp_nsyn_cvt (rs, flavour);
+ abort ();
}
}
static void
-do_neon_cvtr (void)
+do_neon_rshift_round_imm (void)
{
- do_neon_cvt_1 (FALSE);
-}
+ 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_SU_ALL | N_KEY);
+ int imm = inst.operands[2].imm;
-static void
-do_neon_cvt (void)
-{
- do_neon_cvt_1 (TRUE);
+ /* imm == 0 case is encoded as VMOV for V{R}SHR. */
+ if (imm == 0)
+ {
+ inst.operands[2].present = 0;
+ do_neon_mov ();
+ return;
+ }
+
+ constraint (imm < 1 || (unsigned)imm > et.size,
+ _("immediate out of range for shift"));
+ neon_imm_shift (TRUE, et.type == NT_unsigned, neon_quad (rs), et,
+ et.size - imm);
}
static void
-do_neon_cvtb (void)
+do_neon_movhf (void)
{
- inst.instruction = 0xeb20a40;
+ enum neon_shape rs = neon_select_shape (NS_HH, NS_NULL);
+ constraint (rs != NS_HH, _("invalid suffix"));
- /* The sizes are attached to the mnemonic. */
- if (inst.vectype.el[0].type != NT_invtype
- && inst.vectype.el[0].size == 16)
- inst.instruction |= 0x00010000;
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_armv8),
+ _(BAD_FPU));
- /* 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;
+ do_vfp_sp_monadic ();
- encode_arm_vfp_reg (inst.operands[0].reg, VFP_REG_Sd);
- encode_arm_vfp_reg (inst.operands[1].reg, VFP_REG_Sm);
- do_vfp_cond_or_thumb ();
+ inst.is_neon = 1;
+ inst.instruction |= 0xf0000000;
}
-
static void
-do_neon_cvtt (void)
+do_neon_movl (void)
{
- do_neon_cvtb ();
- inst.instruction |= 0x80;
+ struct neon_type_el et = neon_check_type (2, NS_QD,
+ N_EQK | N_DBL, N_SU_32 | N_KEY);
+ unsigned sizebits = et.size >> 3;
+ inst.instruction |= sizebits << 19;
+ neon_two_same (0, et.type == NT_unsigned, -1);
}
static void
-neon_move_immediate (void)
+do_neon_trn (void)
{
- enum neon_shape rs = neon_select_shape (NS_DI, NS_QI, NS_NULL);
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, 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);
- unsigned immlo, immhi = 0, immbits;
- int op, cmode, float_p;
-
- constraint (et.type == NT_invtype,
- _("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;
-
- immlo = inst.operands[1].imm;
- if (inst.operands[1].regisimm)
- immhi = inst.operands[1].reg;
-
- constraint (et.size < 32 && (immlo & ~((1 << et.size) - 1)) != 0,
- _("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)
- {
- /* 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. */
- 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;
- }
- }
-
- inst.instruction &= ~(1 << 5);
- inst.instruction |= op << 5;
-
- inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
- inst.instruction |= HI1 (inst.operands[0].reg) << 22;
- inst.instruction |= neon_quad (rs) << 6;
- inst.instruction |= cmode << 8;
-
- neon_write_immbits (immbits);
+ N_EQK, N_8 | N_16 | N_32 | N_KEY);
+ NEON_ENCODE (INTEGER, inst);
+ neon_two_same (neon_quad (rs), 1, et.size);
}
static void
-do_neon_mvn (void)
+do_neon_zip_uzp (void)
{
- if (inst.operands[1].isreg)
- {
- enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_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;
- inst.instruction |= neon_quad (rs) << 6;
- }
- else
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_8 | N_16 | N_32 | N_KEY);
+ if (rs == NS_DD && et.size == 32)
{
- NEON_ENCODE (IMMED, inst);
- neon_move_immediate ();
+ /* Special case: encode as VTRN.32 <Dd>, <Dm>. */
+ inst.instruction = N_MNEM_vtrn;
+ do_neon_trn ();
+ return;
}
-
- neon_dp_fixup (&inst);
-}
-
-/* Encode instructions of form:
-
- |28/24|23|22|21 20|19 16|15 12|11 8|7|6|5|4|3 0|
- | U |x |D |size | Rn | Rd |x x x x|N|x|M|x| Rm | */
-
-static void
-neon_mixed_length (struct neon_type_el et, unsigned size)
-{
- inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
- inst.instruction |= HI1 (inst.operands[0].reg) << 22;
- inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
- inst.instruction |= HI1 (inst.operands[1].reg) << 7;
- inst.instruction |= LOW4 (inst.operands[2].reg);
- inst.instruction |= HI1 (inst.operands[2].reg) << 5;
- inst.instruction |= (et.type == NT_unsigned) << 24;
- inst.instruction |= neon_logbits (size) << 20;
-
- neon_dp_fixup (&inst);
-}
-
-static void
-do_neon_dyadic_long (void)
-{
- /* FIXME: Type checking for lengthening op. */
- struct neon_type_el et = neon_check_type (3, NS_QDD,
- N_EQK | N_DBL, N_EQK, N_SU_32 | N_KEY);
- neon_mixed_length (et, et.size);
+ neon_two_same (neon_quad (rs), 1, et.size);
}
static void
-do_neon_abal (void)
+do_neon_sat_abs_neg (void)
{
- struct neon_type_el et = neon_check_type (3, NS_QDD,
- N_EQK | N_INT | N_DBL, N_EQK, N_SU_32 | N_KEY);
- neon_mixed_length (et, et.size);
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_S8 | N_S16 | N_S32 | N_KEY);
+ neon_two_same (neon_quad (rs), 1, et.size);
}
static void
-neon_mac_reg_scalar_long (unsigned regtypes, unsigned scalartypes)
+do_neon_pair_long (void)
{
- 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);
- 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);
- NEON_ENCODE (INTEGER, inst);
- neon_mixed_length (et, et.size);
- }
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs, N_EQK, N_SU_32 | N_KEY);
+ /* Unsigned is encoded in OP field (bit 7) for these instruction. */
+ inst.instruction |= (et.type == NT_unsigned) << 7;
+ neon_two_same (neon_quad (rs), 1, et.size);
}
static void
-do_neon_mac_maybe_scalar_long (void)
+do_neon_recip_est (void)
{
- neon_mac_reg_scalar_long (N_S16 | N_S32 | N_U16 | N_U32, N_SU_32);
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK | N_FLT, N_F_16_32 | N_U32 | N_KEY);
+ inst.instruction |= (et.type == NT_float) << 8;
+ neon_two_same (neon_quad (rs), 1, et.size);
}
static void
-do_neon_dyadic_wide (void)
+do_neon_cls (void)
{
- struct neon_type_el et = neon_check_type (3, NS_QQD,
- N_EQK | N_DBL, N_EQK | N_DBL, N_SU_32 | N_KEY);
- neon_mixed_length (et, et.size);
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_S8 | N_S16 | N_S32 | N_KEY);
+ neon_two_same (neon_quad (rs), 1, et.size);
}
static void
-do_neon_dyadic_narrow (void)
-{
- struct neon_type_el et = neon_check_type (3, NS_QDD,
- N_EQK | N_DBL, N_EQK, N_I16 | N_I32 | N_I64 | N_KEY);
- /* Operand sign is unimportant, and the U bit is part of the opcode,
- so force the operand type to integer. */
- et.type = NT_integer;
- neon_mixed_length (et, et.size / 2);
+do_neon_clz (void)
+{
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK, N_I8 | N_I16 | N_I32 | N_KEY);
+ neon_two_same (neon_quad (rs), 1, et.size);
}
static void
-do_neon_mul_sat_scalar_long (void)
+do_neon_cnt (void)
{
- neon_mac_reg_scalar_long (N_S16 | N_S32, N_S16 | N_S32);
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et = neon_check_type (2, rs,
+ N_EQK | N_INT, N_8 | N_KEY);
+ neon_two_same (neon_quad (rs), 1, et.size);
}
static void
-do_neon_vmull (void)
+do_neon_swp (void)
{
- if (inst.operands[2].isscalar)
- do_neon_mac_maybe_scalar_long ();
- 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);
- if (et.type == NT_poly)
- 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_mixed_length (et, et.size);
- }
+ enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
+ neon_two_same (neon_quad (rs), 1, -1);
}
static void
-do_neon_ext (void)
+do_neon_tbl_tbx (void)
{
- enum neon_shape rs = neon_select_shape (NS_DDDI, NS_QQQI, NS_NULL);
- struct neon_type_el et = neon_check_type (3, rs,
- N_EQK, N_EQK, N_8 | N_16 | N_32 | N_64 | N_KEY);
- unsigned imm = (inst.operands[3].imm * et.size) / 8;
+ unsigned listlenbits;
+ neon_check_type (3, NS_DLD, N_EQK, N_EQK, N_8 | N_KEY);
- constraint (imm >= (unsigned) (neon_quad (rs) ? 16 : 8),
- _("shift out of range"));
+ if (inst.operands[1].imm < 1 || inst.operands[1].imm > 4)
+ {
+ first_error (_("bad list length for table lookup"));
+ return;
+ }
+
+ listlenbits = inst.operands[1].imm - 1;
inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
inst.instruction |= HI1 (inst.operands[0].reg) << 22;
inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
inst.instruction |= HI1 (inst.operands[1].reg) << 7;
inst.instruction |= LOW4 (inst.operands[2].reg);
inst.instruction |= HI1 (inst.operands[2].reg) << 5;
- inst.instruction |= neon_quad (rs) << 6;
- inst.instruction |= imm << 8;
+ inst.instruction |= listlenbits << 8;
neon_dp_fixup (&inst);
}
static void
-do_neon_rev (void)
+do_neon_ldm_stm (void)
{
- enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
- struct neon_type_el et = neon_check_type (2, rs,
- N_EQK, N_8 | N_16 | N_32 | N_KEY);
- unsigned op = (inst.instruction >> 7) & 3;
- /* N (width of reversed regions) is encoded as part of the bitmask. We
- extract it here to check the elements to be reversed are smaller.
- Otherwise we'd get a reserved instruction. */
- 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"));
- neon_two_same (neon_quad (rs), 1, et.size);
+ /* P, U and L bits are part of bitmask. */
+ int is_dbmode = (inst.instruction & (1 << 24)) != 0;
+ unsigned offsetbits = inst.operands[1].imm * 2;
+
+ if (inst.operands[1].issingle)
+ {
+ do_vfp_nsyn_ldm_stm (is_dbmode);
+ return;
+ }
+
+ constraint (is_dbmode && !inst.operands[0].writeback,
+ _("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"));
+
+ inst.instruction |= inst.operands[0].reg << 16;
+ inst.instruction |= inst.operands[0].writeback << 21;
+ inst.instruction |= LOW4 (inst.operands[1].reg) << 12;
+ inst.instruction |= HI1 (inst.operands[1].reg) << 22;
+
+ inst.instruction |= offsetbits;
+
+ do_vfp_cond_or_thumb ();
}
static void
-do_neon_dup (void)
+do_neon_ldr_str (void)
{
- if (inst.operands[1].isscalar)
- {
- 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);
- 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;
+ int is_ldr = (inst.instruction & (1 << 20)) != 0;
- if (vfp_or_neon_is_neon (NEON_CHECK_CC) == FAIL)
- return;
+ /* Use of PC in vstr in ARM mode is deprecated in ARMv7.
+ And is UNPREDICTABLE in thumb mode. */
+ if (!is_ldr
+ && inst.operands[1].reg == REG_PC
+ && (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v7) || thumb_mode))
+ {
+ if (thumb_mode)
+ inst.error = _("Use of PC here is UNPREDICTABLE");
+ else if (warn_on_deprecated)
+ as_tsktsk (_("Use of PC here is deprecated"));
+ }
- NEON_ENCODE (SCALAR, inst);
- inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
- inst.instruction |= HI1 (inst.operands[0].reg) << 22;
- inst.instruction |= LOW4 (dm);
- inst.instruction |= HI1 (dm) << 5;
- inst.instruction |= neon_quad (rs) << 6;
- inst.instruction |= x << 17;
- inst.instruction |= sizebits << 16;
+ if (inst.operands[0].issingle)
+ {
+ if (is_ldr)
+ do_vfp_nsyn_opcode ("flds");
+ else
+ do_vfp_nsyn_opcode ("fsts");
- neon_dp_fixup (&inst);
+ /* ARMv8.2 vldr.16/vstr.16 instruction. */
+ if (inst.vectype.el[0].size == 16)
+ do_scalar_fp16_v82_encode ();
}
else
{
- 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);
- /* 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;
- }
- 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. */
- do_vfp_cond_or_thumb ();
+ if (is_ldr)
+ do_vfp_nsyn_opcode ("fldd");
+ else
+ do_vfp_nsyn_opcode ("fstd");
}
}
-/* VMOV has particularly many variations. It can be one of:
- 0. VMOV<c><q> <Qd>, <Qm>
- 1. VMOV<c><q> <Dd>, <Dm>
- (Register operations, which are VORR with Rm = Rn.)
- 2. VMOV<c><q>.<dt> <Qd>, #<imm>
- 3. VMOV<c><q>.<dt> <Dd>, #<imm>
- (Immediate loads.)
- 4. VMOV<c><q>.<size> <Dn[x]>, <Rd>
- (ARM register to scalar.)
- 5. VMOV<c><q> <Dm>, <Rd>, <Rn>
- (Two ARM registers to vector.)
- 6. VMOV<c><q>.<dt> <Rd>, <Dn[x]>
- (Scalar to ARM register.)
- 7. VMOV<c><q> <Rd>, <Rn>, <Dm>
- (Vector to two ARM registers.)
- 8. VMOV.F32 <Sd>, <Sm>
- 9. VMOV.F64 <Dd>, <Dm>
- (VFP register moves.)
- 10. VMOV.F32 <Sd>, #imm
- 11. VMOV.F64 <Dd>, #imm
- (VFP float immediate load.)
- 12. VMOV <Rd>, <Sm>
- (VFP single to ARM reg.)
- 13. VMOV <Sd>, <Rm>
- (ARM reg to VFP single.)
- 14. VMOV <Rd>, <Re>, <Sn>, <Sm>
- (Two ARM regs to two VFP singles.)
- 15. VMOV <Sd>, <Se>, <Rn>, <Rm>
- (Two VFP singles to two ARM regs.)
-
- These cases can be disambiguated using neon_select_shape, except cases 1/9
- and 3/11 which depend on the operand type too.
-
- All the encoded bits are hardcoded by this function.
-
- Cases 4, 6 may be used with VFPv1 and above (only 32-bit transfers!).
- Cases 5, 7 may be used with VFPv2 and above.
-
- FIXME: Some of the checking may be a bit sloppy (in a couple of cases you
- can specify a type where it doesn't make sense to, and is ignored). */
+/* "interleave" version also handles non-interleaving register VLD1/VST1
+ instructions. */
static void
-do_neon_mov (void)
+do_neon_ld_st_interleave (void)
{
- enum neon_shape rs = neon_select_shape (NS_RRFF, NS_FFRR, NS_DRR, NS_RRD,
- NS_QQ, NS_DD, NS_QI, NS_DI, NS_SR, NS_RS, NS_FF, NS_FI, NS_RF, NS_FR,
- NS_NULL);
- struct neon_type_el et;
- const char *ldconst = 0;
-
- switch (rs)
+ struct neon_type_el et = neon_check_type (1, NS_NULL,
+ N_8 | N_16 | N_32 | N_64);
+ unsigned alignbits = 0;
+ unsigned idx;
+ /* The bits in this table go:
+ 0: register stride of one (0) or two (1)
+ 1,2: register list length, minus one (1, 2, 3, 4).
+ 3,4: <n> in instruction type, minus one (VLD<n> / VST<n>).
+ We use -1 for invalid entries. */
+ const int typetable[] =
{
- case NS_DD: /* case 1/9. */
- et = neon_check_type (2, rs, N_EQK, N_F64 | N_KEY);
- /* 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;
- }
- /* fall through. */
+ 0x7, -1, 0xa, -1, 0x6, -1, 0x2, -1, /* VLD1 / VST1. */
+ -1, -1, 0x8, 0x9, -1, -1, 0x3, -1, /* VLD2 / VST2. */
+ -1, -1, -1, -1, 0x4, 0x5, -1, -1, /* VLD3 / VST3. */
+ -1, -1, -1, -1, -1, -1, 0x0, 0x1 /* VLD4 / VST4. */
+ };
+ int typebits;
- case NS_QQ: /* case 0/1. */
+ if (et.type == NT_invtype)
+ return;
+
+ if (inst.operands[1].immisalign)
+ switch (inst.operands[1].imm >> 8)
{
- 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);
+ case 64: alignbits = 1; break;
+ case 128:
+ if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 2
+ && NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4)
+ goto bad_alignment;
+ alignbits = 2;
+ break;
+ case 256:
+ if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4)
+ goto bad_alignment;
+ alignbits = 3;
+ break;
+ default:
+ bad_alignment:
+ first_error (_("bad alignment"));
+ return;
}
- break;
- case NS_DI: /* case 3/11. */
- 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;
- }
- /* fall through. */
+ inst.instruction |= alignbits << 4;
+ inst.instruction |= neon_logbits (et.size) << 6;
- case NS_QI: /* case 2/3. */
- if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH) == FAIL)
- return;
- inst.instruction = 0x0800010;
- neon_move_immediate ();
- neon_dp_fixup (&inst);
- break;
+ /* Bits [4:6] of the immediate in a list specifier encode register stride
+ (minus 1) in bit 4, and list length in bits [5:6]. We put the <n> of
+ VLD<n>/VST<n> in bits [9:8] of the initial bitmask. Suck it out here, look
+ 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);
- 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;
- }
- break;
+ 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;
+}
+
+/* Check alignment is valid for do_neon_ld_st_lane and do_neon_ld_dup.
+ *DO_ALIGN is set to 1 if the relevant alignment bit should be set, 0
+ otherwise. The variable arguments are a list of pairs of legal (size, align)
+ values, terminated with -1. */
+
+static int
+neon_alignment_bit (int size, int align, int *do_alignment, ...)
+{
+ va_list ap;
+ int result = FAIL, thissize, thisalign;
+
+ if (!inst.operands[1].immisalign)
+ {
+ *do_alignment = 0;
+ return SUCCESS;
+ }
- case NS_DRR: /* case 5 (fmdrr). */
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v2),
- _(BAD_FPU));
+ va_start (ap, do_alignment);
- inst.instruction = 0xc400b10;
- do_vfp_cond_or_thumb ();
- inst.instruction |= LOW4 (inst.operands[0].reg);
- inst.instruction |= HI1 (inst.operands[0].reg) << 5;
- inst.instruction |= inst.operands[1].reg << 12;
- inst.instruction |= inst.operands[2].reg << 16;
- break;
+ do
+ {
+ thissize = va_arg (ap, int);
+ if (thissize == -1)
+ break;
+ thisalign = va_arg (ap, int);
- 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;
+ if (size == thissize && align == thisalign)
+ result = SUCCESS;
+ }
+ while (result != SUCCESS);
- 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;
- }
- break;
+ va_end (ap);
- case NS_RRD: /* case 7 (fmrrd). */
- constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_v2),
- _(BAD_FPU));
+ if (result == SUCCESS)
+ *do_alignment = 1;
+ else
+ first_error (_("unsupported alignment for instruction"));
- inst.instruction = 0xc500b10;
- do_vfp_cond_or_thumb ();
- inst.instruction |= inst.operands[0].reg << 12;
- inst.instruction |= inst.operands[1].reg << 16;
- inst.instruction |= LOW4 (inst.operands[2].reg);
- inst.instruction |= HI1 (inst.operands[2].reg) << 5;
- break;
+ return result;
+}
- case NS_FF: /* case 8 (fcpys). */
- do_vfp_nsyn_opcode ("fcpys");
- break;
+static void
+do_neon_ld_st_lane (void)
+{
+ struct neon_type_el et = neon_check_type (1, NS_NULL, N_8 | N_16 | N_32);
+ int align_good, do_alignment = 0;
+ int logsize = neon_logbits (et.size);
+ int align = inst.operands[1].imm >> 8;
+ int n = (inst.instruction >> 8) & 3;
+ int max_el = 64 / et.size;
- case NS_FI: /* case 10 (fconsts). */
- 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);
- }
- else
- first_error (_("immediate out of range"));
- break;
+ if (et.type == NT_invtype)
+ return;
- case NS_RF: /* case 12 (fmrs). */
- do_vfp_nsyn_opcode ("fmrs");
+ constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != n + 1,
+ _("bad list length"));
+ constraint (NEON_LANE (inst.operands[0].imm) >= max_el,
+ _("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"));
+
+ switch (n)
+ {
+ case 0: /* VLD1 / VST1. */
+ align_good = neon_alignment_bit (et.size, align, &do_alignment, 16, 16,
+ 32, 32, -1);
+ if (align_good == FAIL)
+ return;
+ if (do_alignment)
+ {
+ unsigned alignbits = 0;
+ switch (et.size)
+ {
+ case 16: alignbits = 0x1; break;
+ case 32: alignbits = 0x3; break;
+ default: ;
+ }
+ inst.instruction |= alignbits << 4;
+ }
break;
- case NS_FR: /* case 13 (fmsr). */
- do_vfp_nsyn_opcode ("fmsr");
+ case 1: /* VLD2 / VST2. */
+ align_good = neon_alignment_bit (et.size, align, &do_alignment, 8, 16,
+ 16, 32, 32, 64, -1);
+ if (align_good == FAIL)
+ return;
+ if (do_alignment)
+ inst.instruction |= 1 << 4;
break;
- /* The encoders for the fmrrs and fmsrr instructions expect three operands
- (one of which is a list), but we have parsed four. Do some fiddling to
- make the operands what do_vfp_reg2_from_sp2 and do_vfp_sp2_from_reg2
- expect. */
- case NS_RRFF: /* case 14 (fmrrs). */
- constraint (inst.operands[3].reg != inst.operands[2].reg + 1,
- _("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 2: /* VLD3 / VST3. */
+ constraint (inst.operands[1].immisalign,
+ _("can't use alignment with this instruction"));
break;
- case NS_FFRR: /* case 15 (fmsrr). */
- constraint (inst.operands[1].reg != inst.operands[0].reg + 1,
- _("VFP registers must be adjacent"));
- inst.operands[1] = inst.operands[2];
- inst.operands[2] = inst.operands[3];
- inst.operands[0].imm = 2;
- memset (&inst.operands[3], '\0', sizeof (inst.operands[3]));
- do_vfp_nsyn_opcode ("fmsrr");
+ case 3: /* VLD4 / VST4. */
+ align_good = neon_alignment_bit (et.size, align, &do_alignment, 8, 32,
+ 16, 64, 32, 64, 32, 128, -1);
+ if (align_good == FAIL)
+ return;
+ if (do_alignment)
+ {
+ 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:
- abort ();
+ default: ;
}
+
+ /* Reg stride of 2 is encoded in bit 5 when size==16, bit 6 when size==32. */
+ if (n != 0 && NEON_REG_STRIDE (inst.operands[0].imm) == 2)
+ inst.instruction |= 1 << (4 + logsize);
+
+ inst.instruction |= NEON_LANE (inst.operands[0].imm) << (logsize + 5);
+ inst.instruction |= logsize << 10;
}
+/* Encode single n-element structure to all lanes VLD<n> instructions. */
+
static void
-do_neon_rshift_round_imm (void)
+do_neon_ld_dup (void)
{
- 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_SU_ALL | N_KEY);
- int imm = inst.operands[2].imm;
+ struct neon_type_el et = neon_check_type (1, NS_NULL, N_8 | N_16 | N_32);
+ int align_good, do_alignment = 0;
- /* imm == 0 case is encoded as VMOV for V{R}SHR. */
- if (imm == 0)
+ if (et.type == NT_invtype)
+ return;
+
+ switch ((inst.instruction >> 8) & 3)
{
- inst.operands[2].present = 0;
- do_neon_mov ();
- return;
- }
+ 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_alignment, 16, 16, 32, 32, -1);
+ if (align_good == FAIL)
+ 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;
+ }
+ inst.instruction |= neon_logbits (et.size) << 6;
+ break;
- constraint (imm < 1 || (unsigned)imm > et.size,
- _("immediate out of range for shift"));
- neon_imm_shift (TRUE, et.type == NT_unsigned, neon_quad (rs), et,
- et.size - imm);
-}
+ case 1: /* VLD2. */
+ align_good = neon_alignment_bit (et.size, inst.operands[1].imm >> 8,
+ &do_alignment, 8, 16, 16, 32, 32, 64,
+ -1);
+ if (align_good == FAIL)
+ return;
+ constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 2,
+ _("bad list length"));
+ if (NEON_REG_STRIDE (inst.operands[0].imm) == 2)
+ inst.instruction |= 1 << 5;
+ inst.instruction |= neon_logbits (et.size) << 6;
+ break;
-static void
-do_neon_movl (void)
-{
- struct neon_type_el et = neon_check_type (2, NS_QD,
- N_EQK | N_DBL, N_SU_32 | N_KEY);
- unsigned sizebits = et.size >> 3;
- inst.instruction |= sizebits << 19;
- neon_two_same (0, et.type == NT_unsigned, -1);
-}
+ case 2: /* VLD3. */
+ constraint (inst.operands[1].immisalign,
+ _("can't use alignment with this instruction"));
+ constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 3,
+ _("bad list length"));
+ if (NEON_REG_STRIDE (inst.operands[0].imm) == 2)
+ inst.instruction |= 1 << 5;
+ inst.instruction |= neon_logbits (et.size) << 6;
+ break;
-static void
-do_neon_trn (void)
-{
- enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
- struct neon_type_el et = neon_check_type (2, rs,
- N_EQK, N_8 | N_16 | N_32 | N_KEY);
- NEON_ENCODE (INTEGER, inst);
- neon_two_same (neon_quad (rs), 1, et.size);
-}
+ case 3: /* VLD4. */
+ {
+ int align = inst.operands[1].imm >> 8;
+ align_good = neon_alignment_bit (et.size, align, &do_alignment, 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;
-static void
-do_neon_zip_uzp (void)
-{
- enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
- struct neon_type_el et = neon_check_type (2, rs,
- N_EQK, N_8 | N_16 | N_32 | N_KEY);
- if (rs == NS_DD && et.size == 32)
- {
- /* Special case: encode as VTRN.32 <Dd>, <Dm>. */
- inst.instruction = N_MNEM_vtrn;
- do_neon_trn ();
- return;
+ default: ;
}
- neon_two_same (neon_quad (rs), 1, et.size);
-}
-static void
-do_neon_sat_abs_neg (void)
-{
- enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
- struct neon_type_el et = neon_check_type (2, rs,
- N_EQK, N_S8 | N_S16 | N_S32 | N_KEY);
- neon_two_same (neon_quad (rs), 1, et.size);
+ inst.instruction |= do_alignment << 4;
}
-static void
-do_neon_pair_long (void)
-{
- enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
- struct neon_type_el et = neon_check_type (2, rs, N_EQK, N_SU_32 | N_KEY);
- /* Unsigned is encoded in OP field (bit 7) for these instruction. */
- inst.instruction |= (et.type == NT_unsigned) << 7;
- neon_two_same (neon_quad (rs), 1, et.size);
-}
+/* Disambiguate VLD<n> and VST<n> instructions, and fill in common bits (those
+ apart from bits [11:4]. */
static void
-do_neon_recip_est (void)
+do_neon_ldx_stx (void)
{
- enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
- struct neon_type_el et = neon_check_type (2, rs,
- N_EQK | N_FLT, N_F32 | N_U32 | N_KEY);
- inst.instruction |= (et.type == NT_float) << 8;
- neon_two_same (neon_quad (rs), 1, et.size);
-}
+ if (inst.operands[1].isreg)
+ constraint (inst.operands[1].reg == REG_PC, BAD_PC);
-static void
-do_neon_cls (void)
-{
- enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
- struct neon_type_el et = neon_check_type (2, rs,
- N_EQK, N_S8 | N_S16 | N_S32 | N_KEY);
- neon_two_same (neon_quad (rs), 1, et.size);
-}
+ 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;
-static void
-do_neon_clz (void)
-{
- enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
- struct neon_type_el et = neon_check_type (2, rs,
- N_EQK, N_I8 | N_I16 | N_I32 | N_KEY);
- neon_two_same (neon_quad (rs), 1, et.size);
-}
+ 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);
-static void
-do_neon_cnt (void)
-{
- enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
- struct neon_type_el et = neon_check_type (2, rs,
- N_EQK | N_INT, N_8 | N_KEY);
- neon_two_same (neon_quad (rs), 1, et.size);
-}
+ if (inst.operands[1].writeback)
+ {
+ inst.instruction |= 0xd;
+ }
+ else
+ inst.instruction |= 0xf;
+ }
-static void
-do_neon_swp (void)
-{
- enum neon_shape rs = neon_select_shape (NS_DD, NS_QQ, NS_NULL);
- neon_two_same (neon_quad (rs), 1, -1);
+ if (thumb_mode)
+ inst.instruction |= 0xf9000000;
+ else
+ inst.instruction |= 0xf4000000;
}
+/* FP v8. */
static void
-do_neon_tbl_tbx (void)
+do_vfp_nsyn_fpv8 (enum neon_shape rs)
{
- unsigned listlenbits;
- neon_check_type (3, NS_DLD, N_EQK, N_EQK, N_8 | N_KEY);
+ /* Targets like FPv5-SP-D16 don't support FP v8 instructions with
+ D register operands. */
+ if (neon_shape_class[rs] == SC_DOUBLE)
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_armv8),
+ _(BAD_FPU));
- if (inst.operands[1].imm < 1 || inst.operands[1].imm > 4)
+ NEON_ENCODE (FPV8, inst);
+
+ if (rs == NS_FFF || rs == NS_HHH)
{
- first_error (_("bad list length for table lookup"));
- return;
+ do_vfp_sp_dyadic ();
+
+ /* ARMv8.2 fp16 instruction. */
+ if (rs == NS_HHH)
+ do_scalar_fp16_v82_encode ();
}
+ else
+ do_vfp_dp_rd_rn_rm ();
- listlenbits = inst.operands[1].imm - 1;
- inst.instruction |= LOW4 (inst.operands[0].reg) << 12;
- inst.instruction |= HI1 (inst.operands[0].reg) << 22;
- inst.instruction |= LOW4 (inst.operands[1].reg) << 16;
- inst.instruction |= HI1 (inst.operands[1].reg) << 7;
- inst.instruction |= LOW4 (inst.operands[2].reg);
- inst.instruction |= HI1 (inst.operands[2].reg) << 5;
- inst.instruction |= listlenbits << 8;
+ if (rs == NS_DDD)
+ inst.instruction |= 0x100;
- neon_dp_fixup (&inst);
+ inst.instruction |= 0xf0000000;
}
static void
-do_neon_ldm_stm (void)
+do_vsel (void)
{
- /* P, U and L bits are part of bitmask. */
- int is_dbmode = (inst.instruction & (1 << 24)) != 0;
- unsigned offsetbits = inst.operands[1].imm * 2;
-
- if (inst.operands[1].issingle)
- {
- do_vfp_nsyn_ldm_stm (is_dbmode);
- return;
- }
+ set_it_insn_type (OUTSIDE_IT_INSN);
- constraint (is_dbmode && !inst.operands[0].writeback,
- _("writeback (!) must be used for VLDMDB and VSTMDB"));
+ if (try_vfp_nsyn (3, do_vfp_nsyn_fpv8) != SUCCESS)
+ first_error (_("invalid instruction shape"));
+}
- constraint (inst.operands[1].imm < 1 || inst.operands[1].imm > 16,
- _("register list must contain at least 1 and at most 16 "
- "registers"));
+static void
+do_vmaxnm (void)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
- inst.instruction |= inst.operands[0].reg << 16;
- inst.instruction |= inst.operands[0].writeback << 21;
- inst.instruction |= LOW4 (inst.operands[1].reg) << 12;
- inst.instruction |= HI1 (inst.operands[1].reg) << 22;
+ if (try_vfp_nsyn (3, do_vfp_nsyn_fpv8) == SUCCESS)
+ return;
- inst.instruction |= offsetbits;
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH8) == FAIL)
+ return;
- do_vfp_cond_or_thumb ();
+ neon_dyadic_misc (NT_untyped, N_F_16_32, 0);
}
static void
-do_neon_ldr_str (void)
+do_vrint_1 (enum neon_cvt_mode mode)
{
- int is_ldr = (inst.instruction & (1 << 20)) != 0;
+ enum neon_shape rs = neon_select_shape (NS_HH, NS_FF, NS_DD, NS_QQ, NS_NULL);
+ struct neon_type_el et;
- /* Use of PC in vstr in ARM mode is deprecated in ARMv7.
- And is UNPREDICTABLE in thumb mode. */
- if (!is_ldr
- && inst.operands[1].reg == REG_PC
- && ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v7))
- {
- if (!thumb_mode && warn_on_deprecated)
- as_warn (_("Use of PC here is deprecated"));
- else
- inst.error = _("Use of PC here is UNPREDICTABLE");
- }
+ if (rs == NS_NULL)
+ return;
- if (inst.operands[0].issingle)
+ /* Targets like FPv5-SP-D16 don't support FP v8 instructions with
+ D register operands. */
+ if (neon_shape_class[rs] == SC_DOUBLE)
+ constraint (!ARM_CPU_HAS_FEATURE (cpu_variant, fpu_vfp_ext_armv8),
+ _(BAD_FPU));
+
+ et = neon_check_type (2, rs, N_EQK | N_VFP, N_F_ALL | N_KEY
+ | N_VFP);
+ if (et.type != NT_invtype)
{
- if (is_ldr)
- do_vfp_nsyn_opcode ("flds");
+ /* 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 || rs == NS_HH)
+ do_vfp_sp_monadic ();
else
- do_vfp_nsyn_opcode ("fsts");
+ 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 ();
+
+ /* ARMv8.2 fp16 vrint instruction. */
+ if (rs == NS_HH)
+ do_scalar_fp16_v82_encode ();
}
else
{
- if (is_ldr)
- do_vfp_nsyn_opcode ("fldd");
- else
- do_vfp_nsyn_opcode ("fstd");
- }
-}
+ /* Neon encodings (or something broken...). */
+ inst.error = NULL;
+ et = neon_check_type (2, rs, N_EQK, N_F_16_32 | N_KEY);
-/* "interleave" version also handles non-interleaving register VLD1/VST1
- instructions. */
+ if (et.type == NT_invtype)
+ return;
-static void
-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);
- unsigned alignbits = 0;
- unsigned idx;
- /* The bits in this table go:
- 0: register stride of one (0) or two (1)
- 1,2: register list length, minus one (1, 2, 3, 4).
- 3,4: <n> in instruction type, minus one (VLD<n> / VST<n>).
- We use -1 for invalid entries. */
- const int typetable[] =
- {
- 0x7, -1, 0xa, -1, 0x6, -1, 0x2, -1, /* VLD1 / VST1. */
- -1, -1, 0x8, 0x9, -1, -1, 0x3, -1, /* VLD2 / VST2. */
- -1, -1, -1, -1, 0x4, 0x5, -1, -1, /* VLD3 / VST3. */
- -1, -1, -1, -1, -1, -1, 0x0, 0x1 /* VLD4 / VST4. */
- };
- int typebits;
+ set_it_insn_type (OUTSIDE_IT_INSN);
+ NEON_ENCODE (FLOAT, inst);
- if (et.type == NT_invtype)
- return;
+ if (vfp_or_neon_is_neon (NEON_CHECK_CC | NEON_CHECK_ARCH8) == FAIL)
+ return;
- if (inst.operands[1].immisalign)
- switch (inst.operands[1].imm >> 8)
- {
- case 64: alignbits = 1; break;
- case 128:
- if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 2
- && NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4)
- goto bad_alignment;
- alignbits = 2;
- break;
- case 256:
- if (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 4)
- goto bad_alignment;
- alignbits = 3;
- break;
- default:
- bad_alignment:
- first_error (_("bad alignment"));
- 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;
+ /* Mask off the original size bits and reencode them. */
+ inst.instruction = ((inst.instruction & 0xfff3ffff)
+ | neon_logbits (et.size) << 18);
- inst.instruction |= alignbits << 4;
- inst.instruction |= neon_logbits (et.size) << 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 ();
+ }
- /* Bits [4:6] of the immediate in a list specifier encode register stride
- (minus 1) in bit 4, and list length in bits [5:6]. We put the <n> of
- VLD<n>/VST<n> in bits [9:8] of the initial bitmask. Suck it out here, look
- 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);
+ if (thumb_mode)
+ inst.instruction |= 0xfc000000;
+ else
+ inst.instruction |= 0xf0000000;
+ }
+}
- typebits = typetable[idx];
+static void
+do_vrintx (void)
+{
+ do_vrint_1 (neon_cvt_mode_x);
+}
- constraint (typebits == -1, _("bad list type for instruction"));
+static void
+do_vrintz (void)
+{
+ do_vrint_1 (neon_cvt_mode_z);
+}
- inst.instruction &= ~0xf00;
- inst.instruction |= typebits << 8;
+static void
+do_vrintr (void)
+{
+ do_vrint_1 (neon_cvt_mode_r);
}
-/* Check alignment is valid for do_neon_ld_st_lane and do_neon_ld_dup.
- *DO_ALIGN is set to 1 if the relevant alignment bit should be set, 0
- otherwise. The variable arguments are a list of pairs of legal (size, align)
- values, terminated with -1. */
+static void
+do_vrinta (void)
+{
+ do_vrint_1 (neon_cvt_mode_a);
+}
-static int
-neon_alignment_bit (int size, int align, int *do_align, ...)
+static void
+do_vrintn (void)
{
- va_list ap;
- int result = FAIL, thissize, thisalign;
+ do_vrint_1 (neon_cvt_mode_n);
+}
- if (!inst.operands[1].immisalign)
- {
- *do_align = 0;
- return SUCCESS;
- }
+static void
+do_vrintp (void)
+{
+ do_vrint_1 (neon_cvt_mode_p);
+}
- va_start (ap, do_align);
+static void
+do_vrintm (void)
+{
+ do_vrint_1 (neon_cvt_mode_m);
+}
- do
- {
- thissize = va_arg (ap, int);
- if (thissize == -1)
- break;
- thisalign = va_arg (ap, int);
+/* Crypto v1 instructions. */
+static void
+do_crypto_2op_1 (unsigned elttype, int op)
+{
+ set_it_insn_type (OUTSIDE_IT_INSN);
- if (size == thissize && align == thisalign)
- result = SUCCESS;
- }
- while (result != SUCCESS);
+ if (neon_check_type (2, NS_QQ, N_EQK | N_UNT, elttype | N_UNT | N_KEY).type
+ == NT_invtype)
+ return;
- va_end (ap);
+ inst.error = NULL;
- if (result == SUCCESS)
- *do_align = 1;
- else
- first_error (_("unsupported alignment for instruction"));
+ 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;
- return result;
+ if (thumb_mode)
+ inst.instruction |= 0xfc000000;
+ else
+ inst.instruction |= 0xf0000000;
}
static void
-do_neon_ld_st_lane (void)
+do_crypto_3op_1 (int u, int op)
{
- struct neon_type_el et = neon_check_type (1, NS_NULL, N_8 | N_16 | N_32);
- int align_good, do_align = 0;
- int logsize = neon_logbits (et.size);
- int align = inst.operands[1].imm >> 8;
- int n = (inst.instruction >> 8) & 3;
- int max_el = 64 / et.size;
+ set_it_insn_type (OUTSIDE_IT_INSN);
- if (et.type == NT_invtype)
+ 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;
- constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != n + 1,
- _("bad list length"));
- constraint (NEON_LANE (inst.operands[0].imm) >= max_el,
- _("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"));
-
- switch (n)
- {
- case 0: /* VLD1 / VST1. */
- align_good = neon_alignment_bit (et.size, align, &do_align, 16, 16,
- 32, 32, -1);
- if (align_good == FAIL)
- 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;
- }
- break;
-
- case 1: /* VLD2 / VST2. */
- align_good = neon_alignment_bit (et.size, align, &do_align, 8, 16, 16, 32,
- 32, 64, -1);
- if (align_good == FAIL)
- return;
- if (do_align)
- inst.instruction |= 1 << 4;
- break;
-
- case 2: /* VLD3 / VST3. */
- constraint (inst.operands[1].immisalign,
- _("can't use alignment with this instruction"));
- break;
+ inst.error = NULL;
- case 3: /* VLD4 / VST4. */
- align_good = neon_alignment_bit (et.size, align, &do_align, 8, 32,
- 16, 64, 32, 64, 32, 128, -1);
- if (align_good == FAIL)
- 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;
- }
- break;
+ NEON_ENCODE (INTEGER, inst);
+ neon_three_same (1, u, 8 << op);
+}
- default: ;
- }
+static void
+do_aese (void)
+{
+ do_crypto_2op_1 (N_8, 0);
+}
- /* Reg stride of 2 is encoded in bit 5 when size==16, bit 6 when size==32. */
- if (n != 0 && NEON_REG_STRIDE (inst.operands[0].imm) == 2)
- inst.instruction |= 1 << (4 + logsize);
+static void
+do_aesd (void)
+{
+ do_crypto_2op_1 (N_8, 1);
+}
- inst.instruction |= NEON_LANE (inst.operands[0].imm) << (logsize + 5);
- inst.instruction |= logsize << 10;
+static void
+do_aesmc (void)
+{
+ do_crypto_2op_1 (N_8, 2);
}
-/* Encode single n-element structure to all lanes VLD<n> instructions. */
+static void
+do_aesimc (void)
+{
+ do_crypto_2op_1 (N_8, 3);
+}
static void
-do_neon_ld_dup (void)
+do_sha1c (void)
{
- struct neon_type_el et = neon_check_type (1, NS_NULL, N_8 | N_16 | N_32);
- int align_good, do_align = 0;
+ do_crypto_3op_1 (0, 0);
+}
- if (et.type == NT_invtype)
- return;
+static void
+do_sha1p (void)
+{
+ do_crypto_3op_1 (0, 1);
+}
- switch ((inst.instruction >> 8) & 3)
- {
- 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);
- if (align_good == FAIL)
- 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;
- }
- inst.instruction |= neon_logbits (et.size) << 6;
- break;
+static void
+do_sha1m (void)
+{
+ do_crypto_3op_1 (0, 2);
+}
- case 1: /* VLD2. */
- align_good = neon_alignment_bit (et.size, inst.operands[1].imm >> 8,
- &do_align, 8, 16, 16, 32, 32, 64, -1);
- if (align_good == FAIL)
- return;
- constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 2,
- _("bad list length"));
- if (NEON_REG_STRIDE (inst.operands[0].imm) == 2)
- inst.instruction |= 1 << 5;
- inst.instruction |= neon_logbits (et.size) << 6;
- break;
+static void
+do_sha1su0 (void)
+{
+ do_crypto_3op_1 (0, 3);
+}
- case 2: /* VLD3. */
- constraint (inst.operands[1].immisalign,
- _("can't use alignment with this instruction"));
- constraint (NEON_REGLIST_LENGTH (inst.operands[0].imm) != 3,
- _("bad list length"));
- if (NEON_REG_STRIDE (inst.operands[0].imm) == 2)
- inst.instruction |= 1 << 5;
- inst.instruction |= neon_logbits (et.size) << 6;
- break;
+static void
+do_sha256h (void)
+{
+ do_crypto_3op_1 (1, 0);
+}
- 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;
- }
- break;
+static void
+do_sha256h2 (void)
+{
+ do_crypto_3op_1 (1, 1);
+}
- default: ;
- }
+static void
+do_sha256su1 (void)
+{
+ do_crypto_3op_1 (1, 2);
+}
- inst.instruction |= do_align << 4;
+static void
+do_sha1h (void)
+{
+ do_crypto_2op_1 (N_32, -1);
}
-/* Disambiguate VLD<n> and VST<n> instructions, and fill in common bits (those
- apart from bits [11:4]. */
+static void
+do_sha1su1 (void)
+{
+ do_crypto_2op_1 (N_32, 0);
+}
static void
-do_neon_ldx_stx (void)
+do_sha256su0 (void)
{
- if (inst.operands[1].isreg)
- constraint (inst.operands[1].reg == REG_PC, BAD_PC);
+ do_crypto_2op_1 (N_32, 1);
+}
- switch (NEON_LANE (inst.operands[0].imm))
- {
- case NEON_INTERLEAVE_LANES:
- NEON_ENCODE (INTERLV, inst);
- do_neon_ld_st_interleave ();
- break;
+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;
- case NEON_ALL_LANES:
- NEON_ENCODE (DUP, inst);
- do_neon_ld_dup ();
- break;
+ 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);
- default:
- NEON_ENCODE (LANE, inst);
- do_neon_ld_st_lane ();
- }
+ 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"));
+}
- /* 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_crc32b (void)
+{
+ do_crc32_1 (0, 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_crc32h (void)
+{
+ do_crc32_1 (0, 1);
+}
- if (thumb_mode)
- inst.instruction |= 0xf9000000;
- else
- inst.instruction |= 0xf4000000;
+static void
+do_crc32w (void)
+{
+ do_crc32_1 (0, 2);
+}
+
+static void
+do_crc32cb (void)
+{
+ do_crc32_1 (1, 0);
+}
+
+static void
+do_crc32ch (void)
+{
+ do_crc32_1 (1, 1);
+}
+
+static void
+do_crc32cw (void)
+{
+ do_crc32_1 (1, 2);
}
+
\f
/* Overall per-instruction processing. */
switch (exp->X_op)
{
case O_constant:
+ 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. */
+ char name[16 + 8];
+ symbolS *symbol;
+
+ sprintf (name, "*ABS*0x%lx", (unsigned long)exp->X_add_number);
+
+ symbol = symbol_find_or_make (name);
+ S_SET_SEGMENT (symbol, absolute_section);
+ symbol_set_frag (symbol, &zero_address_frag);
+ S_SET_VALUE (symbol, exp->X_add_number);
+ exp->X_op = O_symbol;
+ exp->X_add_symbol = symbol;
+ exp->X_add_number = 0;
+ }
+ /* FALLTHROUGH */
case O_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 */
}
if (warn_on_deprecated && unified_syntax)
- as_warn (_("conditional infixes are deprecated in unified syntax"));
+ as_tsktsk (_("conditional infixes are deprecated in unified syntax"));
affix = base + (opcode->tag - OT_odd_infix_0);
cond = (const struct asm_cond *) hash_find_n (arm_cond_hsh, affix, 2);
gas_assert (cond);
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);
if (warn_on_deprecated && unified_syntax
&& (opcode->tag == OT_cinfix3
|| opcode->tag == OT_cinfix3_deprecated))
- as_warn (_("conditional infixes are deprecated in unified syntax"));
+ as_tsktsk (_("conditional infixes are deprecated in unified syntax"));
inst.cond = cond->value;
return opcode;
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
{
if ((implicit_it_mode & IMPLICIT_IT_MODE_THUMB)
- && ARM_CPU_HAS_FEATURE (cpu_variant, arm_arch_t2))
+ && ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6t2))
{
/* Automatically generate the IT instruction. */
new_automatic_it_block (inst.cond);
}
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") },
+ /* NOTE: 0x00dd is not the real encoding, instead, it is the 'tvalue'
+ field in asm_opcode. 'tvalue' is used at the stage this check happen. */
+ { 0x00dd, 0x7fff, N_("ADD/SUB sp, sp #imm") },
+ { 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_tsktsk (_("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_tsktsk (_("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_tsktsk (_("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)
{
return now_it.state != OUTSIDE_IT_BLOCK;
}
+/* Whether OPCODE only has T32 encoding. Since this function is only used by
+ t32_insn_ok, OPCODE enabled by v6t2 extension bit do not need to be listed
+ here, hence the "known" in the function name. */
+
+static bfd_boolean
+known_t32_only_insn (const struct asm_opcode *opcode)
+{
+ /* Original Thumb-1 wide instruction. */
+ if (opcode->tencode == do_t_blx
+ || opcode->tencode == do_t_branch23
+ || ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_msr)
+ || ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_barrier))
+ return TRUE;
+
+ /* Wide-only instruction added to ARMv8-M. */
+ if (ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_v8m)
+ || ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_atomics)
+ || ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_v6t2_v8m)
+ || ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_div))
+ return TRUE;
+
+ return FALSE;
+}
+
+/* Whether wide instruction variant can be used if available for a valid OPCODE
+ in ARCH. */
+
+static bfd_boolean
+t32_insn_ok (arm_feature_set arch, const struct asm_opcode *opcode)
+{
+ if (known_t32_only_insn (opcode))
+ return TRUE;
+
+ /* Instruction with narrow and wide encoding added to ARMv8-M. Availability
+ of variant T3 of B.W is checked in do_t_branch. */
+ if (ARM_CPU_HAS_FEATURE (arch, arm_ext_v8m)
+ && opcode->tencode == do_t_branch)
+ return TRUE;
+
+ /* Wide instruction variants of all instructions with narrow *and* wide
+ variants become available with ARMv6t2. Other opcodes are either
+ narrow-only or wide-only and are thus available if OPCODE is valid. */
+ if (ARM_CPU_HAS_FEATURE (arch, arm_ext_v6t2))
+ return TRUE;
+
+ /* OPCODE with narrow only instruction variant or wide variant not
+ available. */
+ return FALSE;
+}
+
void
md_assemble (char *str)
{
/* 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;
}
if (warn_on_deprecated && opcode->tag == OT_cinfix3_deprecated)
- as_warn (_("s suffix on comparison instruction is deprecated"));
+ as_tsktsk (_("s suffix on comparison instruction is deprecated"));
/* The value which unconditional instructions should have in place of the
condition field. */
|| (thumb_mode == 1
&& !ARM_CPU_HAS_FEATURE (variant, *opcode->tvariant)))
{
- as_bad (_("selected processor does not support Thumb mode `%s'"), str);
+ as_bad (_("selected processor does not support `%s' in Thumb mode"), str);
return;
}
if (inst.cond != COND_ALWAYS && !unified_syntax
return;
}
- if (!ARM_CPU_HAS_FEATURE (variant, arm_ext_v6t2))
+ /* Two things are addressed here:
+ 1) Implicit require narrow instructions on Thumb-1.
+ This avoids relaxation accidentally introducing Thumb-2
+ instructions.
+ 2) Reject wide instructions in non Thumb-2 cores.
+
+ Only instructions with narrow and wide variants need to be handled
+ but selecting all non wide-only instructions is easier. */
+ if (!ARM_CPU_HAS_FEATURE (variant, arm_ext_v6t2)
+ && !t32_insn_ok (variant, opcode))
{
- if (opcode->tencode != do_t_blx && opcode->tencode != do_t_branch23
- && !(ARM_CPU_HAS_FEATURE(*opcode->tvariant, arm_ext_msr)
- || ARM_CPU_HAS_FEATURE(*opcode->tvariant, arm_ext_barrier)))
+ if (inst.size_req == 0)
+ inst.size_req = 2;
+ else if (inst.size_req == 4)
{
- /* Two things are addressed here.
- 1) Implicit require narrow instructions on Thumb-1.
- This avoids relaxation accidentally introducing Thumb-2
- instructions.
- 2) Reject wide instructions in non Thumb-2 cores. */
- if (inst.size_req == 0)
- inst.size_req = 2;
- else if (inst.size_req == 4)
- {
- as_bad (_("selected processor does not support Thumb-2 mode `%s'"), str);
- return;
- }
+ if (ARM_CPU_HAS_FEATURE (variant, arm_ext_v8m))
+ as_bad (_("selected processor does not support 32bit wide "
+ "variant of instruction `%s'"), str);
+ else
+ as_bad (_("selected processor does not support `%s' in "
+ "Thumb-2 mode"), 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,
*opcode->tvariant);
/* Many Thumb-2 instructions also have Thumb-1 variants, so explicitly
- set those bits when Thumb-2 32-bit instructions are seen. ie.
- anything other than bl/blx and v6-M instructions.
- This is overly pessimistic for relaxable instructions. */
- if (((inst.size == 4 && (inst.instruction & 0xf800e800) != 0xf000e800)
- || inst.relax)
- && !(ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_msr)
- || ARM_CPU_HAS_FEATURE (*opcode->tvariant, arm_ext_barrier)))
+ set those bits when Thumb-2 32-bit instructions are seen. The impact
+ of relaxable instructions will be considered later after we finish all
+ relaxation. */
+ if (ARM_FEATURE_CORE_EQUAL (cpu_variant, arm_arch_any))
+ variant = arm_arch_none;
+ else
+ variant = cpu_variant;
+ if (inst.size == 4 && !t32_insn_ok (variant, opcode))
ARM_MERGE_FEATURE_SETS (thumb_arch_used, thumb_arch_used,
arm_ext_v6t2);
&& !(opcode->avariant &&
ARM_CPU_HAS_FEATURE (cpu_variant, *opcode->avariant)))
{
- as_bad (_("selected processor does not support ARM mode `%s'"), str);
+ as_bad (_("selected processor does not support `%s' in ARM mode"), str);
return;
}
if (inst.size_req)
inst.instruction = opcode->avalue;
if (opcode->tag == OT_unconditionalF)
- inst.instruction |= 0xF << 28;
+ inst.instruction |= 0xFU << 28;
else
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. */
{"psp", 9 }, {"PSP", 9 },
{"primask", 16}, {"PRIMASK", 16},
{"basepri", 17}, {"BASEPRI", 17},
- {"basepri_max", 18}, {"BASEPRI_MAX", 18}, /* Typo, preserved for backwards compatibility. */
- {"basepri_mask",18}, {"BASEPRI_MASK", 18},
+ {"basepri_max", 18}, {"BASEPRI_MAX", 18},
+ {"basepri_max", 18}, {"BASEPRI_MASK", 18}, /* Typo, preserved for backwards compatibility. */
{"faultmask", 19}, {"FAULTMASK", 19},
{"control", 20}, {"CONTROL", 20}
};
{ "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_CORE_LOW (FEAT) }, \
+ { U, CODE, ARM_FEATURE_CORE_LOW (FEAT) }
+
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),
CL("cmnp", 170f000, 2, (RR, SH), cmp),
tCE("mov", 1a00000, _mov, 2, (RR, SH), mov, t_mov_cmp),
- tC3("movs", 1b00000, _movs, 2, (RR, SH), mov, t_mov_cmp),
+ tC3("movs", 1b00000, _movs, 2, (RR, SHG), mov, t_mov_cmp),
tCE("mvn", 1e00000, _mvn, 2, (RR, SH), mov, t_mvn_tst),
tC3("mvns", 1f00000, _mvns, 2, (RR, SH), mov, t_mvn_tst),
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),
TUF("setend", 1010000, b650, 1, (ENDI), setend, t_setend),
#undef THUMB_VARIANT
-#define THUMB_VARIANT & arm_ext_v6t2
+#define THUMB_VARIANT & arm_ext_v6t2_v8m
TCE("ldrex", 1900f9f, e8500f00, 2, (RRnpc_npcsp, ADDR), ldrex, t_ldrex),
TCE("strex", 1800f90, e8400000, 3, (RRnpc_npcsp, RRnpc_npcsp, ADDR),
strex, t_strex),
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6t2
+
TUF("mcrr2", c400000, fc400000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
TUF("mrrc2", c500000, fc500000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
#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),
+ TUF("cps", 1020000, f3af8100, 1, (I31b), imm0, t_cps),
/* ARM V6 not included in V7M (eg. integer SIMD). */
#undef THUMB_VARIANT
#define THUMB_VARIANT & arm_ext_v6_dsp
- TUF("cps", 1020000, f3af8100, 1, (I31b), imm0, t_cps),
TCE("pkhbt", 6800010, eac00000, 4, (RRnpc, RRnpc, RRnpc, oSHll), pkhbt, t_pkhbt),
TCE("pkhtb", 6800050, eac00020, 4, (RRnpc, RRnpc, RRnpc, oSHar), pkhtb, t_pkhtb),
TCE("qadd16", 6200f10, fa90f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
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),
RRnpcb), strexd, t_strexd),
#undef THUMB_VARIANT
-#define THUMB_VARIANT & arm_ext_v6t2
+#define THUMB_VARIANT & arm_ext_v6t2_v8m
TCE("ldrexb", 1d00f9f, e8d00f4f, 2, (RRnpc_npcsp,RRnpcb),
rd_rn, rd_rn),
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("hvc", 1400070, f7e08000, 1, (EXPi), hvc, t_hvc),
TCE("eret", 160006e, f3de8f00, 0, (), noargs, noargs),
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_pan
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_pan
+
+ TUF("setpan", 1100000, b610, 1, (I7), setpan, t_setpan),
+
#undef ARM_VARIANT
-#define ARM_VARIANT & arm_ext_v6t2
+#define ARM_VARIANT & arm_ext_v6t2
#undef THUMB_VARIANT
#define THUMB_VARIANT & arm_ext_v6t2
TCE("ubfx", 7e00050, f3c00000, 4, (RR, RR, I31, I32), bfx, t_bfx),
TCE("mls", 0600090, fb000010, 4, (RRnpc, RRnpc, RRnpc, RRnpc), mlas, t_mla),
- TCE("movw", 3000000, f2400000, 2, (RRnpc, HALF), mov16, t_mov16),
- TCE("movt", 3400000, f2c00000, 2, (RRnpc, HALF), mov16, t_mov16),
TCE("rbit", 6ff0f30, fa90f0a0, 2, (RR, RR), rd_rm, t_rbit),
TC3("ldrht", 03000b0, f8300e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt),
TC3("ldrsbt", 03000d0, f9100e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt),
TC3("strht", 02000b0, f8200e00, 2, (RRnpc_npcsp, ADDR), ldsttv4, t_ldstt),
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6t2_v8m
+ TCE("movw", 3000000, f2400000, 2, (RRnpc, HALF), mov16, t_mov16),
+ TCE("movt", 3400000, f2c00000, 2, (RRnpc, HALF), mov16, t_mov16),
+
/* 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),
-mimplicit-it=[never | arm] modes. */
#undef ARM_VARIANT
#define ARM_VARIANT & arm_ext_v1
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v6t2
TUE("it", bf08, bf08, 1, (COND), it, t_it),
TUE("itt", bf0c, bf0c, 1, (COND), it, t_it),
#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
+
+/* Instructions shared between armv8-a and armv8-m. */
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_atomics
+
+ 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),
+ TCE("ldaex", 1900e9f, e8d00fef, 2, (RRnpc, RRnpcb), rd_rn, rd_rn),
+ 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("stlexb", 1c00e90, e8c00fc0, 3, (RRnpc, RRnpc, RRnpcb),
+ stlex, t_stlex),
+ TCE("stlexh", 1e00e90, e8c00fd0, 3, (RRnpc, RRnpc, RRnpcb),
+ stlex, t_stlex),
+#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("ldaexd", 1b00e9f, e8d000ff, 3, (RRnpc, oRRnpc, RRnpcb),
+ ldrexd, t_ldrexd),
+ TCE("stlexd", 1a00e90, e8c000f0, 4, (RRnpc, RRnpc, oRRnpc, RRnpcb),
+ strexd, t_strexd),
+ /* 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_armv8xd
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & fpu_vfp_ext_armv8xd
+
+ 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),
+
+ /* ARMv8.2 RAS extension. */
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_v8_2
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v8_2
+ TUE ("esb", 320f010, f3af8010, 0, (), noargs, noargs),
+
#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(vnmul, _vnmul, 3, (RVSD, RVSD, RVSD), vfp_nsyn_nmul),
nCE(vnmla, _vnmla, 3, (RVSD, RVSD, RVSD), vfp_nsyn_nmul),
nCE(vnmls, _vnmls, 3, (RVSD, RVSD, RVSD), vfp_nsyn_nmul),
- nCE(vcmp, _vcmp, 2, (RVSD, RVSD_I0), vfp_nsyn_cmp),
- nCE(vcmpe, _vcmpe, 2, (RVSD, RVSD_I0), vfp_nsyn_cmp),
+ nCE(vcmp, _vcmp, 2, (RVSD, RSVD_FI0), vfp_nsyn_cmp),
+ nCE(vcmpe, _vcmpe, 2, (RVSD, RSVD_FI0), vfp_nsyn_cmp),
NCE(vpush, 0, 1, (VRSDLST), vfp_nsyn_push),
NCE(vpop, 0, 1, (VRSDLST), vfp_nsyn_pop),
NCE(vcvtz, 0, 2, (RVSD, RVSD), vfp_nsyn_cvtz),
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! */
NCE(vmov, 0, 1, (VMOV), neon_mov),
NCE(vmovq, 0, 1, (VMOV), neon_mov),
+#undef ARM_VARIANT
+#define ARM_VARIANT & arm_ext_fp16
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_fp16
+ /* New instructions added from v8.2, allowing the extraction and insertion of
+ the upper 16 bits of a 32-bit vector register. */
+ NCE (vmovx, eb00a40, 2, (RVS, RVS), neon_movhf),
+ NCE (vins, eb00ac0, 2, (RVS, RVS), neon_movhf),
+
#undef THUMB_VARIANT
#define THUMB_VARIANT & fpu_neon_ext_v1
#undef ARM_VARIANT
NUF(vbitq, 1200110, 3, (RNQ, RNQ, RNQ), neon_bitfield),
NUF(vbif, 1300110, 3, (RNDQ, RNDQ, RNDQ), neon_bitfield),
NUF(vbifq, 1300110, 3, (RNQ, RNQ, RNQ), neon_bitfield),
- /* Int and float variants, types S8 S16 S32 U8 U16 U32 F32. */
+ /* Int and float variants, types S8 S16 S32 U8 U16 U32 F16 F32. */
nUF(vabd, _vabd, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_if_su),
nUF(vabdq, _vabd, 3, (RNQ, oRNQ, RNQ), neon_dyadic_if_su),
nUF(vmax, _vmax, 3, (RNDQ, oRNDQ, RNDQ), neon_dyadic_if_su),
NUF(vrecpsq, 0000f10, 3, (RNQ, oRNQ, RNQ), neon_step),
NUF(vrsqrts, 0200f10, 3, (RNDQ, oRNDQ, RNDQ), neon_step),
NUF(vrsqrtsq, 0200f10, 3, (RNQ, oRNQ, RNQ), neon_step),
+ /* ARM v8.1 extension. */
+ nUF (vqrdmlah, _vqrdmlah, 3, (RNDQ, oRNDQ, RNDQ_RNSC), neon_qrdmlah),
+ nUF (vqrdmlahq, _vqrdmlah, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_qrdmlah),
+ nUF (vqrdmlsh, _vqrdmlsh, 3, (RNDQ, oRNDQ, RNDQ_RNSC), neon_qrdmlah),
+ nUF (vqrdmlshq, _vqrdmlsh, 3, (RNQ, oRNQ, RNDQ_RNSC), neon_qrdmlah),
/* Two address, int/float. Types S8 S16 S32 F32. */
NUF(vabsq, 1b10300, 2, (RNQ, RNQ), neon_abs_neg),
NUF(vpadalq, 1b00600, 2, (RNQ, RNQ), neon_pair_long),
NUF(vpaddl, 1b00200, 2, (RNDQ, RNDQ), neon_pair_long),
NUF(vpaddlq, 1b00200, 2, (RNQ, RNQ), neon_pair_long),
- /* Reciprocal estimates. Types U32 F32. */
+ /* Reciprocal estimates. Types U32 F16 F32. */
NUF(vrecpe, 1b30400, 2, (RNDQ, RNDQ), neon_recip_est),
NUF(vrecpeq, 1b30400, 2, (RNQ, RNQ), neon_recip_est),
NUF(vrsqrte, 1b30480, 2, (RNDQ, RNDQ), neon_recip_est),
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
+#define ARM_VARIANT NULL
+#undef THUMB_VARIANT
+#define THUMB_VARIANT & arm_ext_v8m
+ TUE("tt", 0, e840f000, 2, (RRnpc, RRnpc), 0, tt),
+ TUE("ttt", 0, e840f040, 2, (RRnpc, RRnpc), 0, tt),
};
#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. */
fixp->fx_file = fragp->fr_file;
fixp->fx_line = fragp->fr_line;
fragp->fr_fix += fragp->fr_var;
+
+ /* Set whether we use thumb-2 ISA based on final relaxation results. */
+ if (thumb_mode && fragp->fr_var == 4 && no_cpu_selected ()
+ && !ARM_CPU_HAS_FEATURE (thumb_arch_used, arm_arch_t2))
+ ARM_MERGE_FEATURE_SETS (arm_arch_used, thumb_arch_used, arm_ext_v6t2);
}
/* Return the size of a relaxable immediate operand instruction.
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;
int align;
align = bfd_get_section_alignment (stdoutput, segment);
- size = ((size + (1 << align) - 1) & ((valueT) -1 << align));
+ size = ((size + (1 << align) - 1) & (-((valueT) 1 << align)));
}
#endif
void
arm_handle_align (fragS * fragP)
{
- static char const arm_noop[2][2][4] =
+ static unsigned char const arm_noop[2][2][4] =
{
{ /* ARMv1 */
{0x00, 0x00, 0xa0, 0xe1}, /* LE */
{0xe3, 0x20, 0xf0, 0x00}, /* BE */
},
};
- static char const thumb_noop[2][2][2] =
+ static unsigned char const thumb_noop[2][2][2] =
{
{ /* Thumb-1 */
{0xc0, 0x46}, /* LE */
{0xbf, 0x00} /* BE */
}
};
- static char const wide_thumb_noop[2][4] =
+ static unsigned char const wide_thumb_noop[2][4] =
{ /* Wide Thumb-2 */
{0xaf, 0xf3, 0x00, 0x80}, /* LE */
{0xf3, 0xaf, 0x80, 0x00}, /* BE */
unsigned bytes, fix, noop_size;
char * p;
- const char * noop;
- const char *narrow_noop = NULL;
+ const unsigned char * noop;
+ const unsigned char *narrow_noop = NULL;
#ifdef OBJ_ELF
enum mstate state;
#endif
if (fragP->tc_frag_data.thumb_mode & (~ MODE_RECORDED))
{
- if (ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6t2))
+ if (ARM_CPU_HAS_FEATURE (selected_cpu_name[0]
+ ? selected_cpu : arm_arch_none, arm_ext_v6t2))
{
narrow_noop = thumb_noop[1][target_big_endian];
noop = wide_thumb_noop[target_big_endian];
}
else
{
- noop = arm_noop[ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v6k) != 0]
+ noop = arm_noop[ARM_CPU_HAS_FEATURE (selected_cpu_name[0]
+ ? selected_cpu : arm_arch_none,
+ arm_ext_v6k) != 0]
[target_big_endian];
noop_size = 4;
#ifdef OBJ_ELF
{
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);
}
void
arm_init_frag (fragS * fragP, int max_chars)
{
+ int frag_thumb_mode;
+
/* If the current ARM vs THUMB mode has not already
been recorded into this frag then do so now. */
if ((fragP->tc_frag_data.thumb_mode & MODE_RECORDED) == 0)
- {
- fragP->tc_frag_data.thumb_mode = thumb_mode | MODE_RECORDED;
+ fragP->tc_frag_data.thumb_mode = thumb_mode | MODE_RECORDED;
- /* Record a mapping symbol for alignment frags. We will delete this
- later if the alignment ends up empty. */
- switch (fragP->fr_type)
- {
- case rs_align:
- case rs_align_test:
- case rs_fill:
- mapping_state_2 (MAP_DATA, max_chars);
- break;
- case rs_align_code:
- mapping_state_2 (thumb_mode ? MAP_THUMB : MAP_ARM, max_chars);
- break;
- default:
- break;
- }
+ frag_thumb_mode = fragP->tc_frag_data.thumb_mode ^ MODE_RECORDED;
+
+ /* Record a mapping symbol for alignment frags. We will delete this
+ later if the alignment ends up empty. */
+ switch (fragP->fr_type)
+ {
+ case rs_align:
+ case rs_align_test:
+ case rs_fill:
+ mapping_state_2 (MAP_DATA, max_chars);
+ break;
+ case rs_align_code:
+ mapping_state_2 (frag_thumb_mode ? MAP_THUMB : MAP_ARM, max_chars);
+ break;
+ default:
+ break;
}
}
{
unwind.opcode_alloc += ARM_OPCODE_CHUNK_SIZE;
if (unwind.opcodes)
- unwind.opcodes = (unsigned char *) xrealloc (unwind.opcodes,
- unwind.opcode_alloc);
+ unwind.opcodes = XRESIZEVEC (unsigned char, unwind.opcodes,
+ unwind.opcode_alloc);
else
- unwind.opcodes = (unsigned char *) xmalloc (unwind.opcode_alloc);
+ unwind.opcodes = XNEWVEC (unsigned char, unwind.opcode_alloc);
}
while (length > 0)
{
/* Start an unwind table entry. HAVE_DATA is nonzero if we have additional
personality routine data. Returns zero, or the index table value for
- and inline entry. */
+ an inline entry. */
static valueT
create_unwind_entry (int have_data)
size = unwind.opcode_count - 2;
}
else
- /* An extra byte is required for the opcode count. */
- size = unwind.opcode_count + 1;
+ {
+ /* PR 16765: Missing or misplaced unwind directives can trigger this. */
+ if (unwind.personality_index != -1)
+ {
+ as_bad (_("attempt to recreate an unwind entry"));
+ return 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;
tc_arm_regname_to_dw2regnum (char *regname)
{
int reg = arm_reg_parse (®name, REG_TYPE_RN);
+ if (reg != FAIL)
+ return reg;
- if (reg == FAIL)
- return -1;
+ /* PR 16694: Allow VFP registers as well. */
+ reg = arm_reg_parse (®name, REG_TYPE_VFS);
+ if (reg != FAIL)
+ return 64 + reg;
- return reg;
+ reg = arm_reg_parse (®name, REG_TYPE_VFD);
+ if (reg != FAIL)
+ return reg + 256;
+
+ return -1;
}
#ifdef TE_PE
&& (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)
}
}
+static bfd_boolean flag_warn_syms = TRUE;
+
+bfd_boolean
+arm_tc_equal_in_insn (int c ATTRIBUTE_UNUSED, char * name)
+{
+ /* PR 18347 - Warn if the user attempts to create a symbol with the same
+ name as an ARM instruction. Whilst strictly speaking it is allowed, it
+ does mean that the resulting code might be very confusing to the reader.
+ Also this warning can be triggered if the user omits an operand before
+ an immediate address, eg:
+
+ LDR =foo
+
+ GAS treats this as an assignment of the value of the symbol foo to a
+ symbol LDR, and so (without this code) it will not issue any kind of
+ warning or error message.
+
+ Note - ARM instructions are case-insensitive but the strings in the hash
+ table are all stored in lower case, so we must first ensure that name is
+ lower case too. */
+ if (flag_warn_syms && arm_ops_hsh)
+ {
+ char * nbuf = strdup (name);
+ char * p;
+
+ for (p = nbuf; *p; p++)
+ *p = TOLOWER (*p);
+ if (hash_find (arm_ops_hsh, nbuf) != NULL)
+ {
+ static struct hash_control * already_warned = NULL;
+
+ if (already_warned == NULL)
+ already_warned = hash_new ();
+ /* Only warn about the symbol once. To keep the code
+ simple we let hash_insert do the lookup for us. */
+ if (hash_insert (already_warned, name, NULL) == NULL)
+ as_warn (_("[-mwarn-syms]: Assignment makes a symbol match an ARM instruction: %s"), name);
+ }
+ else
+ free (nbuf);
+ }
+
+ return FALSE;
+}
+
/* Under ELF we need to default _GLOBAL_OFFSET_TABLE.
Otherwise we have no need to default values of symbols. */
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"));
- }
+ if (!(ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6t2)))
+ 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_8:
if (fixP->fx_done || !seg->use_rela_p)
- md_number_to_chars (buf, value, 1);
+ *buf = value;
break;
case BFD_RELOC_16:
case BFD_RELOC_ARM_THM_TLS_CALL:
case BFD_RELOC_ARM_TLS_DESCSEQ:
case BFD_RELOC_ARM_THM_TLS_DESCSEQ:
- S_SET_THREAD_LOCAL (fixP->fx_addsy);
- break;
-
case BFD_RELOC_ARM_TLS_GOTDESC:
case BFD_RELOC_ARM_TLS_GD32:
case BFD_RELOC_ARM_TLS_LE32:
case BFD_RELOC_ARM_TLS_LDM32:
case BFD_RELOC_ARM_TLS_LDO32:
S_SET_THREAD_LOCAL (fixP->fx_addsy);
- /* fall through */
+ break;
case BFD_RELOC_ARM_GOT32:
case BFD_RELOC_ARM_GOTOFF:
- if (fixP->fx_done || !seg->use_rela_p)
- md_number_to_chars (buf, 0, 4);
break;
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;
case BFD_RELOC_ARM_CP_OFF_IMM:
case BFD_RELOC_ARM_T32_CP_OFF_IMM:
- if (value < -1023 || value > 1023 || (value & 3))
+ 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);
+ if ((newval & 0x0f200f00) == 0x0d000900)
+ {
+ /* This is a fp16 vstr/vldr. The immediate offset in the mnemonic
+ has permitted values that are multiples of 2, in the range 0
+ to 510. */
+ if (value < -510 || value > 510 || (value & 1))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("co-processor offset out of range"));
+ }
+ else if (value < -1023 || value > 1023 || (value & 3))
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;
+ if ((newval & 0x0f200f00) == 0x0d000900)
+ {
+ /* This is a fp16 vstr/vldr.
+
+ It requires the immediate offset in the instruction is shifted
+ left by 1 to be a half-word offset.
+
+ Here, left shift by 1 first, and later right shift by 2
+ should get the right offset. */
+ value <<= 1;
+ }
+ 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);
if (rd == REG_SP)
{
- if (value & ~0x1fc)
+ if (value & ~0x1fc)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("invalid immediate for stack address calculation"));
newval = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST;
}
else if (rs == REG_PC || rs == REG_SP)
{
+ /* PR gas/18541. If the addition is for a defined symbol
+ within range of an ADR instruction then accept it. */
+ if (subtract
+ && value == 4
+ && fixP->fx_addsy != NULL)
+ {
+ subtract = 0;
+
+ if (! S_IS_DEFINED (fixP->fx_addsy)
+ || S_GET_SEGMENT (fixP->fx_addsy) != seg
+ || S_IS_WEAK (fixP->fx_addsy))
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("address calculation needs a strongly defined nearby symbol"));
+ }
+ else
+ {
+ offsetT v = fixP->fx_where + fixP->fx_frag->fr_address;
+
+ /* Round up to the next 4-byte boundary. */
+ if (v & 3)
+ v = (v + 3) & ~ 3;
+ else
+ v += 4;
+ v = S_GET_VALUE (fixP->fx_addsy) - v;
+
+ if (v & ~0x3fc)
+ {
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("symbol too far away"));
+ }
+ else
+ {
+ fixP->fx_done = 1;
+ value = v;
+ }
+ }
+ }
+
if (subtract || value & ~0x3fc)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("invalid immediate for address calculation (value = 0x%08lX)"),
- (unsigned long) value);
+ (unsigned long) (subtract ? - value : value));
newval = (rs == REG_PC ? T_OPCODE_ADD_PC : T_OPCODE_ADD_SP);
newval |= rd << 8;
newval |= value >> 2;
}
return;
+ case BFD_RELOC_ARM_THUMB_ALU_ABS_G0_NC:
+ case BFD_RELOC_ARM_THUMB_ALU_ABS_G1_NC:
+ case BFD_RELOC_ARM_THUMB_ALU_ABS_G2_NC:
+ case BFD_RELOC_ARM_THUMB_ALU_ABS_G3_NC:
+ gas_assert (!fixP->fx_done);
+ {
+ bfd_vma insn;
+ bfd_boolean is_mov;
+ bfd_vma encoded_addend = value;
+
+ /* Check that addend can be encoded in instruction. */
+ if (!seg->use_rela_p && (value < 0 || value > 255))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("the offset 0x%08lX is not representable"),
+ (unsigned long) encoded_addend);
+
+ /* Extract the instruction. */
+ insn = md_chars_to_number (buf, THUMB_SIZE);
+ is_mov = (insn & 0xf800) == 0x2000;
+
+ /* Encode insn. */
+ if (is_mov)
+ {
+ if (!seg->use_rela_p)
+ insn |= encoded_addend;
+ }
+ else
+ {
+ int rd, rs;
+
+ /* Extract the instruction. */
+ /* Encoding is the following
+ 0x8000 SUB
+ 0x00F0 Rd
+ 0x000F Rs
+ */
+ /* The following conditions must be true :
+ - ADD
+ - Rd == Rs
+ - Rd <= 7
+ */
+ rd = (insn >> 4) & 0xf;
+ rs = insn & 0xf;
+ if ((insn & 0x8000) || (rd != rs) || rd > 7)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("Unable to process relocation for thumb opcode: %lx"),
+ (unsigned long) insn);
+
+ /* Encode as ADD immediate8 thumb 1 code. */
+ insn = 0x3000 | (rd << 8);
+
+ /* Place the encoded addend into the first 8 bits of the
+ instruction. */
+ if (!seg->use_rela_p)
+ insn |= encoded_addend;
+ }
+
+ /* Update the instruction. */
+ md_number_to_chars (buf, insn, THUMB_SIZE);
+ }
+ break;
+
case BFD_RELOC_ARM_ALU_PC_G0_NC:
case BFD_RELOC_ARM_ALU_PC_G0:
case BFD_RELOC_ARM_ALU_PC_G1_NC:
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:
arelent * reloc;
bfd_reloc_code_real_type code;
- reloc = (arelent *) xmalloc (sizeof (arelent));
+ reloc = XNEW (arelent);
- reloc->sym_ptr_ptr = (asymbol **) xmalloc (sizeof (asymbol *));
+ reloc->sym_ptr_ptr = XNEW (asymbol *);
*reloc->sym_ptr_ptr = symbol_get_bfdsym (fixp->fx_addsy);
reloc->address = fixp->fx_frag->fr_address + fixp->fx_where;
case BFD_RELOC_ARM_SBREL32:
case BFD_RELOC_ARM_PREL31:
case BFD_RELOC_ARM_TARGET2:
- case BFD_RELOC_ARM_TLS_LE32:
case BFD_RELOC_ARM_TLS_LDO32:
case BFD_RELOC_ARM_PCREL_CALL:
case BFD_RELOC_ARM_PCREL_JUMP:
case BFD_RELOC_ARM_LDC_SB_G1:
case BFD_RELOC_ARM_LDC_SB_G2:
case BFD_RELOC_ARM_V4BX:
+ case BFD_RELOC_ARM_THUMB_ALU_ABS_G0_NC:
+ case BFD_RELOC_ARM_THUMB_ALU_ABS_G1_NC:
+ case BFD_RELOC_ARM_THUMB_ALU_ABS_G2_NC:
+ case BFD_RELOC_ARM_THUMB_ALU_ABS_G3_NC:
code = fixp->fx_r_type;
break;
case BFD_RELOC_ARM_TLS_GOTDESC:
case BFD_RELOC_ARM_TLS_GD32:
+ case BFD_RELOC_ARM_TLS_LE32:
case BFD_RELOC_ARM_TLS_IE32:
case BFD_RELOC_ARM_TLS_LDM32:
/* BFD will include the symbol's address in the addend.
default:
{
- char * type;
+ const char * type;
switch (fixp->fx_r_type)
{
cons_fix_new_arm (fragS * frag,
int where,
int size,
- expressionS * exp)
+ expressionS * exp,
+ bfd_reloc_code_real_type reloc)
{
- bfd_reloc_code_real_type type;
int pcrel = 0;
/* Pick a reloc.
switch (size)
{
case 1:
- type = BFD_RELOC_8;
+ reloc = BFD_RELOC_8;
break;
case 2:
- type = BFD_RELOC_16;
+ reloc = BFD_RELOC_16;
break;
case 4:
default:
- type = BFD_RELOC_32;
+ reloc = BFD_RELOC_32;
break;
case 8:
- type = BFD_RELOC_64;
+ reloc = BFD_RELOC_64;
break;
}
if (exp->X_op == O_secrel)
{
exp->X_op = O_symbol;
- type = BFD_RELOC_32_SECREL;
+ reloc = BFD_RELOC_32_SECREL;
}
#endif
- fix_new_exp (frag, where, (int) size, exp, pcrel, type);
+ fix_new_exp (frag, where, size, exp, pcrel, reloc);
}
#if defined (OBJ_COFF)
}
#endif
- /* Resolve these relocations even if the symbol is extern or weak. */
+ /* Resolve these relocations even if the symbol is extern or weak.
+ Technically this is probably wrong due to symbol preemption.
+ In practice these relocations do not have enough range to be useful
+ at dynamic link time, and some code (e.g. in the Linux kernel)
+ expects these references to be resolved. */
if (fixp->fx_r_type == BFD_RELOC_ARM_IMMEDIATE
|| fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_OFFSET_IMM8
|| fixp->fx_r_type == BFD_RELOC_ARM_ADRL_IMMEDIATE
+ || fixp->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_CP_OFF_IMM_S2
+ || fixp->fx_r_type == BFD_RELOC_ARM_THUMB_OFFSET
|| fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_IMM
|| fixp->fx_r_type == BFD_RELOC_ARM_T32_IMMEDIATE
|| fixp->fx_r_type == BFD_RELOC_ARM_T32_IMM12
- || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_PC12)
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_OFFSET_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_ADD_PC12
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_CP_OFF_IMM
+ || fixp->fx_r_type == BFD_RELOC_ARM_T32_CP_OFF_IMM_S2)
return 0;
/* Always leave these relocations for the linker. */
|| fixP->fx_r_type == BFD_RELOC_ARM_THUMB_MOVT_PCREL)
return FALSE;
+ /* BFD_RELOC_ARM_THUMB_ALU_ABS_Gx_NC relocations have VERY limited
+ offsets, so keep these symbols. */
+ if (fixP->fx_r_type >= BFD_RELOC_ARM_THUMB_ALU_ABS_G0_NC
+ && fixP->fx_r_type <= BFD_RELOC_ARM_THUMB_ALU_ABS_G3_NC)
+ return FALSE;
+
return TRUE;
}
#endif /* defined (OBJ_ELF) || defined (OBJ_COFF) */
#ifdef OBJ_ELF
-
const char *
elf32_arm_target_format (void)
{
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 ();
mcpu_cpu_opt = &cpu_default;
selected_cpu = cpu_default;
}
+ else if (no_cpu_selected ())
+ selected_cpu = cpu_default;
#else
if (mcpu_cpu_opt)
selected_cpu = *mcpu_cpu_opt;
-mthumb-interwork Code supports ARM/Thumb interworking
-m[no-]warn-deprecated Warn about deprecated features
+ -m[no-]warn-syms Warn when symbols match instructions
For now we will also provide support for:
{NULL, no_argument, NULL, 0}
};
+
size_t md_longopts_size = sizeof (md_longopts);
struct arm_option_table
{
- char *option; /* Option name to match. */
- char *help; /* Help information. */
+ const char *option; /* Option name to match. */
+ const char *help; /* Help information. */
int *var; /* Variable to change. */
int value; /* What to change it to. */
- char *deprecated; /* If non-null, print this message. */
+ const char *deprecated; /* If non-null, print this message. */
};
struct arm_option_table arm_opts[] =
{"mwarn-deprecated", NULL, &warn_on_deprecated, 1, NULL},
{"mno-warn-deprecated", N_("do not warn on use of deprecated feature"),
&warn_on_deprecated, 0, NULL},
+ {"mwarn-syms", N_("warn about symbols that match instruction names [default]"), (int *) (& flag_warn_syms), TRUE, NULL},
+ {"mno-warn-syms", N_("disable warnings about symobls that match instructions"), (int *) (& flag_warn_syms), FALSE, NULL},
{NULL, NULL, NULL, 0, NULL}
};
struct arm_legacy_option_table
{
- char *option; /* Option name to match. */
+ const char *option; /* Option name to match. */
const arm_feature_set **var; /* Variable to change. */
const arm_feature_set value; /* What to change it to. */
- char *deprecated; /* If non-null, print this message. */
+ const char *deprecated; /* If non-null, print this message. */
};
const struct arm_legacy_option_table arm_legacy_opts[] =
struct arm_cpu_option_table
{
- char *name;
+ const 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_V6KZ, FPU_NONE, NULL),
+ ARM_CPU_OPT ("arm1176jzf-s", ARM_ARCH_V6KZ, 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_V7VE, FPU_ARCH_NEON_VFP_V4,
+ "Cortex-A7"),
+ ARM_CPU_OPT ("cortex-a8", ARM_ARCH_V7A_SEC,
+ ARM_FEATURE_COPROC (FPU_VFP_V3
+ | FPU_NEON_EXT_V1),
+ "Cortex-A8"),
+ ARM_CPU_OPT ("cortex-a9", ARM_ARCH_V7A_MP_SEC,
+ ARM_FEATURE_COPROC (FPU_VFP_V3
+ | FPU_NEON_EXT_V1),
+ "Cortex-A9"),
+ ARM_CPU_OPT ("cortex-a12", ARM_ARCH_V7VE, FPU_ARCH_NEON_VFP_V4,
+ "Cortex-A12"),
+ ARM_CPU_OPT ("cortex-a15", ARM_ARCH_V7VE, FPU_ARCH_NEON_VFP_V4,
+ "Cortex-A15"),
+ ARM_CPU_OPT ("cortex-a17", ARM_ARCH_V7VE, FPU_ARCH_NEON_VFP_V4,
+ "Cortex-A17"),
+ ARM_CPU_OPT ("cortex-a32", ARM_ARCH_V8A, FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ "Cortex-A32"),
+ ARM_CPU_OPT ("cortex-a35", ARM_ARCH_V8A, FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ "Cortex-A35"),
+ 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-a72", ARM_ARCH_V8A, FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ "Cortex-A72"),
+ 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-r8", ARM_ARCH_V7R_IDIV,
+ FPU_ARCH_VFP_V3D16,
+ "Cortex-R8"),
+ ARM_CPU_OPT ("cortex-m7", ARM_ARCH_V7EM, FPU_NONE, "Cortex-M7"),
+ 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+"),
+ ARM_CPU_OPT ("exynos-m1", ARM_ARCH_V8A, FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ "Samsung " \
+ "Exynos M1"),
+ ARM_CPU_OPT ("qdf24xx", ARM_ARCH_V8A, FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ "Qualcomm "
+ "QDF24XX"),
+
/* ??? 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_LOW (ARM_AEXT_V4T, ARM_CEXT_MAVERICK),
+ FPU_ARCH_MAVERICK, "ARM920T"),
+ /* Marvell processors. */
+ ARM_CPU_OPT ("marvell-pj4", ARM_FEATURE_CORE (ARM_AEXT_V7A | ARM_EXT_MP
+ | ARM_EXT_SEC,
+ ARM_EXT2_V6T2_V8M),
+ FPU_ARCH_VFP_V3D16, NULL),
+ ARM_CPU_OPT ("marvell-whitney", ARM_FEATURE_CORE (ARM_AEXT_V7A | ARM_EXT_MP
+ | ARM_EXT_SEC,
+ ARM_EXT2_V6T2_V8M),
+ FPU_ARCH_NEON_VFP_V4, NULL),
+ /* APM X-Gene family. */
+ ARM_CPU_OPT ("xgene1", ARM_ARCH_V8A, FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ "APM X-Gene 1"),
+ ARM_CPU_OPT ("xgene2", ARM_ARCH_V8A, FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ "APM X-Gene 2"),
+
+ { NULL, 0, ARM_ARCH_NONE, ARM_ARCH_NONE, NULL }
};
+#undef ARM_CPU_OPT
struct arm_arch_option_table
{
- char *name;
+ const 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),
+ /* The official spelling of this variant is ARMv6KZ, the name "armv6zk" is
+ kept to preserve existing behaviour. */
+ ARM_ARCH_OPT ("armv6kz", ARM_ARCH_V6KZ, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6zk", ARM_ARCH_V6KZ, 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),
+ /* The official spelling of this variant is ARMv6KZ, the name "armv6zkt2" is
+ kept to preserve existing behaviour. */
+ ARM_ARCH_OPT ("armv6kzt2", ARM_ARCH_V6KZT2, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv6zkt2", ARM_ARCH_V6KZT2, 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 ("armv7ve", ARM_ARCH_V7VE, 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-m.base", ARM_ARCH_V8M_BASE, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv8-m.main", ARM_ARCH_V8M_MAIN, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv8-a", ARM_ARCH_V8A, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv8.1-a", ARM_ARCH_V8_1A, FPU_ARCH_VFP),
+ ARM_ARCH_OPT ("armv8.2-a", ARM_ARCH_V8_2A, 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;
- const arm_feature_set value;
+ const char *name;
+ size_t name_len;
+ const arm_feature_set merge_value;
+ const arm_feature_set clear_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, M, C, AA) { N, sizeof (N) - 1, M, C, 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_COPROC (CRC_EXT_ARMV8),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V8)),
+ ARM_EXT_OPT ("crypto", FPU_ARCH_CRYPTO_NEON_VFP_ARMV8,
+ ARM_FEATURE_COPROC (FPU_CRYPTO_ARMV8),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V8)),
+ ARM_EXT_OPT ("fp", FPU_ARCH_VFP_ARMV8, ARM_FEATURE_COPROC (FPU_VFP_ARMV8),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V8)),
+ ARM_EXT_OPT ("fp16", ARM_FEATURE_CORE_HIGH (ARM_EXT2_FP16_INST),
+ ARM_FEATURE_CORE_HIGH (ARM_EXT2_FP16_INST),
+ ARM_ARCH_V8_2A),
+ ARM_EXT_OPT ("idiv", ARM_FEATURE_CORE_LOW (ARM_EXT_ADIV | ARM_EXT_DIV),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_ADIV | ARM_EXT_DIV),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V7A | ARM_EXT_V7R)),
+ ARM_EXT_OPT ("iwmmxt",ARM_FEATURE_COPROC (ARM_CEXT_IWMMXT),
+ ARM_FEATURE_COPROC (ARM_CEXT_IWMMXT), ARM_ANY),
+ ARM_EXT_OPT ("iwmmxt2", ARM_FEATURE_COPROC (ARM_CEXT_IWMMXT2),
+ ARM_FEATURE_COPROC (ARM_CEXT_IWMMXT2), ARM_ANY),
+ ARM_EXT_OPT ("maverick", ARM_FEATURE_COPROC (ARM_CEXT_MAVERICK),
+ ARM_FEATURE_COPROC (ARM_CEXT_MAVERICK), ARM_ANY),
+ ARM_EXT_OPT ("mp", ARM_FEATURE_CORE_LOW (ARM_EXT_MP),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_MP),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V7A | ARM_EXT_V7R)),
+ ARM_EXT_OPT ("os", ARM_FEATURE_CORE_LOW (ARM_EXT_OS),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_OS),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V6M)),
+ ARM_EXT_OPT ("pan", ARM_FEATURE_CORE_HIGH (ARM_EXT2_PAN),
+ ARM_FEATURE (ARM_EXT_V8, ARM_EXT2_PAN, 0),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V8)),
+ ARM_EXT_OPT ("rdma", FPU_ARCH_NEON_VFP_ARMV8_1,
+ ARM_FEATURE_COPROC (FPU_NEON_ARMV8 | FPU_NEON_EXT_RDMA),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V8)),
+ ARM_EXT_OPT ("sec", ARM_FEATURE_CORE_LOW (ARM_EXT_SEC),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_SEC),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V6K | ARM_EXT_V7A)),
+ ARM_EXT_OPT ("simd", FPU_ARCH_NEON_VFP_ARMV8,
+ ARM_FEATURE_COPROC (FPU_NEON_ARMV8),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V8)),
+ ARM_EXT_OPT ("virt", ARM_FEATURE_CORE_LOW (ARM_EXT_VIRT | ARM_EXT_ADIV
+ | ARM_EXT_DIV),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_VIRT),
+ ARM_FEATURE_CORE_LOW (ARM_EXT_V7A)),
+ ARM_EXT_OPT ("xscale",ARM_FEATURE_COPROC (ARM_CEXT_XSCALE),
+ ARM_FEATURE_COPROC (ARM_CEXT_XSCALE), ARM_ANY),
+ { NULL, 0, ARM_ARCH_NONE, ARM_ARCH_NONE, ARM_ARCH_NONE }
};
+#undef ARM_EXT_OPT
/* ISA floating-point and Advanced SIMD extensions. */
struct arm_option_fpu_value_table
{
- char *name;
+ const char *name;
const arm_feature_set value;
};
{"vfpv4", FPU_ARCH_VFP_V4},
{"vfpv4-d16", FPU_ARCH_VFP_V4D16},
{"fpv4-sp-d16", FPU_ARCH_VFP_V4_SP_D16},
+ {"fpv5-d16", FPU_ARCH_VFP_V5D16},
+ {"fpv5-sp-d16", FPU_ARCH_VFP_V5_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},
+ {"neon-fp-armv8.1", FPU_ARCH_NEON_VFP_ARMV8_1},
+ {"crypto-neon-fp-armv8.1",
+ FPU_ARCH_CRYPTO_NEON_VFP_ARMV8_1},
{NULL, ARM_ARCH_NONE}
};
struct arm_option_value_table
{
- char *name;
+ const char *name;
long value;
};
struct arm_long_option_table
{
- char * option; /* Substring to match. */
- char * help; /* Help information. */
- int (* func) (char * subopt); /* Function to decode sub-option. */
- char * deprecated; /* If non-null, print this message. */
+ const char * option; /* Substring to match. */
+ const char * help; /* Help information. */
+ int (* func) (const char * subopt); /* Function to decode sub-option. */
+ const char * deprecated; /* If non-null, print this message. */
};
static bfd_boolean
-arm_parse_extension (char * str, const arm_feature_set **opt_p)
+arm_parse_extension (const char *str, const arm_feature_set **opt_p)
{
- arm_feature_set *ext_set = (arm_feature_set *)
- xmalloc (sizeof (arm_feature_set));
+ arm_feature_set *ext_set = XNEW (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;
+ const 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))
/* Add or remove the extension. */
if (adding_value)
- ARM_MERGE_FEATURE_SETS (*ext_set, *ext_set, opt->value);
+ ARM_MERGE_FEATURE_SETS (*ext_set, *ext_set, opt->merge_value);
else
- ARM_CLEAR_FEATURE (*ext_set, *ext_set, opt->value);
+ ARM_CLEAR_FEATURE (*ext_set, *ext_set, opt->clear_value);
break;
}
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 (const char *str)
{
- const struct arm_cpu_option_table * opt;
- char * ext = strchr (str, '+');
- int optlen;
+ const struct arm_cpu_option_table *opt;
+ const 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;
if (opt->canonical_name)
- strcpy (selected_cpu_name, opt->canonical_name);
+ {
+ gas_assert (sizeof selected_cpu_name > strlen (opt->canonical_name));
+ strcpy (selected_cpu_name, opt->canonical_name);
+ }
else
{
- int i;
+ size_t i;
+
+ if (len >= sizeof selected_cpu_name)
+ len = (sizeof selected_cpu_name) - 1;
- 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 (const char *str)
{
const struct arm_arch_option_table *opt;
- char *ext = strchr (str, '+');
- int optlen;
+ const 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 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;
}
static bfd_boolean
-arm_parse_fpu (char * str)
+arm_parse_fpu (const char * str)
{
const struct arm_option_fpu_value_table * opt;
}
static bfd_boolean
-arm_parse_float_abi (char * str)
+arm_parse_float_abi (const char * str)
{
const struct arm_option_value_table * opt;
#ifdef OBJ_ELF
static bfd_boolean
-arm_parse_eabi (char * str)
+arm_parse_eabi (const char * str)
{
const struct arm_option_value_table *opt;
#endif
static bfd_boolean
-arm_parse_it_mode (char * str)
+arm_parse_it_mode (const char * str)
{
bfd_boolean ret = TRUE;
else
{
as_bad (_("unknown implicit IT mode `%s', should be "\
- "arm, thumb, always, or never."), str);
+ "arm, thumb, always, or never."), str);
ret = FALSE;
}
return ret;
}
+static bfd_boolean
+arm_ccs_mode (const char * unused ATTRIBUTE_UNUSED)
+{
+ codecomposer_syntax = TRUE;
+ arm_comment_chars[0] = ';';
+ arm_line_separator_chars[0] = 0;
+ return TRUE;
+}
+
struct arm_long_option_table arm_long_opts[] =
{
{"mcpu=", N_("<cpu name>\t assemble for CPU <cpu name>"),
#endif
{"mimplicit-it=", N_("<mode>\t controls implicit insertion of IT instructions"),
arm_parse_it_mode, NULL},
+ {"mccs", N_("\t\t\t TI CodeComposer Studio syntax compatibility mode"),
+ arm_ccs_mode, NULL},
{NULL, NULL, 0, NULL}
};
int
-md_parse_option (int c, char * arg)
+md_parse_option (int c, const char * arg)
{
struct arm_option_table *opt;
const struct arm_legacy_option_table *fopt;
arm_feature_set flags;
} cpu_arch_ver_table;
-/* Mapping from CPU features to EABI CPU arch values. Table must be sorted
- least features first. */
+/* Mapping from CPU features to EABI CPU arch values. As a general rule, table
+ must be sorted least features first but some reordering is needed, eg. for
+ Thumb-2 instructions to be detected as coming from ARMv6T2. */
static const cpu_arch_ver_table cpu_arch_ver[] =
{
{1, ARM_ARCH_V4},
{11, ARM_ARCH_V6M},
{12, ARM_ARCH_V6SM},
{8, ARM_ARCH_V6T2},
- {10, ARM_ARCH_V7A},
+ {10, ARM_ARCH_V7VE},
{10, ARM_ARCH_V7R},
{10, ARM_ARCH_V7M},
+ {14, ARM_ARCH_V8A},
+ {16, ARM_ARCH_V8M_BASE},
+ {17, ARM_ARCH_V8M_MAIN},
{0, ARM_ARCH_NONE}
};
}
/* Set the public EABI object attributes. */
-static void
+void
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;
+ arm_feature_set arm_arch_v8m_base = ARM_ARCH_V8M_BASE;
const cpu_arch_ver_table *p;
/* Choose the architecture based on the capabilities of the requested cpu
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);
+
+ selected_cpu = flags;
+
+ /* 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;
actually used. Perhaps we should separate out the specified
and implicit cases. Avoid taking this path for -march=all by
checking for contradictory v7-A / v7-M features. */
- if (arch == 10
+ if (arch == TAG_CPU_ARCH_V7
&& !ARM_CPU_HAS_FEATURE (flags, arm_ext_v7a)
&& ARM_CPU_HAS_FEATURE (flags, arm_ext_v7m)
&& ARM_CPU_HAS_FEATURE (flags, arm_ext_v6_dsp))
- arch = 13;
+ arch = TAG_CPU_ARCH_V7E_M;
+
+ ARM_CLEAR_FEATURE (tmp, flags, arm_arch_v8m_base);
+ if (arch == TAG_CPU_ARCH_V8M_BASE && ARM_CPU_HAS_FEATURE (tmp, arm_arch_any))
+ arch = TAG_CPU_ARCH_V8M_MAIN;
+
+ /* In cpu_arch_ver ARMv8-A is before ARMv8-M for atomics to be detected as
+ coming from ARMv8-A. However, since ARMv8-A has more instructions than
+ ARMv8-M, -march=all must be detected as ARMv8-A. */
+ if (arch == TAG_CPU_ARCH_V8M_MAIN
+ && ARM_FEATURE_CORE_EQUAL (selected_cpu, arm_arch_any))
+ arch = TAG_CPU_ARCH_V8;
/* Tag_CPU_name. */
if (selected_cpu_name[0])
aeabi_set_attribute_int (Tag_CPU_arch, arch);
/* Tag_CPU_arch_profile. */
- if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7a))
- aeabi_set_attribute_int (Tag_CPU_arch_profile, 'A');
+ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7a)
+ || ARM_CPU_HAS_FEATURE (flags, arm_ext_v8)
+ || (ARM_CPU_HAS_FEATURE (flags, arm_ext_atomics)
+ && !ARM_CPU_HAS_FEATURE (flags, arm_ext_v8m)))
+ 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)
/* Tag_THUMB_ISA_use. */
if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v4t)
|| arch == 0)
- aeabi_set_attribute_int (Tag_THUMB_ISA_use,
- ARM_CPU_HAS_FEATURE (flags, arm_arch_t2) ? 2 : 1);
+ {
+ int thumb_isa_use;
+
+ if (!ARM_CPU_HAS_FEATURE (flags, arm_ext_v8)
+ && ARM_CPU_HAS_FEATURE (flags, arm_ext_v8m))
+ thumb_isa_use = 3;
+ else if (ARM_CPU_HAS_FEATURE (flags, arm_arch_t2))
+ thumb_isa_use = 2;
+ else
+ thumb_isa_use = 1;
+ aeabi_set_attribute_int (Tag_THUMB_ISA_use, thumb_isa_use);
+ }
/* Tag_VFP_arch. */
- if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_fma))
+ if (ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_armv8xd))
+ aeabi_set_attribute_int (Tag_VFP_arch,
+ ARM_CPU_HAS_FEATURE (flags, fpu_vfp_ext_d32)
+ ? 7 : 8);
+ 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_v8_1))
+ aeabi_set_attribute_int (Tag_Advanced_SIMD_arch, 4);
+ else 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-A and ARMv8-M 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
+ || (arch >= TAG_CPU_ARCH_V8M_BASE
+ && arch <= TAG_CPU_ARCH_V8M_MAIN));
+ if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v8)
+ || ARM_CPU_HAS_FEATURE (flags, arm_ext_v8m))
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 (adding_value)
- ARM_MERGE_FEATURE_SETS (selected_cpu, selected_cpu, opt->value);
+ ARM_MERGE_FEATURE_SETS (selected_cpu, selected_cpu,
+ opt->merge_value);
else
- ARM_CLEAR_FEATURE (selected_cpu, selected_cpu, opt->value);
+ ARM_CLEAR_FEATURE (selected_cpu, selected_cpu, opt->clear_value);
mcpu_cpu_opt = &selected_cpu;
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 ();
}
-/* Apply sym value for relocations only in the case that
- they are for local symbols and you have the respective
- architectural feature for blx and simple switches. */
+/* Apply sym value for relocations only in the case that they are for
+ local symbols in the same segment as the fixup and you have the
+ respective architectural feature for blx and simple switches. */
int
-arm_apply_sym_value (struct fix * fixP)
+arm_apply_sym_value (struct fix * fixP, segT this_seg)
{
if (fixP->fx_addsy
&& ARM_CPU_HAS_FEATURE (selected_cpu, arm_ext_v5t)
+ /* PR 17444: If the local symbol is in a different section then a reloc
+ will always be generated for it, so applying the symbol value now
+ will result in a double offset being stored in the relocation. */
+ && (S_GET_SEGMENT (fixP->fx_addsy) == this_seg)
&& !S_FORCE_RELOC (fixP->fx_addsy, TRUE))
{
switch (fixP->fx_r_type)
case BFD_RELOC_ARM_PCREL_CALL:
case BFD_RELOC_THUMB_PCREL_BLX:
if (THUMB_IS_FUNC (fixP->fx_addsy))
- return 1;
+ return 1;
break;
default: