forked from OSchip/llvm-project
ARM: improve instruction validation for thumb mode
The ARM Architecture Reference Manual states the following: LDM{,IA,DB}: The SP cannot be in the list. The PC can be in the list. If the PC is in the list: • the LR must not be in the list • the instruction must be either outside any IT block, or the last instruction in an IT block. POP: The PC can be in the list. If the PC is in the list: • the LR must not be in the list • the instruction must be either outside any IT block, or the last instruction in an IT block. PUSH: The SP and PC can be in the list in ARM instructions, but not in Thumb instructions. STM:{,IA,DB}: The SP and PC can be in the list in ARM instructions, but not in Thumb instructions. llvm-svn: 224502
This commit is contained in:
parent
69973e0fa0
commit
3a23917d48
|
@ -164,7 +164,10 @@ class ARMAsmParser : public MCTargetAsmParser {
|
|||
// according to count of instructions in block.
|
||||
// ~0U if no active IT block.
|
||||
} ITState;
|
||||
bool inITBlock() { return ITState.CurPosition != ~0U;}
|
||||
bool inITBlock() { return ITState.CurPosition != ~0U; }
|
||||
bool lastInITBlock() {
|
||||
return ITState.CurPosition == 4 - countTrailingZeros(ITState.Mask);
|
||||
}
|
||||
void forwardITPosition() {
|
||||
if (!inITBlock()) return;
|
||||
// Move to the next instruction in the IT block, if there is one. If not,
|
||||
|
@ -186,6 +189,11 @@ class ARMAsmParser : public MCTargetAsmParser {
|
|||
return getParser().Error(L, Msg, Ranges);
|
||||
}
|
||||
|
||||
bool validatetLDMRegList(MCInst Inst, const OperandVector &Operands,
|
||||
unsigned ListNo, bool IsPop = false);
|
||||
bool validatetSTMRegList(MCInst Inst, const OperandVector &Operands,
|
||||
unsigned ListNo);
|
||||
|
||||
int tryParseRegister();
|
||||
bool tryParseRegisterWithWriteBack(OperandVector &);
|
||||
int tryParseShiftRegister(OperandVector &);
|
||||
|
@ -6011,6 +6019,50 @@ static bool instIsBreakpoint(const MCInst &Inst) {
|
|||
|
||||
}
|
||||
|
||||
bool ARMAsmParser::validatetLDMRegList(MCInst Inst,
|
||||
const OperandVector &Operands,
|
||||
unsigned ListNo, bool IsPop) {
|
||||
const ARMOperand &Op = static_cast<const ARMOperand &>(*Operands[ListNo]);
|
||||
bool HasWritebackToken = Op.isToken() && Op.getToken() == "!";
|
||||
|
||||
bool ListContainsSP = listContainsReg(Inst, ListNo, ARM::SP);
|
||||
bool ListContainsLR = listContainsReg(Inst, ListNo, ARM::LR);
|
||||
bool ListContainsPC = listContainsReg(Inst, ListNo, ARM::PC);
|
||||
|
||||
if (!IsPop && ListContainsSP)
|
||||
return Error(Operands[ListNo + HasWritebackToken]->getStartLoc(),
|
||||
"SP may not be in the register list");
|
||||
else if (ListContainsPC && ListContainsLR)
|
||||
return Error(Operands[ListNo + HasWritebackToken]->getStartLoc(),
|
||||
"PC and LR may not be in the register list simultaneously");
|
||||
else if (inITBlock() && !lastInITBlock() && ListContainsPC)
|
||||
return Error(Operands[ListNo + HasWritebackToken]->getStartLoc(),
|
||||
"instruction must be outside of IT block or the last "
|
||||
"instruction in an IT block");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool ARMAsmParser::validatetSTMRegList(MCInst Inst,
|
||||
const OperandVector &Operands,
|
||||
unsigned ListNo) {
|
||||
const ARMOperand &Op = static_cast<const ARMOperand &>(*Operands[ListNo]);
|
||||
bool HasWritebackToken = Op.isToken() && Op.getToken() == "!";
|
||||
|
||||
bool ListContainsSP = listContainsReg(Inst, ListNo, ARM::SP);
|
||||
bool ListContainsPC = listContainsReg(Inst, ListNo, ARM::PC);
|
||||
|
||||
if (ListContainsSP && ListContainsPC)
|
||||
return Error(Operands[ListNo + HasWritebackToken]->getStartLoc(),
|
||||
"SP and PC may not be in the register list");
|
||||
else if (ListContainsSP)
|
||||
return Error(Operands[ListNo + HasWritebackToken]->getStartLoc(),
|
||||
"SP may not be in the register list");
|
||||
else if (ListContainsPC)
|
||||
return Error(Operands[ListNo + HasWritebackToken]->getStartLoc(),
|
||||
"PC may not be in the register list");
|
||||
return false;
|
||||
}
|
||||
|
||||
// FIXME: We would really like to be able to tablegen'erate this.
|
||||
bool ARMAsmParser::validateInstruction(MCInst &Inst,
|
||||
const OperandVector &Operands) {
|
||||
|
@ -6194,9 +6246,9 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst,
|
|||
return Error(Operands[3]->getStartLoc(),
|
||||
"writeback operator '!' not allowed when base register "
|
||||
"in register list");
|
||||
if (listContainsReg(Inst, 3 + HasWritebackToken, ARM::SP))
|
||||
return Error(Operands[3 + HasWritebackToken]->getStartLoc(),
|
||||
"SP not allowed in register list");
|
||||
|
||||
if (validatetLDMRegList(Inst, Operands, 3))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case ARM::LDMIA_UPD:
|
||||
|
@ -6213,13 +6265,14 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst,
|
|||
break;
|
||||
case ARM::t2LDMIA:
|
||||
case ARM::t2LDMDB:
|
||||
case ARM::t2STMIA:
|
||||
case ARM::t2STMDB: {
|
||||
if (listContainsReg(Inst, 3, ARM::SP))
|
||||
return Error(Operands.back()->getStartLoc(),
|
||||
"SP not allowed in register list");
|
||||
if (validatetLDMRegList(Inst, Operands, 3))
|
||||
return true;
|
||||
break;
|
||||
case ARM::t2STMIA:
|
||||
case ARM::t2STMDB:
|
||||
if (validatetSTMRegList(Inst, Operands, 3))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case ARM::t2LDMIA_UPD:
|
||||
case ARM::t2LDMDB_UPD:
|
||||
case ARM::t2STMIA_UPD:
|
||||
|
@ -6228,9 +6281,13 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst,
|
|||
return Error(Operands.back()->getStartLoc(),
|
||||
"writeback register not allowed in register list");
|
||||
|
||||
if (listContainsReg(Inst, 4, ARM::SP))
|
||||
return Error(Operands.back()->getStartLoc(),
|
||||
"SP not allowed in register list");
|
||||
if (Opcode == ARM::t2LDMIA_UPD || Opcode == ARM::t2LDMDB_UPD) {
|
||||
if (validatetLDMRegList(Inst, Operands, 4))
|
||||
return true;
|
||||
} else {
|
||||
if (validatetSTMRegList(Inst, Operands, 4))
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
}
|
||||
case ARM::sysLDMIA_UPD:
|
||||
|
@ -6275,6 +6332,8 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst,
|
|||
!isThumbTwo())
|
||||
return Error(Operands[2]->getStartLoc(),
|
||||
"registers must be in range r0-r7 or pc");
|
||||
if (validatetLDMRegList(Inst, Operands, 2, /*IsPop=*/true))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case ARM::tPUSH: {
|
||||
|
@ -6283,6 +6342,8 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst,
|
|||
!isThumbTwo())
|
||||
return Error(Operands[2]->getStartLoc(),
|
||||
"registers must be in range r0-r7 or lr");
|
||||
if (validatetSTMRegList(Inst, Operands, 2))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case ARM::tSTMIA_UPD: {
|
||||
|
@ -6299,9 +6360,9 @@ bool ARMAsmParser::validateInstruction(MCInst &Inst,
|
|||
return Error(Operands[4]->getStartLoc(),
|
||||
"writeback operator '!' not allowed when base register "
|
||||
"in register list");
|
||||
if (listContainsReg(Inst, 4, ARM::SP) && !inITBlock())
|
||||
return Error(Operands.back()->getStartLoc(),
|
||||
"SP not allowed in register list");
|
||||
|
||||
if (validatetSTMRegList(Inst, Operands, 4))
|
||||
return true;
|
||||
break;
|
||||
}
|
||||
case ARM::tADDrSP: {
|
||||
|
|
|
@ -83,25 +83,25 @@ error: invalid operand for instruction
|
|||
@ CHECK-ERRORS-V8: error: writeback register not allowed in register list
|
||||
@ CHECK-ERRORS-V8: ldmdb r2!, {r2, r3, r4}
|
||||
@ CHECK-ERRORS-V8: ^
|
||||
@ CHECK-ERRORS-V7M: error: SP not allowed in register list
|
||||
@ CHECK-ERRORS-V7M: error: SP may not be in the register list
|
||||
@ CHECK-ERRORS-V7M: ldm r0, {r2, sp}
|
||||
@ CHECK-ERRORS-V7M: ^
|
||||
@ CHECK-ERRORS-V7M: error: SP not allowed in register list
|
||||
@ CHECK-ERRORS-V7M: error: SP may not be in the register list
|
||||
@ CHECK-ERRORS-V7M: ldmia r0, {r2-r3, sp}
|
||||
@ CHECK-ERRORS-V7M: ^
|
||||
@ CHECK-ERRORS-V7M: error: SP not allowed in register list
|
||||
@ CHECK-ERRORS-V7M: error: SP may not be in the register list
|
||||
@ CHECK-ERRORS-V7M: ldmia r0!, {r2-r3, sp}
|
||||
@ CHECK-ERRORS-V7M: ^
|
||||
@ CHECK-ERRORS-V7M: error: SP not allowed in register list
|
||||
@ CHECK-ERRORS-V7M: error: SP may not be in the register list
|
||||
@ CHECK-ERRORS-V7M: ldmfd r2, {r1, r3-r6, sp}
|
||||
@ CHECK-ERRORS-V7M: ^
|
||||
@ CHECK-ERRORS-V7M: error: SP not allowed in register list
|
||||
@ CHECK-ERRORS-V7M: error: SP may not be in the register list
|
||||
@ CHECK-ERRORS-V7M: ldmfd r2!, {r1, r3-r6, sp}
|
||||
@ CHECK-ERRORS-V7M: ^
|
||||
@ CHECK-ERRORS-V7M: error: SP not allowed in register list
|
||||
@ CHECK-ERRORS-V7M: error: SP may not be in the register list
|
||||
@ CHECK-ERRORS-V7M: ldmdb r1, {r2, r3, sp}
|
||||
@ CHECK-ERRORS-V7M: ^
|
||||
@ CHECK-ERRORS-V7M: error: SP not allowed in register list
|
||||
@ CHECK-ERRORS-V7M: error: SP may not be in the register list
|
||||
@ CHECK-ERRORS-V7M: ldmdb r1!, {r2, r3, sp}
|
||||
@ CHECK-ERRORS-V7M: ^
|
||||
|
||||
|
@ -137,16 +137,16 @@ error: invalid operand for instruction
|
|||
@ CHECK-ERRORS-V8: error: writeback register not allowed in register list
|
||||
@ CHECK-ERRORS-V8: stmdb r2!, {r0, r2}
|
||||
@ CHECK-ERRORS-V8: ^
|
||||
@ CHECK-ERRORS-V7M: error: SP not allowed in register list
|
||||
@ CHECK-ERRORS-V7M: error: SP may not be in the register list
|
||||
@ CHECK-ERRORS-V7M: stm r1!, {r2, sp}
|
||||
@ CHECK-ERRORS-V7M: ^
|
||||
@ CHECK-ERRORS-V7M: error: SP not allowed in register list
|
||||
@ CHECK-ERRORS-V7M: error: SP may not be in the register list
|
||||
@ CHECK-ERRORS-V7M: stmia r4!, {r0-r3, sp}
|
||||
@ CHECK-ERRORS-V7M: ^
|
||||
@ CHECK-ERRORS-V7M: error: SP not allowed in register list
|
||||
@ CHECK-ERRORS-V7M: error: SP may not be in the register list
|
||||
@ CHECK-ERRORS-V7M: stmdb r1, {r2, r3, sp}
|
||||
@ CHECK-ERRORS-V7M: ^
|
||||
@ CHECK-ERRORS-V7M: error: SP not allowed in register list
|
||||
@ CHECK-ERRORS-V7M: error: SP may not be in the register list
|
||||
@ CHECK-ERRORS-V7M: stmdb r1!, {r2, r3, sp}
|
||||
@ CHECK-ERRORS-V7M: ^
|
||||
|
||||
|
|
|
@ -0,0 +1,86 @@
|
|||
@ RUN: not llvm-mc -triple thumbv7-eabi -filetype asm -o /dev/null %s 2>&1 \
|
||||
@ RUN: | FileCheck %s
|
||||
|
||||
.syntax unified
|
||||
.thumb
|
||||
|
||||
.global ldm
|
||||
.type ldm,%function
|
||||
ldb:
|
||||
ldm r0!, {r1, sp}
|
||||
@ CHECK: error: SP may not be in the register list
|
||||
@ CHECK: ldm r0!, {r1, sp}
|
||||
@ CHECK: ^
|
||||
ldm r0!, {lr, pc}
|
||||
@ CHECK: error: PC and LR may not be in the register list simultaneously
|
||||
@ CHECK: ldm r0!, {lr, pc}
|
||||
@ CHECK: ^
|
||||
itt eq
|
||||
ldmeq r0!, {r1, pc}
|
||||
ldmeq r0!, {r2, lr}
|
||||
@ CHECK: error: instruction must be outside of IT block or the last instruction in an IT block
|
||||
@ CHECK: ldmeq r0!, {r1, pc}
|
||||
@ CHECK: ^
|
||||
|
||||
.global ldmdb
|
||||
.type ldmdb,%function
|
||||
ldmdb:
|
||||
ldmdb r0!, {r1, sp}
|
||||
@ CHECK: error: SP may not be in the register list
|
||||
ldm r0!, {lr, pc}
|
||||
@ error: PC and LR may not be in the register list simultaneously
|
||||
itt eq
|
||||
ldmeq r0!, {r1, pc}
|
||||
ldmeq r0!, {r2, lr}
|
||||
@ CHECK: error: instruction must be outside of IT block or the last instruction in an IT block
|
||||
@ CHECK: ldmeq r0!, {r1, pc}
|
||||
@ CHECK: ^
|
||||
|
||||
.global stm
|
||||
.type stm,%function
|
||||
stm:
|
||||
stm r0!, {r1, sp}
|
||||
@ CHECK: error: SP may not be in the register list
|
||||
stm r0!, {r2, pc}
|
||||
@ CHECK: error: PC may not be in the register list
|
||||
stm r0!, {sp, pc}
|
||||
@ CHECK: error: SP and PC may not be in the register list
|
||||
|
||||
.global stmdb
|
||||
.type stmdb,%function
|
||||
stmdb:
|
||||
stmdb r0!, {r1, sp}
|
||||
@ CHECK: error: SP may not be in the register list
|
||||
stmdb r0!, {r2, pc}
|
||||
@ CHECK: error: PC may not be in the register list
|
||||
stmdb r0!, {sp, pc}
|
||||
@ CHECK: error: SP and PC may not be in the register list
|
||||
|
||||
.global push
|
||||
.type push,%function
|
||||
push:
|
||||
push {sp}
|
||||
@ CHECK: error: SP may not be in the register list
|
||||
push {pc}
|
||||
@ CHECK: error: PC may not be in the register list
|
||||
push {sp,pc}
|
||||
@ CHECK: error: SP and PC may not be in the register list
|
||||
|
||||
.global pop
|
||||
.type pop,%function
|
||||
pop:
|
||||
pop {sp}
|
||||
@ CHECK-NOT: error: SP may not be in the register list
|
||||
pop {sp, pc}
|
||||
@ CHECK-NOT: error: SP may not be in the register list
|
||||
pop {lr, pc}
|
||||
@ CHECK: error: PC and LR may not be in the register list simultaneously
|
||||
@ CHECK: pop {lr, pc}
|
||||
@ CHECK: ^
|
||||
itt eq
|
||||
popeq {r1, pc}
|
||||
popeq {r2, lr}
|
||||
@ CHECK: error: instruction must be outside of IT block or the last instruction in an IT block
|
||||
@ CHECK: popeq {r1, pc}
|
||||
@ CHECK: ^
|
||||
|
|
@ -554,11 +554,11 @@ pushge {r1, r3, r7}
|
|||
@ PUSH, encoding T2 (32-bit)
|
||||
@ CHECK: [[@LINE+2]]:1: warning: deprecated instruction in IT block
|
||||
it ge
|
||||
pushge {r1, r13, r7}
|
||||
pushge {r1, r3, r7}
|
||||
@ PUSH, encoding T3 (32-bit)
|
||||
@ CHECK: [[@LINE+2]]:1: warning: deprecated instruction in IT block
|
||||
it ge
|
||||
pushge {r13}
|
||||
pushge {r3}
|
||||
|
||||
@ REV, encoding T1
|
||||
@ CHECK: [[@LINE+2]]:1: warning: deprecated instruction in IT block
|
||||
|
@ -614,9 +614,10 @@ stmge r1!, {r2, r3}
|
|||
@ CHECK: [[@LINE+2]]:1: warning: deprecated instruction in IT block
|
||||
it ge
|
||||
stmge r1, {r2, r3}
|
||||
@ STM, encoding T3 (32-bit)
|
||||
@ CHECK: [[@LINE+2]]:1: warning: deprecated instruction in IT block
|
||||
it ge
|
||||
stmge r1!, {r2, r13}
|
||||
stmge r1!, {r2, r3}
|
||||
|
||||
@ LDM, encoding T1
|
||||
@ CHECK: [[@LINE+2]]:1: warning: deprecated instruction in IT block
|
||||
|
|
Loading…
Reference in New Issue