This is the mail archive of the binutils@sourceware.org mailing list for the binutils project.


Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]
Other format: [Raw text]

[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:

Index Nav: [Date Index] [Subject Index] [Author Index] [Thread Index]
Message Nav: [Date Prev] [Date Next] [Thread Prev] [Thread Next]