// 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,
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 &);
}
+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) {
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:
break;
case ARM::t2LDMIA:
case ARM::t2LDMDB:
+ if (validatetLDMRegList(Inst, Operands, 3))
+ return true;
+ break;
case ARM::t2STMIA:
- case ARM::t2STMDB: {
- if (listContainsReg(Inst, 3, ARM::SP))
- return Error(Operands.back()->getStartLoc(),
- "SP not allowed in register list");
+ case ARM::t2STMDB:
+ if (validatetSTMRegList(Inst, Operands, 3))
+ return true;
break;
- }
case ARM::t2LDMIA_UPD:
case ARM::t2LDMDB_UPD:
case ARM::t2STMIA_UPD:
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:
!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: {
!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: {
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: {
@ 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: ^
@ 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: ^
--- /dev/null
+@ 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: ^
+