This is the mail archive of the
binutils@sources.redhat.com
mailing list for the binutils project.
Thumb32 assembler (40/69)
- From: Zack Weinberg <zack at codesourcery dot com>
- To: binutils <binutils at sourceware dot org>
- Date: Tue, 26 Apr 2005 02:55:15 -0700
- Subject: Thumb32 assembler (40/69)
Refactor thumb_add_sub in a similar way to the previous patch's
treatment of thumb_mov_compare, and convert it to parse_operands.
Again, code redundant with md_apply_fix3 gets deleted.
zw
* config/tc-arm.c (OP_oRR_iEX): New operand parse code.
(parse_operands): Implement it.
(thumb_add_sub): Rename do_t_add_sub. Remove second argument. Look at
inst.instruction to see if this is a subtraction. Use parse_operands.
Reorganize code for clarity. Defer most processing of immediate
operands to md_apply_fix3.
(do_t_shift): Correct comment.
(do_t_add, do_t_sub): Delete.
(tinsns): Use do_t_add_sub for add and sub. Indicate that sub is
a subtraction in its opcode.
(md_apply_fix3 <case BFD_RELOC_ARM_THUMB_ADD>): Check for invalid Hi
registers here. Handle value being negative. Issue less jargon-y
diagnostics.
===================================================================
Index: gas/config/tc-arm.c
--- gas/config/tc-arm.c (revision 42)
+++ gas/config/tc-arm.c (revision 43)
@@ -4321,6 +4321,7 @@
#define OP_oRL 211 /* optional Thumb low register */
#define OP_oRL_iEX 300 /* optional Thumb low reg or expression */
+#define OP_oRR_iEX 301 /* optional ARM reg or expression */
/* Macro for referring to one of the above constants as a number.
Should appear solely in parse_operands(). */
@@ -4525,6 +4526,7 @@
case OP_(RR_EX): po_reg_or_goto (REG_TYPE_RN, EXP); break;
case OP_(RR_EXr): po_reg_or_goto (REG_TYPE_RN, EXPr); break;
case OP_(oRL_iEX):
+ case OP_(oRR_iEX):
case OP_(RL_iEX):
case OP_(RR_iEX): po_reg_or_goto (REG_TYPE_RN, iEXP); break;
@@ -6198,171 +6200,75 @@
(thumb_*) come first, in alphabetical order, then the
per-instruction routines (do_t_*), also in alphabetical order. */
-/* Parse an add or subtract instruction, SUBTRACT is non-zero if the opcode
- was SUB. */
+/* Parse an add or subtract instruction. The high bit of inst.instruction
+ is set if the opcode was SUB. */
static void
-thumb_add_sub (char * str, int subtract)
+do_t_add_sub (char * str)
{
- int Rd, Rs, Rn = FAIL;
+ int Rd, Rs, Rn;
+ int subtract = !!(inst.instruction & 0x8000);
- if ((Rd = thumb_reg (&str, THUMB_REG_ANY)) == FAIL
- || skip_past_comma (&str) == FAIL)
+ if (parse_operands (str, OPERANDS3(RR,RR_iEX,oRR_iEX)))
+ return;
+ Rd = inst.operands[0].reg;
+ if (inst.operands[2].present)
{
- if (! inst.error)
- inst.error = BAD_ARGS;
- return;
- }
+ /* If there were three operands, operand 1 must be a register. */
+ if (!inst.operands[1].isreg)
+ {
+ inst.error = BAD_ARGS;
+ return;
+ }
- if (is_immediate_prefix (*str))
- {
- Rs = Rd;
- str++;
- expression_or_fail (&inst.reloc.exp, &str);
- }
- else
- {
- if ((Rs = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
- return;
-
- if (skip_past_comma (&str) == FAIL)
+ Rs = inst.operands[1].reg;
+ if (!inst.operands[2].isreg) /* Rd, Rs, #imm */
{
- /* Two operand format, shuffle the registers
- and pretend there are 3. */
- Rn = Rs;
- Rs = Rd;
+ inst.instruction |= (Rd << 4) | Rs;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ return;
}
- else if (is_immediate_prefix (*str))
- {
- str++;
- expression_or_fail (&inst.reloc.exp, &str);
- }
- else if ((Rn = thumb_reg (&str, THUMB_REG_ANY)) == FAIL)
- return;
+ else /* Rd, Rs, Rn */
+ Rn = inst.operands[2].reg;
}
-
- /* We now have Rd and Rs set to registers, and Rn set to a register or FAIL;
- for the latter case, EXPR contains the immediate that was found. */
- if (Rn != FAIL)
+ else if (!inst.operands[1].isreg) /* Rd, #imm -> Rd, Rd, #imm */
{
- /* All register format. */
- if (Rd > 7 || Rs > 7 || Rn > 7)
- {
- if (Rs != Rd)
- {
- inst.error = _("dest and source1 must be the same register");
- return;
- }
-
- /* Can't do this for SUB. */
- if (subtract)
- {
- inst.error = _("subtract valid only on lo regs");
- return;
- }
-
- inst.instruction = (T_OPCODE_ADD_HI
- | (Rd > 7 ? THUMB_H1 : 0)
- | (Rn > 7 ? THUMB_H2 : 0));
- inst.instruction |= (Rd & 7) | ((Rn & 7) << 3);
- }
- else
- {
- inst.instruction = subtract ? T_OPCODE_SUB_R3 : T_OPCODE_ADD_R3;
- inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
- }
+ inst.instruction |= (Rd << 4) | Rd;
+ inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ return;
}
- else
+ else /* Rd, Rs -> Rd, Rd, Rs */
{
- /* Immediate expression, now things start to get nasty. */
+ Rs = inst.operands[0].reg;
+ Rn = inst.operands[1].reg;
+ }
- /* First deal with HI regs, only very restricted cases allowed:
- Adjusting SP, and using PC or SP to get an address. */
- if ((Rd > 7 && (Rd != REG_SP || Rs != REG_SP))
- || (Rs > 7 && Rs != REG_SP && Rs != REG_PC))
+ /* We now have Rd and Rs, and Rn set to registers. */
+ if (Rd > 7 || Rs > 7 || Rn > 7)
+ {
+ if (Rs != Rd)
{
- inst.error = _("invalid Hi register with immediate");
+ inst.error = _("dest and source1 must be the same register");
return;
}
- if (inst.reloc.exp.X_op != O_constant)
+ /* Can't do this for SUB. */
+ if (subtract)
{
- /* Value isn't known yet, all we can do is store all the fragments
- we know about in the instruction and let the reloc hacking
- work it all out. */
- inst.instruction = (subtract ? 0x8000 : 0) | (Rd << 4) | Rs;
- inst.reloc.type = BFD_RELOC_ARM_THUMB_ADD;
+ inst.error = _("subtract valid only on lo regs");
+ return;
}
- else
- {
- int offset = inst.reloc.exp.X_add_number;
- if (subtract)
- offset = - offset;
-
- if (offset < 0)
- {
- offset = - offset;
- subtract = 1;
-
- /* Quick check, in case offset is MIN_INT. */
- if (offset < 0)
- {
- inst.error = _("immediate value out of range");
- return;
- }
- }
- /* Note - you cannot convert a subtract of 0 into an
- add of 0 because the carry flag is set differently. */
- else if (offset > 0)
- subtract = 0;
-
- if (Rd == REG_SP)
- {
- if (offset & ~0x1fc)
- {
- inst.error = _("invalid immediate value for stack adjust");
- return;
- }
- inst.instruction = subtract ? T_OPCODE_SUB_ST : T_OPCODE_ADD_ST;
- inst.instruction |= offset >> 2;
- }
- else if (Rs == REG_PC || Rs == REG_SP)
- {
- if (subtract
- || (offset & ~0x3fc))
- {
- inst.error = _("invalid immediate for address calculation");
- return;
- }
- inst.instruction = (Rs == REG_PC ? T_OPCODE_ADD_PC
- : T_OPCODE_ADD_SP);
- inst.instruction |= (Rd << 8) | (offset >> 2);
- }
- else if (Rs == Rd)
- {
- if (offset & ~0xff)
- {
- inst.error = _("immediate value out of range");
- return;
- }
- inst.instruction = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
- inst.instruction |= (Rd << 8) | offset;
- }
- else
- {
- if (offset & ~0x7)
- {
- inst.error = _("immediate value out of range");
- return;
- }
- inst.instruction = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
- inst.instruction |= Rd | (Rs << 3) | (offset << 6);
- }
- }
+ inst.instruction = T_OPCODE_ADD_HI;
+ inst.instruction |= (Rd & 8) << 4;
+ inst.instruction |= (Rd & 7);
+ inst.instruction |= Rn << 3;
}
-
- end_of_line (str);
+ else
+ {
+ inst.instruction = subtract ? T_OPCODE_SUB_R3 : T_OPCODE_ADD_R3;
+ inst.instruction |= Rd | (Rs << 3) | (Rn << 6);
+ }
}
static void
@@ -6645,8 +6551,8 @@
else /* Rd, imm -> Rd, Rd, imm */
Rs = inst.operands[0].reg;
- /* If we get here, we are doing a shift by an immediate. Rd and Rs
- are the registers, and inst.reloc is the immediate. */
+ /* If we get here, we are doing a shift by an immediate. inst.operands[0].reg
+ and Rs are the registers, and inst.reloc is the immediate. */
switch (inst.instruction)
{
case T_OPCODE_ASR_R: inst.instruction = T_OPCODE_ASR_I; break;
@@ -6658,12 +6564,6 @@
}
static void
-do_t_add (char * str)
-{
- thumb_add_sub (str, 0);
-}
-
-static void
do_t_adr (char * str)
{
if (parse_operands (str, OPERANDS2(RL,EXP)))
@@ -6958,12 +6858,6 @@
}
static void
-do_t_sub (char * str)
-{
- thumb_add_sub (str, 1);
-}
-
-static void
do_t_swi (char * str)
{
if (parse_operands (str, OPERANDS1(EXP)))
@@ -9707,7 +9601,7 @@
{
/* Thumb v1 (ARMv4T). */
{"adc", 0x4140, 2, ARM_EXT_V4T, do_t_arit},
- {"add", 0x0000, 2, ARM_EXT_V4T, do_t_add},
+ {"add", 0x0000, 2, ARM_EXT_V4T, do_t_add_sub},
{"and", 0x4000, 2, ARM_EXT_V4T, do_t_arit},
{"asr", 0x4100, 2, ARM_EXT_V4T, do_t_shift},
{"b", T_OPCODE_BRANCH, 2, ARM_EXT_V4T, do_t_branch12},
@@ -9759,7 +9653,7 @@
{"strb", 0x0000, 2, ARM_EXT_V4T, do_t_strb},
{"strh", 0x0000, 2, ARM_EXT_V4T, do_t_strh},
{"swi", 0xdf00, 2, ARM_EXT_V4T, do_t_swi},
- {"sub", 0x0000, 2, ARM_EXT_V4T, do_t_sub},
+ {"sub", 0x8000, 2, ARM_EXT_V4T, do_t_add_sub},
{"tst", T_OPCODE_TST, 2, ARM_EXT_V4T, do_t_arit},
/* Pseudo ops: */
{"adr", 0x000f, 2, ARM_EXT_V4T, do_t_adr},
@@ -11083,8 +10977,25 @@
{
int rd = (newval >> 4) & 0xf;
int rs = newval & 0xf;
- int subtract = newval & 0x8000;
+ int subtract = !!(newval & 0x8000);
+ /* Check for HI regs, only very restricted cases allowed:
+ Adjusting SP, and using PC or SP to get an address. */
+ if ((rd > 7 && (rd != REG_SP || rs != REG_SP))
+ || (rs > 7 && rs != REG_SP && rs != REG_PC))
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("invalid Hi register with immediate"));
+
+ /* If value is negative, choose the opposite instruction. */
+ if (value < 0)
+ {
+ value = -value;
+ subtract = !subtract;
+ if (value < 0)
+ as_bad_where (fixP->fx_file, fixP->fx_line,
+ _("immediate value out of range"));
+ }
+
if (rd == REG_SP)
{
if (value & ~0x1fc)
@@ -11095,8 +11006,7 @@
}
else if (rs == REG_PC || rs == REG_SP)
{
- if (subtract ||
- value & ~0x3fc)
+ if (subtract || value & ~0x3fc)
as_bad_where (fixP->fx_file, fixP->fx_line,
_("invalid immediate for address calculation (value = 0x%08lX)"),
(unsigned long) value);
@@ -11108,7 +11018,7 @@
{
if (value & ~0xff)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("invalid 8bit immediate"));
+ _("immediate value out of range"));
newval = subtract ? T_OPCODE_SUB_I8 : T_OPCODE_ADD_I8;
newval |= (rd << 8) | value;
}
@@ -11116,7 +11026,7 @@
{
if (value & ~0x7)
as_bad_where (fixP->fx_file, fixP->fx_line,
- _("invalid 3bit immediate"));
+ _("immediate value out of range"));
newval = subtract ? T_OPCODE_SUB_I3 : T_OPCODE_ADD_I3;
newval |= rd | (rs << 3) | (value << 6);
}