aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--gas/ChangeLog12
-rw-r--r--gas/config/tc-arm.c75
-rw-r--r--gas/testsuite/ChangeLog8
-rw-r--r--gas/testsuite/gas/arm/thumb2_bcond.d25
-rw-r--r--gas/testsuite/gas/arm/thumb2_bcond.s25
-rw-r--r--gas/testsuite/gas/arm/thumb2_it_bad.d3
-rw-r--r--gas/testsuite/gas/arm/thumb2_it_bad.l12
-rw-r--r--gas/testsuite/gas/arm/thumb2_it_bad.s24
8 files changed, 168 insertions, 16 deletions
diff --git a/gas/ChangeLog b/gas/ChangeLog
index 7ea4f29..53f3bb2 100644
--- a/gas/ChangeLog
+++ b/gas/ChangeLog
@@ -1,5 +1,17 @@
2006-03-20 Paul Brook <paul@codesourcery.com>
+ * 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.
+
+2006-03-20 Paul Brook <paul@codesourcery.com>
+
* config/tc-arm.c (output_relax_insn): Call dwarf2_emit_insn before
outputting the insn.
diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c
index 01b49e3..583acf2 100644
--- a/gas/config/tc-arm.c
+++ b/gas/config/tc-arm.c
@@ -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;
}
@@ -8225,9 +8258,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:
@@ -8322,13 +8362,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)
{
@@ -8824,7 +8866,8 @@ static struct asm_barrier_opt barrier_opt_names[] =
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 }
@@ -9162,7 +9205,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),
diff --git a/gas/testsuite/ChangeLog b/gas/testsuite/ChangeLog
index 982f74b..cde2329 100644
--- a/gas/testsuite/ChangeLog
+++ b/gas/testsuite/ChangeLog
@@ -1,3 +1,11 @@
+2006-03-20 Paul Brook <paul@codesourcery.com>
+
+ * 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.
+
2006-03-17 Paul Brook <paul@codesourcery.com>
* gas/arm/thumb32.d: Add ldm and stm tests.
diff --git a/gas/testsuite/gas/arm/thumb2_bcond.d b/gas/testsuite/gas/arm/thumb2_bcond.d
new file mode 100644
index 0000000..90b8e20
--- /dev/null
+++ b/gas/testsuite/gas/arm/thumb2_bcond.d
@@ -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
diff --git a/gas/testsuite/gas/arm/thumb2_bcond.s b/gas/testsuite/gas/arm/thumb2_bcond.s
new file mode 100644
index 0000000..4a066f2
--- /dev/null
+++ b/gas/testsuite/gas/arm/thumb2_bcond.s
@@ -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
diff --git a/gas/testsuite/gas/arm/thumb2_it_bad.d b/gas/testsuite/gas/arm/thumb2_it_bad.d
new file mode 100644
index 0000000..1cca8b9
--- /dev/null
+++ b/gas/testsuite/gas/arm/thumb2_it_bad.d
@@ -0,0 +1,3 @@
+#name: Invalid IT instructions
+#as:
+#error-output: thumb2_it_bad.l
diff --git a/gas/testsuite/gas/arm/thumb2_it_bad.l b/gas/testsuite/gas/arm/thumb2_it_bad.l
new file mode 100644
index 0000000..e2e96cd
--- /dev/null
+++ b/gas/testsuite/gas/arm/thumb2_it_bad.l
@@ -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'
diff --git a/gas/testsuite/gas/arm/thumb2_it_bad.s b/gas/testsuite/gas/arm/thumb2_it_bad.s
new file mode 100644
index 0000000..6add4fb
--- /dev/null
+++ b/gas/testsuite/gas/arm/thumb2_it_bad.s
@@ -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: