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.
This commit is contained in:
parent
6e1cb1a6e6
commit
dfa9f0d57b
|
@ -1,3 +1,15 @@
|
||||||
|
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>
|
2006-03-20 Paul Brook <paul@codesourcery.com>
|
||||||
|
|
||||||
* config/tc-arm.c (output_relax_insn): Call dwarf2_emit_insn before
|
* config/tc-arm.c (output_relax_insn): Call dwarf2_emit_insn before
|
||||||
|
|
|
@ -568,6 +568,8 @@ struct asm_opcode
|
||||||
#define BAD_HIREG _("lo register required")
|
#define BAD_HIREG _("lo register required")
|
||||||
#define BAD_THUMB32 _("instruction not supported in Thumb16 mode")
|
#define BAD_THUMB32 _("instruction not supported in Thumb16 mode")
|
||||||
#define BAD_ADDR_MODE _("instruction does not accept this addressing 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_ops_hsh;
|
||||||
static struct hash_control *arm_cond_hsh;
|
static struct hash_control *arm_cond_hsh;
|
||||||
|
@ -6597,6 +6599,7 @@ do_t_bfx (void)
|
||||||
static void
|
static void
|
||||||
do_t_blx (void)
|
do_t_blx (void)
|
||||||
{
|
{
|
||||||
|
constraint (current_it_mask && current_it_mask != 0x10, BAD_BRANCH);
|
||||||
if (inst.operands[0].isreg)
|
if (inst.operands[0].isreg)
|
||||||
/* We have a register, so this is BLX(2). */
|
/* We have a register, so this is BLX(2). */
|
||||||
inst.instruction |= inst.operands[0].reg << 3;
|
inst.instruction |= inst.operands[0].reg << 3;
|
||||||
|
@ -6618,7 +6621,20 @@ static void
|
||||||
do_t_branch (void)
|
do_t_branch (void)
|
||||||
{
|
{
|
||||||
int opcode;
|
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;
|
opcode = T_MNEM_bcond;
|
||||||
else
|
else
|
||||||
opcode = inst.instruction;
|
opcode = inst.instruction;
|
||||||
|
@ -6626,23 +6642,23 @@ do_t_branch (void)
|
||||||
if (unified_syntax && inst.size_req == 4)
|
if (unified_syntax && inst.size_req == 4)
|
||||||
{
|
{
|
||||||
inst.instruction = THUMB_OP32(opcode);
|
inst.instruction = THUMB_OP32(opcode);
|
||||||
if (inst.cond == COND_ALWAYS)
|
if (cond == COND_ALWAYS)
|
||||||
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH25;
|
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH25;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
assert (inst.cond != 0xF);
|
assert (cond != 0xF);
|
||||||
inst.instruction |= inst.cond << 22;
|
inst.instruction |= cond << 22;
|
||||||
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH20;
|
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH20;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inst.instruction = THUMB_OP16(opcode);
|
inst.instruction = THUMB_OP16(opcode);
|
||||||
if (inst.cond == COND_ALWAYS)
|
if (cond == COND_ALWAYS)
|
||||||
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12;
|
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH12;
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
inst.instruction |= inst.cond << 8;
|
inst.instruction |= cond << 8;
|
||||||
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9;
|
inst.reloc.type = BFD_RELOC_THUMB_PCREL_BRANCH9;
|
||||||
}
|
}
|
||||||
/* Allow section relaxation. */
|
/* Allow section relaxation. */
|
||||||
|
@ -6656,6 +6672,8 @@ do_t_branch (void)
|
||||||
static void
|
static void
|
||||||
do_t_bkpt (void)
|
do_t_bkpt (void)
|
||||||
{
|
{
|
||||||
|
constraint (inst.cond != COND_ALWAYS,
|
||||||
|
_("instruction is always unconditional"));
|
||||||
if (inst.operands[0].present)
|
if (inst.operands[0].present)
|
||||||
{
|
{
|
||||||
constraint (inst.operands[0].imm > 255,
|
constraint (inst.operands[0].imm > 255,
|
||||||
|
@ -6667,6 +6685,7 @@ do_t_bkpt (void)
|
||||||
static void
|
static void
|
||||||
do_t_branch23 (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.type = BFD_RELOC_THUMB_PCREL_BRANCH23;
|
||||||
inst.reloc.pc_rel = 1;
|
inst.reloc.pc_rel = 1;
|
||||||
|
|
||||||
|
@ -6685,6 +6704,7 @@ do_t_branch23 (void)
|
||||||
static void
|
static void
|
||||||
do_t_bx (void)
|
do_t_bx (void)
|
||||||
{
|
{
|
||||||
|
constraint (current_it_mask && current_it_mask != 0x10, BAD_BRANCH);
|
||||||
inst.instruction |= inst.operands[0].reg << 3;
|
inst.instruction |= inst.operands[0].reg << 3;
|
||||||
/* ??? FIXME: Should add a hacky reloc here if reg is REG_PC. The reloc
|
/* ??? 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
|
should cause the alignment to be checked once it is known. This is
|
||||||
|
@ -6694,6 +6714,7 @@ do_t_bx (void)
|
||||||
static void
|
static void
|
||||||
do_t_bxj (void)
|
do_t_bxj (void)
|
||||||
{
|
{
|
||||||
|
constraint (current_it_mask && current_it_mask != 0x10, BAD_BRANCH);
|
||||||
if (inst.operands[0].reg == REG_PC)
|
if (inst.operands[0].reg == REG_PC)
|
||||||
as_tsktsk (_("use of r15 in bxj is not really useful"));
|
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;
|
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
|
static void
|
||||||
do_t_cpsi (void)
|
do_t_cpsi (void)
|
||||||
{
|
{
|
||||||
|
constraint (current_it_mask, BAD_NOT_IT);
|
||||||
if (unified_syntax
|
if (unified_syntax
|
||||||
&& (inst.operands[1].present || inst.size_req == 4)
|
&& (inst.operands[1].present || inst.size_req == 4)
|
||||||
&& ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6_notm))
|
&& ARM_CPU_HAS_FEATURE (cpu_variant, arm_ext_v6_notm))
|
||||||
|
@ -6757,6 +6786,7 @@ do_t_cpy (void)
|
||||||
static void
|
static void
|
||||||
do_t_czb (void)
|
do_t_czb (void)
|
||||||
{
|
{
|
||||||
|
constraint (current_it_mask, BAD_NOT_IT);
|
||||||
constraint (inst.operands[0].reg > 7, BAD_HIREG);
|
constraint (inst.operands[0].reg > 7, BAD_HIREG);
|
||||||
inst.instruction |= inst.operands[0].reg;
|
inst.instruction |= inst.operands[0].reg;
|
||||||
inst.reloc.pc_rel = 1;
|
inst.reloc.pc_rel = 1;
|
||||||
|
@ -6793,6 +6823,7 @@ do_t_it (void)
|
||||||
{
|
{
|
||||||
unsigned int cond = inst.operands[0].imm;
|
unsigned int cond = inst.operands[0].imm;
|
||||||
|
|
||||||
|
constraint (current_it_mask, BAD_NOT_IT);
|
||||||
current_it_mask = (inst.instruction & 0xf) | 0x10;
|
current_it_mask = (inst.instruction & 0xf) | 0x10;
|
||||||
current_cc = cond;
|
current_cc = cond;
|
||||||
|
|
||||||
|
@ -7627,6 +7658,7 @@ do_t_rsb (void)
|
||||||
static void
|
static void
|
||||||
do_t_setend (void)
|
do_t_setend (void)
|
||||||
{
|
{
|
||||||
|
constraint (current_it_mask, BAD_NOT_IT);
|
||||||
if (inst.operands[0].imm)
|
if (inst.operands[0].imm)
|
||||||
inst.instruction |= 0x8;
|
inst.instruction |= 0x8;
|
||||||
}
|
}
|
||||||
|
@ -7895,12 +7927,13 @@ do_t_tb (void)
|
||||||
int half;
|
int half;
|
||||||
|
|
||||||
half = (inst.instruction & 0x10) != 0;
|
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,
|
constraint (inst.operands[0].imm == 15,
|
||||||
_("PC is not a valid index register"));
|
_("PC is not a valid index register"));
|
||||||
constraint (!half && inst.operands[0].shifted,
|
constraint (!half && inst.operands[0].shifted,
|
||||||
_("instruction does not allow shifted index"));
|
_("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;
|
inst.instruction |= (inst.operands[0].reg << 16) | inst.operands[0].imm;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8225,9 +8258,16 @@ opcode_lookup (char **str)
|
||||||
|
|
||||||
case OT_unconditional:
|
case OT_unconditional:
|
||||||
case OT_unconditionalF:
|
case OT_unconditionalF:
|
||||||
|
if (thumb_mode)
|
||||||
|
{
|
||||||
|
inst.cond = cond->value;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
/* delayed diagnostic */
|
/* delayed diagnostic */
|
||||||
inst.error = BAD_COND;
|
inst.error = BAD_COND;
|
||||||
inst.cond = COND_ALWAYS;
|
inst.cond = COND_ALWAYS;
|
||||||
|
}
|
||||||
return opcode;
|
return opcode;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
|
@ -8322,13 +8362,15 @@ md_assemble (char *str)
|
||||||
{
|
{
|
||||||
int cond;
|
int cond;
|
||||||
cond = current_cc ^ ((current_it_mask >> 4) & 1) ^ 1;
|
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"));
|
as_bad (_("incorrect condition in IT block"));
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
current_it_mask <<= 1;
|
|
||||||
current_it_mask &= 0x1f;
|
|
||||||
}
|
}
|
||||||
else if (inst.cond != COND_ALWAYS && opcode->tencode != do_t_branch)
|
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)
|
TxCM(m1,m2, aop, T_MNEM_##top, nops, ops, ae, te)
|
||||||
|
|
||||||
/* Mnemonic that cannot be conditionalized. The ARM condition-code
|
/* 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) \
|
#define TUE(mnem, op, top, nops, ops, ae, te) \
|
||||||
{ #mnem, OPS##nops ops, OT_unconditional, 0x##op, 0x##top, ARM_VARIANT, \
|
{ #mnem, OPS##nops ops, OT_unconditional, 0x##op, 0x##top, ARM_VARIANT, \
|
||||||
THUMB_VARIANT, do_##ae, do_##te }
|
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). */
|
/* ARM V6 not included in V7M (eg. integer SIMD). */
|
||||||
#undef THUMB_VARIANT
|
#undef THUMB_VARIANT
|
||||||
#define THUMB_VARIANT &arm_ext_v6_notm
|
#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(pkhbt, 6800010, eac00000, 4, (RRnpc, RRnpc, RRnpc, oSHll), pkhbt, t_pkhbt),
|
||||||
TCE(pkhtb, 6800050, eac00020, 4, (RRnpc, RRnpc, RRnpc, oSHar), pkhtb, t_pkhtb),
|
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),
|
TCE(qadd16, 6200f10, fa90f010, 3, (RRnpc, RRnpc, RRnpc), rd_rn_rm, t_simd),
|
||||||
|
|
|
@ -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>
|
2006-03-17 Paul Brook <paul@codesourcery.com>
|
||||||
|
|
||||||
* gas/arm/thumb32.d: Add ldm and stm tests.
|
* gas/arm/thumb32.d: Add ldm and stm tests.
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,3 @@
|
||||||
|
#name: Invalid IT instructions
|
||||||
|
#as:
|
||||||
|
#error-output: 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'
|
|
@ -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:
|
Loading…
Reference in New Issue