mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-07-24 22:24:54 +00:00
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
This commit is contained in:
@@ -5416,6 +5416,7 @@ validateInstruction(MCInst &Inst,
|
|||||||
"bitfield width must be in range [1,32-lsb]");
|
"bitfield width must be in range [1,32-lsb]");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
// Notionally handles ARM::tLDMIA_UPD too.
|
||||||
case ARM::tLDMIA: {
|
case ARM::tLDMIA: {
|
||||||
// If we're parsing Thumb2, the .w variant is available and handles
|
// If we're parsing Thumb2, the .w variant is available and handles
|
||||||
// most cases that are normally illegal for a Thumb1 LDM instruction.
|
// most cases that are normally illegal for a Thumb1 LDM instruction.
|
||||||
@@ -5444,7 +5445,19 @@ validateInstruction(MCInst &Inst,
|
|||||||
|
|
||||||
break;
|
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()))
|
if (listContainsReg(Inst, 3, Inst.getOperand(0).getReg()))
|
||||||
return Error(Operands[4]->getStartLoc(),
|
return Error(Operands[4]->getStartLoc(),
|
||||||
"writeback operator '!' not allowed when base register "
|
"writeback operator '!' not allowed when base register "
|
||||||
@@ -5490,10 +5503,19 @@ validateInstruction(MCInst &Inst,
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
case ARM::tSTMIA_UPD: {
|
case ARM::tSTMIA_UPD: {
|
||||||
bool ListContainsBase;
|
bool ListContainsBase, InvalidLowList;
|
||||||
if (checkLowRegisterList(Inst, 4, 0, 0, ListContainsBase) && !isThumbTwo())
|
InvalidLowList = checkLowRegisterList(Inst, 4, Inst.getOperand(0).getReg(),
|
||||||
|
0, ListContainsBase);
|
||||||
|
if (InvalidLowList && !isThumbTwo())
|
||||||
return Error(Operands[4]->getStartLoc(),
|
return Error(Operands[4]->getStartLoc(),
|
||||||
"registers must be in range r0-r7");
|
"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;
|
break;
|
||||||
}
|
}
|
||||||
case ARM::tADDrSP: {
|
case ARM::tADDrSP: {
|
||||||
|
@@ -1203,20 +1203,22 @@ static DecodeStatus DecodeRegListOperand(MCInst &Inst, unsigned Val,
|
|||||||
uint64_t Address, const void *Decoder) {
|
uint64_t Address, const void *Decoder) {
|
||||||
DecodeStatus S = MCDisassembler::Success;
|
DecodeStatus S = MCDisassembler::Success;
|
||||||
|
|
||||||
bool writebackLoad = false;
|
bool NeedDisjointWriteback = false;
|
||||||
unsigned writebackReg = 0;
|
unsigned WritebackReg = 0;
|
||||||
switch (Inst.getOpcode()) {
|
switch (Inst.getOpcode()) {
|
||||||
default:
|
default:
|
||||||
break;
|
break;
|
||||||
case ARM::LDMIA_UPD:
|
case ARM::LDMIA_UPD:
|
||||||
case ARM::LDMDB_UPD:
|
case ARM::LDMDB_UPD:
|
||||||
case ARM::LDMIB_UPD:
|
case ARM::LDMIB_UPD:
|
||||||
case ARM::LDMDA_UPD:
|
case ARM::LDMDA_UPD:
|
||||||
case ARM::t2LDMIA_UPD:
|
case ARM::t2LDMIA_UPD:
|
||||||
case ARM::t2LDMDB_UPD:
|
case ARM::t2LDMDB_UPD:
|
||||||
writebackLoad = true;
|
case ARM::t2STMIA_UPD:
|
||||||
writebackReg = Inst.getOperand(0).getReg();
|
case ARM::t2STMDB_UPD:
|
||||||
break;
|
NeedDisjointWriteback = true;
|
||||||
|
WritebackReg = Inst.getOperand(0).getReg();
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Empty register lists are not allowed.
|
// 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)))
|
if (!Check(S, DecodeGPRRegisterClass(Inst, i, Address, Decoder)))
|
||||||
return MCDisassembler::Fail;
|
return MCDisassembler::Fail;
|
||||||
// Writeback not allowed if Rn is in the target list.
|
// 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);
|
Check(S, MCDisassembler::SoftFail);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -429,3 +429,10 @@
|
|||||||
|
|
||||||
bkpteq #7
|
bkpteq #7
|
||||||
@ CHECK-ERRORS: error: instruction 'bkpt' is not predicable, but condition code specified
|
@ 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
|
||||||
|
@@ -57,6 +57,8 @@ error: invalid operand for instruction
|
|||||||
ldm r2!, {r5, r8}
|
ldm r2!, {r5, r8}
|
||||||
ldm r2, {r5, r7}
|
ldm r2, {r5, r7}
|
||||||
ldm r2!, {r2, r3, r4}
|
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: error: registers must be in range r0-r7
|
||||||
@ CHECK-ERRORS: ldm r2!, {r5, r8}
|
@ CHECK-ERRORS: ldm r2!, {r5, r8}
|
||||||
@ CHECK-ERRORS: ^
|
@ 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: error: writeback operator '!' not allowed when base register in register list
|
||||||
@ CHECK-ERRORS: ldm r2!, {r2, r3, r4}
|
@ CHECK-ERRORS: ldm r2!, {r2, r3, r4}
|
||||||
@ CHECK-ERRORS: ^
|
@ 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
|
@ Invalid writeback and register lists for PUSH/POP
|
||||||
pop {r1, r2, r10}
|
pop {r1, r2, r10}
|
||||||
@@ -81,12 +89,20 @@ error: invalid operand for instruction
|
|||||||
@ Invalid writeback and register lists for STM
|
@ Invalid writeback and register lists for STM
|
||||||
stm r1, {r2, r6}
|
stm r1, {r2, r6}
|
||||||
stm r1!, {r2, r9}
|
stm r1!, {r2, r9}
|
||||||
|
stm r2!, {r2, r9}
|
||||||
|
stmdb r2!, {r0, r2}
|
||||||
@ CHECK-ERRORS: error: instruction requires: thumb2
|
@ CHECK-ERRORS: error: instruction requires: thumb2
|
||||||
@ CHECK-ERRORS: stm r1, {r2, r6}
|
@ CHECK-ERRORS: stm r1, {r2, r6}
|
||||||
@ CHECK-ERRORS: ^
|
@ CHECK-ERRORS: ^
|
||||||
@ CHECK-ERRORS: error: registers must be in range r0-r7
|
@ CHECK-ERRORS: error: registers must be in range r0-r7
|
||||||
@ CHECK-ERRORS: stm r1!, {r2, r9}
|
@ CHECK-ERRORS: stm r1!, {r2, r9}
|
||||||
@ CHECK-ERRORS: ^
|
@ 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.
|
@ Out of range immediates for LSL instruction.
|
||||||
lsls r4, r5, #-1
|
lsls r4, r5, #-1
|
||||||
|
@@ -389,3 +389,19 @@
|
|||||||
[0x80 0xf9 0x30 0x0b]
|
[0x80 0xf9 0x30 0x0b]
|
||||||
# CHECK: invalid instruction encoding
|
# CHECK: invalid instruction encoding
|
||||||
# CHECK-NEXT: [0x80 0xf9 0x30 0x0b]
|
# 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]
|
||||||
|
Reference in New Issue
Block a user