This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
Thumb32 assembler (42/69)
- From: Zack Weinberg <zack at codesourcery dot com>
- To: binutils <binutils at sourceware dot org>
- Date: Tue, 26 Apr 2005 02:55:24 -0700
- Subject: Thumb32 assembler (42/69)
Here we are going after bigger game: Thumb addresses can now be
handled by parse_operands. This, plus some adjustments to tinsns,
allows the removal of lots of tiny wrapper functions. And, again,
constant handling is deferred to md_apply_fix3 so that code is not
duplicated between there and the encoder functions.
zw
* tc-arm.c (THUMB_LOAD_BIT): New constant.
(parse_thumb_address): New function.
(OP_TADDR): New operand parse code.
(parse_operands): Handle it. Delete code for OP_RLlb and OP_RLtb.
(thumb_load_store): Rename do_t_ldst. Use parse_operands. Remove
second and third arguments; determine size and direction of operation
from inst.instruction. Defer most constant handling to md_apply_fix3.
(do_t_lds): Use TADDR, not RLlb and RLtb.
(THUMB_REG_LO, THUMB_REG_ANY, THUMB_LOAD, THUMB_STORE, THUMB_WORD)
(THUMB_HALFWORD, THUMB_BYTE, thumb_reg, OP_RLlb, OP_RLtb, do_t_ldr)
(do_t_ldrb, do_t_ldrh, do_t_str, do_t_strb, do_t_strh): Delete.
(tinsns): Use do_t_ldst for ldr ldrb ldrh str strb strh. Set opcode
for these to the opcode for the 3-register form.
(md_apply_fix3 <case BFD_RELOC_ARM_THUMB_OFFSET>): Correct check
for non-word-aligned offset from PC.
===================================================================
Index: gas/config/tc-arm.c
--- gas/config/tc-arm.c (revision 44)
+++ gas/config/tc-arm.c (revision 45)
@@ -197,6 +197,7 @@
int imm;
int present : 1; /* operand present */
int isreg : 1; /* operand was a register */
+ int immisreg : 1; /* .imm field is a second register */
int writeback : 1; /* operand has trailing ! */
int hasreloc : 1; /* operand has relocation suffix */
int postind : 1; /* operand is post-indexed */
@@ -555,22 +556,10 @@
#define T_OPCODE_BRANCH 0xe7fe
#define THUMB_SIZE 2 /* Size of thumb instruction. */
-#define THUMB_REG_LO 0x1
-#define THUMB_REG_ANY 0x3
-#define THUMB_H1 0x0080
-#define THUMB_H2 0x0040
-
-#define THUMB_LOAD 0
-#define THUMB_STORE 1
-
#define THUMB_PP_PC_LR 0x0100
+#define THUMB_LOAD_BIT 0x0800
-/* These three are used for immediate shifts, do not alter. */
-#define THUMB_WORD 2
-#define THUMB_HALFWORD 1
-#define THUMB_BYTE 0
-
struct thumb_opcode
{
/* Basic string to match. */
@@ -1275,28 +1264,6 @@
} \
} while (0)
-/* Parse and validate that a register is of the right form, this saves
- repeated checking of this information in many similar cases.
- Unlike the 32-bit case we do not insert the register into the opcode
- here, since the position is often unknown until the full instruction
- has been parsed. */
-
-static int
-thumb_reg (char ** strp, int hi_lo)
-{
- int reg;
-
- if ((reg = reg_required_here (strp, -1, REG_TYPE_RN)) == FAIL)
- return FAIL;
-
- if (hi_lo == THUMB_REG_LO && reg > 7)
- {
- inst.error = BAD_HIREG;
- return FAIL;
- }
- return reg;
-}
-
/* Register lists. */
static long
@@ -2451,6 +2418,100 @@
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 (is_immediate_prefix (*s))
+ {
+ s++;
+ if (my_get_expression (&inst.reloc.exp, &s))
+ return FAIL;
+ }
+ else
+ {
+ if ((Ro = arm_reg_parse (&s, REG_TYPE_RN)) == FAIL)
+ return FAIL;
+ if (Ro > 7 || Rb > 7)
+ {
+ inst.error = BAD_HIREG;
+ return FAIL;
+ }
+ inst.operands[i].imm = Ro;
+ inst.operands[i].immisreg = 1;
+ }
+ }
+ 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))
+ 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))
+ 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. */
@@ -4270,8 +4331,6 @@
#define OP_RRw 051 /* ARM register, not the PC, optional trailing ! */
#define OP_RL 052 /* Thumb low register */
-#define OP_RLlb 053 /* Thumb low register, leading [ */
-#define OP_RLtb 054 /* Thumb low register, trailing ] */
#define OP_RLw 055 /* Thumb low register, optional trailing ! */
#define OP_CPSF 060 /* CPS flags */
@@ -4281,6 +4340,8 @@
#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 */
+
/* This-or-that operands. All have bit 7 set. */
#define OP_RR_EX 100 /* ARM register or expression */
#define OP_RL_iEX 101 /* Thumb low register or expression with imm prefix */
@@ -4412,16 +4473,6 @@
}
break;
- case OP_(RLlb):
- po_char_or_fail ('[');
- po_reg_or_fail (REG_TYPE_RN);
- break;
-
- case OP_(RLtb):
- po_reg_or_fail (REG_TYPE_RN);
- po_char_or_fail (']');
- break;
-
/* Immediates */
I0:
case OP_(I0): po_imm_or_fail ( 0, 0, FALSE); break;
@@ -4552,6 +4603,12 @@
val = vfp_parse_reg_list (&str, &inst.operands[i].reg, 1);
break;
+ /* Addressing modes */
+ case OP_(TADDR):
+ if (parse_thumb_address (&str, i))
+ return FAIL;
+ break;
+
default:
as_fatal ("unhandled operand code %03o", *p);
}
@@ -4571,8 +4628,6 @@
case OP_(oRL):
case OP_(RL):
- case OP_(RLlb):
- case OP_(RLtb):
case OP_(RLw):
case OP_(oRL_iEX):
case OP_(RL_iEX):
@@ -6251,75 +6306,24 @@
}
static void
-thumb_load_store (char * str, int load_store, int size)
+do_t_ldst (char *str)
{
- int Rd, Rb, Ro = FAIL;
+ if (parse_operands (str, OPERANDS2(RL,TADDR)))
+ return;
- if ((Rd = thumb_reg (&str, THUMB_REG_LO)) == FAIL
- || skip_past_comma (&str) == FAIL)
+ if (!inst.operands[1].isreg) /* =expr pseudo */
{
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
-
- if (*str == '[')
- {
- str++;
- if ((Rb = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
- return;
-
- if (skip_past_comma (&str) != FAIL)
+ if (inst.instruction & THUMB_LOAD_BIT)
{
- if (is_immediate_prefix (*str))
- {
- str++;
- expression_or_fail (&inst.reloc.exp, &str);
- }
- else if ((Ro = thumb_reg (&str, THUMB_REG_LO)) == FAIL)
- return;
- }
- else
- {
- inst.reloc.exp.X_op = O_constant;
- inst.reloc.exp.X_add_number = 0;
- }
-
- if (*str != ']')
- {
- inst.error = _("expected ']'");
- return;
- }
- str++;
- }
- else if (*str == '=')
- {
- if (load_store != THUMB_LOAD)
- {
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);
-
- end_of_line (str);
-
- 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
&& ((inst.reloc.exp.X_add_number & ~0xFF) == 0))
{
/* This can be done with a mov instruction. */
-
- inst.instruction = T_OPCODE_MOV_I8 | (Rd << 8);
+ inst.instruction = T_OPCODE_MOV_I8 | (inst.operands[0].reg << 8);
inst.instruction |= inst.reloc.exp.X_add_number;
return;
}
@@ -6327,123 +6331,67 @@
/* Insert into literal pool. */
if (add_to_lit_pool () == FAIL)
{
- if (!inst.error)
- inst.error = "literal pool insertion failed";
+ inst.error = _("literal pool insertion failed");
return;
}
inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
- inst.reloc.pc_rel = 1;
- inst.instruction = T_OPCODE_LDR_PC | (Rd << 8);
- /* Adjust ARM pipeline offset to Thumb. */
- inst.reloc.exp.X_add_number += 4;
-
- return;
+ inst.reloc.exp.X_add_number += 4; /* pipeline offset */
+ inst.operands[1].reg = REG_PC;
}
- else
- {
- expression_or_fail (&inst.reloc.exp, &str);
- inst.instruction = T_OPCODE_LDR_PC | (Rd << 8);
- inst.reloc.pc_rel = 1;
- inst.reloc.exp.X_add_number -= 4; /* Pipeline offset. */
- inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
- end_of_line (str);
- return;
- }
-
- if (Rb == REG_PC || Rb == REG_SP)
+ if (inst.operands[1].reg == REG_PC || inst.operands[1].reg == REG_SP)
{
- if (size != THUMB_WORD)
+ if (inst.instruction & 0x0600)
{
inst.error = _("byte or halfword not valid for base register");
return;
}
- else if (Rb == REG_PC && load_store != THUMB_LOAD)
+ else if (inst.operands[1].reg == REG_PC
+ && !(inst.instruction & THUMB_LOAD_BIT))
{
inst.error = _("r15 based store not allowed");
return;
}
- else if (Ro != FAIL)
+ else if (inst.operands[1].immisreg)
{
inst.error = _("invalid base register for register offset");
return;
}
- if (Rb == REG_PC)
+ if (inst.operands[1].reg == REG_PC)
inst.instruction = T_OPCODE_LDR_PC;
- else if (load_store == THUMB_LOAD)
+ else if (inst.instruction & THUMB_LOAD_BIT)
inst.instruction = T_OPCODE_LDR_SP;
else
inst.instruction = T_OPCODE_STR_SP;
- inst.instruction |= Rd << 8;
- if (inst.reloc.exp.X_op == O_constant)
- {
- unsigned offset = inst.reloc.exp.X_add_number;
-
- if (offset & ~0x3fc)
- {
- inst.error = _("invalid offset");
- return;
- }
-
- inst.instruction |= offset >> 2;
- }
- else
- inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+ inst.instruction |= inst.operands[0].reg << 8;
}
- else if (Rb > 7)
+ else if (!inst.operands[1].immisreg)
{
- inst.error = _("invalid base register in load/store");
- return;
- }
- else if (Ro == FAIL)
- {
/* Immediate offset. */
- if (size == THUMB_WORD)
- inst.instruction = (load_store == THUMB_LOAD
- ? T_OPCODE_LDR_IW : T_OPCODE_STR_IW);
- else if (size == THUMB_HALFWORD)
- inst.instruction = (load_store == THUMB_LOAD
- ? T_OPCODE_LDR_IH : T_OPCODE_STR_IH);
- else
- inst.instruction = (load_store == THUMB_LOAD
- ? T_OPCODE_LDR_IB : T_OPCODE_STR_IB);
-
- inst.instruction |= Rd | (Rb << 3);
-
- if (inst.reloc.exp.X_op == O_constant)
+ switch (inst.instruction)
{
- unsigned offset = inst.reloc.exp.X_add_number;
-
- if (offset & ~(0x1f << size))
- {
- inst.error = _("invalid offset");
- return;
- }
- inst.instruction |= (offset >> size) << 6;
+ case T_OPCODE_STR_RW: inst.instruction = T_OPCODE_STR_IW; break;
+ case T_OPCODE_STR_RH: inst.instruction = T_OPCODE_STR_IH; break;
+ case T_OPCODE_STR_RB: inst.instruction = T_OPCODE_STR_IB; break;
+ case T_OPCODE_LDR_RW: inst.instruction = T_OPCODE_LDR_IW; break;
+ case T_OPCODE_LDR_RH: inst.instruction = T_OPCODE_LDR_IH; break;
+ case T_OPCODE_LDR_RB: inst.instruction = T_OPCODE_LDR_IB; break;
+ default: abort ();
}
- else
- inst.reloc.type = BFD_RELOC_ARM_THUMB_OFFSET;
+
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
}
else
{
- /* Register offset. */
- if (size == THUMB_WORD)
- inst.instruction = (load_store == THUMB_LOAD
- ? T_OPCODE_LDR_RW : T_OPCODE_STR_RW);
- else if (size == THUMB_HALFWORD)
- inst.instruction = (load_store == THUMB_LOAD
- ? T_OPCODE_LDR_RH : T_OPCODE_STR_RH);
- else
- inst.instruction = (load_store == THUMB_LOAD
- ? T_OPCODE_LDR_RB : T_OPCODE_STR_RB);
-
- inst.instruction |= Rd | (Rb << 3) | (Ro << 6);
+ /* Register offset. Opcode is already correct. */
+ inst.instruction |= inst.operands[0].reg;
+ inst.instruction |= inst.operands[1].reg << 3;
+ inst.instruction |= inst.operands[1].imm << 6;
}
-
- end_of_line (str);
}
static void
@@ -6678,9 +6626,7 @@
if (parse_operands (str, OPERANDS1(RR)))
return;
- /* This sets THUMB_H2 from the top bit of reg. */
inst.instruction |= (inst.operands[0].reg << 3);
-
/* ??? FIXME: Should add a hacky reloc here if reg is REG_PC. The reloc
should cause the alignment to be checked once it is known. This is
because BX PC only works if the instruction is word aligned. */
@@ -6741,32 +6687,19 @@
}
static void
-do_t_ldr (char * str)
-{
- thumb_load_store (str, THUMB_LOAD, THUMB_WORD);
-}
-
-static void
-do_t_ldrb (char * str)
-{
- thumb_load_store (str, THUMB_LOAD, THUMB_BYTE);
-}
-
-static void
-do_t_ldrh (char * str)
-{
- thumb_load_store (str, THUMB_LOAD, THUMB_HALFWORD);
-}
-
-static void
do_t_lds (char * str)
{
- if (parse_operands (str, OPERANDS3(RL,RLlb,RLtb)))
+ if (parse_operands (str, OPERANDS2(RL,TADDR)))
return;
+ if (!inst.operands[1].isreg || !inst.operands[1].immisreg)
+ {
+ inst.error = _("invalid addressing mode");
+ return;
+ }
inst.instruction |= inst.operands[0].reg;
inst.instruction |= inst.operands[1].reg << 3;
- inst.instruction |= inst.operands[2].reg << 6;
+ inst.instruction |= inst.operands[1].imm << 6;
}
static void
@@ -6819,24 +6752,6 @@
}
static void
-do_t_str (char * str)
-{
- thumb_load_store (str, THUMB_STORE, THUMB_WORD);
-}
-
-static void
-do_t_strb (char * str)
-{
- thumb_load_store (str, THUMB_STORE, THUMB_BYTE);
-}
-
-static void
-do_t_strh (char * str)
-{
- thumb_load_store (str, THUMB_STORE, THUMB_HALFWORD);
-}
-
-static void
do_t_swi (char * str)
{
if (parse_operands (str, OPERANDS1(EXP)))
@@ -9568,9 +9483,9 @@
{"cmp", T_OPCODE_CMP_HR,2, ARM_EXT_V4T, do_t_mov_cmp},
{"eor", 0x4040, 2, ARM_EXT_V4T, do_t_arit},
{"ldmia", 0xc800, 2, ARM_EXT_V4T, do_t_ldmstm},
- {"ldr", 0x0000, 2, ARM_EXT_V4T, do_t_ldr},
- {"ldrb", 0x0000, 2, ARM_EXT_V4T, do_t_ldrb},
- {"ldrh", 0x0000, 2, ARM_EXT_V4T, do_t_ldrh},
+ {"ldr", T_OPCODE_LDR_RW,2, ARM_EXT_V4T, do_t_ldst},
+ {"ldrb", T_OPCODE_LDR_RB,2, ARM_EXT_V4T, do_t_ldst},
+ {"ldrh", T_OPCODE_LDR_RH,2, ARM_EXT_V4T, do_t_ldst},
{"ldrsb", 0x5600, 2, ARM_EXT_V4T, do_t_lds},
{"ldrsh", 0x5e00, 2, ARM_EXT_V4T, do_t_lds},
{"ldsb", 0x5600, 2, ARM_EXT_V4T, do_t_lds},
@@ -9587,9 +9502,9 @@
{"ror", 0x41c0, 2, ARM_EXT_V4T, do_t_arit},
{"sbc", 0x4180, 2, ARM_EXT_V4T, do_t_arit},
{"stmia", 0xc000, 2, ARM_EXT_V4T, do_t_ldmstm},
- {"str", 0x0000, 2, ARM_EXT_V4T, do_t_str},
- {"strb", 0x0000, 2, ARM_EXT_V4T, do_t_strb},
- {"strh", 0x0000, 2, ARM_EXT_V4T, do_t_strh},
+ {"str", T_OPCODE_STR_RW,2, ARM_EXT_V4T, do_t_ldst},
+ {"strb", T_OPCODE_STR_RB,2, ARM_EXT_V4T, do_t_ldst},
+ {"strh", T_OPCODE_STR_RH,2, ARM_EXT_V4T, do_t_ldst},
{"swi", 0xdf00, 2, ARM_EXT_V4T, do_t_swi},
{"sub", 0x8000, 2, ARM_EXT_V4T, do_t_add_sub},
{"tst", T_OPCODE_TST, 2, ARM_EXT_V4T, do_t_arit},
@@ -10839,11 +10754,11 @@
aligned (since the final address produced must be, and
we can only describe word-aligned immediate offsets). */
- if ((fixP->fx_frag->fr_address + fixP->fx_where + value) & 3)
+ if (value & 3)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("invalid offset, target not word aligned (0x%08X)"),
- (unsigned int) (fixP->fx_frag->fr_address
- + fixP->fx_where + value));
+ _("invalid offset, target not word aligned (0x%08lX)"),
+ (((unsigned int) fixP->fx_frag->fr_address
+ + (unsigned int) fixP->fx_where) & ~3) + value);
if ((value + 2) & ~0x3fe)
as_bad_where (fixP->fx_file, fixP->fx_line,