blob: f77165513d917c16d1efedab740929826b37a3b0 [file] [log] [blame]
/* tc-arm.c -- Assemble for the ARM
Copyright (C) 1994-2021 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)
Cirrus coprocessor fixes by Petko Manolov (petkan@nucleusys.com)
Cirrus coprocessor fixes by Vladimir Ivanov (vladitx@nucleusys.com)
This file is part of GAS, the GNU Assembler.
GAS is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GAS is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GAS; see the file COPYING. If not, write to the Free
Software Foundation, 51 Franklin Street - Fifth Floor, Boston, MA
02110-1301, USA. */
#include "as.h"
#include <limits.h>
#include <stdarg.h>
#define NO_RELOC 0
#include "safe-ctype.h"
#include "subsegs.h"
#include "obstack.h"
#include "libiberty.h"
#include "opcode/arm.h"
#include "cpu-arm.h"
#ifdef OBJ_ELF
#include "elf/arm.h"
#include "dw2gencfi.h"
#endif
#include "dwarf2dbg.h"
#ifdef OBJ_ELF
/* Must be at least the size of the largest unwind opcode (currently two). */
#define ARM_OPCODE_CHUNK_SIZE 8
/* This structure holds the unwinding state. */
static struct
{
symbolS * proc_start;
symbolS * table_entry;
symbolS * personality_routine;
int personality_index;
/* The segment containing the function. */
segT saved_seg;
subsegT saved_subseg;
/* Opcodes generated from this function. */
unsigned char * opcodes;
int opcode_count;
int opcode_alloc;
/* The number of bytes pushed to the stack. */
offsetT frame_size;
/* We don't add stack adjustment opcodes immediately so that we can merge
multiple adjustments. We can also omit the final adjustment
when using a frame pointer. */
offsetT pending_offset;
/* These two fields are set by both unwind_movsp and unwind_setfp. They
hold the reg+offset to use when restoring sp from a frame pointer. */
offsetT fp_offset;
int fp_reg;
/* Nonzero if an unwind_setfp directive has been seen. */
unsigned fp_used:1;
/* Nonzero if the last opcode restores sp from fp_reg. */
unsigned sp_restored:1;
} unwind;
/* Whether --fdpic was given. */
static int arm_fdpic;
#endif /* OBJ_ELF */
/* Results from operand parsing worker functions. */
typedef enum
{
PARSE_OPERAND_SUCCESS,
PARSE_OPERAND_FAIL,
PARSE_OPERAND_FAIL_NO_BACKTRACK
} parse_operand_result;
enum arm_float_abi
{
ARM_FLOAT_ABI_HARD,
ARM_FLOAT_ABI_SOFTFP,
ARM_FLOAT_ABI_SOFT
};
/* 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
changing gas' default behaviour depending upon the build host.
If you have a target that requires a default CPU option then the you
should define CPU_DEFAULT here. */
#endif
/* Perform range checks on positive and negative overflows by checking if the
VALUE given fits within the range of an BITS sized immediate. */
static bool out_of_range_p (offsetT value, offsetT bits)
{
gas_assert (bits < (offsetT)(sizeof (value) * 8));
return (value & ~((1 << bits)-1))
&& ((value & ~((1 << bits)-1)) != ~((1 << bits)-1));
}
#ifndef FPU_DEFAULT
# ifdef TE_LINUX
# define FPU_DEFAULT FPU_ARCH_FPA
# elif defined (TE_NetBSD)
# ifdef OBJ_ELF
# define FPU_DEFAULT FPU_ARCH_VFP /* Soft-float, but VFP order. */
# else
/* Legacy a.out format. */
# define FPU_DEFAULT FPU_ARCH_FPA /* Soft-float, but FPA order. */
# endif
# elif defined (TE_VXWORKS)
# define FPU_DEFAULT FPU_ARCH_VFP /* Soft-float, VFP order. */
# else
/* For backwards compatibility, default to FPA. */
# define FPU_DEFAULT FPU_ARCH_FPA
# endif
#endif /* ifndef FPU_DEFAULT */
#define streq(a, b) (strcmp (a, b) == 0)
/* Current set of feature bits available (CPU+FPU). Different from
selected_cpu + selected_fpu in case of autodetection since the CPU
feature bits are then all set. */
static arm_feature_set cpu_variant;
/* Feature bits used in each execution state. Used to set build attribute
(in particular Tag_*_ISA_use) in CPU autodetection mode. */
static arm_feature_set arm_arch_used;
static arm_feature_set thumb_arch_used;
/* Flags stored in private area of BFD structure. */
static int uses_apcs_26 = false;
static int atpcs = false;
static int support_interwork = false;
static int uses_apcs_float = false;
static int pic_code = false;
static int fix_v4bx = false;
/* Warn on using deprecated features. */
static int warn_on_deprecated = true;
static int warn_on_restrict_it = false;
/* Understand CodeComposer Studio assembly syntax. */
bool 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
assembly flags. */
/* CPU and FPU feature bits set for legacy CPU and FPU options (eg. -marm1
instead of -mcpu=arm1). */
static const arm_feature_set *legacy_cpu = NULL;
static const arm_feature_set *legacy_fpu = NULL;
/* CPU, extension and FPU feature bits selected by -mcpu. */
static const arm_feature_set *mcpu_cpu_opt = NULL;
static arm_feature_set *mcpu_ext_opt = NULL;
static const arm_feature_set *mcpu_fpu_opt = NULL;
/* CPU, extension and FPU feature bits selected by -march. */
static const arm_feature_set *march_cpu_opt = NULL;
static arm_feature_set *march_ext_opt = NULL;
static const arm_feature_set *march_fpu_opt = NULL;
/* Feature bits selected by -mfpu. */
static const arm_feature_set *mfpu_opt = NULL;
/* Constants for known architecture features. */
static const arm_feature_set fpu_default = FPU_DEFAULT;
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 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;
#ifdef OBJ_ELF
static const arm_feature_set fpu_arch_maverick = FPU_ARCH_MAVERICK;
#endif
static const arm_feature_set fpu_endian_pure = FPU_ARCH_ENDIAN_PURE;
#ifdef CPU_DEFAULT
static const arm_feature_set cpu_default = CPU_DEFAULT;
#endif
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_V2);
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_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);
/* Only for compatability of hint instructions. */
static const arm_feature_set arm_ext_v6k_v6t2 =
ARM_FEATURE_CORE_LOW (ARM_EXT_V6K | ARM_EXT_V6T2);
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_v8r = ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8R);
#ifdef OBJ_ELF
static const arm_feature_set ATTRIBUTE_UNUSED arm_ext_v7m = ARM_FEATURE_CORE_LOW (ARM_EXT_V7M);
#endif
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_CORE (ARM_EXT_V6M | ARM_EXT_V7M,
ARM_EXT2_V8M | ARM_EXT2_V8M_MAIN);
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_v8m_main =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8M_MAIN);
static const arm_feature_set arm_ext_v8_1m_main =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8_1M_MAIN);
/* Instructions in ARMv8-M only found in M profile architectures. */
static const arm_feature_set arm_ext_v8m_m_only =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8M | ARM_EXT2_V8M_MAIN);
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);
#ifdef OBJ_ELF
/* DSP instructions Tag_DSP_extension refers to. */
static const arm_feature_set arm_ext_dsp =
ARM_FEATURE_CORE_LOW (ARM_EXT_V5E | ARM_EXT_V5ExP | ARM_EXT_V6_DSP);
#endif
static const arm_feature_set arm_ext_ras =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_RAS);
/* FP16 instructions. */
static const arm_feature_set arm_ext_fp16 =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_FP16_INST);
static const arm_feature_set arm_ext_fp16_fml =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_FP16_FML);
static const arm_feature_set arm_ext_v8_2 =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8_2A);
static const arm_feature_set arm_ext_v8_3 =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_V8_3A);
static const arm_feature_set arm_ext_sb =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_SB);
static const arm_feature_set arm_ext_predres =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_PREDRES);
static const arm_feature_set arm_ext_bf16 =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_BF16);
static const arm_feature_set arm_ext_i8mm =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_I8MM);
static const arm_feature_set arm_ext_crc =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_CRC);
static const arm_feature_set arm_ext_cde =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE);
static const arm_feature_set arm_ext_cde0 =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE0);
static const arm_feature_set arm_ext_cde1 =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE1);
static const arm_feature_set arm_ext_cde2 =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE2);
static const arm_feature_set arm_ext_cde3 =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE3);
static const arm_feature_set arm_ext_cde4 =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE4);
static const arm_feature_set arm_ext_cde5 =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE5);
static const arm_feature_set arm_ext_cde6 =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE6);
static const arm_feature_set arm_ext_cde7 =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_CDE7);
static const arm_feature_set arm_arch_any = ARM_ANY;
static const arm_feature_set fpu_any = FPU_ANY;
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_cext_iwmmxt2 =
ARM_FEATURE_COPROC (ARM_CEXT_IWMMXT2);
static const arm_feature_set arm_cext_iwmmxt =
ARM_FEATURE_COPROC (ARM_CEXT_IWMMXT);
static const arm_feature_set arm_cext_xscale =
ARM_FEATURE_COPROC (ARM_CEXT_XSCALE);
static const arm_feature_set arm_cext_maverick =
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_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_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_COPROC (FPU_NEON_EXT_V1 | FPU_VFP_EXT_V3);
static const arm_feature_set mve_ext =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_MVE);
static const arm_feature_set mve_fp_ext =
ARM_FEATURE_CORE_HIGH (ARM_EXT2_MVE_FP);
/* Note: This has more than one bit set, which means using it with
mark_feature_used (which returns if *any* of the bits are set in the current
cpu variant) can give surprising results. */
static const arm_feature_set armv8m_fp =
ARM_FEATURE_COPROC (FPU_VFP_V5_SP_D16);
#ifdef OBJ_ELF
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);
#endif
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 fpu_neon_ext_v8_1 =
ARM_FEATURE_COPROC (FPU_NEON_EXT_RDMA);
static const arm_feature_set fpu_neon_ext_dotprod =
ARM_FEATURE_COPROC (FPU_NEON_EXT_DOTPROD);
static const arm_feature_set pacbti_ext =
ARM_FEATURE_CORE_HIGH_HIGH (ARM_EXT3_PACBTI);
static int mfloat_abi_opt = -1;
/* Architecture feature bits selected by the last -mcpu/-march or .cpu/.arch
directive. */
static arm_feature_set selected_arch = ARM_ARCH_NONE;
/* Extension feature bits selected by the last -mcpu/-march or .arch_extension
directive. */
static arm_feature_set selected_ext = ARM_ARCH_NONE;
/* Feature bits selected by the last -mcpu/-march or by the combination of the
last .cpu/.arch directive .arch_extension directives since that
directive. */
static arm_feature_set selected_cpu = ARM_ARCH_NONE;
/* FPU feature bits selected by the last -mfpu or .fpu directive. */
static arm_feature_set selected_fpu = FPU_NONE;
/* Feature bits selected by the last .object_arch directive. */
static arm_feature_set selected_object_arch = ARM_ARCH_NONE;
/* Must be long enough to hold any of the names in arm_cpus. */
static const struct arm_ext_table * selected_ctx_ext_table = NULL;
static char selected_cpu_name[20];
extern FLONUM_TYPE generic_floating_point_number;
/* Return if no cpu was selected on command-line. */
static bool
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;
# else
static int meabi_flags = EF_ARM_EABI_UNKNOWN;
# endif
static int attributes_set_explicitly[NUM_KNOWN_OBJ_ATTRIBUTES];
bool
arm_is_eabi (void)
{
return (EF_ARM_EABI_VERSION (meabi_flags) >= EF_ARM_EABI_VER4);
}
#endif
#ifdef OBJ_ELF
/* Pre-defined "_GLOBAL_OFFSET_TABLE_" */
symbolS * GOT_symbol;
#endif
/* 0: assemble for ARM,
1: assemble for Thumb,
2: assemble for Thumb even though target CPU does not support thumb
instructions. */
static int thumb_mode = 0;
/* A value distinct from the possible values for thumb_mode that we
can use to record whether thumb_mode has been copied into the
tc_frag_data field of a frag. */
#define MODE_RECORDED (1 << 4)
/* Specifies the intrinsic IT insn behavior mode. */
enum implicit_it_mode
{
IMPLICIT_IT_MODE_NEVER = 0x00,
IMPLICIT_IT_MODE_ARM = 0x01,
IMPLICIT_IT_MODE_THUMB = 0x02,
IMPLICIT_IT_MODE_ALWAYS = (IMPLICIT_IT_MODE_ARM | IMPLICIT_IT_MODE_THUMB)
};
static int implicit_it_mode = IMPLICIT_IT_MODE_ARM;
/* If unified_syntax is true, we are processing the new unified
ARM/Thumb syntax. Important differences from the old ARM mode:
- Immediate operands do not require a # prefix.
- Conditional affixes always appear at the end of the
instruction. (For backward compatibility, those instructions
that formerly had them in the middle, continue to accept them
there.)
- The IT instruction may appear, and if it does is validated
against subsequent conditional affixes. It does not generate
machine code.
Important differences from the old Thumb mode:
- Immediate operands do not require a # prefix.
- Most of the V6T2 instructions are only available in unified mode.
- The .N and .W suffixes are recognized and honored (it is an error
if they cannot be honored).
- All instructions set the flags if and only if they have an 's' affix.
- Conditional affixes may be used. They are validated against
preceding IT instructions. Unlike ARM mode, you cannot use a
conditional affix except in the scope of an IT instruction. */
static bool 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,
NT_untyped,
NT_integer,
NT_float,
NT_poly,
NT_signed,
NT_bfloat,
NT_unsigned
};
struct neon_type_el
{
enum neon_el_type type;
unsigned size;
};
#define NEON_MAX_TYPE_ELS 5
struct neon_type
{
struct neon_type_el el[NEON_MAX_TYPE_ELS];
unsigned elems;
};
enum pred_instruction_type
{
OUTSIDE_PRED_INSN,
INSIDE_VPT_INSN,
INSIDE_IT_INSN,
INSIDE_IT_LAST_INSN,
IF_INSIDE_IT_LAST_INSN, /* Either outside or inside;
if inside, should be the last one. */
NEUTRAL_IT_INSN, /* This could be either inside or outside,
i.e. BKPT and NOP. */
IT_INSN, /* The IT insn has been parsed. */
VPT_INSN, /* The VPT/VPST insn has been parsed. */
MVE_OUTSIDE_PRED_INSN , /* Instruction to indicate a MVE instruction without
a predication code. */
MVE_UNPREDICABLE_INSN, /* MVE instruction that is non-predicable. */
};
/* The maximum number of operands we need. */
#define ARM_IT_MAX_OPERANDS 6
#define ARM_IT_MAX_RELOCS 3
struct arm_it
{
const char * error;
unsigned long instruction;
unsigned int size;
unsigned int size_req;
unsigned int cond;
/* "uncond_value" is set to the value in place of the conditional field in
unconditional versions of the instruction, or -1u if nothing is
appropriate. */
unsigned int uncond_value;
struct neon_type vectype;
/* This does not indicate an actual NEON instruction, only that
the mnemonic accepts neon-style type suffixes. */
int is_neon;
/* Set to the opcode if the instruction needs relaxation.
Zero if the instruction is not relaxed. */
unsigned long relax;
struct
{
bfd_reloc_code_real_type type;
expressionS exp;
int pc_rel;
} relocs[ARM_IT_MAX_RELOCS];
enum pred_instruction_type pred_insn_type;
struct
{
unsigned reg;
signed int imm;
struct neon_type_el vectype;
unsigned present : 1; /* Operand present. */
unsigned isreg : 1; /* Operand was a register. */
unsigned immisreg : 2; /* .imm field is a second register.
0: imm, 1: gpr, 2: MVE Q-register. */
unsigned isscalar : 2; /* Operand is a (SIMD) scalar:
0) not scalar,
1) Neon scalar,
2) MVE scalar. */
unsigned immisalign : 1; /* Immediate is an alignment specifier. */
unsigned immisfloat : 1; /* Immediate was parsed as a float. */
/* Note: we abuse "regisimm" to mean "is Neon register" in VMOV
instructions. This allows us to disambiguate ARM <-> vector insns. */
unsigned regisimm : 1; /* 64-bit immediate, reg forms high 32 bits. */
unsigned isvec : 1; /* Is a single, double or quad VFP/Neon reg. */
unsigned isquad : 1; /* Operand is SIMD quad register. */
unsigned issingle : 1; /* Operand is VFP single-precision register. */
unsigned iszr : 1; /* Operand is ZR register. */
unsigned hasreloc : 1; /* Operand has relocation suffix. */
unsigned writeback : 1; /* Operand has trailing ! */
unsigned preind : 1; /* Preindexed address. */
unsigned postind : 1; /* Postindexed address. */
unsigned negative : 1; /* Index register was negated. */
unsigned shifted : 1; /* Shift applied to operation. */
unsigned shift_kind : 3; /* Shift operation (enum shift_kind). */
} operands[ARM_IT_MAX_OPERANDS];
};
static struct arm_it inst;
#define NUM_FLOAT_VALS 8
const char * fp_const[] =
{
"0.0", "1.0", "2.0", "3.0", "4.0", "5.0", "0.5", "10.0", 0
};
LITTLENUM_TYPE fp_values[NUM_FLOAT_VALS][MAX_LITTLENUMS];
#define FAIL (-1)
#define SUCCESS (0)
#define SUFF_S 1
#define SUFF_D 2
#define SUFF_E 3
#define SUFF_P 4
#define CP_T_X 0x00008000
#define CP_T_Y 0x00400000
#define CONDS_BIT 0x00100000
#define LOAD_BIT 0x00100000
#define DOUBLE_LOAD_FLAG 0x00000001
struct asm_cond
{
const char * template_name;
unsigned long value;
};
#define COND_ALWAYS 0xE
struct asm_psr
{
const char * template_name;
unsigned long field;
};
struct asm_barrier_opt
{
const char * template_name;
unsigned long value;
const arm_feature_set arch;
};
/* The bit that distinguishes CPSR and SPSR. */
#define SPSR_BIT (1 << 22)
/* The individual PSR flag bits. */
#define PSR_c (1 << 16)
#define PSR_x (1 << 17)
#define PSR_s (1 << 18)
#define PSR_f (1 << 19)
struct reloc_entry
{
const char * name;
bfd_reloc_code_real_type reloc;
};
enum vfp_reg_pos
{
VFP_REG_Sd, VFP_REG_Sm, VFP_REG_Sn,
VFP_REG_Dd, VFP_REG_Dm, VFP_REG_Dn
};
enum vfp_ldstm_type
{
VFP_LDSTMIA, VFP_LDSTMDB, VFP_LDSTMIAX, VFP_LDSTMDBX
};
/* Bits for DEFINED field in neon_typed_alias. */
#define NTA_HASTYPE 1
#define NTA_HASINDEX 2
struct neon_typed_alias
{
unsigned char defined;
unsigned char index;
struct neon_type_el eltype;
};
/* ARM register categories. This includes coprocessor numbers and various
architecture extensions' registers. Each entry should have an error message
in reg_expected_msgs below. */
enum arm_reg_type
{
REG_TYPE_RN,
REG_TYPE_CP,
REG_TYPE_CN,
REG_TYPE_FN,
REG_TYPE_VFS,
REG_TYPE_VFD,
REG_TYPE_NQ,
REG_TYPE_VFSD,
REG_TYPE_NDQ,
REG_TYPE_NSD,
REG_TYPE_NSDQ,
REG_TYPE_VFC,
REG_TYPE_MVF,
REG_TYPE_MVD,
REG_TYPE_MVFX,
REG_TYPE_MVDX,
REG_TYPE_MVAX,
REG_TYPE_MQ,
REG_TYPE_DSPSC,
REG_TYPE_MMXWR,
REG_TYPE_MMXWC,
REG_TYPE_MMXWCG,
REG_TYPE_XSCALE,
REG_TYPE_RNB,
REG_TYPE_ZR
};
/* Structure for a hash table entry for a register.
If TYPE is REG_TYPE_VFD or REG_TYPE_NQ, the NEON field can point to extra
information which states whether a vector type or index is specified (for a
register alias created with .dn or .qn). Otherwise NEON should be NULL. */
struct reg_entry
{
const char * name;
unsigned int number;
unsigned char type;
unsigned char builtin;
struct neon_typed_alias * neon;
};
/* Diagnostics used when we don't get a register of the expected type. */
const char * const reg_expected_msgs[] =
{
[REG_TYPE_RN] = N_("ARM register expected"),
[REG_TYPE_CP] = N_("bad or missing co-processor number"),
[REG_TYPE_CN] = N_("co-processor register expected"),
[REG_TYPE_FN] = N_("FPA register expected"),
[REG_TYPE_VFS] = N_("VFP single precision register expected"),
[REG_TYPE_VFD] = N_("VFP/Neon double precision register expected"),
[REG_TYPE_NQ] = N_("Neon quad precision register expected"),
[REG_TYPE_VFSD] = N_("VFP single or double precision register expected"),
[REG_TYPE_NDQ] = N_("Neon double or quad precision register expected"),
[REG_TYPE_NSD] = N_("Neon single or double precision register expected"),
[REG_TYPE_NSDQ] = N_("VFP single, double or Neon quad precision register"
" expected"),
[REG_TYPE_VFC] = N_("VFP system register expected"),
[REG_TYPE_MVF] = N_("Maverick MVF register expected"),
[REG_TYPE_MVD] = N_("Maverick MVD register expected"),
[REG_TYPE_MVFX] = N_("Maverick MVFX register expected"),
[REG_TYPE_MVDX] = N_("Maverick MVDX register expected"),
[REG_TYPE_MVAX] = N_("Maverick MVAX register expected"),
[REG_TYPE_DSPSC] = N_("Maverick DSPSC register expected"),
[REG_TYPE_MMXWR] = N_("iWMMXt data register expected"),
[REG_TYPE_MMXWC] = N_("iWMMXt control register expected"),
[REG_TYPE_MMXWCG] = N_("iWMMXt scalar register expected"),
[REG_TYPE_XSCALE] = N_("XScale accumulator register expected"),
[REG_TYPE_MQ] = N_("MVE vector register expected"),
[REG_TYPE_RNB] = "",
[REG_TYPE_ZR] = N_("ZR register expected"),
};
/* 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
/* ARM instructions take 4bytes in the object file, Thumb instructions
take 2: */
#define INSN_SIZE 4
struct asm_opcode
{
/* Basic string to match. */
const char * template_name;
/* Parameters to instruction. */
unsigned int operands[8];
/* Conditional tag - see opcode_lookup. */
unsigned int tag : 4;
/* Basic instruction code. */
unsigned int avalue;
/* Thumb-format instruction code. */
unsigned int tvalue;
/* Which architecture variant provides this instruction. */
const arm_feature_set * avariant;
const arm_feature_set * tvariant;
/* Function to call to encode instruction in ARM format. */
void (* aencode) (void);
/* Function to call to encode instruction in Thumb format. */
void (* tencode) (void);
/* Indicates whether this instruction may be vector predicated. */
unsigned int mayBeVecPred : 1;
};
/* Defines for various bits that we will want to toggle. */
#define INST_IMMEDIATE 0x02000000
#define OFFSET_REG 0x02000000
#define HWOFFSET_IMM 0x00400000
#define SHIFT_BY_REG 0x00000010
#define PRE_INDEX 0x01000000
#define INDEX_UP 0x00800000
#define WRITE_BACK 0x00200000
#define LDM_TYPE_2_OR_3 0x00400000
#define CPSI_MMOD 0x00020000
#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 DATA_OP_SHIFT 21
#define SBIT_SHIFT 20
#define T2_OPCODE_MASK 0xfe1fffff
#define T2_DATA_OP_SHIFT 21
#define T2_SBIT_SHIFT 20
#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
#define OPCODE_SUB 2
#define OPCODE_RSB 3
#define OPCODE_ADD 4
#define OPCODE_ADC 5
#define OPCODE_SBC 6
#define OPCODE_RSC 7
#define OPCODE_TST 8
#define OPCODE_TEQ 9
#define OPCODE_CMP 10
#define OPCODE_CMN 11
#define OPCODE_ORR 12
#define OPCODE_MOV 13
#define OPCODE_BIC 14
#define OPCODE_MVN 15
#define T2_OPCODE_AND 0
#define T2_OPCODE_BIC 1
#define T2_OPCODE_ORR 2
#define T2_OPCODE_ORN 3
#define T2_OPCODE_EOR 4
#define T2_OPCODE_ADD 8
#define T2_OPCODE_ADC 10
#define T2_OPCODE_SBC 11
#define T2_OPCODE_SUB 13
#define T2_OPCODE_RSB 14
#define T_OPCODE_MUL 0x4340
#define T_OPCODE_TST 0x4200
#define T_OPCODE_CMN 0x42c0
#define T_OPCODE_NEG 0x4240
#define T_OPCODE_MVN 0x43c0
#define T_OPCODE_ADD_R3 0x1800
#define T_OPCODE_SUB_R3 0x1a00
#define T_OPCODE_ADD_HI 0x4400
#define T_OPCODE_ADD_ST 0xb000
#define T_OPCODE_SUB_ST 0xb080
#define T_OPCODE_ADD_SP 0xa800
#define T_OPCODE_ADD_PC 0xa000
#define T_OPCODE_ADD_I8 0x3000
#define T_OPCODE_SUB_I8 0x3800
#define T_OPCODE_ADD_I3 0x1c00
#define T_OPCODE_SUB_I3 0x1e00
#define T_OPCODE_ASR_R 0x4100
#define T_OPCODE_LSL_R 0x4080
#define T_OPCODE_LSR_R 0x40c0
#define T_OPCODE_ROR_R 0x41c0
#define T_OPCODE_ASR_I 0x1000
#define T_OPCODE_LSL_I 0x0000
#define T_OPCODE_LSR_I 0x0800
#define T_OPCODE_MOV_I8 0x2000
#define T_OPCODE_CMP_I8 0x2800
#define T_OPCODE_CMP_LR 0x4280
#define T_OPCODE_MOV_HR 0x4600
#define T_OPCODE_CMP_HR 0x4500
#define T_OPCODE_LDR_PC 0x4800
#define T_OPCODE_LDR_SP 0x9800
#define T_OPCODE_STR_SP 0x9000
#define T_OPCODE_LDR_IW 0x6800
#define T_OPCODE_STR_IW 0x6000
#define T_OPCODE_LDR_IH 0x8800
#define T_OPCODE_STR_IH 0x8000
#define T_OPCODE_LDR_IB 0x7800
#define T_OPCODE_STR_IB 0x7000
#define T_OPCODE_LDR_RW 0x5800
#define T_OPCODE_STR_RW 0x5000
#define T_OPCODE_LDR_RH 0x5a00
#define T_OPCODE_STR_RH 0x5200
#define T_OPCODE_LDR_RB 0x5c00
#define T_OPCODE_STR_RB 0x5400
#define T_OPCODE_PUSH 0xb400
#define T_OPCODE_POP 0xbc00
#define T_OPCODE_BRANCH 0xe000
#define THUMB_SIZE 2 /* Size of thumb instruction. */
#define THUMB_PP_PC_LR 0x0100
#define THUMB_LOAD_BIT 0x0800
#define THUMB2_LOAD_BIT 0x00100000
#define BAD_SYNTAX _("syntax error")
#define BAD_ARGS _("bad arguments to instruction")
#define BAD_SP _("r13 not allowed here")
#define BAD_PC _("r15 not allowed here")
#define BAD_ODD _("Odd register not allowed here")
#define BAD_EVEN _("Even register not allowed here")
#define BAD_COND _("instruction cannot be conditional")
#define BAD_OVERLAP _("registers may not be the same")
#define BAD_HIREG _("lo register required")
#define BAD_THUMB32 _("instruction not supported in Thumb16 mode")
#define BAD_ADDR_MODE _("instruction does not accept this addressing mode")
#define BAD_BRANCH _("branch must be last instruction in IT block")
#define BAD_BRANCH_OFF _("branch out of range or not a multiple of 2")
#define BAD_NO_VPT _("instruction not allowed in VPT block")
#define BAD_NOT_IT _("instruction not allowed in IT block")
#define BAD_NOT_VPT _("instruction missing MVE vector predication code")
#define BAD_FPU _("selected FPU does not support instruction")
#define BAD_OUT_IT _("thumb conditional instruction should be in IT block")
#define BAD_OUT_VPT \
_("vector predicated instruction should be in VPT/VPST block")
#define BAD_IT_COND _("incorrect condition in IT block")
#define BAD_VPT_COND _("incorrect condition in VPT/VPST block")
#define BAD_IT_IT _("IT falling in the range of a previous IT block")
#define MISSING_FNSTART _("missing .fnstart before unwinding directive")
#define BAD_PC_ADDRESSING \
_("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 BAD_BF16 _("selected processor does not support bf16 instruction")
#define BAD_CDE _("selected processor does not support cde instruction")
#define BAD_CDE_COPROC _("coprocessor for insn is not enabled for cde")
#define UNPRED_REG(R) _("using " R " results in unpredictable behaviour")
#define THUMB1_RELOC_ONLY _("relocation valid in thumb1 code only")
#define MVE_NOT_IT _("Warning: instruction is UNPREDICTABLE in an IT " \
"block")
#define MVE_NOT_VPT _("Warning: instruction is UNPREDICTABLE in a VPT " \
"block")
#define MVE_BAD_PC _("Warning: instruction is UNPREDICTABLE with PC" \
" operand")
#define MVE_BAD_SP _("Warning: instruction is UNPREDICTABLE with SP" \
" operand")
#define BAD_SIMD_TYPE _("bad type in SIMD instruction")
#define BAD_MVE_AUTO \
_("GAS auto-detection mode and -march=all is deprecated for MVE, please" \
" use a valid -march or -mcpu option.")
#define BAD_MVE_SRCDEST _("Warning: 32-bit element size and same destination "\
"and source operands makes instruction UNPREDICTABLE")
#define BAD_EL_TYPE _("bad element type for instruction")
#define MVE_BAD_QREG _("MVE vector register Q[0..7] expected")
#define BAD_PACBTI _("selected processor does not support PACBTI extention")
static htab_t arm_ops_hsh;
static htab_t arm_cond_hsh;
static htab_t arm_vcond_hsh;
static htab_t arm_shift_hsh;
static htab_t arm_psr_hsh;
static htab_t arm_v7m_psr_hsh;
static htab_t arm_reg_hsh;
static htab_t arm_reloc_hsh;
static htab_t arm_barrier_opt_hsh;
/* Stuff needed to resolve the label ambiguity
As:
...
label: <insn>
may differ from:
...
label:
<insn> */
symbolS * last_label_seen;
static int label_is_thumb_function_name = false;
/* Literal pool structure. Held on a per-section
and per-sub-section basis. */
#define MAX_LITERAL_POOL_SIZE 1024
typedef struct literal_pool
{
expressionS literals [MAX_LITERAL_POOL_SIZE];
unsigned int next_free_entry;
unsigned int id;
symbolS * symbol;
segT section;
subsegT sub_section;
#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_pred seg_info (now_seg)->tc_segment_info_data.current_pred
#else
static struct current_pred now_pred;
#endif
static inline int
now_pred_compatible (int cond)
{
return (cond & ~1) == (now_pred.cc & ~1);
}
static inline int
conditional_insn (void)
{
return inst.cond != COND_ALWAYS;
}
static int in_pred_block (void);
static int handle_pred_state (void);
static void force_automatic_it_block_close (void);
static void it_fsm_post_encode (void);
#define set_pred_insn_type(type) \
do \
{ \
inst.pred_insn_type = type; \
if (handle_pred_state () == FAIL) \
return; \
} \
while (0)
#define set_pred_insn_type_nonvoid(type, failret) \
do \
{ \
inst.pred_insn_type = type; \
if (handle_pred_state () == FAIL) \
return failret; \
} \
while(0)
#define set_pred_insn_type_last() \
do \
{ \
if (inst.cond == COND_ALWAYS) \
set_pred_insn_type (IF_INSIDE_IT_LAST_INSN); \
else \
set_pred_insn_type (INSIDE_IT_LAST_INSN); \
} \
while (0)
/* Toggle value[pos]. */
#define TOGGLE_BIT(value, pos) (value ^ (1 << pos))
/* Pure syntax. */
/* This array holds the chars that always start a comment. If the
pre-processor is disabled, these aren't very useful. */
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'
.line and .file directives will appear in the pre-processed output. */
/* Note that input_file.c hand checks for '#' at the beginning of the
first line of the input file. This is because the compiler outputs
#NO_APP at the beginning of its output. */
/* Also note that comments like this one will always work. */
const char line_comment_chars[] = "#";
char arm_line_separator_chars[] = ";";
/* Chars that can be used to separate mant
from exp in floating point numbers. */
const char EXP_CHARS[] = "eE";
/* Chars that mean this number is a floating point constant. */
/* As in 0f12.456 */
/* or 0d1.2345e12 */
const char FLT_CHARS[] = "rRsSfFdDxXeEpPHh";
/* Prefix characters that indicate the start of an immediate
value. */
#define is_immediate_prefix(C) ((C) == '#' || (C) == '$')
/* Separator character handling. */
#define skip_whitespace(str) do { if (*(str) == ' ') ++(str); } while (0)
enum fp_16bit_format
{
ARM_FP16_FORMAT_IEEE = 0x1,
ARM_FP16_FORMAT_ALTERNATIVE = 0x2,
ARM_FP16_FORMAT_DEFAULT = 0x3
};
static enum fp_16bit_format fp16_format = ARM_FP16_FORMAT_DEFAULT;
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)++;
return SUCCESS;
}
else
return FAIL;
}
#define skip_past_comma(str) skip_past_char (str, ',')
/* Arithmetic expressions (possibly involving symbols). */
/* Return TRUE if anything in the expression is a bignum. */
static bool
walk_no_bignums (symbolS * sp)
{
if (symbol_get_value_expression (sp)->X_op == O_big)
return true;
if (symbol_get_value_expression (sp)->X_add_symbol)
{
return (walk_no_bignums (symbol_get_value_expression (sp)->X_add_symbol)
|| (symbol_get_value_expression (sp)->X_op_symbol
&& walk_no_bignums (symbol_get_value_expression (sp)->X_op_symbol)));
}
return false;
}
static bool in_my_get_expression = false;
/* Third argument to my_get_expression. */
#define GE_NO_PREFIX 0
#define GE_IMM_PREFIX 1
#define GE_OPT_PREFIX 2
/* This is a bit of a hack. Use an optional prefix, and also allow big (64-bit)
immediates, as can be used in Neon VMVN and VMOV immediate instructions. */
#define GE_OPT_PREFIX_BIG 3
static int
my_get_expression (expressionS * ep, char ** str, int prefix_mode)
{
char * save_in;
/* In unified syntax, all prefixes are optional. */
if (unified_syntax)
prefix_mode = (prefix_mode == GE_OPT_PREFIX_BIG) ? prefix_mode
: GE_OPT_PREFIX;
switch (prefix_mode)
{
case GE_NO_PREFIX: break;
case GE_IMM_PREFIX:
if (!is_immediate_prefix (**str))
{
inst.error = _("immediate expression requires a # prefix");
return FAIL;
}
(*str)++;
break;
case GE_OPT_PREFIX:
case GE_OPT_PREFIX_BIG:
if (is_immediate_prefix (**str))
(*str)++;
break;
default:
abort ();
}
memset (ep, 0, sizeof (expressionS));
save_in = input_line_pointer;
input_line_pointer = *str;
in_my_get_expression = true;
expression (ep);
in_my_get_expression = false;
if (ep->X_op == O_illegal || ep->X_op == O_absent)
{
/* We found a bad or missing expression in md_operand(). */
*str = input_line_pointer;
input_line_pointer = save_in;
if (inst.error == NULL)
inst.error = (ep->X_op == O_absent
? _("missing expression") :_("bad expression"));
return 1;
}
/* Get rid of any bignums now, so that we don't generate an error for which
we can't establish a line number later on. Big numbers are never valid
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
&& (walk_no_bignums (ep->X_add_symbol)
|| (ep->X_op_symbol
&& walk_no_bignums (ep->X_op_symbol))))))
{
inst.error = _("invalid constant");
*str = input_line_pointer;
input_line_pointer = save_in;
return 1;
}
*str = input_line_pointer;
input_line_pointer = save_in;
return SUCCESS;
}
/* Turn a string in input_line_pointer into a floating point constant
of type TYPE, and store the appropriate bytes in *LITP. The number
of LITTLENUMS emitted is stored in *SIZEP. An error message is
returned, or NULL on OK.
Note that fp constants aren't represent in the normal way on the ARM.
In big endian mode, things are as expected. However, in little endian
mode fp constants are big-endian word-wise, and little-endian byte-wise
within the words. For example, (double) 1.1 in big endian mode is
the byte sequence 3f f1 99 99 99 99 99 9a, and in little endian mode is
the byte sequence 99 99 f1 3f 9a 99 99 99.
??? The format of 12 byte floats is uncertain according to gcc's arm.h. */
const char *
md_atof (int type, char * litP, int * sizeP)
{
int prec;
LITTLENUM_TYPE words[MAX_LITTLENUMS];
char *t;
int i;
switch (type)
{
case 'H':
case 'h':
/* bfloat16, despite not being part of the IEEE specification, can also
be handled by atof_ieee(). */
case 'b':
prec = 1;
break;
case 'f':
case 'F':
case 's':
case 'S':
prec = 2;
break;
case 'd':
case 'D':
case 'r':
case 'R':
prec = 4;
break;
case 'x':
case 'X':
prec = 5;
break;
case 'p':
case 'P':
prec = 5;
break;
default:
*sizeP = 0;
return _("Unrecognized or unsupported floating point constant");
}
t = atof_ieee (input_line_pointer, type, words);
if (t)
input_line_pointer = t;
*sizeP = prec * sizeof (LITTLENUM_TYPE);
if (target_big_endian || prec == 1)
for (i = 0; i < prec; i++)
{
md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE));
litP += sizeof (LITTLENUM_TYPE);
}
else if (ARM_CPU_HAS_FEATURE (cpu_variant, fpu_endian_pure))
for (i = prec - 1; i >= 0; i--)
{
md_number_to_chars (litP, (valueT) words[i], sizeof (LITTLENUM_TYPE));
litP += sizeof (LITTLENUM_TYPE);
}
else
/* For a 4 byte float the order of elements in `words' is 1 0.
For an 8 byte float the order is 1 0 3 2. */
for (i = 0; i < prec; i += 2)
{
md_number_to_chars (litP, (valueT) words[i + 1],
sizeof (LITTLENUM_TYPE));
md_number_to_chars (litP + sizeof (LITTLENUM_TYPE),
(valueT) words[i], sizeof (LITTLENUM_TYPE));
litP += 2 * sizeof (LITTLENUM_TYPE);
}
return NULL;
}
/* We handle all bad expressions here, so that we can report the faulty
instruction in the error message. */
void
md_operand (expressionS * exp)
{
if (in_my_get_expression)
exp->X_op = O_illegal;
}
/* Immediate values. */
#ifdef OBJ_ELF
/* Generic immediate-value read function for use in directives.
Accepts anything that 'expression' can fold to a constant.
*val receives the number. */
static int
immediate_for_directive (int *val)
{
expressionS exp;
exp.X_op = O_illegal;
if (is_immediate_prefix (*input_line_pointer))
{
input_line_pointer++;
expression (&exp);
}
if (exp.X_op != O_constant)
{
as_bad (_("expected #constant"));
ignore_rest_of_line ();
return FAIL;
}
*val = exp.X_add_number;
return SUCCESS;
}
#endif
/* Register parsing. */
/* Generic register parser. CCP points to what should be the
beginning of a register name. If it is indeed a valid register
name, advance CCP over it and return the reg_entry structure;
otherwise return NULL. Does not issue diagnostics. */
static struct reg_entry *
arm_reg_parse_multi (char **ccp)
{
char *start = *ccp;
char *p;
struct reg_entry *reg;
skip_whitespace (start);
#ifdef REGISTER_PREFIX
if (*start != REGISTER_PREFIX)
return NULL;
start++;
#endif
#ifdef OPTIONAL_REGISTER_PREFIX
if (*start == OPTIONAL_REGISTER_PREFIX)
start++;
#endif
p = start;
if (!ISALPHA (*p) || !is_name_beginner (*p))
return NULL;
do
p++;
while (ISALPHA (*p) || ISDIGIT (*p) || *p == '_');
reg = (struct reg_entry *) str_hash_find_n (arm_reg_hsh, start, p - start);
if (!reg)
return NULL;
*ccp = p;
return reg;
}
static int
arm_reg_alt_syntax (char **ccp, char *start, struct reg_entry *reg,
enum arm_reg_type type)
{
/* Alternative syntaxes are accepted for a few register classes. */
switch (type)
{
case REG_TYPE_MVF:
case REG_TYPE_MVD:
case REG_TYPE_MVFX:
case REG_TYPE_MVDX:
/* Generic coprocessor register names are allowed for these. */
if (reg && reg->type == REG_TYPE_CN)
return reg->number;
break;
case REG_TYPE_CP:
/* For backward compatibility, a bare number is valid here. */
{
unsigned long processor = strtoul (start, ccp, 10);
if (*ccp != start && processor <= 15)
return processor;
}
/* Fall through. */
case REG_TYPE_MMXWC:
/* WC includes WCG. ??? I'm not sure this is true for all
instructions that take WC registers. */
if (reg && reg->type == REG_TYPE_MMXWCG)
return reg->number;
break;
default:
break;
}
return FAIL;
}
/* As arm_reg_parse_multi, but the register must be of type TYPE, and the
return value is the register number or FAIL. */
static int
arm_reg_parse (char **ccp, enum arm_reg_type type)
{
char *start = *ccp;
struct reg_entry *reg = arm_reg_parse_multi (ccp);
int ret;
/* Do not allow a scalar (reg+index) to parse as a register. */
if (reg && reg->neon && (reg->neon->defined & NTA_HASINDEX))
return FAIL;
if (reg && reg->type == type)
return reg->number;
if ((ret = arm_reg_alt_syntax (ccp, start, reg, type)) != FAIL)
return ret;
*ccp = start;
return FAIL;
}
/* Parse a Neon type specifier. *STR should point at the leading '.'
character. Does no verification at this stage that the type fits the opcode
properly. E.g.,
.i32.i32.s16
.s32.f32
.u16
Can all be legally parsed by this function.
Fills in neon_type struct pointer with parsed information, and updates STR
to point after the parsed type specifier. Returns SUCCESS if this was a legal
type, FAIL if not. */
static int
parse_neon_type (struct neon_type *type, char **str)
{
char *ptr = *str;
if (type)
type->elems = 0;
while (type->elems < NEON_MAX_TYPE_ELS)
{
enum neon_el_type thistype = NT_untyped;
unsigned thissize = -1u;
if (*ptr != '.')
break;
ptr++;
/* Just a size without an explicit type. */
if (ISDIGIT (*ptr))
goto parsesize;
switch (TOLOWER (*ptr))
{
case 'i': thistype = NT_integer; break;
case 'f': thistype = NT_float; break;
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 'b':
thistype = NT_bfloat;
switch (TOLOWER (*(++ptr)))
{
case 'f':
ptr += 1;
thissize = strtoul (ptr, &ptr, 10);
if (thissize != 16)
{
as_bad (_("bad size %d in type specifier"), thissize);
return FAIL;
}
goto done;
case '0': case '1': case '2': case '3': case '4':
case '5': case '6': case '7': case '8': case '9':
case ' ': case '.':
as_bad (_("unexpected type character `b' -- did you mean `bf'?"));
return FAIL;
default:
break;
}
break;
default:
as_bad (_("unexpected character `%c' in type specifier"), *ptr);
return FAIL;
}
ptr++;
/* .f is an abbreviation for .f32. */
if (thistype == NT_float && !ISDIGIT (*ptr))
thissize = 32;
else
{
parsesize:
thissize = strtoul (ptr, &ptr, 10);
if (thissize != 8 && thissize != 16 && thissize != 32
&& 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].size = thissize;
type->elems++;
}
}
/* Empty/missing type is not a successful parse. */
if (type->elems == 0)
return FAIL;
*str = ptr;
return SUCCESS;
}
/* Errors may be set multiple times during parsing or bit encoding
(particularly in the Neon bits), but usually the earliest error which is set
will be the most meaningful. Avoid overwriting it with later (cascading)
errors by calling this function. */
static void
first_error (const char *err)
{
if (!inst.error)
inst.error = err;
}
/* Parse a single type, e.g. ".s32", leading period included. */
static int
parse_neon_operand_type (struct neon_type_el *vectype, char **ccp)
{
char *str = *ccp;
struct neon_type optype;
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;
}
}
else
{
first_error (_("vector type expected"));
return FAIL;
}
}
else
return FAIL;
*ccp = str;
return SUCCESS;
}
/* Special meanings for indices (which have a range of 0-7), which will fit into
a 4-bit integer. */
#define NEON_ALL_LANES 15
#define NEON_INTERLEAVE_LANES 14
/* 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 bool
mark_feature_used (const arm_feature_set *feature)
{
/* Do not support the use of MVE only instructions when in auto-detection or
-march=all. */
if (((feature == &mve_ext) || (feature == &mve_fp_ext))
&& ARM_CPU_IS_ANY (cpu_variant))
{
first_error (BAD_MVE_AUTO);
return false;
}
/* 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 either a register or a scalar, with an optional type. Return the
register number, and optionally fill in the actual type of the register
when multiple alternatives were given (NEON_TYPE_NDQ) in *RTYPE, and
type/index information in *TYPEINFO. */
static int
parse_typed_reg_or_scalar (char **ccp, enum arm_reg_type type,
enum arm_reg_type *rtype,
struct neon_typed_alias *typeinfo)
{
char *str = *ccp;
struct reg_entry *reg = arm_reg_parse_multi (&str);
struct neon_typed_alias atype;
struct neon_type_el parsetype;
atype.defined = 0;
atype.index = -1;
atype.eltype.type = NT_invtype;
atype.eltype.size = -1;
/* Try alternate syntax for some types of register. Note these are mutually
exclusive with the Neon syntax extensions. */
if (reg == NULL)
{
int altreg = arm_reg_alt_syntax (&str, *ccp, reg, type);
if (altreg != FAIL)
*ccp = str;
if (typeinfo)
*typeinfo = atype;
return altreg;
}
/* Undo polymorphism when a set of register types may be accepted. */
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))
|| (type == REG_TYPE_NSDQ
&& (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD
|| reg->type == REG_TYPE_NQ))
|| (type == REG_TYPE_NSD
&& (reg->type == REG_TYPE_VFS || reg->type == REG_TYPE_VFD))
|| (type == REG_TYPE_MMXWC
&& (reg->type == REG_TYPE_MMXWCG)))
type = (enum arm_reg_type) reg->type;
if (type == REG_TYPE_MQ)
{
if (!ARM_CPU_HAS_FEATURE (cpu_variant, mve_ext))
return FAIL;
if (!reg || reg->type != REG_TYPE_NQ)
return FAIL;
if (reg->number > 14 && !mark_feature_used (&fpu_vfp_ext_d32))
{
first_error (_("expected MVE register [q0..q7]"));
return FAIL;
}
type = REG_TYPE_NQ;
}
else if (ARM_CPU_HAS_FEATURE (cpu_variant, mve_ext)
&& (type == REG_TYPE_NQ))
return FAIL;
if (type != reg->type)
return FAIL;
if (reg->neon)
atype = *reg->neon;
if (parse_neon_operand_type (&parsetype, &str) == SUCCESS)
{
if ((atype.defined & NTA_HASTYPE) != 0)
{
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
&& !(type == REG_TYPE_VFS
&& ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v8_2))
&& !(type == REG_TYPE_NQ
&& ARM_CPU_HAS_FEATURE (cpu_variant, mve_ext)))
{
if (ARM_CPU_HAS_FEATURE (cpu_variant, mve_ext))
first_error (_("only D and Q registers may be indexed"));
else
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;
}
atype.defined |= NTA_HASINDEX;
if (skip_past_char (&str, ']') == SUCCESS)
atype.index = NEON_ALL_LANES;
else
{
expressionS exp;
my_get_expression (&exp, &str, GE_NO_PREFIX);
if (exp.X_op != O_constant)
{
first_error (_("constant expression required"));
return FAIL;
}
if (skip_past_char (&str, ']') == FAIL)
return FAIL;
atype.index = exp.X_add_number;
}
}
if (typeinfo)
*typeinfo = atype;
if (rtype)
*rtype = type;
*ccp = str;
return reg->number;
}
/* Like arm_reg_parse, but also allow the following extra features:
- If RTYPE is non-zero, return the (possibly restricted) type of the
register (e.g. Neon double or quad reg when either has been requested).
- If this is a Neon vector type with additional type information, fill
in the struct pointed to by VECTYPE (if non-NULL).
This function will fault on encountering a scalar. */
static int
arm_typed_reg_parse (char **ccp, enum arm_reg_type type,
enum arm_reg_type *rtype, struct neon_type_el *vectype)
{
struct neon_typed_alias atype;
char *str = *ccp;
int reg = parse_typed_reg_or_scalar (&str, type, rtype, &atype);
if (reg == FAIL)
return FAIL;
/* Do not allow regname(... to parse as a register. */
if (*str == '(')
return FAIL;
/* Do not allow a scalar (reg+index) to parse as a register. */
if ((atype.defined & NTA_HASINDEX) != 0)
{
first_error (_("register operand expected, but got scalar"));
return FAIL;
}
if (vectype)
*vectype = atype.eltype;
*ccp = str;
return reg;
}
#define NEON_SCALAR_REG(X) ((X) >> 4)
#define NEON_SCALAR_INDEX(X) ((X) & 15)
/* Parse a Neon scalar. Most of the time when we're parsing a scalar, we don't
have enough information to be able to do a good job bounds-checking. So, we
just do easy checks here, and do further checks later. */
static int
parse_scalar (char **ccp, int elsize, struct neon_type_el *type, enum
arm_reg_type reg_type)
{
int reg;
char *str = *ccp;
struct neon_typed_alias atype;
unsigned reg_size;
reg = parse_typed_reg_or_scalar (&str, reg_type, NULL, &atype);
switch (reg_type)
{
case REG_TYPE_VFS:
reg_size = 32;
break;
case REG_TYPE_VFD:
reg_size = 64;
break;
case REG_TYPE_MQ:
reg_size = 128;
break;
default:
gas_assert (0);
return FAIL;
}
if (reg == FAIL || (atype.defined & NTA_HASINDEX) == 0)
return FAIL;
if (reg_type != REG_TYPE_MQ && atype.index == NEON_ALL_LANES)
{
first_error (_("scalar must have an index"));
return FAIL;
}
else if (atype.index >= reg_size / elsize)
{
first_error (_("scalar index out of range"));
return FAIL;
}
if (type)
*type = atype.eltype;
*ccp = str;
return reg * 16 + atype.index;
}
/* Types of registers in a list. */
enum reg_list_els
{
REGLIST_RN,
REGLIST_CLRM,
REGLIST_VFP_S,
REGLIST_VFP_S_VPR,
REGLIST_VFP_D,
REGLIST_VFP_D_VPR,
REGLIST_NEON_D
};
/* Parse an ARM register list. Returns the bitmask, or FAIL. */
static long
parse_reg_list (char ** strp, enum reg_list_els etype)
{
char *str = *strp;
long range = 0;
int another_range;
gas_assert (etype == REGLIST_RN || etype == REGLIST_CLRM);
/* We come back here if we get ranges concatenated by '+' or '|'. */
do
{
skip_whitespace (str);
another_range = 0;
if (*str == '{')
{
int in_range = 0;
int cur_reg = -1;
str++;
do
{
int reg;
const char apsr_str[] = "apsr";
int apsr_str_len = strlen (apsr_str);
reg = arm_reg_parse (&str, REG_TYPE_RN);
if (etype == REGLIST_CLRM)
{
if (reg == REG_SP || reg == REG_PC)
reg = FAIL;
else if (reg == FAIL
&& !strncasecmp (str, apsr_str, apsr_str_len)
&& !ISALPHA (*(str + apsr_str_len)))
{
reg = 15;
str += apsr_str_len;
}
if (reg == FAIL)
{
first_error (_("r0-r12, lr or APSR expected"));
return FAIL;
}
}
else /* etype == REGLIST_RN. */
{
if (reg == FAIL)
{
first_error (_(reg_expected_msgs[REGLIST_RN]));
return FAIL;
}
}
if (in_range)
{
int i;
if (reg <= cur_reg)
{
first_error (_("bad range in register list"));
return FAIL;
}
for (i = cur_reg + 1; i < reg; i++)
{
if (range & (1 << i))
as_tsktsk
(_("Warning: duplicated register (r%d) in register list"),
i);
else
range |= 1 << i;
}
in_range = 0;
}
if (range & (1 << reg))
as_tsktsk (_("Warning: duplicated register (r%d) in register list"),
reg);
else if (reg <= cur_reg)
as_tsktsk (_("Warning: register range not in ascending order"));
range |= 1 << reg;
cur_reg = reg;
}
while (skip_past_comma (&str) != FAIL
|| (in_range = 1, *str++ == '-'));
str--;
if (skip_past_char (&str, '}') == FAIL)
{
first_error (_("missing `}'"));
return FAIL;
}
}
else if (etype == REGLIST_RN)
{
expressionS exp;
if (my_get_expression (&exp, &str, GE_NO_PREFIX))
return FAIL;
if (exp.X_op == O_constant)
{
if (exp.X_add_number
!= (exp.X_add_number & 0x0000ffff))
{
inst.error = _("invalid register mask");
return FAIL;
}
if ((range & exp.X_add_number) != 0)
{
int regno = range & exp.X_add_number;
regno &= -regno;
regno = (1 << regno) - 1;
as_tsktsk
(_("Warning: duplicated register (r%d) in register list"),
regno);
}
range |= exp.X_add_number;
}
else
{
if (inst.relocs[0].type != 0)
{
inst.error = _("expression too complex");
return FAIL;
}
memcpy (&inst.relocs[0].exp, &exp, sizeof (expressionS));
inst.relocs[0].type = BFD_RELOC_ARM_MULTI;
inst.relocs[0].pc_rel = 0;
}
}
if (*str == '|' || *str == '+')
{
str++;
another_range = 1;
}
}
while (another_range);
*strp = str;
return range;
}
/* Parse a VFP register list. If the string is invalid return FAIL.
Otherwise return the number of registers, and set PBASE to the first
register. Parses registers of type ETYPE.
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.
- 32 D registers may be used (also true for VFPv3).
FIXME: Types are ignored in these register lists, which is probably a
bug. */
static int
parse_vfp_reg_list (char **ccp, unsigned int *pbase, enum reg_list_els etype,
bool *partial_match)
{
char *str = *ccp;
int base_reg;
int new_base;
enum arm_reg_type regtype = (enum arm_reg_type) 0;
int max_regs = 0;
int count = 0;
int warned = 0;
unsigned long mask = 0;
int i;
bool vpr_seen = false;
bool expect_vpr =
(etype == REGLIST_VFP_S_VPR) || (etype == REGLIST_VFP_D_VPR);
if (skip_past_char (&str, '{') == FAIL)
{
inst.error = _("expecting {");
return FAIL;
}
switch (etype)
{
case REGLIST_VFP_S:
case REGLIST_VFP_S_VPR:
regtype = REG_TYPE_VFS;
max_regs = 32;
break;
case REGLIST_VFP_D:
case REGLIST_VFP_D_VPR:
regtype = REG_TYPE_VFD;
break;
case REGLIST_NEON_D:
regtype = REG_TYPE_NDQ;
break;
default:
gas_assert (0);
}
if (etype != REGLIST_VFP_S && etype != REGLIST_VFP_S_VPR)
{
/* 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);
}
else
max_regs = 16;
}
base_reg = max_regs;
*partial_match = false;
do
{
unsigned int setmask = 1, addregs = 1;
const char vpr_str[] = "vpr";
size_t vpr_str_len = strlen (vpr_str);
new_base = arm_typed_reg_parse (&str, regtype, &regtype, NULL);
if (expect_vpr)
{
if (new_base == FAIL
&& !strncasecmp (str, vpr_str, vpr_str_len)
&& !ISALPHA (*(str + vpr_str_len))
&& !vpr_seen)
{
vpr_seen = true;
str += vpr_str_len;
if (count == 0)
base_reg = 0; /* Canonicalize VPR only on d0 with 0 regs. */
}
else if (vpr_seen)
{
first_error (_("VPR expected last"));
return FAIL;
}
else if (new_base == FAIL)
{
if (regtype == REG_TYPE_VFS)
first_error (_("VFP single precision register or VPR "
"expected"));
else /* regtype == REG_TYPE_VFD. */
first_error (_("VFP/Neon double precision register or VPR "
"expected"));
return FAIL;
}
}
else if (new_base == FAIL)
{
first_error (_(reg_expected_msgs[regtype]));
return FAIL;
}
*partial_match = true;
if (vpr_seen)
continue;
if (new_base >= max_regs)
{
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;
}
if (new_base < base_reg)
base_reg = new_base;
if (mask & (setmask << new_base))
{
first_error (_("invalid register list"));
return FAIL;
}
if ((mask >> new_base) != 0 && ! warned && !vpr_seen)
{
as_tsktsk (_("register list not in ascending order"));
warned = 1;
}
mask |= setmask << new_base;
count += addregs;
if (*str == '-') /* We have the start of a range expression */
{
int high_range;
str++;
if ((high_range = arm_typed_reg_parse (&str, regtype, NULL, NULL))
== 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 (regtype == REG_TYPE_NQ)
high_range = high_range + 1;
if (high_range <= new_base)
{
inst.error = _("register range not in ascending order");
return FAIL;
}
for (new_base += addregs; new_base <= high_range; new_base += addregs)
{
if (mask & (setmask << new_base))
{
inst.error = _("invalid register list");
return FAIL;
}
mask |= setmask << new_base;
count += addregs;
}
}
}
while (skip_past_comma (&str) != FAIL);
str++;
/* Sanity check -- should have raised a parse error above. */
if ((!vpr_seen && count == 0) || count > max_regs)
abort ();
*pbase = base_reg;
if (expect_vpr && !vpr_seen)
{
first_error (_("VPR expected last"));
return FAIL;
}
/* Final test -- the registers must be consecutive. */
mask >>= base_reg;
for (i = 0; i < count; i++)
{
if ((mask & (1u << i)) == 0)
{
inst.error = _("non-contiguous register range");
return FAIL;
}
}
*ccp = str;
return count;
}
/* True if two alias types are the same. */
static bool
neon_alias_types_same (struct neon_typed_alias *a, struct neon_typed_alias *b)
{
if (!a && !b)
return true;
if (!a || !b)
return false;
if (a->defined != b->defined)
return false;
if ((a->defined & NTA_HASTYPE) != 0
&& (a->eltype.type != b->eltype.type
|| a->eltype.size != b->eltype.size))
return false;
if ((a->defined & NTA_HASINDEX) != 0
&& (a->index != b->index))
return false;
return true;
}
/* Parse element/structure lists for Neon VLD<n> and VST<n> instructions.
The base register is put in *PBASE.
The lane (or one of the NEON_*_LANES constants) is placed in bits [3:0] of
the return value.
The register stride (minus one) is put in bit 4 of the return value.
Bits [6:5] encode the list length (minus one).
The type of the list elements is put in *ELTYPE, if non-NULL. */
#define NEON_LANE(X) ((X) & 0xf)
#define NEON_REG_STRIDE(X) ((((X) >> 4) & 1) + 1)
#define NEON_REGLIST_LENGTH(X) ((((X) >> 5) & 3) + 1)
static int
parse_neon_el_struct_list (char **str, unsigned *pbase,
int mve,
struct neon_type_el *eltype)
{
char *ptr = *str;
int base_reg = -1;
int reg_incr = -1;
int count = 0;
int lane = -1;
int leading_brace = 0;
enum arm_reg_type rtype = REG_TYPE_NDQ;
const char *const incr_error = mve ? _("register stride must be 1") :
_("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;
do
{
struct neon_typed_alias atype;
if (mve)
rtype = REG_TYPE_MQ;
int getreg = parse_typed_reg_or_scalar (&ptr, rtype, &rtype, &atype);
if (getreg == 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;
}
else if (reg_incr == -1)
{
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;
}
if (! neon_alias_types_same (&atype, &firsttype))
{
first_error (_(type_error));
return FAIL;
}
/* Handle Dn-Dm or Qn-Qm syntax. Can only be used with non-indexed list
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;
}
/* If we're using Q registers, we can't use [] or [n] syntax. */
if (rtype == REG_TYPE_NQ)
{
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;
}
}
else if (lane == -1)
lane = NEON_INTERLEAVE_LANES;
else if (lane != NEON_INTERLEAVE_LANES)
{
first_error (_(type_error));
return FAIL;
}
count++;
}
while ((count != 1 || leading_brace) && skip_past_comma (&ptr) != FAIL);
/* No lane set by [x]. We must be interleaving structures. */
if (lane == -1)
lane = NEON_INTERLEAVE_LANES;
/* Sanity check. */
if (lane == -1 || base_reg == -1 || count < 1 || (!mve && count > 4)
|| (count > 1 && reg_incr == -1))
{
first_error (_("error parsing element/structure list"));
return FAIL;
}
if ((count > 1 || leading_brace) && skip_past_char (&ptr, '}') == FAIL)
{
first_error (_("expected }"));
return FAIL;
}
if (reg_incr == -1)
reg_incr = 1;
if (eltype)
*eltype = firsttype.eltype;
*pbase = base_reg;
*str = ptr;
return lane | ((reg_incr - 1) << 4) | ((count - 1) << 5);
}
/* Parse an explicit relocation suffix on an expression. This is
either nothing, or a word in parentheses. Note that if !OBJ_ELF,
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)
{
struct reloc_entry *r;
char *p, *q;
if (**str != '(')
return BFD_RELOC_UNUSED;
p = *str + 1;
q = p;
while (*q && *q != ')' && *q != ',')
q++;
if (*q != ')')
return -1;
if ((r = (struct reloc_entry *)
str_hash_find_n (arm_reloc_hsh, p, q - p)) == NULL)
return -1;
*str = q + 1;
return r->reloc;
}
/* Directives: register aliases. */
static struct reg_entry *
insert_reg_alias (char *str, unsigned number, int type)
{
struct reg_entry *new_reg;
const char *name;
if ((new_reg = (struct reg_entry *) str_hash_find (arm_reg_hsh, str)) != 0)
{
if (new_reg->builtin)
as_warn (_("ignoring attempt to redefine built-in register '%s'"), str);
/* Only warn about a redefinition if it's not defined as the
same register. */
else if (new_reg->number != number || new_reg->type != type)
as_warn (_("ignoring redefinition of register alias '%s'"), str);
return NULL;
}
name = xstrdup (str);
new_reg = XNEW (struct reg_entry);
new_reg->name = name;
new_reg->number = number;
new_reg->type = type;
new_reg->builtin = false;
new_reg->neon = NULL;
str_hash_insert (arm_reg_hsh, name, new_reg, 0);
return new_reg;
}
static void
insert_neon_reg_alias (char *str, int number, int type,
struct neon_typed_alias *atype)
{
struct reg_entry *reg = insert_reg_alias (str, number, type);
if (!reg)
{
first_error (_("attempt to redefine typed alias"));
return;
}
if (atype)
{
reg->neon = XNEW (struct neon_typed_alias);
*reg->neon = *atype;
}
}
/* Look for the .req directive. This is of the form:
new_register_name .req existing_register_name
If we find one, or if it looks sufficiently like one that we want to
handle any error here, return TRUE. Otherwise return FALSE. */
static bool
create_register_alias (char * newname, char *p)
{
struct reg_entry *old;
char *oldname, *nbuf;
size_t nlen;
/* The input scrubber ensures that whitespace after the mnemonic is
collapsed to single spaces. */
oldname = p;
if (!startswith (oldname, " .req "))
return false;
oldname += 6;
if (*oldname == '\0')
return false;
old = (struct reg_entry *) str_hash_find (arm_reg_hsh, oldname);
if (!old)
{
as_warn (_("unknown register '%s' -- .req ignored"), oldname);
return true;
}
/* If TC_CASE_SENSITIVE is defined, then newname already points to
the desired alias name, and p points to its end. If not, then
the desired alias name is in the global original_case_string. */
#ifdef TC_CASE_SENSITIVE
nlen = p - newname;
#else
newname = original_case_string;
nlen = strlen (newname);
#endif
nbuf = xmemdup0 (newname, nlen);
/* Create aliases under the new name as stated; an all-lowercase
version of the new name; and an all-uppercase version of the new
name. */
if (insert_reg_alias (nbuf, old->number, old->type) != NULL)
{
for (p = nbuf; *p; p++)
*p = TOUPPER (*p);
if (strncmp (nbuf, newname, nlen))
{
/* If this attempt to create an additional alias fails, do not bother
trying to create the all-lower case alias. We will fail and issue
a second, duplicate error message. This situation arises when the
programmer does something like:
foo .req r0
Foo .req r1
The second .req creates the "Foo" alias but then fails to create
the artificial FOO alias because it has already been created by the
first .req. */
if (insert_reg_alias (nbuf, old->number, old->type) == NULL)
{
free (nbuf);
return true;
}
}
for (p = nbuf; *p; p++)
*p = TOLOWER (*p);
if (strncmp (nbuf, newname, nlen))
insert_reg_alias (nbuf, old->number, old->type);
}
free (nbuf);
return true;
}
/* Create a Neon typed/indexed register alias using directives, e.g.:
X .dn d5.s32[1]
Y .qn 6.s16
Z .dn d7
T .dn Z[0]
These typed registers can be used instead of the types specified after the
Neon mnemonic, so long as all operands given have types. Types can also be
specified directly, e.g.:
vadd d0.s32, d1.s32, d2.s32 */
static bool
create_neon_reg_alias (char *newname, char *p)
{
enum arm_reg_type basetype;
struct reg_entry *basereg;
struct reg_entry mybasereg;
struct neon_type ntype;
struct neon_typed_alias typeinfo;
char *namebuf, *nameend ATTRIBUTE_UNUSED;
int namelen;
typeinfo.defined = 0;
typeinfo.eltype.type = NT_invtype;
typeinfo.eltype.size = -1;
typeinfo.index = -1;
nameend = p;
if (startswith (p, " .dn "))
basetype = REG_TYPE_VFD;
else if (startswith (p, " .qn "))
basetype = REG_TYPE_NQ;
else
return false;
p += 5;
if (*p == '\0')
return false;
basereg = arm_reg_parse_multi (&p);
if (basereg && basereg->type != basetype)
{
as_bad (_("bad type for register"));
return false;
}
if (basereg == NULL)
{
expressionS exp;
/* 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;
}
basereg = &mybasereg;
basereg->number = (basetype == REG_TYPE_NQ) ? exp.X_add_number * 2
: exp.X_add_number;
basereg->neon = 0;
}
if (basereg->neon)
typeinfo = *basereg->neon;
if (parse_neon_type (&ntype, &p) == SUCCESS)
{
/* We got a type. */
if (typeinfo.defined & NTA_HASTYPE)
{
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;
}
typeinfo.eltype = ntype.el[0];
}
if (skip_past_char (&p, '[') == SUCCESS)
{
expressionS exp;
/* We got a scalar index. */
if (typeinfo.defined & NTA_HASINDEX)
{
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;
}
typeinfo.defined |= NTA_HASINDEX;
typeinfo.index = exp.X_add_number;
if (skip_past_char (&p, ']') == FAIL)
{
as_bad (_("expecting ]"));
return false;
}
}
/* If TC_CASE_SENSITIVE is defined, then newname already points to
the desired alias name, and p points to its end. If not, then
the desired alias name is in the global original_case_string. */
#ifdef TC_CASE_SENSITIVE
namelen = nameend - newname;
#else
newname = original_case_string;
namelen = strlen (newname);
#endif
namebuf = xmemdup0 (newname, namelen);
insert_neon_reg_alias (namebuf, basereg->number, basetype,
typeinfo.defined != 0 ? &typeinfo : NULL);
/* Insert name in all uppercase. */
for (p = namebuf; *p; p++)
*p = TOUPPER (*p);
if (strncmp (namebuf, newname, namelen))
insert_neon_reg_alias (namebuf, basereg->number, basetype,
typeinfo.defined != 0 ? &typeinfo : NULL);
/* Insert name in all lowercase. */
for (p = namebuf; *p; p++)
*p = TOLOWER (*p);
if (strncmp (namebuf, newname, namelen))
insert_neon_reg_alias (namebuf, basereg->number, basetype,
typeinfo.defined != 0 ? &typeinfo : NULL);
free (namebuf);
return true;
}
/* Should never be called, as .req goes between the alias and the
register name, not at the beginning of the line. */
static void
s_req (int a ATTRIBUTE_UNUSED)
{
as_bad (_("invalid syntax for .req directive"));
}
static void
s_dn (int a ATTRIBUTE_UNUSED)
{
as_bad (_("invalid syntax for .dn directive"));
}
static void
s_qn (int a ATTRIBUTE_UNUSED)
{
as_bad (_("invalid syntax for .qn directive"));
}
/* The .unreq directive deletes an alias which was previously defined
by .req. For example:
my_alias .req r11
.unreq my_alias */
static void
s_unreq (int a ATTRIBUTE_UNUSED)
{
char * name;
char saved_char;
name = input_line_pointer;
while (*input_line_pointer != 0
&& *input_line_pointer != ' '
&& *input_line_pointer != '\n')
++input_line_pointer;
saved_char = *input_line_pointer;
*input_line_pointer = 0;
if (!*name)
as_bad (_("invalid syntax for .unreq directive"));
else
{
struct reg_entry *reg
= (struct reg_entry *) str_hash_find (arm_reg_hsh, name);
if (!reg)
as_bad (_("unknown register alias '%s'"), name);
else if (reg->builtin)
as_warn (_("ignoring attempt to use .unreq on fixed register name: '%s'"),
name);
else
{
char * p;
char * nbuf;
str_hash_delete (arm_reg_hsh, name);
free ((char *) reg->name);
free (reg->neon);
free (reg);
/* Also locate the all upper case and all lower case versions.
Do not complain if we cannot find one or the other as it
was probably deleted above. */
nbuf = strdup (name);
for (p = nbuf; *p; p++)
*p = TOUPPER (*p);
reg = (struct reg_entry *) str_hash_find (arm_reg_hsh, nbuf);
if (reg)
{
str_hash_delete (arm_reg_hsh, nbuf);
free ((char *) reg->name);
free (reg->neon);
free (reg);
}
for (p = nbuf; *p; p++)
*p = TOLOWER (*p);
reg = (struct reg_entry *) str_hash_find (arm_reg_hsh, nbuf);
if (reg)
{
str_hash_delete (arm_reg_hsh, nbuf);
free ((char *) reg->name);
free (reg->neon);
free (reg);
}
free (nbuf);
}
}
*input_line_pointer = saved_char;
demand_empty_rest_of_line ();
}
/* Directives: Instruction set selection. */
#ifdef OBJ_ELF
/* This code is to handle mapping symbols as defined in the ARM ELF spec.
(See "Mapping symbols", section 4.5.5, ARM AAELF version 1.0).
Note that previously, $a and $t has type STT_FUNC (BSF_OBJECT flag),
and $d has type STT_OBJECT (BSF_OBJECT flag). Now all three are untyped. */
/* Create a new mapping symbol for the transition to STATE. */
static void
make_mapping_symbol (enum mstate state, valueT value, fragS *frag)
{
symbolS * symbolP;
const char * symname;
int type;
switch (state)
{
case MAP_DATA:
symname = "$d";
type = BSF_NO_FLAGS;
break;
case MAP_ARM:
symname = "$a";
type = BSF_NO_FLAGS;
break;
case MAP_THUMB:
symname = "$t";
type = BSF_NO_FLAGS;
break;
default:
abort ();
}
symbolP = symbol_new (symname, now_seg, frag, value);
symbol_get_bfdsym (symbolP)->flags |= type | BSF_LOCAL;
switch (state)
{
case MAP_ARM:
THUMB_SET_FUNC (symbolP, 0);
ARM_SET_THUMB (symbolP, 0);
ARM_SET_INTERWORK (symbolP, support_interwork);
break;
case MAP_THUMB:
THUMB_SET_FUNC (symbolP, 1);
ARM_SET_THUMB (symbolP, 1);
ARM_SET_INTERWORK (symbolP, support_interwork);
break;
case MAP_DATA:
default:
break;
}
/* Save the mapping symbols for future reference. Also check that
we do not place two mapping symbols at the same offset within a
frag. We'll handle overlap between frags in
check_mapping_symbols.
If .fill or other data filling directive generates zero sized data,
the mapping symbol for the following code will have the same value
as the one generated for the data filling directive. In this case,
we replace the old symbol with the new one at the same address. */
if (value == 0)
{
if (frag->tc_frag_data.first_map != NULL)
{
know (S_GET_VALUE (frag->tc_frag_data.first_map) == 0);
symbol_remove (frag->tc_frag_data.first_map, &symbol_rootP, &symbol_lastP);
}
frag->tc_frag_data.first_map = symbolP;
}
if (frag->tc_frag_data.last_map != NULL)
{
know (S_GET_VALUE (frag->tc_frag_data.last_map) <= S_GET_VALUE (symbolP));
if (S_GET_VALUE (frag->tc_frag_data.last_map) == S_GET_VALUE (symbolP))
symbol_remove (frag->tc_frag_data.last_map, &symbol_rootP, &symbol_lastP);
}
frag->tc_frag_data.last_map = symbolP;
}
/* We must sometimes convert a region marked as code to data during
code alignment, if an odd number of bytes have to be padded. The
code mapping symbol is pushed to an aligned address. */
static void
insert_data_mapping_symbol (enum mstate state,
valueT value, fragS *frag, offsetT bytes)
{
/* If there was already a mapping symbol, remove it. */
if (frag->tc_frag_data.last_map != NULL
&& S_GET_VALUE (frag->tc_frag_data.last_map) == frag->fr_address + value)
{
symbolS *symp = frag->tc_frag_data.last_map;
if (value == 0)
{
know (frag->tc_frag_data.first_map == symp);
frag->tc_frag_data.first_map = NULL;
}
frag->tc_frag_data.last_map = NULL;
symbol_remove (symp, &symbol_rootP, &symbol_lastP);
}
make_mapping_symbol (MAP_DATA, value, frag);
make_mapping_symbol (state, value + bytes, frag);
}
static void mapping_state_2 (enum mstate state, int max_chars);
/* Set the mapping state to STATE. Only call this when about to
emit some STATE bytes to the file. */
#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;
if (mapstate == state)
/* The mapping symbol has already been emitted.
There is nothing else to do. */
return;
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 involve 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);
}
/* Same as mapping_state, but MAX_CHARS bytes have already been
allocated. Put the mapping symbol that far back. */
static void
mapping_state_2 (enum mstate state, int max_chars)
{
enum mstate mapstate = seg_info (now_seg)->tc_segment_info_data.mapstate;
if (!SEG_NORMAL (now_seg))
return;
if (mapstate == state)
/* The mapping symbol has already been emitted.
There is nothing else to do. */
return;
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)
#endif
/* Find the real, Thumb encoded start of a Thumb function. */
#ifdef OBJ_COFF
static symbolS *
find_real_start (symbolS * symbolP)
{
char * real_start;
const char * name = S_GET_NAME (symbolP);
symbolS * new_target;
/* This definition must agree with the one in gcc/config/arm/thumb.c. */
#define STUB_NAME ".real_start_of"
if (name == NULL)
abort ();
/* The compiler may generate BL instructions to local labels because
it needs to perform a branch to a far away location. These labels
do not have a corresponding ".real_start_of" label. We check
both for S_IS_LOCAL and for a leading dot, to give a way to bypass
the ".real_start_of" convention for nonlocal branches. */
if (S_IS_LOCAL (symbolP) || name[0] == '.')
return symbolP;
real_start = concat (STUB_NAME, name, NULL);
new_target = symbol_find (real_start);
free (real_start);
if (new_target == NULL)
{
as_warn (_("Failed to find real start of function: %s\n"), name);
new_target = symbolP;
}
return new_target;
}
#endif
static void
opcode_select (int width)
{
switch (width)
{
case 16:
if (! thumb_mode)
{
if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v4t))
as_bad (_("selected processor does not support THUMB opcodes"));
thumb_mode = 1;
/* No need to force the alignment, since we will have been
coming from ARM mode, which is word-aligned. */
record_alignment (now_seg, 1);
}
break;
case 32:
if (thumb_mode)
{
if (!ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v1))
as_bad (_("selected processor does not support ARM opcodes"));
thumb_mode = 0;
if (!need_pass_2)
frag_align (2, 0, 0);
record_alignment (now_seg, 1);
}
break;
default:
as_bad (_("invalid instruction size selected (%d)"), width);
}
}
static void
s_arm (int ignore ATTRIBUTE_UNUSED)
{
opcode_select (32);
demand_empty_rest_of_line ();
}
static void
s_thumb (int ignore ATTRIBUTE_UNUSED)
{
opcode_select (16);
demand_empty_rest_of_line ();
}
static void
s_code (int unused ATTRIBUTE_UNUSED)
{
int temp;
temp = get_absolute_expression ();
switch (temp)
{
case 16:
case 32:
opcode_select (temp);
break;
default:
as_bad (_("invalid operand to .code directive (%d) (expecting 16 or 32)"), temp);
}
}
static void
s_force_thumb (int ignore ATTRIBUTE_UNUSED)
{
/* If we are not already in thumb mode go into it, EVEN if
the target processor does not support thumb instructions.
This is used by gcc/config/arm/lib1funcs.asm for example
to compile interworking support functions even if the
target processor should not support interworking. */
if (! thumb_mode)
{
thumb_mode = 2;
record_alignment (now_seg, 1);
}
demand_empty_rest_of_line ();
}
static void
s_thumb_func (int ignore ATTRIBUTE_UNUSED)
{
s_thumb (0);
/* The following label is the name/address of the start of a Thumb function.
We need to know this for the interworking support. */
label_is_thumb_function_name = true;
}
/* Perform a .set directive, but also mark the alias as
being a thumb function. */
static void
s_thumb_set (int equiv)
{
/* XXX the following is a duplicate of the code for s_set() in read.c
We cannot just call that code as we need to get at the symbol that
is created. */
char * name;
char delim;
char * end_name;
symbolS * symbolP;
/* Especial apologies for the random logic:
This just grew, and could be parsed much more simply!
Dean - in haste. */
delim = get_symbol_name (& name);
end_name = input_line_pointer;
(void) restore_line_pointer (delim);
if (*input_line_pointer != ',')
{
*end_name = 0;
as_bad (_("expected comma after name \"%s\""), name);
*end_name = delim;
ignore_rest_of_line ();
return;
}
input_line_pointer++;
*end_name = 0;
if (name[0] == '.' && name[1] == '\0')
{
/* XXX - this should not happen to .thumb_set. */
abort ();
}
if ((symbolP = symbol_find (name)) == NULL
&& (symbolP = md_undefined_symbol (name)) == NULL)
{
#ifndef NO_LISTING
/* When doing symbol listings, play games with dummy fragments living
outside the normal fragment chain to record the file and line info
for this symbol. */
if (listing & LISTING_SYMBOLS)
{
extern struct list_info_struct * listing_tail;
fragS * dummy_frag = (fragS * ) xmalloc (sizeof (fragS));
memset (dummy_frag, 0, sizeof (fragS));
dummy_frag->fr_type = rs_fill;
dummy_frag->line = listing_tail;
symbolP = symbol_new (name, undefined_section, dummy_frag, 0);
dummy_frag->fr_symbol = symbolP;
}
else
#endif
symbolP = symbol_new (name, undefined_section, &zero_address_frag, 0);
#ifdef OBJ_COFF
/* "set" symbols are local unless otherwise specified. */