From 9c1911b1059f6d90262b89dc5407be5bb787075e Mon Sep 17 00:00:00 2001 From: Saleem Abdulrasool Date: Thu, 18 Dec 2014 05:24:38 +0000 Subject: [PATCH] ARM: improve instruction validation for thumb mode MIME-Version: 1.0 Content-Type: text/plain; charset=utf8 Content-Transfer-Encoding: 8bit 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. git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@224502 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/Target/ARM/AsmParser/ARMAsmParser.cpp | 91 +++++++++++++++++++---- test/MC/ARM/thumb-diagnostics.s | 22 +++--- test/MC/ARM/thumb-load-store-multiple.s | 86 +++++++++++++++++++++ test/MC/ARM/v8_IT_manual.s | 7 +- 4 files changed, 177 insertions(+), 29 deletions(-) create mode 100644 test/MC/ARM/thumb-load-store-multiple.s diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index e2f7dfb17f9..e9860764962 100644 --- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -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(*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(*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: + 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: @@ -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: { diff --git a/test/MC/ARM/thumb-diagnostics.s b/test/MC/ARM/thumb-diagnostics.s index a2e494f0850..bd26d06865c 100644 --- a/test/MC/ARM/thumb-diagnostics.s +++ b/test/MC/ARM/thumb-diagnostics.s @@ -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: ^ diff --git a/test/MC/ARM/thumb-load-store-multiple.s b/test/MC/ARM/thumb-load-store-multiple.s new file mode 100644 index 00000000000..e00fc66e8f9 --- /dev/null +++ b/test/MC/ARM/thumb-load-store-multiple.s @@ -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: ^ + diff --git a/test/MC/ARM/v8_IT_manual.s b/test/MC/ARM/v8_IT_manual.s index 4b63aa82dd6..160e98ce8b4 100644 --- a/test/MC/ARM/v8_IT_manual.s +++ b/test/MC/ARM/v8_IT_manual.s @@ -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 -- 2.34.1