This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
Thumb32 assembler (48/69)
- From: Zack Weinberg <zack at codesourcery dot com>
- To: binutils <binutils at sourceware dot org>
- Date: Tue, 26 Apr 2005 02:55:58 -0700
- Subject: Thumb32 assembler (48/69)
This one's a big Twinkie - making all memory address parsing work via
parse_operands. There is now just one "address" operand code, and the
encoding functions are responsible for rejecting forms that don't work
with their particular instructions. And, alas, we lose a few more
diagnostic tests.
With this patch, there are no more encoding functions that do their
own parsing. Further patches will capitalize on that.
zw
* config/tc-arm.c (struct arm_it): Add shifted field to operands.
Expand shift_kind to 3 bits.
(validate_immediate): Rename encode_immediate_arm.
(validate_immediate_twopart, validate_offset_imm): Move next to sole
remaining callers, md_apply_fix3 and subroutines.
(char_or_fail, comma_or_fail, immediate_or_fail, reg_required_here)
(reg_or_fail, note_reg_or_fail, reg_nonpc_or_fail)
(note_reg_nonpc_or_fail, ldst_extend, ldst_extend_v4)
(ld_mode_required_here, parse_cp_address, parse_thumb_address)
Delete.
(reg_list, po_reg_or_fail): Use arm_reg_parse.
(parse_shift): Don't convert RRX to ROR here. Reorganize slightly.
(encode_shift_arm): Convert RRX to ROR here. Set inst.reloc here
for immediate shifts.
(parse_address, encode_addr_mode_common_arm, encode_addr_mode_2_arm)
(encode_addr_mode_3_arm, move_or_literal_pool)
: New functions.
(add_to_lit_pool): Don't compensate for pipeline offset here.
(OP_CADDR, OP_TADDR): Delete.
(parse_operands): Handle OP_ADDR, not CADDR or TADDR.
(do_ldrd, do_ldrex, do_ldst, do_ldstt, do_ldstv4, do_ldsttv4, do_pld):
Use parse_operands and the appropriate encode_addr_mode function.
(end_of_line): Fold into sole remaining caller, parse_operands.
(do_lstc, do_t_lds, do_t_ldst, do_vfp_sp_ldst, do_vfp_dp_ldst)
(do_fpa_ldst): Use ADDR, not CADDR or TADDR.
(negate_data_op, md_apply_fix3): Update for function renames.
(md_apply_fix3 <case BFD_RELOC_ARM_LITERAL>): Share code with
case BFD_RELOC_ARM_OFFSET_IMM.
* testsuite/gas/arm/armv1-bad.s: Remove more tests for out-of-range
immediates.
* testsuite/gas/arm/armv1-bad.l: Update to match.
===================================================================
Index: gas/testsuite/gas/arm/armv1-bad.l
--- gas/testsuite/gas/arm/armv1-bad.l (revision 50)
+++ gas/testsuite/gas/arm/armv1-bad.l (revision 51)
@@ -1,11 +1,9 @@
[^:]*: Assembler messages:
[^:]*:4: Error: invalid pseudo operation -- `str r0,=0x00ff0000'
[^:]*:5: Error: bad expression -- `ldr r0,{r1}'
-[^:]*:6: Error: address offset too large -- `ldr r0,\[r1,#4096\]'
-[^:]*:7: Error: address offset too large -- `ldr r0,\[r1,#-4096\]'
-[^:]*:8: Error: bad instruction `cmpl r0,r0'
-[^:]*:9: Error: selected processor does not support `strh r0,\[r1\]'
+[^:]*:6: Error: bad instruction `cmpl r0,r0'
+[^:]*:7: Error: selected processor does not support `strh r0,\[r1\]'
+[^:]*:8: Warning: writeback of base register is UNPREDICTABLE
+[^:]*:9: Warning: writeback of base register when in register list is UNPREDICTABLE
[^:]*:10: Warning: writeback of base register is UNPREDICTABLE
-[^:]*:11: Warning: writeback of base register when in register list is UNPREDICTABLE
-[^:]*:12: Warning: writeback of base register is UNPREDICTABLE
-[^:]*:14: Warning: if writeback register is in list, it must be the lowest reg in the list
+[^:]*:12: Warning: if writeback register is in list, it must be the lowest reg in the list
===================================================================
Index: gas/testsuite/gas/arm/armv1-bad.s
--- gas/testsuite/gas/arm/armv1-bad.s (revision 50)
+++ gas/testsuite/gas/arm/armv1-bad.s (revision 51)
@@ -3,8 +3,6 @@
entry:
str r0, =0x00ff0000
ldr r0, {r1}
- ldr r0, [r1, #4096]
- ldr r0, [r1, #-4096]
cmpl r0, r0
strh r0, [r1]
ldmfa r4!, {r8, r9}^
===================================================================
Index: gas/config/tc-arm.c
--- gas/config/tc-arm.c (revision 50)
+++ gas/config/tc-arm.c (revision 51)
@@ -196,14 +196,15 @@
unsigned reg;
unsigned imm;
unsigned present : 1; /* operand present */
- unsigned isreg : 1; /* operand was a register */
+ unsigned isreg : 1; /* operand was a register */
unsigned immisreg : 1; /* .imm field is a second 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 shift_kind : 2; /* shift operation (enum shift_kind, not RRX) */
+ unsigned shifted : 1; /* shift applied to operation */
+ unsigned shift_kind : 3; /* shift operation (enum shift_kind) */
} operands[6];
};
@@ -595,13 +596,6 @@
#define skip_whitespace(str) do { if (*(str) == ' ') ++(str); } while (0)
-static void
-end_of_line (char * str)
-{
- if (*str != '\0' && !inst.error)
- inst.error = _("garbage following instruction");
-}
-
static inline int
skip_past_char (char ** str, char c)
{
@@ -613,18 +607,7 @@
else
return FAIL;
}
-
-#define char_or_fail(str, c) \
-do { \
- if (skip_past_char (str, c) == FAIL) \
- { \
- inst.error = BAD_ARGS; \
- return; \
- } \
- } while (0)
-
#define skip_past_comma(str) skip_past_char (str, ',')
-#define comma_or_fail(str) char_or_fail(str, ',')
/* Arithmetic expressions (possibly involving symbols). */
@@ -953,12 +936,6 @@
return SUCCESS;
}
-#define immediate_or_fail(str, var, min, max, popt) \
-do { \
- if (immediate_required_here (str, var, min, max, popt) == FAIL) \
- return; \
- } while (0)
-
/* As above, but for use in directives, and does not provide
range checking. */
@@ -986,11 +963,11 @@
#define rotate_left(v, n) (v << n | v >> (32 - n))
-/* Check that an immediate is valid.
- If so, convert it to the right format. */
+/* If VAL can be encoded in the immediate field of an ARM instruction,
+ return the encoded form. Otherwise, return FAIL. */
static unsigned int
-validate_immediate (unsigned int val)
+encode_immediate_arm (unsigned int val)
{
unsigned int a;
unsigned int i;
@@ -1002,52 +979,6 @@
return FAIL;
}
-/* Check to see if an immediate can be computed as two separate immediate
- values, added together. We already know that this value cannot be
- computed by just one ARM instruction. */
-
-static unsigned int
-validate_immediate_twopart (unsigned int val,
- unsigned int * highpart)
-{
- unsigned int a;
- unsigned int i;
-
- for (i = 0; i < 32; i += 2)
- if (((a = rotate_left (val, i)) & 0xff) != 0)
- {
- if (a & 0xff00)
- {
- if (a & ~ 0xffff)
- continue;
- * highpart = (a >> 8) | ((i + 24) << 7);
- }
- else if (a & 0xff0000)
- {
- if (a & 0xff000000)
- continue;
- * highpart = (a >> 16) | ((i + 16) << 7);
- }
- else
- {
- assert (a & 0xff000000);
- * highpart = (a >> 24) | ((i + 8) << 7);
- }
-
- return (a & 0xff) | (i << 7);
- }
-
- return FAIL;
-}
-
-static int
-validate_offset_imm (unsigned int val, int hwse)
-{
- if ((hwse && val > 255) || val > 4095)
- return FAIL;
- return val;
-}
-
/* Register parsing. */
/* Generic register parser. CCP points to what should be the
@@ -1154,67 +1085,6 @@
}
}
-/* A register of type TYPE is expected at this point. SHIFT is the
- place to put it in inst.instruction. Returns the register number,
- or FAIL. */
-
-static int
-reg_required_here (char **str, int shift, enum arm_reg_type type)
-{
- int reg;
-
- if ((reg = arm_reg_parse (str, type)) == FAIL)
- {
- inst.error = gettext (reg_expected_msgs[type]);
- return FAIL;
- }
-
- if (shift >= 0)
- {
- /* VFP SP registers require special handling. */
- if (type == REG_TYPE_VFS)
- vfp_sp_encode_reg (reg, shift);
- else
- inst.instruction |= reg << shift;
- }
- return reg;
-}
-
-#define reg_or_fail(str, shift, type) \
-do { \
- if (reg_required_here (str, shift, type) == FAIL) \
- return; \
-} while (0)
-
-#define note_reg_or_fail(var, str, shift, type) \
-do { \
- if ((var = reg_required_here (str, shift, type)) == FAIL) \
- return; \
-} while (0)
-
-/* Convenience macros for the common case of needing an ARM register
- that isn't the PC. */
-#define reg_nonpc_or_fail(str, shift) \
-do { \
- int reg_; \
- note_reg_or_fail (reg_, str, shift, REG_TYPE_RN); \
- if (reg_ == REG_PC) \
- { \
- inst.error = BAD_PC; \
- return; \
- } \
-} while (0)
-
-#define note_reg_nonpc_or_fail(var, str, shift) \
-do { \
- note_reg_or_fail (var, str, shift, REG_TYPE_RN); \
- if (var == REG_PC) \
- { \
- inst.error = BAD_PC; \
- return; \
- } \
-} while (0)
-
/* Register lists. */
static long
@@ -1239,8 +1109,11 @@
{
int reg;
- if ((reg = reg_required_here (& str, -1, REG_TYPE_RN)) == FAIL)
- return FAIL;
+ if ((reg = arm_reg_parse (&str, REG_TYPE_RN)) == FAIL)
+ {
+ inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+ return FAIL;
+ }
if (in_range)
{
@@ -1546,31 +1419,22 @@
default: abort ();
}
- if (shift == SHIFT_RRX)
+ if (shift != SHIFT_RRX)
{
- inst.operands[i].shift_kind = SHIFT_ROR;
- *str = p;
- return SUCCESS;
- }
+ /* Whitespace can appear here if the next thing is a bare digit. */
+ skip_whitespace (p);
- /* Whitespace can appear here if the next thing is a bare digit. */
- skip_whitespace (p);
-
- if (mode == NO_SHIFT_RESTRICT
- && (reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
- {
- inst.operands[i].imm = reg;
- inst.operands[i].immisreg = 1;
- inst.operands[i].shift_kind = shift;
- *str = p;
- return SUCCESS;
+ if (mode == NO_SHIFT_RESTRICT
+ && (reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
+ {
+ inst.operands[i].imm = reg;
+ inst.operands[i].immisreg = 1;
+ }
+ else if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+ return FAIL;
}
- else if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
- return FAIL;
-
- inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
- inst.reloc.pc_rel = 0;
inst.operands[i].shift_kind = shift;
+ inst.operands[i].shifted = 1;
*str = p;
return SUCCESS;
}
@@ -1580,19 +1444,21 @@
static void
encode_shift_arm (int i)
{
- inst.instruction |= inst.operands[i].shift_kind << 5;
- if (inst.operands[i].immisreg)
+ if (inst.operands[i].shift_kind == SHIFT_RRX)
+ inst.instruction |= SHIFT_ROR << 5;
+ else
{
- inst.instruction |= SHIFT_BY_REG;
- inst.instruction |= inst.operands[i].imm << 8;
+ inst.instruction |= inst.operands[i].shift_kind << 5;
+ if (inst.operands[i].immisreg)
+ {
+ inst.instruction |= SHIFT_BY_REG;
+ inst.instruction |= inst.operands[i].imm << 8;
+ }
+ else
+ inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
}
}
-/* Non-register data operands: reg-or-immediate operands,
- relocation tags, load/store expressions. */
-
-
-
/* Parse a <shifter_operand> for an ARM data processing instruction:
#<immediate>
@@ -1701,221 +1567,178 @@
return r->reloc;
}
-static int
-ldst_extend (char ** str)
-{
- int add = INDEX_UP;
+/* Parse all forms of an ARM address expression. Information is written
+ to inst.operands[i] and/or inst.reloc.
- switch (**str)
- {
- case '#':
- case '$':
- (*str)++;
- if (my_get_expression (& inst.reloc.exp, str, GE_NO_PREFIX))
- return FAIL;
+ Preindexed addressing (.preind=1):
- if (inst.reloc.exp.X_op == O_constant)
- {
- int value = inst.reloc.exp.X_add_number;
+ [Rn, #offset] .reg=Rn .reloc.exp=offset
+ [Rn, +/-Rm] .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+ [Rn, +/-Rm, shift] .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+ .shift_kind=shift .reloc.exp=shift_imm
- if (value < -4095 || value > 4095)
- {
- inst.error = _("address offset too large");
- return FAIL;
- }
+ These three may have a trailing ! which causes .writeback to be set also.
- if (value < 0)
- {
- value = -value;
- add = 0;
- }
+ Postindexed addressing (.postind=1, .writeback=1):
- inst.instruction |= add | value;
- }
- else
- {
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
- inst.reloc.pc_rel = 0;
- }
- return SUCCESS;
+ [Rn], #offset .reg=Rn .reloc.exp=offset
+ [Rn], +/-Rm .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+ [Rn], +/-Rm, shift .reg=Rn .imm=Rm .immisreg=1 .negative=0/1
+ .shift_kind=shift .reloc.exp=shift_imm
- case '-':
- add = 0;
- /* Fall through. */
+ Unindexed addressing (.preind=0, .postind=0):
- case '+':
- (*str)++;
- /* Fall through. */
+ [Rn], {option} .reg=Rn .imm=option .immisreg=0
- default:
- if (reg_required_here (str, 0, REG_TYPE_RN) == FAIL)
- return FAIL;
+ Other:
- inst.instruction |= add | OFFSET_REG;
- if (skip_past_comma (str) == SUCCESS)
+ [Rn]{!} shorthand for [Rn,#0]{!}
+ =immediate .isreg=0 .reloc.exp=immediate
+ label .reg=PC .reloc.pc_rel=1 .reloc.exp=label
+
+ It is the caller's responsibility to check for addressing modes not
+ supported by the instruction, and to set inst.reloc.type. */
+
+static int
+parse_address (char **str, int i)
+{
+ char *p = *str;
+ int reg;
+
+ if (skip_past_char (&p, '[') == FAIL)
+ {
+ if (skip_past_char (&p, '=') == FAIL)
{
- if (parse_shift (str, 5, SHIFT_IMMEDIATE)) /* XXX Kludge */
- return FAIL;
- encode_shift_arm (5);
+ /* bare address - translate to PC-relative offset */
+ inst.reloc.pc_rel = 1;
+ inst.operands[i].reg = REG_PC;
+ inst.operands[i].isreg = 1;
+ inst.operands[i].preind = 1;
}
+ /* else a load-constant pseudo op, no special treatment needed here */
+
+ if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
+ return FAIL;
+
+ *str = p;
return SUCCESS;
}
-}
-static int
-ldst_extend_v4 (char ** str)
-{
- int add = INDEX_UP;
+ if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) == FAIL)
+ {
+ inst.error = _(reg_expected_msgs[REG_TYPE_RN]);
+ return FAIL;
+ }
+ inst.operands[i].reg = reg;
+ inst.operands[i].isreg = 1;
- switch (**str)
+ if (skip_past_comma (&p) == SUCCESS)
{
- case '#':
- case '$':
- (*str)++;
- if (my_get_expression (& inst.reloc.exp, str, GE_NO_PREFIX))
- return FAIL;
+ inst.operands[i].preind = 1;
- if (inst.reloc.exp.X_op == O_constant)
+ if (*p == '+') p++;
+ else if (*p == '-') p++, inst.operands[i].negative = 1;
+
+ if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
{
- int value = inst.reloc.exp.X_add_number;
+ inst.operands[i].imm = reg;
+ inst.operands[i].immisreg = 1;
- if (value < -255 || value > 255)
- {
- inst.error = _("address offset too large");
+ if (skip_past_comma (&p) == SUCCESS)
+ if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
return FAIL;
- }
-
- if (value < 0)
- {
- value = -value;
- add = 0;
- }
-
- /* Halfword and signextension instructions have the
- immediate value split across bits 11..8 and bits 3..0. */
- inst.instruction |= (add | HWOFFSET_IMM
- | ((value >> 4) << 8) | (value & 0xF));
}
else
{
- inst.instruction |= HWOFFSET_IMM;
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
- inst.reloc.pc_rel = 0;
+ if (inst.operands[i].negative)
+ {
+ inst.operands[i].negative = 0;
+ p--;
+ }
+ if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+ return FAIL;
}
- return SUCCESS;
-
- case '-':
- add = 0;
- /* Fall through. */
-
- case '+':
- (*str)++;
- /* Fall through. */
-
- default:
- if (reg_required_here (str, 0, REG_TYPE_RN) == FAIL)
- return FAIL;
-
- inst.instruction |= add;
- return SUCCESS;
}
-}
-/* Expects **str -> after a comma. May be leading blanks.
- Advances *str, recognizing a load mode, and setting inst.instruction.
- Returns rn, or else FAIL (in which case may set inst.error
- and not advance str)
-
- Note: doesn't know Rd, so no err checks that require such knowledge. */
-
-static int
-ld_mode_required_here (char ** string)
-{
- char * str = * string;
- int rn;
- int pre_inc = 0;
-
- if (* str == '[')
+ if (skip_past_char (&p, ']') == FAIL)
{
- str++;
+ inst.error = _("']' expected");
+ return FAIL;
+ }
- if ((rn = reg_required_here (&str, 16, REG_TYPE_RN)) == FAIL)
- return FAIL;
+ if (skip_past_char (&p, '!') == SUCCESS)
+ inst.operands[i].writeback = 1;
- if (* str == ']')
+ else if (skip_past_comma (&p) == SUCCESS)
+ {
+ if (skip_past_char (&p, '{') == SUCCESS)
{
- str ++;
+ /* [Rn], {expr} - unindexed, with option */
+ if (immediate_required_here (&p, &inst.operands[i].imm,
+ 0, 255, TRUE) == FAIL)
+ return FAIL;
- if (skip_past_comma (& str) == SUCCESS)
+ if (skip_past_char (&p, '}') == FAIL)
{
- /* [Rn],... (post inc) */
- if (ldst_extend_v4 (&str) == FAIL)
- return FAIL;
+ inst.error = _("'}' expected at end of 'option' field");
+ return FAIL;
}
- else /* [Rn] */
+ if (inst.operands[i].preind)
{
- if (* str == '!')
- {
- str ++;
- inst.instruction |= WRITE_BACK;
- }
-
- inst.instruction |= INDEX_UP | HWOFFSET_IMM;
- pre_inc = 1;
+ inst.error = _("cannot combine index with option");
+ return FAIL;
}
+ *str = p;
+ return SUCCESS;
}
- else /* [Rn,...] */
+ else
{
- if (skip_past_comma (& str) == FAIL)
+ inst.operands[i].postind = 1;
+ inst.operands[i].writeback = 1;
+
+ if (inst.operands[i].preind)
{
- inst.error = _("pre-indexed expression expected");
+ inst.error = _("cannot combine pre- and post-indexing");
return FAIL;
}
- pre_inc = 1;
+ if (*p == '+') p++;
+ else if (*p == '-') p++, inst.operands[i].negative = 1;
- if (ldst_extend_v4 (&str) == FAIL)
- return FAIL;
-
- if (* str ++ != ']')
+ if ((reg = arm_reg_parse (&p, REG_TYPE_RN)) != FAIL)
{
- inst.error = _("missing ]");
- return FAIL;
- }
+ inst.operands[i].imm = reg;
+ inst.operands[i].immisreg = 1;
- if (* str == '!')
+ if (skip_past_comma (&p) == SUCCESS)
+ if (parse_shift (&p, i, SHIFT_IMMEDIATE) == FAIL)
+ return FAIL;
+ }
+ else
{
- str ++;
- inst.instruction |= WRITE_BACK;
+ if (inst.operands[i].negative)
+ {
+ inst.operands[i].negative = 0;
+ p--;
+ }
+ if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
+ return FAIL;
}
}
}
- else if (* str == '=') /* ldr's "r,=label" syntax */
- /* We should never reach here, because <text> = <expression> is
- caught gas/read.c read_a_source_file() as a .set operation. */
- return FAIL;
- else /* PC +- 8 bit immediate offset. */
- {
- if (my_get_expression (& inst.reloc.exp, & str, GE_NO_PREFIX))
- return FAIL;
- inst.instruction |= HWOFFSET_IMM; /* The I bit. */
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
- inst.reloc.exp.X_add_number -= 8; /* PC rel adjust. */
- inst.reloc.pc_rel = 1;
- inst.instruction |= (REG_PC << 16);
-
- rn = REG_PC;
- pre_inc = 1;
+ /* If at this point neither .preind nor .postind is set, we have a
+ bare [Rn]{!}, which is shorthand for [Rn,#0]{!}. */
+ if (inst.operands[i].preind == 0 && inst.operands[i].postind == 0)
+ {
+ inst.operands[i].preind = 1;
+ inst.reloc.exp.X_op = O_constant;
+ inst.reloc.exp.X_add_number = 0;
}
-
- inst.instruction |= (pre_inc ? PRE_INDEX : 0);
- * string = str;
-
- return rn;
+ *str = p;
+ return SUCCESS;
}
-
/* Miscellaneous: PSR flags, endian specifiers, coprocessor
operands. */
@@ -1976,111 +1799,118 @@
return FAIL;
}
-/* Parse a coprocessor address expression:
-
- [Rl]{!} shorthand for [Rl,#0]{!}
- [Rl,offset]{!} +-8 bits, *4 (usually) - preindex, opt. writeback
- [Rl],offset +-8 bits, *4 (usually) - postindex
- [Rl],\{number\} 8 bits - no relocation - "unindexed" (magic)
- label translated to [PC, offset]
-
- Data is written to inst.operands[i] and/or inst.reloc.exp.
- Returns SUCCESS or FAIL. Does not check for no-writeback / no-unindex. */
-
-static int
-parse_cp_address (char **str, int i)
+/* Subroutine of encode_addr_mode_2_arm and encode_addr_mode_3_arm. */
+static void
+encode_addr_mode_common_arm (int i, bfd_boolean is_t)
{
- char *p = *str;
- int reg;
-
- if (skip_past_char (&p, '[') == FAIL)
- { /* bare address - translate to PC-relative offset */
- if (my_get_expression (&inst.reloc.exp, &p, GE_NO_PREFIX))
- return FAIL;
-
- inst.reloc.pc_rel = 1;
- inst.reloc.exp.X_add_number -= 8; /* PC rel adjust. */
- inst.operands[i].reg = REG_PC;
- inst.operands[i].preind = 1;
- *str = p;
- return SUCCESS;
- }
-
- if ((reg = reg_required_here (&p, -1, REG_TYPE_RN)) == FAIL)
- return FAIL;
- inst.operands[i].reg = reg;
-
- if (skip_past_char (&p, ']') == SUCCESS)
+ assert (inst.operands[i].isreg);
+ inst.instruction |= inst.operands[i].reg << 16;
+
+ if (inst.operands[i].preind)
{
- if (*p == '\0' || *p == '!') /* [Rn]{!} - shorthand for [Rn,#0]{!} */
+ if (is_t)
{
- inst.operands[i].preind = 1;
- inst.operands[i].writeback = (*p == '!');
- inst.reloc.exp.X_op = O_constant;
- inst.reloc.exp.X_add_number = 0;
- *str = p + (*p == '!');
- return SUCCESS;
+ inst.error = _("instruction does not accept preindexed addressing");
+ return;
}
+ inst.instruction |= PRE_INDEX;
+ if (inst.operands[i].writeback)
+ inst.instruction |= WRITE_BACK;
+
+ }
+ else if (inst.operands[i].postind)
+ {
+ assert (inst.operands[i].writeback);
+ if (is_t)
+ inst.instruction |= WRITE_BACK;
+ }
+ else /* unindexed - only for coprocessor */
+ {
+ inst.error = _("instruction does not accept unindexed addressing");
+ return;
+ }
+
+ if (((inst.instruction & WRITE_BACK) || !(inst.instruction & PRE_INDEX))
+ && (((inst.instruction & 0x000f0000) >> 16)
+ == ((inst.instruction & 0x0000f000) >> 12)))
+ as_warn ((inst.instruction & LOAD_BIT)
+ ? _("destination register same as write-back base")
+ : _("source register same as write-back base"));
+}
- if (skip_past_comma (&p) == FAIL)
- {
- inst.error = _("comma expected after closing square bracket");
- return FAIL;
- }
+/* inst.operands[i] was set up by parse_address. Encode it into an
+ ARM-format mode 2 load or store instruction. If is_t is true,
+ reject forms that cannot be used with a T instruction (i.e. not
+ post-indexed). */
+static void
+encode_addr_mode_2_arm (int i, bfd_boolean is_t)
+{
+ encode_addr_mode_common_arm (i, is_t);
- if (skip_past_char (&p, '{') == SUCCESS)
+ if (inst.operands[i].immisreg)
+ {
+ inst.instruction |= INST_IMMEDIATE; /* yes, this is backwards */
+ inst.instruction |= inst.operands[i].imm << 0;
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
+ if (inst.operands[i].shifted)
{
- /* [Rn], {expr} - unindexed, with option */
- if (immediate_required_here (&p, &inst.operands[i].imm,
- 0, 255, TRUE) == FAIL)
- return FAIL;
-
- if (skip_past_char (&p, '}') == FAIL)
+ if (inst.operands[i].shift_kind == SHIFT_RRX)
+ inst.instruction |= SHIFT_ROR << 5;
+ else
{
- inst.error = _("'}' expected at end of 'option' field");
- return FAIL;
+ inst.instruction |= inst.operands[i].shift_kind << 5;
+ inst.reloc.type = BFD_RELOC_ARM_SHIFT_IMM;
}
}
- else
- {
- if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
- return FAIL;
-
- inst.operands[i].postind = 1;
- inst.operands[i].writeback = 1;
- }
}
- else
+ else /* immediate offset in inst.reloc */
{
- /* '['Rn, #expr']'[!] */
- if (skip_past_comma (&p) == FAIL)
- {
- inst.error = _("pre-indexed expression expected");
- return FAIL;
- }
+ if (inst.reloc.type == BFD_RELOC_UNUSED)
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
+ if (inst.reloc.pc_rel)
+ inst.reloc.exp.X_add_number -= 8; /* pipeline offset */
+ }
+}
- if (my_get_expression (&inst.reloc.exp, &p, GE_IMM_PREFIX))
- return FAIL;
+/* inst.operands[i] was set up by parse_address. Encode it into an
+ ARM-format mode 3 load or store instruction. Reject forms that
+ cannot be used with such instructions. If is_t is true, reject
+ forms that cannot be used with a T instruction (i.e. not
+ post-indexed). */
+static void
+encode_addr_mode_3_arm (int i, bfd_boolean is_t)
+{
+ if (inst.operands[i].immisreg && inst.operands[i].shifted)
+ {
+ inst.error = _("instruction does not accept scaled register index");
+ return;
+ }
+
+ encode_addr_mode_common_arm (i, is_t);
- if (skip_past_char (&p, ']') == FAIL)
- {
- inst.error = _("missing ]");
- return FAIL;
- }
-
- if (skip_past_char (&p, '!') == SUCCESS)
- inst.operands[i].writeback = 1;
-
- inst.operands[i].preind = 1;
+ if (inst.operands[i].immisreg)
+ {
+ inst.instruction |= inst.operands[i].imm << 0;
+ if (!inst.operands[i].negative)
+ inst.instruction |= INDEX_UP;
}
- *str = p;
- return SUCCESS;
+ else /* immediate offset in inst.reloc */
+ {
+ inst.instruction |= HWOFFSET_IMM;
+ if (inst.reloc.type == BFD_RELOC_UNUSED)
+ inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
+ if (inst.reloc.pc_rel)
+ inst.reloc.exp.X_add_number -= 8; /* pipeline offset */
+ }
}
-/* inst.operands[i] was set up by parse_cp_address. Encode it into an
- ARM-format instruction. If wb_ok is false, reject use of writeback;
- if unind_ok is false, reject use of unindexed addressing. If
- reloc_override is not 0, use it instead of BFD_ARM_CP_OFF_IMM. */
+/* inst.operands[i] was set up by parse_address. Encode it into an
+ ARM-format instruction. Reject all forms which cannot be encoded
+ into a coprocessor load/store instruction. If wb_ok is false,
+ reject use of writeback; if unind_ok is false, reject use of
+ unindexed addressing. If reloc_override is not 0, use it instead
+ of BFD_ARM_CP_OFF_IMM. */
static int
encode_cp_address_arm (int i, int wb_ok, int unind_ok, int reloc_override)
@@ -2124,97 +1954,11 @@
inst.reloc.type = reloc_override;
else
inst.reloc.type = BFD_RELOC_ARM_CP_OFF_IMM;
+ if (inst.reloc.pc_rel)
+ inst.reloc.exp.X_add_number -= 8;
return SUCCESS;
}
-/* Parse a Thumb address expression:
-
- [Rl]
- [Rl, Rl]
- [Rl, #imm] ; 5 bits, *4
- [SP/PC, #imm] ; 8 bits, *4
- =imm ; pseudo (mov or lit-pool load)
- label ; translated to [PC, offset]
-
- Data is written to inst.operands[i] and/or inst.reloc.exp.
- Returns SUCCESS or FAIL. */
-
-static int
-parse_thumb_address (char **str, int i)
-{
- char *s = *str;
- int Rb, Ro;
-
- if (*s == '[')
- {
- s++;
- if ((Rb = arm_reg_parse (&s, REG_TYPE_RN)) == FAIL)
- return FAIL;
- if (Rb > 7 && Rb != REG_SP && Rb != REG_PC)
- {
- inst.error = BAD_HIREG;
- return FAIL;
- }
- inst.operands[i].reg = Rb;
- inst.operands[i].isreg = 1;
- if (skip_past_comma (&s) != FAIL)
- {
- if ((Ro = arm_reg_parse (&s, REG_TYPE_RN)) != FAIL)
- {
- if (Ro > 7 || Rb > 7)
- {
- inst.error = BAD_HIREG;
- return FAIL;
- }
- inst.operands[i].imm = Ro;
- inst.operands[i].immisreg = 1;
- }
- else if (my_get_expression (&inst.reloc.exp, &s, GE_IMM_PREFIX))
- return FAIL;
- }
- else
- {
- /* [Rd] == [Rd,#0] */
- inst.reloc.exp.X_op = O_constant;
- inst.reloc.exp.X_add_number = 0;
- }
- if (*s++ != ']')
- {
- inst.error = BAD_ARGS;
- return FAIL;
- }
- }
- else if (*s == '=')
- {
- s++;
- if (my_get_expression (&inst.reloc.exp, &s, GE_NO_PREFIX))
- return FAIL;
-
- if ( inst.reloc.exp.X_op != O_constant
- && inst.reloc.exp.X_op != O_symbol)
- {
- inst.error = "Constant expression expected";
- return FAIL;
- }
- }
- else
- {
- if (my_get_expression (&inst.reloc.exp, &s, GE_NO_PREFIX))
- return FAIL;
-
- inst.reloc.exp.X_add_number -= 4; /* Pipeline offset. */
- if (inst.reloc.exp.X_op != O_constant)
- inst.reloc.pc_rel = 1;
- inst.operands[i].reg = REG_PC;
- inst.operands[i].isreg = 1;
- }
-
- if (!inst.operands[i].immisreg)
- inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
- *str = s;
- return SUCCESS;
-}
-
/* Parse the flags argument to CPSI[ED]. Returns FAIL on error, or a
value suitable for splatting into the AIF field of the instruction. */
@@ -3000,12 +2744,87 @@
}
inst.reloc.exp.X_op = O_symbol;
- inst.reloc.exp.X_add_number = ((int) entry) * 4 - 8;
+ inst.reloc.exp.X_add_number = ((int) entry) * 4;
inst.reloc.exp.X_add_symbol = pool->symbol;
return SUCCESS;
}
+/* inst.reloc.exp describes an "=expr" load pseudo-operation.
+ Determine whether it can be performed with a move instruction; if
+ it can, convert inst.instruction to that move instruction and
+ return 1; if it can't, convert inst.instruction to a literal-pool
+ load and return 0. If this is not a valid thing to do in the
+ current context, set inst.error and return 1.
+
+ inst.operands[i] describes the destination register. */
+
+static int
+move_or_literal_pool (int i, bfd_boolean thumb_p, bfd_boolean mode_3)
+{
+ if ((inst.instruction & (thumb_p ? THUMB_LOAD_BIT : LOAD_BIT)) == 0)
+ {
+ inst.error = _("invalid pseudo operation");
+ return 1;
+ }
+ if (inst.reloc.exp.X_op != O_constant && inst.reloc.exp.X_op != O_symbol)
+ {
+ inst.error = _("constant expression expected");
+ return 1;
+ }
+ if (inst.reloc.exp.X_op == O_constant)
+ {
+ if (thumb_p)
+ {
+ if ((inst.reloc.exp.X_add_number & ~0xFF) == 0)
+ {
+ /* This can be done with a mov instruction. */
+ inst.instruction = T_OPCODE_MOV_I8 | (inst.operands[i].reg << 8);
+ inst.instruction |= inst.reloc.exp.X_add_number;
+ return 1;
+ }
+ }
+ else
+ {
+ int value = encode_immediate_arm (inst.reloc.exp.X_add_number);
+ if (value != FAIL)
+ {
+ /* This can be done with a mov instruction. */
+ inst.instruction &= LITERAL_MASK;
+ inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
+ inst.instruction |= value & 0xfff;
+ return 1;
+ }
+
+ value = encode_immediate_arm (~inst.reloc.exp.X_add_number);
+ if (value != FAIL)
+ {
+ /* This can be done with a mvn instruction. */
+ inst.instruction &= LITERAL_MASK;
+ inst.instruction |= INST_IMMEDIATE | (OPCODE_MVN << DATA_OP_SHIFT);
+ inst.instruction |= value & 0xfff;
+ return 1;
+ }
+ }
+ }
+
+ if (add_to_lit_pool () == FAIL)
+ {
+ inst.error = _("literal pool insertion failed");
+ return 1;
+ }
+ inst.operands[1].reg = REG_PC;
+ inst.operands[1].isreg = 1;
+ inst.operands[1].preind = 1;
+ inst.reloc.pc_rel = 1;
+ inst.reloc.type = (thumb_p
+ ? BFD_RELOC_ARM_THUMB_OFFSET
+ : (mode_3
+ ? BFD_RELOC_ARM_HWLITERAL
+ : BFD_RELOC_ARM_LITERAL));
+ return 0;
+}
+
/* Can't use symbol_new here, so have to create a symbol and then at
a later date assign it a value. Thats what these functions do. */
@@ -4043,9 +3862,7 @@
#define OP_VRSLST 064 /* VFP single-precision register list */
#define OP_VRDLST 065 /* VFP double-precision register list */
-#define OP_TADDR 070 /* Thumb memory address expression */
-#define OP_CADDR 071 /* Co-processor memory address expression */
-#define OP_ADDR 072 /* ARM memory address expression (mode 2 or 3) */
+#define OP_ADDR 072 /* Memory address expression (any mode) */
#define OP_SHOP 073 /* shifter_operand */
/* This-or-that operands. All have bit 7 set. */
@@ -4111,7 +3928,7 @@
} while (0)
#define po_reg_or_fail(regtype) do { \
- val = reg_required_here (&str, -1, regtype); \
+ val = arm_reg_parse (&str, regtype); \
if (val == FAIL) \
{ \
inst.error = _(reg_expected_msgs[regtype]); \
@@ -4328,16 +4145,11 @@
break;
/* Addressing modes */
- case OP_(TADDR):
- if (parse_thumb_address (&str, i))
+ case OP_(ADDR):
+ if (parse_address (&str, i))
return FAIL;
break;
- case OP_(CADDR):
- if (parse_cp_address (&str, i))
- return FAIL;
- break;
-
case OP_(SHOP):
if (parse_shifter_operand (&str, i))
return FAIL;
@@ -4416,7 +4228,10 @@
}
done:
- end_of_line (str);
+ /* Check that we have parsed all the arguments. */
+ if (*str != '\0' && !inst.error)
+ inst.error = _("garbage following instruction");
+
return inst.error ? FAIL : SUCCESS;
bad_args:
@@ -4866,50 +4681,47 @@
static void
do_ldrd (char * str)
{
- int rd;
- int rn;
+ if (parse_operands (str, OPERANDS2(RR,ADDR)))
+ return;
- note_reg_or_fail (rd, &str, 12, REG_TYPE_RN);
-
- comma_or_fail (&str);
-
- if ((rn = ld_mode_required_here (& str)) == FAIL)
+ if (!inst.operands[1].isreg)
{
- if (!inst.error)
- inst.error = BAD_ARGS;
+ inst.error = _("'[' expected");
return;
}
- /* inst.instruction has now been zapped with Rd and the addressing mode. */
- if (rd & 1) /* Unpredictable result if Rd is odd. */
+ if (inst.operands[0].reg % 2 != 0)
{
inst.error = _("destination register must be even");
return;
}
-
- if (rd == REG_LR)
+ if (inst.operands[0].reg == REG_LR)
{
inst.error = _("r14 not allowed here");
return;
}
- if (((rd == rn) || (rd + 1 == rn))
- && ((inst.instruction & WRITE_BACK)
- || (!(inst.instruction & PRE_INDEX))))
- as_warn (_("pre/post-indexing used when modified address register is destination"));
-
- /* For an index-register load, the index register must not overlap the
- destination (even if not write-back). */
- if ((inst.instruction & V4_STR_BIT) == 0
- && (inst.instruction & HWOFFSET_IMM) == 0)
+ if (inst.instruction & LOAD_BIT)
{
- int rm = inst.instruction & 0x0000000f;
+ /* encode_addr_mode_3_arm will diagnose overlap between the base
+ register and the first register written; we have to diagnose
+ overlap between the base and the second register written here. */
- if (rm == rd || (rm == rd + 1))
- as_warn (_("ldrd destination registers must not overlap index register"));
+ if ((inst.operands[1].reg == inst.operands[0].reg + 1)
+ && (inst.operands[1].writeback || inst.operands[1].postind))
+ as_warn (_("base register written back, and overlaps "
+ "second destination register"));
+
+ /* For an index-register load, the index register must not overlap the
+ destination (even if not write-back). */
+ else if (inst.operands[1].immisreg
+ && (inst.operands[1].imm == inst.operands[0].reg
+ || inst.operands[1].imm == inst.operands[0].reg + 1))
+ as_warn (_("index register overlaps destination register"));
}
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_addr_mode_3_arm (1, /*is_t=*/FALSE);
}
/* ARM V6 Load Register Exclusive instruction (argument parse).
@@ -4931,220 +4743,40 @@
static void
do_ldst (char * str)
{
- int pre_inc = 0;
- int conflict_reg;
- int value;
+ if (parse_operands (str, OPERANDS2(RR,ADDR)))
+ return;
- note_reg_or_fail (conflict_reg, &str, 12, REG_TYPE_RN);
- comma_or_fail (&str);
-
- if (*str == '[')
- {
- int reg;
-
- str++;
-
- note_reg_or_fail (reg, &str, 16, REG_TYPE_RN);
-
- /* Conflicts can occur on stores as well as loads. */
- conflict_reg = (conflict_reg == reg);
-
- if (*str == ']')
- {
- str ++;
-
- if (skip_past_comma (&str) == SUCCESS)
- {
- /* [Rn],... (post inc) */
- if (ldst_extend (&str) == FAIL)
- return;
- if (conflict_reg)
- as_warn ((inst.instruction & LOAD_BIT)
- ? _("destination register same as write-back base")
- : _("source register same as write-back base"));
- }
- else
- {
- /* [Rn] */
-
- if (*str == '!')
- {
- if (conflict_reg)
- as_warn ((inst.instruction & LOAD_BIT)
- ? _("destination register same as write-back base")
- : _("source register same as write-back base"));
- str++;
- inst.instruction |= WRITE_BACK;
- }
-
- inst.instruction |= INDEX_UP;
- pre_inc = 1;
- }
- }
- else
- {
- /* [Rn,...] */
- comma_or_fail (&str);
-
- pre_inc = 1;
- if (ldst_extend (&str) == FAIL)
- return;
-
- if (*str++ != ']')
- {
- inst.error = _("missing ]");
- return;
- }
-
- if (*str == '!')
- {
- if (conflict_reg)
- as_warn ((inst.instruction & LOAD_BIT)
- ? _("destination register same as write-back base")
- : _("source register same as write-back base"));
- str++;
- inst.instruction |= WRITE_BACK;
- }
- }
- }
- else if (*str == '=')
- {
- if ((inst.instruction & LOAD_BIT) == 0)
- {
- inst.error = _("invalid pseudo operation");
- return;
- }
-
- /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op. */
- str++;
-
- expression_or_fail (&inst.reloc.exp, &str, GE_NO_PREFIX);
-
- if (inst.reloc.exp.X_op != O_constant
- && inst.reloc.exp.X_op != O_symbol)
- {
- inst.error = _("constant expression expected");
- return;
- }
-
- if (inst.reloc.exp.X_op == O_constant)
- {
- value = validate_immediate (inst.reloc.exp.X_add_number);
-
- if (value != FAIL)
- {
- /* This can be done with a mov instruction. */
- inst.instruction &= LITERAL_MASK;
- inst.instruction |= (INST_IMMEDIATE
- | (OPCODE_MOV << DATA_OP_SHIFT));
- inst.instruction |= value & 0xfff;
- end_of_line (str);
- return;
- }
-
- value = validate_immediate (~inst.reloc.exp.X_add_number);
-
- if (value != FAIL)
- {
- /* This can be done with a mvn instruction. */
- inst.instruction &= LITERAL_MASK;
- inst.instruction |= (INST_IMMEDIATE
- | (OPCODE_MVN << DATA_OP_SHIFT));
- inst.instruction |= value & 0xfff;
- end_of_line (str);
- return;
- }
- }
-
- /* Insert into literal pool. */
- if (add_to_lit_pool () == FAIL)
- {
- if (!inst.error)
- inst.error = _("literal pool insertion failed");
- return;
- }
-
- /* Change the instruction exp to point to the pool. */
- inst.reloc.type = BFD_RELOC_ARM_LITERAL;
- inst.reloc.pc_rel = 1;
- inst.instruction |= (REG_PC << 16);
- pre_inc = 1;
- }
- else
- {
- expression_or_fail (&inst.reloc.exp, &str, GE_NO_PREFIX);
-
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM;
-#ifndef TE_WINCE
- /* PC rel adjust. */
- inst.reloc.exp.X_add_number -= 8;
-#endif
- inst.reloc.pc_rel = 1;
- inst.instruction |= (REG_PC << 16);
- pre_inc = 1;
- }
-
- inst.instruction |= (pre_inc ? PRE_INDEX : 0);
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 12;
+ if (!inst.operands[1].isreg)
+ if (move_or_literal_pool (0, /*thumb_p=*/FALSE, /*mode_3=*/FALSE))
+ return;
+ encode_addr_mode_2_arm (1, /*is_t=*/FALSE);
}
static void
do_ldstt (char * str)
{
- int conflict_reg;
+ if (parse_operands (str, OPERANDS2(RR,ADDR)))
+ return;
- note_reg_or_fail (conflict_reg, &str, 12, REG_TYPE_RN);
- comma_or_fail (&str);
-
- if (*str == '[')
+ /* ldrt/strt always use post-indexed addressing. Turn [Rn] into [Rn]! and
+ reject [Rn,...]. */
+ if (inst.operands[1].preind)
{
- int reg;
-
- str++;
-
- note_reg_or_fail (reg, &str, 16, REG_TYPE_RN);
-
- /* ldrt/strt always use post-indexed addressing, so if the base is
- the same as Rd, we warn. */
- if (conflict_reg == reg)
- as_warn ((inst.instruction & LOAD_BIT)
- ? _("destination register same as write-back base")
- : _("source register same as write-back base"));
-
- if (*str == ']')
+ if (inst.reloc.exp.X_op == O_constant && inst.reloc.exp.X_add_number == 0)
{
- str ++;
-
- if (skip_past_comma (&str) == SUCCESS)
- {
- /* [Rn],... (post inc) */
- if (ldst_extend (&str) == FAIL)
- return;
- }
- else
- {
- /* [Rn] */
-
- /* Skip a write-back '!'. */
- if (*str == '!')
- str++;
-
- inst.instruction |= INDEX_UP;
- }
+ inst.operands[1].preind = 0;
+ inst.operands[1].postind = 1;
+ inst.operands[1].writeback = 1;
}
else
{
- inst.error = _("post-indexed expression expected");
+ inst.error = _("this instruction requires a post-indexed address");
return;
}
}
- else
- {
- inst.error = _("post-indexed expression expected");
- return;
- }
-
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_addr_mode_2_arm (1, /*is_t=*/TRUE);
}
/* Halfword and signed-byte load/store operations. */
@@ -5152,226 +4784,40 @@
static void
do_ldstv4 (char * str)
{
- int pre_inc = 0;
- int conflict_reg;
- int value;
+ if (parse_operands (str, OPERANDS2(RR,ADDR)))
+ return;
- note_reg_or_fail (conflict_reg, &str, 12, REG_TYPE_RN);
- comma_or_fail (&str);
-
- if (*str == '[')
- {
- int reg;
-
- str++;
-
- note_reg_or_fail (reg, &str, 16, REG_TYPE_RN);
-
- /* Conflicts can occur on stores as well as loads. */
- conflict_reg = (conflict_reg == reg);
-
- if (*str == ']')
- {
- str ++;
-
- if (skip_past_comma (&str) == SUCCESS)
- {
- /* [Rn],... (post inc) */
- if (ldst_extend_v4 (&str) == FAIL)
- return;
- if (conflict_reg)
- as_warn ((inst.instruction & LOAD_BIT)
- ? _("destination register same as write-back base")
- : _("source register same as write-back base"));
- }
- else
- {
- /* [Rn] */
- inst.instruction |= HWOFFSET_IMM;
-
- if (*str == '!')
- {
- if (conflict_reg)
- as_warn ((inst.instruction & LOAD_BIT)
- ? _("destination register same as write-back base")
- : _("source register same as write-back base"));
- str++;
- inst.instruction |= WRITE_BACK;
- }
-
- inst.instruction |= INDEX_UP;
- pre_inc = 1;
- }
- }
- else
- {
- /* [Rn,...] */
- if (skip_past_comma (&str) == FAIL)
- {
- inst.error = _("pre-indexed expression expected");
- return;
- }
-
- pre_inc = 1;
- if (ldst_extend_v4 (&str) == FAIL)
- return;
-
- if (*str++ != ']')
- {
- inst.error = _("missing ]");
- return;
- }
-
- if (*str == '!')
- {
- if (conflict_reg)
- as_warn ((inst.instruction & LOAD_BIT)
- ? _("destination register same as write-back base")
- : _("source register same as write-back base"));
- str++;
- inst.instruction |= WRITE_BACK;
- }
- }
- }
- else if (*str == '=')
- {
- if ((inst.instruction & LOAD_BIT) == 0)
- {
- inst.error = _("invalid pseudo operation");
- return;
- }
-
- /* XXX Does this work correctly for half-word/byte ops? */
- /* Parse an "ldr Rd, =expr" instruction; this is another pseudo op. */
- str++;
-
- expression_or_fail (&inst.reloc.exp, &str, GE_NO_PREFIX);
-
- if (inst.reloc.exp.X_op != O_constant
- && inst.reloc.exp.X_op != O_symbol)
- {
- inst.error = _("constant expression expected");
- return;
- }
-
- if (inst.reloc.exp.X_op == O_constant)
- {
- value = validate_immediate (inst.reloc.exp.X_add_number);
-
- if (value != FAIL)
- {
- /* This can be done with a mov instruction. */
- inst.instruction &= LITERAL_MASK;
- inst.instruction |= INST_IMMEDIATE | (OPCODE_MOV << DATA_OP_SHIFT);
- inst.instruction |= value & 0xfff;
- end_of_line (str);
- return;
- }
-
- value = validate_immediate (~ inst.reloc.exp.X_add_number);
-
- if (value != FAIL)
- {
- /* This can be done with a mvn instruction. */
- inst.instruction &= LITERAL_MASK;
- inst.instruction |= INST_IMMEDIATE | (OPCODE_MVN << DATA_OP_SHIFT);
- inst.instruction |= value & 0xfff;
- end_of_line (str);
- return;
- }
- }
-
- /* Insert into literal pool. */
- if (add_to_lit_pool () == FAIL)
- {
- if (!inst.error)
- inst.error = _("literal pool insertion failed");
- return;
- }
-
- /* Change the instruction exp to point to the pool. */
- inst.instruction |= HWOFFSET_IMM;
- inst.reloc.type = BFD_RELOC_ARM_HWLITERAL;
- inst.reloc.pc_rel = 1;
- inst.instruction |= (REG_PC << 16);
- pre_inc = 1;
- }
- else
- {
- expression_or_fail (&inst.reloc.exp, &str, GE_NO_PREFIX);
-
- inst.instruction |= HWOFFSET_IMM;
- inst.reloc.type = BFD_RELOC_ARM_OFFSET_IMM8;
-#ifndef TE_WINCE
- /* PC rel adjust. */
- inst.reloc.exp.X_add_number -= 8;
-#endif
- inst.reloc.pc_rel = 1;
- inst.instruction |= (REG_PC << 16);
- pre_inc = 1;
- }
-
- inst.instruction |= (pre_inc ? PRE_INDEX : 0);
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 12;
+ if (!inst.operands[1].isreg)
+ if (move_or_literal_pool (0, /*thumb_p=*/FALSE, /*mode_3=*/TRUE))
+ return;
+ encode_addr_mode_3_arm (1, /*is_t=*/FALSE);
}
static void
do_ldsttv4 (char * str)
{
- int conflict_reg;
+ if (parse_operands (str, OPERANDS2(RR,ADDR)))
+ return;
- note_reg_or_fail (conflict_reg, &str, 12, REG_TYPE_RN);
- comma_or_fail (&str);
-
- if (*str == '[')
+ /* ldrt/strt always use post-indexed addressing. Turn [Rn] into [Rn]! and
+ reject [Rn,...]. */
+ if (inst.operands[1].preind)
{
- int reg;
-
- str++;
-
- note_reg_or_fail (reg, &str, 16, REG_TYPE_RN);
-
- /* ldrt/strt always use post-indexed addressing, so if the base is
- the same as Rd, we warn. */
- if (reg == conflict_reg)
- as_warn ((inst.instruction & LOAD_BIT)
- ? _("destination register same as write-back base")
- : _("source register same as write-back base"));
-
- if (*str == ']')
+ if (inst.reloc.exp.X_op == O_constant && inst.reloc.exp.X_add_number == 0)
{
- str ++;
-
- if (skip_past_comma (&str) == SUCCESS)
- {
- /* [Rn],... (post inc) */
- if (ldst_extend_v4 (&str) == FAIL)
- return;
- }
- else
- {
- /* [Rn] */
-
- /* Skip a write-back '!'. */
- if (*str == '!')
- str++;
-
- inst.instruction |= (INDEX_UP|HWOFFSET_IMM);
- }
+ inst.operands[1].preind = 0;
+ inst.operands[1].postind = 1;
+ inst.operands[1].writeback = 1;
}
else
{
- inst.error = _("post-indexed expression expected");
+ inst.error = _("this instruction requires a post-indexed address");
return;
}
}
- else
- {
- inst.error = _("post-indexed expression expected");
- return;
- }
-
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg << 12;
+ encode_addr_mode_3_arm (1, /*is_t=*/TRUE);
}
/* Co-processor register load/store.
@@ -5379,7 +4825,7 @@
static void
do_lstc (char * str)
{
- if (parse_operands (str, OPERANDS3(RCP,RCN,CADDR)))
+ if (parse_operands (str, OPERANDS3(RCP,RCN,ADDR)))
return;
inst.instruction |= inst.operands[0].reg << 8;
@@ -5581,67 +5027,32 @@
static void
do_pld (char * str)
{
- int rd;
+ if (parse_operands (str, OPERANDS1(ADDR)))
+ return;
- if (* str != '[')
+ if (!inst.operands[0].isreg)
{
inst.error = _("'[' expected after PLD mnemonic");
return;
}
-
- ++str;
-
- note_reg_or_fail (rd, &str, 16, REG_TYPE_RN);
-
- if (*str == ']')
+ if (inst.operands[0].postind)
{
- /* [Rn], ... ? */
- ++str;
-
- /* Post-indexed addressing is not allowed with PLD. */
- if (skip_past_comma (&str) == SUCCESS)
- {
- inst.error
- = _("post-indexed expression used in preload instruction");
- return;
- }
- else if (*str == '!') /* [Rn]! */
- {
- inst.error = _("writeback used in preload instruction");
- ++str;
- }
- else /* [Rn] */
- inst.instruction |= INDEX_UP | PRE_INDEX;
+ inst.error = _("post-indexed expression used in preload instruction");
+ return;
}
- else /* [Rn, ...] */
+ if (inst.operands[0].writeback)
{
- if (skip_past_comma (& str) == FAIL)
- {
- inst.error = _("pre-indexed expression expected");
- return;
- }
-
- if (ldst_extend (&str) == FAIL)
- return;
-
- if (* str != ']')
- {
- inst.error = _("missing ]");
- return;
- }
-
- ++ str;
-
- if (* str == '!') /* [Rn]! */
- {
- inst.error = _("writeback used in preload instruction");
- ++ str;
- }
-
- inst.instruction |= PRE_INDEX;
+ inst.error = _("writeback used in preload instruction");
+ return;
}
+ if (!inst.operands[0].preind)
+ {
+ inst.error = _("unindexed addressing used in preload instruction");
+ return;
+ }
- end_of_line (str);
+ inst.instruction |= inst.operands[0].reg;
+ encode_addr_mode_2_arm (0, /*is_t=*/FALSE);
}
/* ARM V5E (El Segundo) saturating-add/subtract (argument parse)
@@ -6211,13 +5622,21 @@
static void
do_t_lds (char * str)
{
- if (parse_operands (str, OPERANDS2(RL,TADDR)))
+ if (parse_operands (str, OPERANDS2(RL,ADDR)))
return;
- if (!inst.operands[1].isreg || !inst.operands[1].immisreg)
+ /* Only [Rn,Rm] is acceptable. */
+ if (!inst.operands[1].isreg || !inst.operands[1].immisreg
+ || inst.operands[1].postind || inst.operands[1].shifted
+ || inst.operands[1].negative)
{
inst.error = _("invalid addressing mode");
return;
}
+ if (inst.operands[1].reg > 7 || inst.operands[1].imm > 7)
+ {
+ inst.error = BAD_HIREG;
+ return;
+ }
inst.instruction |= inst.operands[0].reg;
inst.instruction |= inst.operands[1].reg << 3;
@@ -6227,36 +5646,17 @@
static void
do_t_ldst (char *str)
{
- if (parse_operands (str, OPERANDS2(RL,TADDR)))
+ if (parse_operands (str, OPERANDS2(RL,ADDR)))
return;
- if (!inst.operands[1].isreg) /* =expr pseudo */
- {
- if (inst.instruction & THUMB_LOAD_BIT)
- {
- inst.error = _("invalid pseudo operation");
- return;
- }
+ if (!inst.operands[1].isreg)
+ if (move_or_literal_pool (0, /*thumb_p=*/TRUE, /*mode_3=*/FALSE))
+ return;
- if (inst.reloc.exp.X_op == O_constant
- && ((inst.reloc.exp.X_add_number & ~0xFF) == 0))
- {
- /* This can be done with a mov instruction. */
- inst.instruction = T_OPCODE_MOV_I8 | (inst.operands[0].reg << 8);
- inst.instruction |= inst.reloc.exp.X_add_number;
- return;
- }
-
- /* Insert into literal pool. */
- if (add_to_lit_pool () == FAIL)
- {
- inst.error = _("literal pool insertion failed");
- return;
- }
-
- inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
- inst.reloc.exp.X_add_number += 4; /* pipeline offset */
- inst.operands[1].reg = REG_PC;
+ if (!inst.operands[1].preind || inst.operands[1].shifted)
+ {
+ inst.error = _("Thumb does not support this addressing mode");
+ return;
}
if (inst.operands[1].reg == REG_PC || inst.operands[1].reg == REG_SP)
@@ -6286,9 +5686,20 @@
inst.instruction = T_OPCODE_STR_SP;
inst.instruction |= inst.operands[0].reg << 8;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ if (inst.reloc.pc_rel)
+ inst.reloc.exp.X_add_number -= 4; /* pipeline offset */
+ return;
}
- else if (!inst.operands[1].immisreg)
+
+ if (inst.operands[0].reg > 7)
{
+ inst.error = BAD_HIREG;
+ return;
+ }
+
+ if (!inst.operands[1].immisreg)
+ {
/* Immediate offset. */
switch (inst.instruction)
{
@@ -6303,10 +5714,21 @@
inst.instruction |= inst.operands[0].reg;
inst.instruction |= inst.operands[1].reg << 3;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
}
else
{
/* Register offset. Opcode is already correct. */
+ if (inst.operands[1].reg > 7)
+ {
+ inst.error = BAD_HIREG;
+ return;
+ }
+ else if (inst.operands[1].negative)
+ {
+ inst.error = _("Thumb does not support [Rn,-Rm] addressing");
+ return;
+ }
inst.instruction |= inst.operands[0].reg;
inst.instruction |= inst.operands[1].reg << 3;
inst.instruction |= inst.operands[1].imm << 6;
@@ -6666,7 +6088,7 @@
static void
do_vfp_sp_ldst (char * str)
{
- if (parse_operands (str, OPERANDS2(RVS,CADDR)))
+ if (parse_operands (str, OPERANDS2(RVS,ADDR)))
return;
vfp_sp_encode_reg (inst.operands[0].reg, VFP_REG_Sd);
@@ -6676,7 +6098,7 @@
static void
do_vfp_dp_ldst (char * str)
{
- if (parse_operands (str, OPERANDS2(RVD,CADDR)))
+ if (parse_operands (str, OPERANDS2(RVD,ADDR)))
return;
inst.instruction |= inst.operands[0].reg << VFP_REG_Dd;
@@ -6832,7 +6254,7 @@
static void
do_fpa_ldst (char * str)
{
- if (parse_operands (str, OPERANDS2(RF,CADDR)))
+ if (parse_operands (str, OPERANDS2(RF,ADDR)))
return;
inst.instruction |= (inst.operands[0].reg << 12);
encode_cp_address_arm (1, TRUE, TRUE, 0);
@@ -6841,7 +6263,7 @@
static void
do_fpa_ldmstm (char * str)
{
- if (parse_operands (str, OPERANDS3(RF,bI4,CADDR)))
+ if (parse_operands (str, OPERANDS3(RF,bI4,ADDR)))
return;
inst.instruction |= (inst.operands[0].reg << 12);
@@ -7032,7 +6454,7 @@
static void
do_iwmmxt_wldst (char * str)
{
- if (parse_operands (str, OPERANDS2(RIWR,CADDR)))
+ if (parse_operands (str, OPERANDS2(RIWR,ADDR)))
return;
inst.instruction |= (inst.operands[0].reg << 12);
@@ -7042,7 +6464,7 @@
static void
do_iwmmxt_wldstw (char * str)
{
- if (parse_operands (str, OPERANDS2(RIWR_RIWC,CADDR)))
+ if (parse_operands (str, OPERANDS2(RIWR_RIWC,ADDR)))
return;
/* RIWR_RIWC clears .isreg for a control register. */
@@ -7451,25 +6873,25 @@
static void
do_mav_ldst_1 (char * str)
{
- do_mav_ldst (str, OPERANDS2(RMF,CADDR));
+ do_mav_ldst (str, OPERANDS2(RMF,ADDR));
}
static void
do_mav_ldst_2 (char * str)
{
- do_mav_ldst (str, OPERANDS2(RMD,CADDR));
+ do_mav_ldst (str, OPERANDS2(RMD,ADDR));
}
static void
do_mav_ldst_3 (char * str)
{
- do_mav_ldst (str, OPERANDS2(RMFX,CADDR));
+ do_mav_ldst (str, OPERANDS2(RMFX,ADDR));
}
static void
do_mav_ldst_4 (char * str)
{
- do_mav_ldst (str, OPERANDS2(RMDX,CADDR));
+ do_mav_ldst (str, OPERANDS2(RMDX,ADDR));
}
/* XScale instructions. Also sorted arithmetic before move. */
@@ -9846,6 +9268,53 @@
return 0;
}
+/* Subroutine of md_apply_fix3. Check to see if an immediate can be
+ computed as two separate immediate values, added together. We
+ already know that this value cannot be computed by just one ARM
+ instruction. */
+
+static unsigned int
+validate_immediate_twopart (unsigned int val,
+ unsigned int * highpart)
+{
+ unsigned int a;
+ unsigned int i;
+
+ for (i = 0; i < 32; i += 2)
+ if (((a = rotate_left (val, i)) & 0xff) != 0)
+ {
+ if (a & 0xff00)
+ {
+ if (a & ~ 0xffff)
+ continue;
+ * highpart = (a >> 8) | ((i + 24) << 7);
+ }
+ else if (a & 0xff0000)
+ {
+ if (a & 0xff000000)
+ continue;
+ * highpart = (a >> 16) | ((i + 16) << 7);
+ }
+ else
+ {
+ assert (a & 0xff000000);
+ * highpart = (a >> 24) | ((i + 8) << 7);
+ }
+
+ return (a & 0xff) | (i << 7);
+ }
+
+ return FAIL;
+}
+
+static int
+validate_offset_imm (unsigned int val, int hwse)
+{
+ if ((hwse && val > 255) || val > 4095)
+ return FAIL;
+ return val;
+}
+
/* Subroutine of md_apply_fix3. Do those data_ops which can take a
negative immediate constant by altering the instruction. A bit of
a hack really.
@@ -9864,8 +9333,8 @@
int op, new_inst;
unsigned long negated, inverted;
- negated = validate_immediate (-value);
- inverted = validate_immediate (~value);
+ negated = encode_immediate_arm (-value);
+ inverted = encode_immediate_arm (~value);
op = (*instruction >> DATA_OP_SHIFT) & 0xf;
switch (op)
@@ -9992,7 +9461,7 @@
break;
}
- newimm = validate_immediate (value);
+ newimm = encode_immediate_arm (value);
temp = md_chars_to_number (buf, INSN_SIZE);
/* If the instruction will fail, see if we can fix things up by
@@ -10015,7 +9484,7 @@
unsigned int highpart = 0;
unsigned int newinsn = 0xe1a00000; /* nop. */
- newimm = validate_immediate (value);
+ newimm = encode_immediate_arm (value);
temp = md_chars_to_number (buf, INSN_SIZE);
/* If the instruction will fail, see if we can fix things up by
@@ -10060,6 +9529,7 @@
break;
case BFD_RELOC_ARM_OFFSET_IMM:
+ case BFD_RELOC_ARM_LITERAL:
sign = value >= 0;
if (value < 0)
@@ -10067,9 +9537,13 @@
if (validate_offset_imm (value, 0) == FAIL)
{
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("bad immediate value for offset (%ld)"),
- (long) value);
+ if (fixP->fx_r_type == BFD_RELOC_ARM_LITERAL)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid literal constant: pool needs to be closer"));
+ else
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("bad immediate value for offset (%ld)"),
+ (long) value);
break;
}
@@ -10103,25 +9577,6 @@
md_number_to_chars (buf, newval, INSN_SIZE);
break;
- case BFD_RELOC_ARM_LITERAL:
- sign = value >= 0;
-
- if (value < 0)
- value = - value;
-
- if (validate_offset_imm (value, 0) == FAIL)
- {
- as_bad_where (fixP->fx_file, fixP->fx_line,
- _("invalid literal constant: pool needs to be closer"));
- break;
- }
-
- newval = md_chars_to_number (buf, INSN_SIZE);
- newval &= 0xff7ff000;
- newval |= value | (sign ? INDEX_UP : 0);
- md_number_to_chars (buf, newval, INSN_SIZE);
- break;
-
case BFD_RELOC_ARM_SHIFT_IMM:
newval = md_chars_to_number (buf, INSN_SIZE);
if (((unsigned long) value) > 32