This is the mail archive of the
binutils@sourceware.org
mailing list for the binutils project.
[patch] Thumb-2 conditional execution improvements
- From: Paul Brook <paul at codesourcery dot com>
- To: binutils at sourceware dot org
- Date: Sun, 19 Mar 2006 22:09:03 +0000
- Subject: [patch] Thumb-2 conditional execution improvements
The attached patch improves gas handling of the Thumb-2 IT (If Then)
conditional execution instruction and related constraints. Specifically:
- Conditional branches are encoded as unconditional branches when inside an IT
block, and must be the last instruction in an IT block..
- BKPT is always unconditional, even inside an IT block.
- Some unconditional Arm instructions can be conditional in Thumb mode. I've
changed the initial parsing routine to allow all thumb instructions to be
conditional, then enforced truly unconditional instructions in individual
routines.
- Testcases for all the above.
Tested with cross to arm-none-eabi.
Ok?
Paul
2006-03-20 Paul Brook <paul@codesourcery.com>
gas/
* config/tc-arm.c (BAD_BRANCH, BAD_NOT_IT): Define.
(do_t_branch): Encode branches inside IT blocks as unconditional.
(do_t_cps): New function.
(do_t_blx, do_t_bkpt, do_t_branch23, do_t_bx, do_t_bxj, do_t_cpsi,
do_t_czb, do_t_it, do_t_setend, do_t_tb): Add IT constaints.
(opcode_lookup): Allow conditional suffixes on all instructions in
Thumb mode.
(md_assemble): Advance condexec state before checking for errors.
(insns): Use do_t_cps.
gas/testsuite/
* gas/arm/thumb2_bcond.d: New test.
* gas/arm/thumb2_bcond.s: New test.
* gas/arm/thumb2_it_bad.d: New test.
* gas/arm/thumb2_it_bad.l: New test.
* gas/arm/thumb2_it_bad.s: New test.
Index: gas/config/tc-arm.c
===================================================================
RCS file: /var/cvsroot/src-cvs/src/gas/config/tc-arm.c,v
retrieving revision 1.248
diff -u -p -r1.248 tc-arm.c
--- gas/config/tc-arm.c 17 Mar 2006 14:03:36 -0000 1.248
+++ gas/config/tc-arm.c 19 Mar 2006 21:33:15 -0000
@@ -568,6 +568,8 @@ struct asm_opcode
#define BAD_HIREG _("lo register required")
#define BAD_THUMB32 _("instruction not supported in Thumb16 mode")
#define BAD_ADDR_MODE _("instruction does not accept this addressing mode");
+#define BAD_BRANCH _("branch must be last instruction in IT block")
+#define BAD_NOT_IT _("instruction not allowed in IT block")
static struct hash_control *arm_ops_hsh;
static struct hash_control *arm_cond_hsh;
@@ -6597,6 +6599,7 @@ do_t_bfx (void)
static void
do_t_blx (void)
{
+ constraint (current_it_mask && current_it_mask != 0x10, BAD_BRANCH);
if (inst.operands[0].isreg)
/* We have a register, so this is BLX(2). */
inst.instruction |= inst.operands[0].reg << 3;
@@ -6618,7 +6621,20 @@ static void
do_t_branch (void)
{
int opcode;
- if (inst.cond != COND_ALWAYS)
+ int cond;
+
+ if (current_it_mask)
+ {
+ /* Conditional branches inside IT blocks are encoded as unconditional
+ branches. */
+ cond = COND_ALWAYS;
+ /* A branch must be the last instruction in an IT block. */
+ constraint (current_it_mask != 0x10, BAD_BRANCH);
+ }
+ else
+ cond = inst.cond;
+
+ if (cond != COND_ALWAYS)
opcode = T_MNEM_bcond;
else
opcode = inst.instruction;
@@ -6626,23 +6642,23 @@ do_t_branch (void)
if (unified_syntax && inst.size_req == 4)
{
inst.instruction = THUMB_OP32(opcode);
- if (inst.cond == COND_ALWAYS)
+ if (cond == COND_ALWAYS)
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH25;
else
{
- assert (inst.cond != 0xF);
- inst.instruction |= inst.cond << 22;
+ assert (cond != 0xF);
+ inst.instruction |= cond << 22;
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH20;
}
}
else
{
inst.instruction = THUMB_OP16(opcode);
- if (inst.cond == COND_ALWAYS)
+ if (cond == COND_ALWAYS)
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12;
else
{
- inst.instruction |= inst.cond << 8;
+ inst.instruction |= cond << 8;
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9;
}
/* Allow section relaxation. */
@@ -6656,6 +6672,8 @@ do_t_branch (void)
static void
do_t_bkpt (void)
{
+ constraint (inst.cond != COND_ALWAYS,
+ _("instruction is always unconditional"));
if (inst.operands[0].present)
{
constraint (inst.operands[0].imm > 255,
@@ -6667,6 +6685,7 @@ do_t_bkpt (void)
static void
do_t_branch23 (void)
{
+ constraint (current_it_mask && current_it_mask != 0x10, BAD_BRANCH);
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH23;
inst.reloc.pc_rel = 1;
@@ -6685,6 +6704,7 @@ do_t_branch23 (void)
static void
do_t_bx (void)
{
+ constraint (current_it_mask && current_it_mask != 0x10, BAD_BRANCH);
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
@@ -6694,6 +6714,7 @@ do_t_bx (void)
static void
do_t_bxj (void)
{
+ constraint (current_it_mask && current_it_mask != 0x10, BAD_BRANCH);
if (inst.operands[0].reg == REG_PC)
as_tsktsk (_("use of r15 in bxj is not really useful"));
@@ -6709,8 +6730,16 @@ do_t_clz (void)
}
static void
+do_t_cps (void)
+{
+ constraint (current_it_mask, BAD_NOT_IT);
+ inst.instruction |= inst.operands[0].imm;
+}
+
+static void
do_t_cpsi (void)
{
+ constraint (current_it_mask, BAD_NOT_IT);
if (unified_syntax
&& (inst.operands[1].present || inst.size_req == 4)
&& ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6_notm))
@@ -6757,6 +6786,7 @@ do_t_cpy (void)
static void
do_t_czb (void)
{
+ constraint (current_it_mask, BAD_NOT_IT);
constraint (inst.operands[0].reg > 7, BAD_HIREG);
inst.instruction |= inst.operands[0].reg;
inst.reloc.pc_rel = 1;
@@ -6793,6 +6823,7 @@ do_t_it (void)
{
unsigned int cond = inst.operands[0].imm;
+ constraint (current_it_mask, BAD_NOT_IT);
current_it_mask = (inst.instruction & 0xf) | 0x10;
current_cc = cond;
@@ -7627,6 +7658,7 @@ do_t_rsb (void)
static void
do_t_setend (void)
{
+ constraint (current_it_mask, BAD_NOT_IT);
if (inst.operands[0].imm)
inst.instruction |= 0x8;
}
@@ -7895,12 +7927,13 @@ do_t_tb (void)
int half;
half = (inst.instruction & 0x10) != 0;
+ constraint (current_it_mask && current_it_mask != 0x10, BAD_BRANCH);
+ constraint (inst.operands[0].immisreg,
+ _("instruction requires register index"));
constraint (inst.operands[0].imm == 15,
_("PC is not a valid index register"));
constraint (!half && inst.operands[0].shifted,
_("instruction does not allow shifted index"));
- constraint (half && !inst.operands[0].shifted,
- _("instruction requires shifted index"));
inst.instruction |= (inst.operands[0].reg << 16) | inst.operands[0].imm;
}
@@ -8223,9 +8256,16 @@ opcode_lookup (char **str)
case OT_unconditional:
case OT_unconditionalF:
- /* delayed diagnostic */
- inst.error = BAD_COND;
- inst.cond = COND_ALWAYS;
+ if (thumb_mode)
+ {
+ inst.cond = cond->value;
+ }
+ else
+ {
+ /* delayed diagnostic */
+ inst.error = BAD_COND;
+ inst.cond = COND_ALWAYS;
+ }
return opcode;
default:
@@ -8320,13 +8360,15 @@ md_assemble (char *str)
{
int cond;
cond = current_cc ^ ((current_it_mask >> 4) & 1) ^ 1;
- if (cond != inst.cond)
+ current_it_mask <<= 1;
+ current_it_mask &= 0x1f;
+ /* The BKPT instruction is unconditional even in an IT block. */
+ if (!inst.error
+ && cond != inst.cond && opcode->tencode != do_t_bkpt)
{
as_bad (_("incorrect condition in IT block"));
return;
}
- current_it_mask <<= 1;
- current_it_mask &= 0x1f;
}
else if (inst.cond != COND_ALWAYS && opcode->tencode != do_t_branch)
{
@@ -8822,7 +8864,8 @@ static struct asm_barrier_opt barrier_op
TxCM(m1,m2, aop, T_MNEM_##top, nops, ops, ae, te)
/* Mnemonic that cannot be conditionalized. The ARM condition-code
- field is still 0xE. */
+ field is still 0xE. Many of the Thumb variants can be executed
+ conditionally, so this is checked separately. */
#define TUE(mnem, op, top, nops, ops, ae, te) \
{ #mnem, OPS##nops ops, OT_unconditional, 0x##op, 0x##top, ARM_VARIANT, \
THUMB_VARIANT, do_##ae, do_##te }
@@ -9160,7 +9203,7 @@ static const struct asm_opcode insns[] =
/* ARM V6 not included in V7M (eg. integer SIMD). */
#undef THUMB_VARIANT
#define THUMB_VARIANT &arm_ext_v6_notm
- TUF(cps, 1020000, f3af8100, 1, (I31b), imm0, imm0),
+ TUF(cps, 1020000, f3af8100, 1, (I31b), imm0, t_cps),
TCE(pkhbt, 6800010, eac00000, 4, (RRnpc, RRnpc, RRnpc, oSHll), pkhbt, t_pkhbt),
TCE(pkhtb, 6800050, eac00020, 4, (RRnpc, RRnpc, RRnpc, oSHar), pkhtb, t_pkhtb),
TCE(qadd16, 6200f10, fa90f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
Index: gas/testsuite/gas/arm/thumb2_bcond.d
===================================================================
RCS file: gas/testsuite/gas/arm/thumb2_bcond.d
diff -N gas/testsuite/gas/arm/thumb2_bcond.d
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gas/testsuite/gas/arm/thumb2_bcond.d 19 Mar 2006 20:53:07 -0000
@@ -0,0 +1,25 @@
+# as:
+# objdump: -dr --prefix-addresses --show-raw-insn
+
+.*: +file format .*arm.*
+
+Disassembly of section .text:
+0+000 <[^>]+> bf18 it ne
+0+002 <[^>]+> e7fd b(|ne).n 0+0 <[^>]+>
+0+004 <[^>]+> bf38 it cc
+0+006 <[^>]+> f7ff bffb b(|cc).w 0+0 <[^>]+>
+0+00a <[^>]+> bf28 it cs
+0+00c <[^>]+> f7ff fff8 bl(|cs) 0+0 <[^>]+>
+0+010 <[^>]+> bfb8 it lt
+0+012 <[^>]+> 47a8 blx(|lr) r5
+0+014 <[^>]+> bf08 it eq
+0+016 <[^>]+> 4740 bx(|eq) r8
+0+018 <[^>]+> bfc8 it gt
+0+01a <[^>]+> e8d4 f001 tbb(|gt) \[r4, r1\]
+0+01e <[^>]+> bfb8 it lt
+0+020 <[^>]+> df00 svc(|lt) 0
+0+022 <[^>]+> bfdc itt le
+0+024 <[^>]+> be00 bkpt 0x0000
+0+026 <[^>]+> bf00 nop
+0+028 <[^>]+> bf00 nop
+0+02a <[^>]+> bf00 nop
Index: gas/testsuite/gas/arm/thumb2_bcond.s
===================================================================
RCS file: gas/testsuite/gas/arm/thumb2_bcond.s
diff -N gas/testsuite/gas/arm/thumb2_bcond.s
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gas/testsuite/gas/arm/thumb2_bcond.s 19 Mar 2006 20:53:07 -0000
@@ -0,0 +1,25 @@
+ .text
+ .arch armv7
+ .thumb
+ .syntax unified
+ .thumb_func
+thumb2_bcond:
+ it ne
+ bne thumb2_bcond
+ it cc
+ bcc.w thumb2_bcond
+ it cs
+ blcs thumb2_bcond
+ it lt
+ blxlt r5
+ it eq
+ bxeq r8
+ it gt
+ tbbgt [r4, r1]
+ it lt
+ svclt 0
+ itt le
+ bkpt #0
+ nople
+ nop
+ nop
Index: gas/testsuite/gas/arm/thumb2_it_bad.d
===================================================================
RCS file: gas/testsuite/gas/arm/thumb2_it_bad.d
diff -N gas/testsuite/gas/arm/thumb2_it_bad.d
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gas/testsuite/gas/arm/thumb2_it_bad.d 16 Mar 2006 02:33:44 -0000
@@ -0,0 +1,3 @@
+#name: Invalid IT instructions
+#as:
+#error-output: thumb2_it_bad.l
Index: gas/testsuite/gas/arm/thumb2_it_bad.l
===================================================================
RCS file: gas/testsuite/gas/arm/thumb2_it_bad.l
diff -N gas/testsuite/gas/arm/thumb2_it_bad.l
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gas/testsuite/gas/arm/thumb2_it_bad.l 19 Mar 2006 21:40:49 -0000
@@ -0,0 +1,12 @@
+[^:]*: Assembler messages:
+[^:]*:8: Error: branch must be last instruction in IT block -- `beq foo'
+[^:]*:9: Error: branch must be last instruction in IT block -- `bleq foo'
+[^:]*:10: Error: branch must be last instruction in IT block -- `blxeq r0'
+[^:]*:11: Error: instruction not allowed in IT block -- `cbzeq r0,foo'
+[^:]*:13: Error: branch must be last instruction in IT block -- `bxeq r0'
+[^:]*:14: Error: branch must be last instruction in IT block -- `tbbeq \[r0,r1\]'
+[^:]*:15: Error: instruction not allowed in IT block -- `cpsieeq f'
+[^:]*:17: Error: instruction not allowed in IT block -- `cpseq #0x10'
+[^:]*:19: Error: instruction is always unconditional -- `bkpteq 0'
+[^:]*:20: Error: instruction not allowed in IT block -- `setendeq le'
+[^:]*:22: Error: instruction not allowed in IT block -- `iteq eq'
Index: gas/testsuite/gas/arm/thumb2_it_bad.s
===================================================================
RCS file: gas/testsuite/gas/arm/thumb2_it_bad.s
diff -N gas/testsuite/gas/arm/thumb2_it_bad.s
--- /dev/null 1 Jan 1970 00:00:00 -0000
+++ gas/testsuite/gas/arm/thumb2_it_bad.s 19 Mar 2006 21:36:49 -0000
@@ -0,0 +1,24 @@
+ .text
+ .syntax unified
+ .arch armv7a
+ .thumb
+ .thumb_func
+thumb2_it_bad:
+ itttt eq
+ beq foo
+ bleq foo
+ blxeq r0
+ cbzeq r0, foo
+ ittt eq
+ bxeq r0
+ tbbeq [r0, r1]
+ cpsieeq f
+ it eq
+ cpseq #0x10
+ itt eq
+ bkpteq 0
+ setendeq le
+ it eq
+ iteq eq
+ nop
+foo: