Skip to content

Commit

Permalink
ARM: provide diagnostics on more writeback LDM/STM instructions
Browse files Browse the repository at this point in the history
The set of circumstances where the writeback register is allowed to be in the
list of registers is rather baroque, but I think this implements them all on
the assembly parsing side.

For disassembly, we still warn about an ARM-mode LDM even if the architecture
revision is < v7 (the required architecture information isn't available). It's
a silly instruction anyway, so hopefully no-one will mind.

rdar://problem/15223374

git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@193185 91177308-0d34-0410-b5e6-96231b3b80d8
  • Loading branch information
TNorthover committed Oct 22, 2013
1 parent e18273b commit 01b0e94
Show file tree
Hide file tree
Showing 5 changed files with 80 additions and 17 deletions.
28 changes: 25 additions & 3 deletions lib/Target/ARM/AsmParser/ARMAsmParser.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5416,6 +5416,7 @@ validateInstruction(MCInst &Inst,
"bitfield width must be in range [1,32-lsb]");
return false;
}
// Notionally handles ARM::tLDMIA_UPD too.
case ARM::tLDMIA: {
// If we're parsing Thumb2, the .w variant is available and handles
// most cases that are normally illegal for a Thumb1 LDM instruction.
Expand Down Expand Up @@ -5444,7 +5445,19 @@ validateInstruction(MCInst &Inst,

break;
}
case ARM::t2LDMIA_UPD: {
case ARM::LDMIA_UPD:
case ARM::LDMDB_UPD:
case ARM::LDMIB_UPD:
case ARM::LDMDA_UPD:
// ARM variants loading and updating the same register are only officially
// UNPREDICTABLE on v7 upwards. Goodness knows what they did before.
if (!hasV7Ops())
break;
// Fallthrough
case ARM::t2LDMIA_UPD:
case ARM::t2LDMDB_UPD:
case ARM::t2STMIA_UPD:
case ARM::t2STMDB_UPD: {
if (listContainsReg(Inst, 3, Inst.getOperand(0).getReg()))
return Error(Operands[4]->getStartLoc(),
"writeback operator '!' not allowed when base register "
Expand Down Expand Up @@ -5490,10 +5503,19 @@ validateInstruction(MCInst &Inst,
break;
}
case ARM::tSTMIA_UPD: {
bool ListContainsBase;
if (checkLowRegisterList(Inst, 4, 0, 0, ListContainsBase) && !isThumbTwo())
bool ListContainsBase, InvalidLowList;
InvalidLowList = checkLowRegisterList(Inst, 4, Inst.getOperand(0).getReg(),
0, ListContainsBase);
if (InvalidLowList && !isThumbTwo())
return Error(Operands[4]->getStartLoc(),
"registers must be in range r0-r7");

// This would be converted to a 32-bit stm, but that's not valid if the
// writeback register is in the list.
if (InvalidLowList && ListContainsBase)
return Error(Operands[4]->getStartLoc(),
"writeback operator '!' not allowed when base register "
"in register list");
break;
}
case ARM::tADDrSP: {
Expand Down
30 changes: 16 additions & 14 deletions lib/Target/ARM/Disassembler/ARMDisassembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1203,20 +1203,22 @@ static DecodeStatus DecodeRegListOperand(MCInst &Inst, unsigned Val,
uint64_t Address, const void *Decoder) {
DecodeStatus S = MCDisassembler::Success;

bool writebackLoad = false;
unsigned writebackReg = 0;
bool NeedDisjointWriteback = false;
unsigned WritebackReg = 0;
switch (Inst.getOpcode()) {
default:
break;
case ARM::LDMIA_UPD:
case ARM::LDMDB_UPD:
case ARM::LDMIB_UPD:
case ARM::LDMDA_UPD:
case ARM::t2LDMIA_UPD:
case ARM::t2LDMDB_UPD:
writebackLoad = true;
writebackReg = Inst.getOperand(0).getReg();
break;
default:
break;
case ARM::LDMIA_UPD:
case ARM::LDMDB_UPD:
case ARM::LDMIB_UPD:
case ARM::LDMDA_UPD:
case ARM::t2LDMIA_UPD:
case ARM::t2LDMDB_UPD:
case ARM::t2STMIA_UPD:
case ARM::t2STMDB_UPD:
NeedDisjointWriteback = true;
WritebackReg = Inst.getOperand(0).getReg();
break;
}

// Empty register lists are not allowed.
Expand All @@ -1226,7 +1228,7 @@ static DecodeStatus DecodeRegListOperand(MCInst &Inst, unsigned Val,
if (!Check(S, DecodeGPRRegisterClass(Inst, i, Address, Decoder)))
return MCDisassembler::Fail;
// Writeback not allowed if Rn is in the target list.
if (writebackLoad && writebackReg == Inst.end()[-1].getReg())
if (NeedDisjointWriteback && WritebackReg == Inst.end()[-1].getReg())
Check(S, MCDisassembler::SoftFail);
}
}
Expand Down
7 changes: 7 additions & 0 deletions test/MC/ARM/diagnostics.s
Original file line number Diff line number Diff line change
Expand Up @@ -429,3 +429,10 @@

bkpteq #7
@ CHECK-ERRORS: error: instruction 'bkpt' is not predicable, but condition code specified

ldm r2!, {r2, r3}
ldmdb r2!, {r2, r3}
ldmda r2!, {r2, r3}
@ CHECK-ERRORS: error: writeback operator '!' not allowed when base register in register list
@ CHECK-ERRORS: error: writeback operator '!' not allowed when base register in register list
@ CHECK-ERRORS: error: writeback operator '!' not allowed when base register in register list
16 changes: 16 additions & 0 deletions test/MC/ARM/thumb-diagnostics.s
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,8 @@ error: invalid operand for instruction
ldm r2!, {r5, r8}
ldm r2, {r5, r7}
ldm r2!, {r2, r3, r4}
ldm r2!, {r2, r3, r4, r10}
ldmdb r2!, {r2, r3, r4}
@ CHECK-ERRORS: error: registers must be in range r0-r7
@ CHECK-ERRORS: ldm r2!, {r5, r8}
@ CHECK-ERRORS: ^
Expand All @@ -66,6 +68,12 @@ error: invalid operand for instruction
@ CHECK-ERRORS: error: writeback operator '!' not allowed when base register in register list
@ CHECK-ERRORS: ldm r2!, {r2, r3, r4}
@ CHECK-ERRORS: ^
@ CHECK-ERRORS-V8: error: writeback operator '!' not allowed when base register in register list
@ CHECK-ERRORS-V8: ldm r2!, {r2, r3, r4, r10}
@ CHECK-ERRORS-V8: ^
@ CHECK-ERRORS-V8: error: writeback operator '!' not allowed when base register in register list
@ CHECK-ERRORS-V8: ldmdb r2!, {r2, r3, r4}
@ CHECK-ERRORS-V8: ^

@ Invalid writeback and register lists for PUSH/POP
pop {r1, r2, r10}
Expand All @@ -81,12 +89,20 @@ error: invalid operand for instruction
@ Invalid writeback and register lists for STM
stm r1, {r2, r6}
stm r1!, {r2, r9}
stm r2!, {r2, r9}
stmdb r2!, {r0, r2}
@ CHECK-ERRORS: error: instruction requires: thumb2
@ CHECK-ERRORS: stm r1, {r2, r6}
@ CHECK-ERRORS: ^
@ CHECK-ERRORS: error: registers must be in range r0-r7
@ CHECK-ERRORS: stm r1!, {r2, r9}
@ CHECK-ERRORS: ^
@ CHECK-ERRORS-V8: error: writeback operator '!' not allowed when base register in register list
@ CHECK-ERRORS-V8: stm r2!, {r2, r9}
@ CHECK-ERRORS-V8: ^
@ CHECK-ERRORS-V8: error: writeback operator '!' not allowed when base register in register list
@ CHECK-ERRORS-V8: stmdb r2!, {r0, r2}
@ CHECK-ERRORS-V8: ^

@ Out of range immediates for LSL instruction.
lsls r4, r5, #-1
Expand Down
16 changes: 16 additions & 0 deletions test/MC/Disassembler/ARM/invalid-thumbv7.txt
Original file line number Diff line number Diff line change
Expand Up @@ -389,3 +389,19 @@
[0x80 0xf9 0x30 0x0b]
# CHECK: invalid instruction encoding
# CHECK-NEXT: [0x80 0xf9 0x30 0x0b]


#------------------------------------------------------------------------------
# Unpredictable STMs
#------------------------------------------------------------------------------

# 32-bit Thumb STM instructions cannot have a writeback register which appears
# in the list.

[0xa1,0xe8,0x07,0x04]
# CHECK: warning: potentially undefined instruction encoding
# CHECK-NEXT: [0xa1,0xe8,0x07,0x04]

[0x21,0xe9,0x07,0x04]
# CHECK: warning: potentially undefined instruction encoding
# CHECK-NEXT: [0x21,0xe9,0x07,0x04]

0 comments on commit 01b0e94

Please sign in to comment.