/* tc-arm.c -- Assemble for the ARM
Copyright 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, 2003,
- 2004, 2005, 2006
+ 2004, 2005, 2006, 2007
Free Software Foundation, Inc.
Contributed by Richard Earnshaw (rwe@pegasus.esprit.ec.org)
Modified by David Taylor (dtaylor@armltd.co.uk)
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 2, or (at your option)
+ 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,
unsigned immisreg : 1; /* .imm field is a second register. */
unsigned isscalar : 1; /* Operand is a (Neon) 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. */
#define OPCODE_MASK 0xfe1fffff
#define V4_STR_BIT 0x00000020
+#define T2_SUBS_PC_LR 0xf3de8f00
+
#define DATA_OP_SHIFT 21
#define T2_OPCODE_MASK 0xfe1fffff
s_align (int unused ATTRIBUTE_UNUSED)
{
int temp;
+ bfd_boolean fill_p;
long temp_fill;
long max_alignment = 15;
{
input_line_pointer++;
temp_fill = get_absolute_expression ();
+ fill_p = TRUE;
}
else
- temp_fill = 0;
+ {
+ fill_p = FALSE;
+ temp_fill = 0;
+ }
if (!temp)
temp = 2;
/* Only make a frag if we HAVE to. */
if (temp && !need_pass_2)
- frag_align (temp, (int) temp_fill, 0);
+ {
+ 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_arm_eabi_attribute (int ignored ATTRIBUTE_UNUSED)
{
- expressionS exp;
- bfd_boolean is_string;
- int tag;
- unsigned int i = 0;
- char *s = NULL;
- char saved_char;
-
- expression (& exp);
- if (exp.X_op != O_constant)
- goto bad;
-
- tag = exp.X_add_number;
- if (tag == 4 || tag == 5 || tag == 32 || (tag > 32 && (tag & 1) != 0))
- is_string = 1;
- else
- is_string = 0;
-
- if (skip_past_comma (&input_line_pointer) == FAIL)
- goto bad;
- if (tag == 32 || !is_string)
- {
- expression (& exp);
- if (exp.X_op != O_constant)
- {
- as_bad (_("expected numeric constant"));
- ignore_rest_of_line ();
- return;
- }
- i = exp.X_add_number;
- }
- if (tag == Tag_compatibility
- && skip_past_comma (&input_line_pointer) == FAIL)
- {
- as_bad (_("expected comma"));
- ignore_rest_of_line ();
- return;
- }
- if (is_string)
- {
- skip_whitespace(input_line_pointer);
- if (*input_line_pointer != '"')
- goto bad_string;
- input_line_pointer++;
- s = input_line_pointer;
- while (*input_line_pointer && *input_line_pointer != '"')
- input_line_pointer++;
- if (*input_line_pointer != '"')
- goto bad_string;
- saved_char = *input_line_pointer;
- *input_line_pointer = 0;
- }
- else
- {
- s = NULL;
- saved_char = 0;
- }
-
- if (tag == Tag_compatibility)
- elf32_arm_add_eabi_attr_compat (stdoutput, i, s);
- else if (is_string)
- elf32_arm_add_eabi_attr_string (stdoutput, tag, s);
- else
- elf32_arm_add_eabi_attr_int (stdoutput, tag, i);
-
- if (s)
- {
- *input_line_pointer = saved_char;
- input_line_pointer++;
- }
- demand_empty_rest_of_line ();
- return;
-bad_string:
- as_bad (_("bad string constant"));
- ignore_rest_of_line ();
- return;
-bad:
- as_bad (_("expected <tag> , <value>"));
- ignore_rest_of_line ();
+ s_vendor_attribute (OBJ_ATTR_PROC);
}
#endif /* OBJ_ELF */
/* Parse an 8-bit "quarter-precision" floating point number of the form:
0baBbbbbbc defgh000 00000000 00000000.
- The minus-zero case needs special handling, since it can't be encoded in the
- "quarter-precision" float format, but can nonetheless be loaded as an integer
- constant. */
+ The zero and minus-zero cases need special handling, since they can't be
+ encoded in the "quarter-precision" float format, but can nonetheless be
+ loaded as integer constants. */
static unsigned
parse_qfloat_immediate (char **ccp, int *immed)
{
char *str = *ccp;
+ char *fpnum;
LITTLENUM_TYPE words[MAX_LITTLENUMS];
+ int found_fpchar = 0;
skip_past_char (&str, '#');
+ /* We must not accidentally parse an integer as a floating-point number. Make
+ sure that the value we parse is not an integer by checking for special
+ characters '.' or 'e'.
+ FIXME: This is a horrible hack, but doing better is tricky because type
+ information isn't in a very usable state at parse time. */
+ fpnum = str;
+ skip_whitespace (fpnum);
+
+ if (strncmp (fpnum, "0x", 2) == 0)
+ return FAIL;
+ else
+ {
+ for (; *fpnum != '\0' && *fpnum != ' ' && *fpnum != '\n'; fpnum++)
+ if (*fpnum == '.' || *fpnum == 'e' || *fpnum == 'E')
+ {
+ found_fpchar = 1;
+ break;
+ }
+
+ if (!found_fpchar)
+ return FAIL;
+ }
+
if ((str = atof_ieee (str, 's', words)) != NULL)
{
unsigned fpword = 0;
fpword |= words[i];
}
- if (is_quarter_float (fpword) || fpword == 0x80000000)
+ if (is_quarter_float (fpword) || (fpword & 0x7fffffff) == 0)
*immed = fpword;
else
return FAIL;
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> */
OP_oRR, /* ARM register */
OP_oRRnpc, /* ARM register, not the PC */
+ OP_oRRw, /* ARM register, not r15, optional trailing ! */
OP_oRND, /* Optional Neon double precision register */
OP_oRNQ, /* Optional Neon quad precision register */
OP_oRNDQ, /* Optional Neon double or quad precision register */
backtrack_index = i;
}
- if (i > 0)
+ if (i > 0 && (i > 1 || inst.operands[0].present))
po_char_or_fail (',');
switch (upat[i])
case OP_RVD: po_reg_or_fail (REG_TYPE_VFD); break;
case OP_oRND:
case OP_RND: po_reg_or_fail (REG_TYPE_VFD); break;
- case OP_RVC: po_reg_or_fail (REG_TYPE_VFC); break;
+ case OP_RVC:
+ po_reg_or_goto (REG_TYPE_VFC, coproc_reg);
+ break;
+ /* Also accept generic coprocessor regs for unknown registers. */
+ coproc_reg:
+ po_reg_or_fail (REG_TYPE_CN);
+ break;
case OP_RMF: po_reg_or_fail (REG_TYPE_MVF); break;
case OP_RMD: po_reg_or_fail (REG_TYPE_MVD); break;
case OP_RMFX: po_reg_or_fail (REG_TYPE_MVFX); break;
break;
case OP_RRw:
+ case OP_oRRw:
po_reg_or_fail (REG_TYPE_RN);
if (skip_past_char (&str, '!') == SUCCESS)
inst.operands[i].writeback = 1;
case OP_RRnpc:
case OP_RRnpcb:
case OP_RRw:
+ case OP_oRRw:
case OP_RRnpc_I0:
if (inst.operands[i].isreg && inst.operands[i].reg == REG_PC)
inst.error = BAD_PC;
static void
do_mlas (void)
{
- /* This restriction does not apply to mls (nor to mla in v6, but
- that's hard to detect at present). */
+ /* 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"));
+ 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
inst.instruction |= inst.operands[1].reg;
inst.instruction |= inst.operands[2].reg << 8;
- if (inst.operands[0].reg == inst.operands[1].reg)
- as_tsktsk (_("rd and rm should be different in mul"));
+ 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
inst.instruction |= inst.operands[2].reg << 8;
}
-/* ARM V6 srs (argument parse). */
+/* ARM V6 srs (argument parse). The variable fields in the encoding are
+ the same for both ARM and Thumb-2. */
static void
do_srs (void)
{
- inst.instruction |= inst.operands[0].imm;
- if (inst.operands[0].writeback)
+ int reg;
+
+ if (inst.operands[0].present)
+ {
+ reg = inst.operands[0].reg;
+ constraint (reg != 13, _("SRS base register must be r13"));
+ }
+ else
+ reg = 13;
+
+ inst.instruction |= reg << 16;
+ inst.instruction |= inst.operands[1].imm;
+ if (inst.operands[0].writeback || inst.operands[1].writeback)
inst.instruction |= WRITE_BACK;
}
if (inst.size_req == 4
|| (inst.size_req != 2 && !opcode))
{
- if (Rs == REG_PC)
+ if (Rd == REG_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.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 (load)
+ {
+ if (mask & (1 << 14)
+ && mask & (1 << 15))
+ inst.error = _("LR and PC should not both be in register list");
+
+ if ((mask & (1 << base)) != 0
+ && writeback)
+ as_warn (_("base register should not be in register list "
+ "when written back"));
+ }
+ else
+ {
+ if (mask & (1 << 15))
+ inst.error = _("PC not allowed in register list");
+
+ if (mask & (1 << base))
+ as_warn (_("value stored for r%d is UNPREDICTABLE"), base);
+ }
+
+ 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)
{
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[0].reg <= 7
- && !(inst.operands[1].imm & ~0xff)
- && (inst.instruction == T_MNEM_stmia
- ? inst.operands[0].writeback
- : (inst.operands[0].writeback
- == !(inst.operands[1].imm & (1 << inst.operands[0].reg)))))
+ && !(inst.operands[1].imm & ~0xff))
{
- if (inst.instruction == T_MNEM_stmia
- && (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 UNPREDICTABLE"),
- inst.operands[0].reg);
+ mask = 1 << inst.operands[0].reg;
- inst.instruction = THUMB_OP16 (inst.instruction);
- inst.instruction |= inst.operands[0].reg << 8;
- inst.instruction |= inst.operands[1].imm;
- }
- else
- {
- if (inst.operands[1].imm & (1 << 13))
- as_warn (_("SP should not be in register list"));
- if (inst.instruction == T_MNEM_stmia)
+ if (inst.operands[0].reg <= 7
+ && (inst.instruction == T_MNEM_stmia
+ ? inst.operands[0].writeback
+ : (inst.operands[0].writeback
+ == !(inst.operands[1].imm & mask))))
{
- if (inst.operands[1].imm & (1 << 15))
- as_warn (_("PC should not be in register list"));
- if (inst.operands[1].imm & (1 << inst.operands[0].reg))
+ 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 UNPREDICTABLE"),
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
+ else if (inst.operands[0] .reg == REG_SP
+ && inst.operands[0].writeback)
{
- if (inst.operands[1].imm & (1 << 14)
- && inst.operands[1].imm & (1 << 15))
- as_warn (_("LR and PC should not both be in register list"));
- if ((inst.operands[1].imm & (1 << inst.operands[0].reg))
- && inst.operands[0].writeback)
- as_warn (_("base register should not be in register list "
- "when written back"));
+ inst.instruction = THUMB_OP16 (inst.instruction == T_MNEM_stmia
+ ? T_MNEM_push : T_MNEM_pop);
+ inst.instruction |= inst.operands[1].imm;
+ narrow = TRUE;
}
+ }
+
+ if (!narrow)
+ {
if (inst.instruction < 0xffff)
inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= inst.operands[0].reg << 16;
- inst.instruction |= inst.operands[1].imm;
- if (inst.operands[0].writeback)
- inst.instruction |= WRITE_BACK;
+
+ 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)
|| 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
+ && inst.operands[0].reg == REG_PC
+ && inst.operands[1].reg == REG_LR)
+ {
+ inst.instruction = T2_SUBS_PC_LR;
+ return;
+ }
+
if (!inst.operands[1].isreg)
{
/* Immediate operand. */
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 (current_it_mask)
+ narrow = !flags;
+ else
+ narrow = flags;
+
+ if (inst.size_req == 4)
+ narrow = FALSE;
+
+ if (!low_regs || inst.operands[1].imm > 7)
+ narrow = FALSE;
+
+ if (inst.operands[0].reg != inst.operands[1].reg)
+ 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 |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].imm << 3;
+ }
+ else
+ {
+ if (flags)
+ inst.instruction |= CONDS_BIT;
+
+ inst.instruction |= inst.operands[0].reg << 8;
+ inst.instruction |= inst.operands[1].reg << 16;
+ inst.instruction |= inst.operands[1].imm;
+ }
+ }
else if (!narrow)
{
- inst.instruction = THUMB_OP32 (inst.instruction);
- inst.instruction |= inst.operands[0].reg << r0off;
- encode_thumb32_shifted_operand (1);
+ /* 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 (current_it_mask)
+ 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 |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_SHIFT;
+ }
+ else
+ {
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction |= inst.operands[0].reg << r0off;
+ encode_thumb32_shifted_operand (1);
+ }
}
else
switch (inst.instruction)
mask = inst.operands[0].imm;
if ((mask & ~0xff) == 0)
- inst.instruction = THUMB_OP16 (inst.instruction);
+ inst.instruction = THUMB_OP16 (inst.instruction) | mask;
else if ((inst.instruction == T_MNEM_push
&& (mask & ~0xff) == 1 << REG_LR)
|| (inst.instruction == T_MNEM_pop
{
inst.instruction = THUMB_OP16 (inst.instruction);
inst.instruction |= THUMB_PP_PC_LR;
- mask &= 0xff;
+ inst.instruction |= mask & 0xff;
}
else if (unified_syntax)
{
- if (mask & (1 << 13))
- inst.error = _("SP not allowed in register list");
- if (inst.instruction == T_MNEM_push)
- {
- if (mask & (1 << 15))
- inst.error = _("PC not allowed in register list");
- }
- else
- {
- if (mask & (1 << 14)
- && mask & (1 << 15))
- inst.error = _("LR and PC should not both be in register list");
- }
- if ((mask & (mask - 1)) == 0)
- {
- /* Single register push/pop implemented as str/ldr. */
- if (inst.instruction == T_MNEM_push)
- inst.instruction = 0xf84d0d04; /* str reg, [sp, #-4]! */
- else
- inst.instruction = 0xf85d0b04; /* ldr reg, [sp], #4 */
- mask = ffs(mask) - 1;
- mask <<= 12;
- }
- else
- inst.instruction = THUMB_OP32 (inst.instruction);
+ inst.instruction = THUMB_OP32 (inst.instruction);
+ encode_thumb2_ldmstm(13, mask, TRUE);
}
else
{
inst.error = _("invalid register list to push/pop instruction");
return;
}
-
- inst.instruction |= mask;
}
static void
inst.instruction |= Rs << 16;
if (!inst.operands[2].isreg)
{
- inst.instruction = (inst.instruction & 0xe1ffffff) | 0x10000000;
- inst.reloc.type = BFD_RELOC_ARM_T32_IMMEDIATE;
+ bfd_boolean narrow;
+
+ if ((inst.instruction & 0x00100000) != 0)
+ narrow = (current_it_mask == 0);
+ else
+ narrow = (current_it_mask != 0);
+
+ if (Rd > 7 || Rs > 7)
+ narrow = FALSE;
+
+ 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);
try smaller element sizes. */
static int
-neon_cmode_for_move_imm (unsigned immlo, unsigned immhi, unsigned *immbits,
- int *op, int size, enum neon_el_type type)
+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)
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;
+ int op, cmode, float_p;
constraint (et.type == NT_invtype,
_("operand size must be specified for immediate VMOV"));
constraint (et.size < 32 && (immlo & ~((1 << et.size) - 1)) != 0,
_("immediate has bits set outside the operand size"));
- if ((cmode = neon_cmode_for_move_imm (immlo, immhi, &immbits, &op,
+ 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. */
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, &immbits, &op,
- et.size, et.type)) == FAIL)
+ 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;
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;
+ constraint (imm >= (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;
return;
}
+ if (!ARM_CPU_HAS_FEATURE (variant, arm_ext_v6t2) && !inst.size_req)
+ {
+ /* Implicit require narrow instructions on Thumb-1. This avoids
+ relaxation accidentally introducing Thumb-2 instructions. */
+ if (opcode->tencode != do_t_blx && opcode->tencode != do_t_branch23)
+ inst.size_req = 2;
+ }
+
/* Check conditional suffixes. */
if (current_it_mask)
{
return;
}
}
+
+ /* Something has gone badly wrong if we try to relax a fixed size
+ instruction. */
+ 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
/* VFP control registers. */
REGDEF(fpsid,0,VFC), REGDEF(fpscr,1,VFC), REGDEF(fpexc,8,VFC),
REGDEF(FPSID,0,VFC), REGDEF(FPSCR,1,VFC), REGDEF(FPEXC,8,VFC),
+ REGDEF(fpinst,9,VFC), REGDEF(fpinst2,10,VFC),
+ REGDEF(FPINST,9,VFC), REGDEF(FPINST2,10,VFC),
+ REGDEF(mvfr0,7,VFC), REGDEF(mvfr1,6,VFC),
+ REGDEF(MVFR0,7,VFC), REGDEF(MVFR1,6,VFC),
/* Maverick DSP coprocessor registers. */
REGSET(mvf,MVF), REGSET(mvd,MVD), REGSET(mvfx,MVFX), REGSET(mvdx,MVDX),
/* Table of V7M psr names. */
static const struct asm_psr v7m_psrs[] =
{
- {"apsr", 0 },
- {"iapsr", 1 },
- {"eapsr", 2 },
- {"psr", 3 },
- {"ipsr", 5 },
- {"epsr", 6 },
- {"iepsr", 7 },
- {"msp", 8 },
- {"psp", 9 },
- {"primask", 16},
- {"basepri", 17},
- {"basepri_max", 18},
- {"faultmask", 19},
- {"control", 20}
+ {"apsr", 0 }, {"APSR", 0 },
+ {"iapsr", 1 }, {"IAPSR", 1 },
+ {"eapsr", 2 }, {"EAPSR", 2 },
+ {"psr", 3 }, {"PSR", 3 },
+ {"xpsr", 3 }, {"XPSR", 3 }, {"xPSR", 3 },
+ {"ipsr", 5 }, {"IPSR", 5 },
+ {"epsr", 6 }, {"EPSR", 6 },
+ {"iepsr", 7 }, {"IEPSR", 7 },
+ {"msp", 8 }, {"MSP", 8 },
+ {"psp", 9 }, {"PSP", 9 },
+ {"primask", 16}, {"PRIMASK", 16},
+ {"basepri", 17}, {"BASEPRI", 17},
+ {"basepri_max", 18}, {"BASEPRI_MAX", 18},
+ {"faultmask", 19}, {"FAULTMASK", 19},
+ {"control", 20}, {"CONTROL", 20}
};
/* Table of all shift-in-operand names. */
tCE(push, 92d0000, push, 1, (REGLST), push_pop, t_push_pop),
tCE(pop, 8bd0000, pop, 1, (REGLST), push_pop, t_push_pop),
+ /* These may simplify to neg. */
+ TCE(rsb, 0600000, ebc00000, 3, (RR, oRR, SH), arit, t_rsb),
+ TC3(rsbs, 0700000, ebd00000, 3, (RR, oRR, SH), arit, t_rsb),
+
#undef THUMB_VARIANT
#define THUMB_VARIANT &arm_ext_v6
TCE(cpy, 1a00000, 4600, 2, (RR, RR), rd_rm, t_cpy),
/* V1 instructions with no Thumb analogue prior to V6T2. */
#undef THUMB_VARIANT
#define THUMB_VARIANT &arm_ext_v6t2
- TCE(rsb, 0600000, ebc00000, 3, (RR, oRR, SH), arit, t_rsb),
- TC3(rsbs, 0700000, ebd00000, 3, (RR, oRR, SH), arit, t_rsb),
TCE(teq, 1300000, ea900f00, 2, (RR, SH), cmp, t_mvn_tst),
TC3w(teqs, 1300000, ea900f00, 2, (RR, SH), cmp, t_mvn_tst),
CL(teqp, 130f000, 2, (RR, SH), cmp),
#undef ARM_VARIANT
#define ARM_VARIANT &arm_ext_v5e /* ARM Architecture 5TE. */
TUF(pld, 450f000, f810f000, 1, (ADDR), pld, t_pld),
- TC3(ldrd, 00000d0, e9500000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd),
- TC3(strd, 00000f0, e9400000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd),
+ TC3(ldrd, 00000d0, e8500000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd),
+ TC3(strd, 00000f0, e8400000, 3, (RRnpc, oRRnpc, ADDRGLDRS), ldrd, t_ldstd),
TCE(mcrr, c400000, ec400000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
TCE(mrrc, c500000, ec500000, 5, (RCP, I15b, RRnpc, RRnpc, RCN), co_reg2c, co_reg2c),
#undef THUMB_VARIANT
#define THUMB_VARIANT &arm_ext_v6t2
TCE(ldrex, 1900f9f, e8500f00, 2, (RRnpc, ADDR), ldrex, t_ldrex),
+ TCE(strex, 1800f90, e8400000, 3, (RRnpc, RRnpc, ADDR), strex, t_strex),
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),
TCE(smuadx, 700f030, fb20f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
TCE(smusd, 700f050, fb40f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
TCE(smusdx, 700f070, fb40f010, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
- TUF(srsia, 8cd0500, e980c000, 1, (I31w), srs, srs),
- UF(srsib, 9cd0500, 1, (I31w), srs),
- UF(srsda, 84d0500, 1, (I31w), srs),
- TUF(srsdb, 94d0500, e800c000, 1, (I31w), srs, srs),
+ TUF(srsia, 8c00500, e980c000, 2, (oRRw, I31w), srs, srs),
+ UF(srsib, 9c00500, 2, (oRRw, I31w), srs),
+ UF(srsda, 8400500, 2, (oRRw, I31w), srs),
+ TUF(srsdb, 9400500, e800c000, 2, (oRRw, I31w), srs, srs),
TCE(ssat16, 6a00f30, f3200000, 3, (RRnpc, I16, RRnpc), ssat16, t_ssat16),
- TCE(strex, 1800f90, e8400000, 3, (RRnpc, RRnpc, ADDR), strex, t_strex),
TCE(umaal, 0400090, fbe00060, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smlal, t_mlal),
TCE(usad8, 780f010, fb70f000, 3, (RRnpc, RRnpc, RRnpc), smul, t_simd),
TCE(usada8, 7800010, fb700000, 4, (RRnpc, RRnpc, RRnpc, RRnpc),smla, t_mla),
nUF(vmull, vmull, 3, (RNQ, RND, RND_RNSC), neon_vmull),
/* Extract. Size 8. */
- NUF(vext, 0b00000, 4, (RNDQ, oRNDQ, RNDQ, I7), neon_ext),
- NUF(vextq, 0b00000, 4, (RNQ, oRNQ, RNQ, I7), neon_ext),
+ NUF(vext, 0b00000, 4, (RNDQ, oRNDQ, RNDQ, I15), neon_ext),
+ NUF(vextq, 0b00000, 4, (RNQ, oRNQ, RNQ, I15), neon_ext),
/* Two registers, miscellaneous. */
/* Reverse. Sizes 8 16 32 (must be < size in opcode). */
as_bad_where (fixP->fx_file, fixP->fx_line,
_("invalid literal constant: pool needs to be closer"));
else
- as_bad (_("bad immediate value for half-word offset (%ld)"),
+ as_bad (_("bad immediate value for 8-bit offset (%ld)"),
(long) value);
break;
}
case BFD_RELOC_THUMB_PCREL_BRANCH7: /* CBZ */
/* CBZ can only branch forward. */
- if (value & ~0x7e)
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("branch out of range"));
- if (fixP->fx_done || !seg->use_rela_p)
+ /* Attempts to use CBZ to branch to the next instruction
+ (which, strictly speaking, are prohibited) will be turned into
+ no-ops.
+
+ FIXME: It may be better to remove the instruction completely and
+ perform relaxation. */
+ if (value == -2)
{
newval = md_chars_to_number (buf, THUMB_SIZE);
- newval |= ((value & 0x3e) << 2) | ((value & 0x40) << 3);
+ newval = 0xbf00; /* NOP encoding T1 */
md_number_to_chars (buf, newval, THUMB_SIZE);
}
+ else
+ {
+ if (value & ~0x7e)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("branch out of range"));
+
+ if (fixP->fx_done || !seg->use_rela_p)
+ {
+ newval = md_chars_to_number (buf, THUMB_SIZE);
+ newval |= ((value & 0x3e) << 2) | ((value & 0x40) << 3);
+ md_number_to_chars (buf, newval, THUMB_SIZE);
+ }
+ }
break;
case BFD_RELOC_THUMB_PCREL_BRANCH9: /* Conditional branch. */
for (i = 0; p[i]; i++)
p[i] = TOUPPER (p[i]);
}
- elf32_arm_add_eabi_attr_string (stdoutput, 5, p);
+ bfd_elf_add_proc_attr_string (stdoutput, 5, p);
}
/* Tag_CPU_arch. */
- elf32_arm_add_eabi_attr_int (stdoutput, 6, arch);
+ bfd_elf_add_proc_attr_int (stdoutput, 6, arch);
/* Tag_CPU_arch_profile. */
if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7a))
- elf32_arm_add_eabi_attr_int (stdoutput, 7, 'A');
+ bfd_elf_add_proc_attr_int (stdoutput, 7, 'A');
else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7r))
- elf32_arm_add_eabi_attr_int (stdoutput, 7, 'R');
+ bfd_elf_add_proc_attr_int (stdoutput, 7, 'R');
else if (ARM_CPU_HAS_FEATURE (flags, arm_ext_v7m))
- elf32_arm_add_eabi_attr_int (stdoutput, 7, 'M');
+ bfd_elf_add_proc_attr_int (stdoutput, 7, 'M');
/* Tag_ARM_ISA_use. */
if (ARM_CPU_HAS_FEATURE (arm_arch_used, arm_arch_full))
- elf32_arm_add_eabi_attr_int (stdoutput, 8, 1);
+ bfd_elf_add_proc_attr_int (stdoutput, 8, 1);
/* Tag_THUMB_ISA_use. */
if (ARM_CPU_HAS_FEATURE (thumb_arch_used, arm_arch_full))
- elf32_arm_add_eabi_attr_int (stdoutput, 9,
+ bfd_elf_add_proc_attr_int (stdoutput, 9,
ARM_CPU_HAS_FEATURE (thumb_arch_used, arm_arch_t2) ? 2 : 1);
/* Tag_VFP_arch. */
if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_vfp_ext_v3)
|| ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_vfp_ext_v3))
- elf32_arm_add_eabi_attr_int (stdoutput, 10, 3);
+ bfd_elf_add_proc_attr_int (stdoutput, 10, 3);
else if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_vfp_ext_v2)
|| ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_vfp_ext_v2))
- elf32_arm_add_eabi_attr_int (stdoutput, 10, 2);
+ bfd_elf_add_proc_attr_int (stdoutput, 10, 2);
else if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_vfp_ext_v1)
|| ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_vfp_ext_v1)
|| ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_vfp_ext_v1xd)
|| ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_vfp_ext_v1xd))
- elf32_arm_add_eabi_attr_int (stdoutput, 10, 1);
+ bfd_elf_add_proc_attr_int (stdoutput, 10, 1);
/* Tag_WMMX_arch. */
if (ARM_CPU_HAS_FEATURE (thumb_arch_used, arm_cext_iwmmxt)
|| ARM_CPU_HAS_FEATURE (arm_arch_used, arm_cext_iwmmxt))
- elf32_arm_add_eabi_attr_int (stdoutput, 11, 1);
+ bfd_elf_add_proc_attr_int (stdoutput, 11, 1);
/* Tag_NEON_arch. */
if (ARM_CPU_HAS_FEATURE (thumb_arch_used, fpu_neon_ext_v1)
|| ARM_CPU_HAS_FEATURE (arm_arch_used, fpu_neon_ext_v1))
- elf32_arm_add_eabi_attr_int (stdoutput, 12, 1);
+ bfd_elf_add_proc_attr_int (stdoutput, 12, 1);
}
-/* Add the .ARM.attributes section. */
+/* Add the default contents for the .ARM.attributes section. */
void
arm_md_end (void)
{
- segT s;
- char *p;
- addressT addr;
- offsetT size;
-
if (EF_ARM_EABI_VERSION (meabi_flags) < EF_ARM_EABI_VER4)
return;
aeabi_set_public_attributes ();
- size = elf32_arm_eabi_attr_size (stdoutput);
- s = subseg_new (".ARM.attributes", 0);
- bfd_set_section_flags (stdoutput, s, SEC_READONLY | SEC_DATA);
- addr = frag_now_fix ();
- p = frag_more (size);
- elf32_arm_set_eabi_attr_contents (stdoutput, (bfd_byte *)p, size);
}
#endif /* OBJ_ELF */
ignore_rest_of_line ();
}
+/* Copy symbol information. */
+void
+arm_copy_symbol_attributes (symbolS *dest, symbolS *src)
+{
+ ARM_GET_FLAG (dest) = ARM_GET_FLAG (src);
+}