From dfa9f0d57b133318a0de825a53a8b8ced0e86914 Mon Sep 17 00:00:00 2001 From: Paul Brook Date: Mon, 20 Mar 2006 15:38:02 +0000 Subject: [PATCH] 2006-03-20 Paul Brook 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. --- gas/ChangeLog | 12 +++++ gas/config/tc-arm.c | 75 +++++++++++++++++++++------ gas/testsuite/ChangeLog | 8 +++ gas/testsuite/gas/arm/thumb2_bcond.d | 25 +++++++++ gas/testsuite/gas/arm/thumb2_bcond.s | 25 +++++++++ gas/testsuite/gas/arm/thumb2_it_bad.d | 3 ++ gas/testsuite/gas/arm/thumb2_it_bad.l | 12 +++++ gas/testsuite/gas/arm/thumb2_it_bad.s | 24 +++++++++ 8 files changed, 168 insertions(+), 16 deletions(-) create mode 100644 gas/testsuite/gas/arm/thumb2_bcond.d create mode 100644 gas/testsuite/gas/arm/thumb2_bcond.s create mode 100644 gas/testsuite/gas/arm/thumb2_it_bad.d create mode 100644 gas/testsuite/gas/arm/thumb2_it_bad.l create mode 100644 gas/testsuite/gas/arm/thumb2_it_bad.s diff --git a/gas/ChangeLog b/gas/ChangeLog index 7ea4f29f4c..53f3bb2513 100644 --- a/gas/ChangeLog +++ b/gas/ChangeLog @@ -1,3 +1,15 @@ +2006-03-20 Paul Brook + + * 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 * config/tc-arm.c (output_relax_insn): Call dwarf2_emit_insn before diff --git a/gas/config/tc-arm.c b/gas/config/tc-arm.c index 01b49e3e32..583acf27ba 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")); @@ -6708,9 +6729,17 @@ do_t_clz (void) inst.instruction |= inst.operands[1].reg; } +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 982f74b246..cde2329160 100644 --- a/gas/testsuite/ChangeLog +++ b/gas/testsuite/ChangeLog @@ -1,3 +1,11 @@ +2006-03-20 Paul Brook + + * 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 * 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 0000000000..90b8e2096d --- /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 0000000000..4a066f2438 --- /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 0000000000..1cca8b9650 --- /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 0000000000..e2e96cddb4 --- /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 0000000000..6add4fb517 --- /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: