From: Tim Northover Date: Tue, 22 Oct 2013 19:00:39 +0000 (+0000) Subject: ARM: provide diagnostics on more writeback LDM/STM instructions X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=01b0e94bb731310e72f66977e4b57cd3f3280ba4;p=oota-llvm.git ARM: provide diagnostics on more writeback LDM/STM instructions 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 --- diff --git a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp index 084ac9b0a16..7f2993d87f0 100644 --- a/lib/Target/ARM/AsmParser/ARMAsmParser.cpp +++ b/lib/Target/ARM/AsmParser/ARMAsmParser.cpp @@ -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. @@ -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 " @@ -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: { diff --git a/lib/Target/ARM/Disassembler/ARMDisassembler.cpp b/lib/Target/ARM/Disassembler/ARMDisassembler.cpp index 4872d732870..5be1b6957a4 100644 --- a/lib/Target/ARM/Disassembler/ARMDisassembler.cpp +++ b/lib/Target/ARM/Disassembler/ARMDisassembler.cpp @@ -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. @@ -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); } } diff --git a/test/MC/ARM/diagnostics.s b/test/MC/ARM/diagnostics.s index 55bb1dd37ca..5eae4d216e4 100644 --- a/test/MC/ARM/diagnostics.s +++ b/test/MC/ARM/diagnostics.s @@ -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 diff --git a/test/MC/ARM/thumb-diagnostics.s b/test/MC/ARM/thumb-diagnostics.s index a82d497ceac..8b55e3740da 100644 --- a/test/MC/ARM/thumb-diagnostics.s +++ b/test/MC/ARM/thumb-diagnostics.s @@ -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: ^ @@ -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} @@ -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 diff --git a/test/MC/Disassembler/ARM/invalid-thumbv7.txt b/test/MC/Disassembler/ARM/invalid-thumbv7.txt index 16970844ae8..2c84b8a7aa5 100644 --- a/test/MC/Disassembler/ARM/invalid-thumbv7.txt +++ b/test/MC/Disassembler/ARM/invalid-thumbv7.txt @@ -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]