ARM: Fix subtle bug in LDM.
If the instruction faults mid-op the base register shouldn't be written back.
This commit is contained in:
parent
4c7a7796ad
commit
53ab306acc
|
@ -58,8 +58,13 @@ MacroMemOp::MacroMemOp(const char *mnem, ExtMachInst machInst,
|
||||||
{
|
{
|
||||||
uint32_t regs = reglist;
|
uint32_t regs = reglist;
|
||||||
uint32_t ones = number_of_ones(reglist);
|
uint32_t ones = number_of_ones(reglist);
|
||||||
// Remember that writeback adds a uop
|
// Remember that writeback adds a uop or two and the temp register adds one
|
||||||
numMicroops = ones + (writeback ? 1 : 0) + 1;
|
numMicroops = ones + (writeback ? (load ? 2 : 1) : 0) + 1;
|
||||||
|
|
||||||
|
// It's technically legal to do a lot of nothing
|
||||||
|
if (!ones)
|
||||||
|
numMicroops = 1;
|
||||||
|
|
||||||
microOps = new StaticInstPtr[numMicroops];
|
microOps = new StaticInstPtr[numMicroops];
|
||||||
uint32_t addr = 0;
|
uint32_t addr = 0;
|
||||||
|
|
||||||
|
@ -70,28 +75,13 @@ MacroMemOp::MacroMemOp(const char *mnem, ExtMachInst machInst,
|
||||||
addr += 4;
|
addr += 4;
|
||||||
|
|
||||||
StaticInstPtr *uop = microOps;
|
StaticInstPtr *uop = microOps;
|
||||||
StaticInstPtr wbUop;
|
|
||||||
if (writeback) {
|
|
||||||
if (up) {
|
|
||||||
wbUop = new MicroAddiUop(machInst, rn, rn, ones * 4);
|
|
||||||
} else {
|
|
||||||
wbUop = new MicroSubiUop(machInst, rn, rn, ones * 4);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add 0 to Rn and stick it in ureg0.
|
// Add 0 to Rn and stick it in ureg0.
|
||||||
// This is equivalent to a move.
|
// This is equivalent to a move.
|
||||||
*uop = new MicroAddiUop(machInst, INTREG_UREG0, rn, 0);
|
*uop = new MicroAddiUop(machInst, INTREG_UREG0, rn, 0);
|
||||||
|
|
||||||
// Write back at the start for loads. This covers the ldm exception return
|
|
||||||
// case where the base needs to be written in the old mode. Stores may need
|
|
||||||
// the original value of the base, but they don't change mode and can
|
|
||||||
// write back at the end like before.
|
|
||||||
if (load && writeback) {
|
|
||||||
*++uop = wbUop;
|
|
||||||
}
|
|
||||||
|
|
||||||
unsigned reg = 0;
|
unsigned reg = 0;
|
||||||
|
unsigned regIdx = 0;
|
||||||
bool force_user = user & !bits(reglist, 15);
|
bool force_user = user & !bits(reglist, 15);
|
||||||
bool exception_ret = user & bits(reglist, 15);
|
bool exception_ret = user & bits(reglist, 15);
|
||||||
|
|
||||||
|
@ -101,12 +91,20 @@ MacroMemOp::MacroMemOp(const char *mnem, ExtMachInst machInst,
|
||||||
reg++;
|
reg++;
|
||||||
replaceBits(regs, reg, 0);
|
replaceBits(regs, reg, 0);
|
||||||
|
|
||||||
unsigned regIdx = reg;
|
regIdx = reg;
|
||||||
if (force_user) {
|
if (force_user) {
|
||||||
regIdx = intRegInMode(MODE_USER, regIdx);
|
regIdx = intRegInMode(MODE_USER, regIdx);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (load) {
|
if (load) {
|
||||||
|
if (writeback && i == ones - 1) {
|
||||||
|
// If it's a writeback and this is the last register
|
||||||
|
// do the load into a temporary register which we'll move
|
||||||
|
// into the final one later
|
||||||
|
*++uop = new MicroLdrUop(machInst, INTREG_UREG1, INTREG_UREG0,
|
||||||
|
up, addr);
|
||||||
|
} else {
|
||||||
|
// Otherwise just do it normally
|
||||||
if (reg == INTREG_PC && exception_ret) {
|
if (reg == INTREG_PC && exception_ret) {
|
||||||
// This must be the exception return form of ldm.
|
// This must be the exception return form of ldm.
|
||||||
*++uop = new MicroLdrRetUop(machInst, regIdx,
|
*++uop = new MicroLdrRetUop(machInst, regIdx,
|
||||||
|
@ -115,6 +113,7 @@ MacroMemOp::MacroMemOp(const char *mnem, ExtMachInst machInst,
|
||||||
*++uop = new MicroLdrUop(machInst, regIdx,
|
*++uop = new MicroLdrUop(machInst, regIdx,
|
||||||
INTREG_UREG0, up, addr);
|
INTREG_UREG0, up, addr);
|
||||||
}
|
}
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
*++uop = new MicroStrUop(machInst, regIdx, INTREG_UREG0, up, addr);
|
*++uop = new MicroStrUop(machInst, regIdx, INTREG_UREG0, up, addr);
|
||||||
}
|
}
|
||||||
|
@ -125,8 +124,32 @@ MacroMemOp::MacroMemOp(const char *mnem, ExtMachInst machInst,
|
||||||
addr -= 4;
|
addr -= 4;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (!load && writeback) {
|
if (writeback && ones) {
|
||||||
*++uop = wbUop;
|
// put the register update after we're done all loading
|
||||||
|
if (up)
|
||||||
|
*++uop = new MicroAddiUop(machInst, rn, rn, ones * 4);
|
||||||
|
else
|
||||||
|
*++uop = new MicroSubiUop(machInst, rn, rn, ones * 4);
|
||||||
|
|
||||||
|
// If this was a load move the last temporary value into place
|
||||||
|
// this way we can't take an exception after we update the base
|
||||||
|
// register.
|
||||||
|
if (load && reg == INTREG_PC && exception_ret) {
|
||||||
|
*++uop = new MicroUopRegMovRet(machInst, 0, INTREG_UREG1);
|
||||||
|
warn("creating instruction with exception return at curTick:%d\n",
|
||||||
|
curTick());
|
||||||
|
} else if (load) {
|
||||||
|
*++uop = new MicroUopRegMov(machInst, regIdx, INTREG_UREG1);
|
||||||
|
if (reg == INTREG_PC) {
|
||||||
|
(*uop)->setFlag(StaticInstBase::IsControl);
|
||||||
|
(*uop)->setFlag(StaticInstBase::IsCondControl);
|
||||||
|
(*uop)->setFlag(StaticInstBase::IsIndirectControl);
|
||||||
|
// This is created as a RAS POP
|
||||||
|
if (rn == INTREG_SP)
|
||||||
|
(*uop)->setFlag(StaticInstBase::IsReturn);
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
(*uop)->setLastMicroop();
|
(*uop)->setLastMicroop();
|
||||||
|
|
|
@ -86,24 +86,27 @@ let {{
|
||||||
'predicate_test': predicateTest},
|
'predicate_test': predicateTest},
|
||||||
['IsMicroop'])
|
['IsMicroop'])
|
||||||
|
|
||||||
microLdrRetUopCode = '''
|
microRetUopCode = '''
|
||||||
CPSR cpsr = Cpsr;
|
CPSR cpsr = Cpsr;
|
||||||
SCTLR sctlr = Sctlr;
|
SCTLR sctlr = Sctlr;
|
||||||
uint32_t newCpsr =
|
uint32_t newCpsr =
|
||||||
cpsrWriteByInstr(cpsr | CondCodes, Spsr, 0xF, true, sctlr.nmfi);
|
cpsrWriteByInstr(cpsr | CondCodes, Spsr, 0xF, true, sctlr.nmfi);
|
||||||
Cpsr = ~CondCodesMask & newCpsr;
|
Cpsr = ~CondCodesMask & newCpsr;
|
||||||
CondCodes = CondCodesMask & newCpsr;
|
CondCodes = CondCodesMask & newCpsr;
|
||||||
IWNPC = cSwap(Mem.uw, cpsr.e) | ((Spsr & 0x20) ? 1 : 0);
|
IWNPC = cSwap(%s, cpsr.e) | ((Spsr & 0x20) ? 1 : 0);
|
||||||
ForcedItState = ((((CPSR)Spsr).it2 << 2) & 0xFC)
|
ForcedItState = ((((CPSR)Spsr).it2 << 2) & 0xFC)
|
||||||
| (((CPSR)Spsr).it1 & 0x3);
|
| (((CPSR)Spsr).it1 & 0x3);
|
||||||
'''
|
'''
|
||||||
|
|
||||||
microLdrRetUopIop = InstObjParams('ldr_ret_uop', 'MicroLdrRetUop',
|
microLdrRetUopIop = InstObjParams('ldr_ret_uop', 'MicroLdrRetUop',
|
||||||
'MicroMemOp',
|
'MicroMemOp',
|
||||||
{'memacc_code': microLdrRetUopCode,
|
{'memacc_code':
|
||||||
|
microRetUopCode % 'Mem.uw',
|
||||||
'ea_code':
|
'ea_code':
|
||||||
'EA = URb + (up ? imm : -imm);',
|
'EA = URb + (up ? imm : -imm);',
|
||||||
'predicate_test': condPredicateTest},
|
'predicate_test': condPredicateTest},
|
||||||
['IsMicroop','IsNonSpeculative','IsSerializeAfter'])
|
['IsMicroop','IsNonSpeculative',
|
||||||
|
'IsSerializeAfter'])
|
||||||
|
|
||||||
microStrUopCode = "Mem = cSwap(URa.uw, ((CPSR)Cpsr).e);"
|
microStrUopCode = "Mem = cSwap(URa.uw, ((CPSR)Cpsr).e);"
|
||||||
microStrUopIop = InstObjParams('str_uop', 'MicroStrUop',
|
microStrUopIop = InstObjParams('str_uop', 'MicroStrUop',
|
||||||
|
@ -608,6 +611,13 @@ let {{
|
||||||
'predicate_test': predicateTest},
|
'predicate_test': predicateTest},
|
||||||
['IsMicroop'])
|
['IsMicroop'])
|
||||||
|
|
||||||
|
microUopRegMovRetIop = InstObjParams('movret_uop', 'MicroUopRegMovRet',
|
||||||
|
'MicroIntMov',
|
||||||
|
{'code': microRetUopCode % 'URb',
|
||||||
|
'predicate_test': predicateTest},
|
||||||
|
['IsMicroop', 'IsNonSpeculative',
|
||||||
|
'IsSerializeAfter'])
|
||||||
|
|
||||||
setPCCPSRDecl = '''
|
setPCCPSRDecl = '''
|
||||||
CPSR cpsrOrCondCodes = URc;
|
CPSR cpsrOrCondCodes = URc;
|
||||||
SCTLR sctlr = Sctlr;
|
SCTLR sctlr = Sctlr;
|
||||||
|
@ -634,6 +644,7 @@ let {{
|
||||||
MicroIntRegDeclare.subst(microAddUopIop) + \
|
MicroIntRegDeclare.subst(microAddUopIop) + \
|
||||||
MicroIntRegDeclare.subst(microSubUopIop) + \
|
MicroIntRegDeclare.subst(microSubUopIop) + \
|
||||||
MicroIntMovDeclare.subst(microUopRegMovIop) + \
|
MicroIntMovDeclare.subst(microUopRegMovIop) + \
|
||||||
|
MicroIntMovDeclare.subst(microUopRegMovRetIop) + \
|
||||||
MicroSetPCCPSRDeclare.subst(microUopSetPCCPSRIop)
|
MicroSetPCCPSRDeclare.subst(microUopSetPCCPSRIop)
|
||||||
|
|
||||||
decoder_output = MicroIntImmConstructor.subst(microAddiUopIop) + \
|
decoder_output = MicroIntImmConstructor.subst(microAddiUopIop) + \
|
||||||
|
@ -641,6 +652,7 @@ let {{
|
||||||
MicroIntRegConstructor.subst(microAddUopIop) + \
|
MicroIntRegConstructor.subst(microAddUopIop) + \
|
||||||
MicroIntRegConstructor.subst(microSubUopIop) + \
|
MicroIntRegConstructor.subst(microSubUopIop) + \
|
||||||
MicroIntMovConstructor.subst(microUopRegMovIop) + \
|
MicroIntMovConstructor.subst(microUopRegMovIop) + \
|
||||||
|
MicroIntMovConstructor.subst(microUopRegMovRetIop) + \
|
||||||
MicroSetPCCPSRConstructor.subst(microUopSetPCCPSRIop)
|
MicroSetPCCPSRConstructor.subst(microUopSetPCCPSRIop)
|
||||||
|
|
||||||
exec_output = PredOpExecute.subst(microAddiUopIop) + \
|
exec_output = PredOpExecute.subst(microAddiUopIop) + \
|
||||||
|
@ -648,6 +660,7 @@ let {{
|
||||||
PredOpExecute.subst(microAddUopIop) + \
|
PredOpExecute.subst(microAddUopIop) + \
|
||||||
PredOpExecute.subst(microSubUopIop) + \
|
PredOpExecute.subst(microSubUopIop) + \
|
||||||
PredOpExecute.subst(microUopRegMovIop) + \
|
PredOpExecute.subst(microUopRegMovIop) + \
|
||||||
|
PredOpExecute.subst(microUopRegMovRetIop) + \
|
||||||
PredOpExecute.subst(microUopSetPCCPSRIop)
|
PredOpExecute.subst(microUopSetPCCPSRIop)
|
||||||
|
|
||||||
}};
|
}};
|
||||||
|
|
|
@ -267,6 +267,7 @@ class StaticInstBase : public RefCounted
|
||||||
|
|
||||||
void setLastMicroop() { flags[IsLastMicroop] = true; }
|
void setLastMicroop() { flags[IsLastMicroop] = true; }
|
||||||
void setDelayedCommit() { flags[IsDelayedCommit] = true; }
|
void setDelayedCommit() { flags[IsDelayedCommit] = true; }
|
||||||
|
void setFlag(Flags f) { flags[f] = true; }
|
||||||
|
|
||||||
/// Operation class. Used to select appropriate function unit in issue.
|
/// Operation class. Used to select appropriate function unit in issue.
|
||||||
OpClass opClass() const { return _opClass; }
|
OpClass opClass() const { return _opClass; }
|
||||||
|
|
Loading…
Reference in a new issue