mirror of
https://github.com/c64scene-ar/llvm-6502.git
synced 2025-07-31 09:25:42 +00:00
ARMLoadStoreOptimizer: Fix errata 602117 handling and make testcase actually test for it
This fixes PR23912 Differential Revision: http://reviews.llvm.org/D10620 git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@240582 91177308-0d34-0410-b5e6-96231b3b80d8
This commit is contained in:
@@ -1464,119 +1464,124 @@ bool ARMLoadStoreOpt::FixInvalidRegPairOp(MachineBasicBlock &MBB,
|
|||||||
MachineBasicBlock::iterator &MBBI) {
|
MachineBasicBlock::iterator &MBBI) {
|
||||||
MachineInstr *MI = &*MBBI;
|
MachineInstr *MI = &*MBBI;
|
||||||
unsigned Opcode = MI->getOpcode();
|
unsigned Opcode = MI->getOpcode();
|
||||||
if (Opcode == ARM::LDRD || Opcode == ARM::STRD) {
|
if (Opcode != ARM::LDRD && Opcode != ARM::STRD && Opcode != ARM::t2LDRDi8)
|
||||||
const MachineOperand &BaseOp = MI->getOperand(2);
|
return false;
|
||||||
unsigned BaseReg = BaseOp.getReg();
|
|
||||||
unsigned EvenReg = MI->getOperand(0).getReg();
|
|
||||||
unsigned OddReg = MI->getOperand(1).getReg();
|
|
||||||
unsigned EvenRegNum = TRI->getDwarfRegNum(EvenReg, false);
|
|
||||||
unsigned OddRegNum = TRI->getDwarfRegNum(OddReg, false);
|
|
||||||
// ARM errata 602117: LDRD with base in list may result in incorrect base
|
|
||||||
// register when interrupted or faulted.
|
|
||||||
bool Errata602117 = EvenReg == BaseReg && STI->isCortexM3();
|
|
||||||
if (!Errata602117 &&
|
|
||||||
((EvenRegNum & 1) == 0 && (EvenRegNum + 1) == OddRegNum))
|
|
||||||
return false;
|
|
||||||
|
|
||||||
MachineBasicBlock::iterator NewBBI = MBBI;
|
const MachineOperand &BaseOp = MI->getOperand(2);
|
||||||
bool isT2 = Opcode == ARM::t2LDRDi8 || Opcode == ARM::t2STRDi8;
|
unsigned BaseReg = BaseOp.getReg();
|
||||||
bool isLd = Opcode == ARM::LDRD || Opcode == ARM::t2LDRDi8;
|
unsigned EvenReg = MI->getOperand(0).getReg();
|
||||||
bool EvenDeadKill = isLd ?
|
unsigned OddReg = MI->getOperand(1).getReg();
|
||||||
MI->getOperand(0).isDead() : MI->getOperand(0).isKill();
|
unsigned EvenRegNum = TRI->getDwarfRegNum(EvenReg, false);
|
||||||
bool EvenUndef = MI->getOperand(0).isUndef();
|
unsigned OddRegNum = TRI->getDwarfRegNum(OddReg, false);
|
||||||
bool OddDeadKill = isLd ?
|
|
||||||
MI->getOperand(1).isDead() : MI->getOperand(1).isKill();
|
|
||||||
bool OddUndef = MI->getOperand(1).isUndef();
|
|
||||||
bool BaseKill = BaseOp.isKill();
|
|
||||||
bool BaseUndef = BaseOp.isUndef();
|
|
||||||
bool OffKill = isT2 ? false : MI->getOperand(3).isKill();
|
|
||||||
bool OffUndef = isT2 ? false : MI->getOperand(3).isUndef();
|
|
||||||
int OffImm = getMemoryOpOffset(MI);
|
|
||||||
unsigned PredReg = 0;
|
|
||||||
ARMCC::CondCodes Pred = getInstrPredicate(MI, PredReg);
|
|
||||||
|
|
||||||
if (OddRegNum > EvenRegNum && OffImm == 0) {
|
// ARM errata 602117: LDRD with base in list may result in incorrect base
|
||||||
// Ascending register numbers and no offset. It's safe to change it to a
|
// register when interrupted or faulted.
|
||||||
// ldm or stm.
|
bool Errata602117 = EvenReg == BaseReg &&
|
||||||
unsigned NewOpc = (isLd)
|
(Opcode == ARM::LDRD || Opcode == ARM::t2LDRDi8) && STI->isCortexM3();
|
||||||
? (isT2 ? ARM::t2LDMIA : ARM::LDMIA)
|
// ARM LDRD/STRD needs consecutive registers.
|
||||||
: (isT2 ? ARM::t2STMIA : ARM::STMIA);
|
bool NonConsecutiveRegs = (Opcode == ARM::LDRD || Opcode == ARM::STRD) &&
|
||||||
if (isLd) {
|
(EvenRegNum % 2 != 0 || EvenRegNum + 1 != OddRegNum);
|
||||||
BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(NewOpc))
|
|
||||||
.addReg(BaseReg, getKillRegState(BaseKill))
|
if (!Errata602117 && !NonConsecutiveRegs)
|
||||||
.addImm(Pred).addReg(PredReg)
|
return false;
|
||||||
.addReg(EvenReg, getDefRegState(isLd) | getDeadRegState(EvenDeadKill))
|
|
||||||
.addReg(OddReg, getDefRegState(isLd) | getDeadRegState(OddDeadKill));
|
MachineBasicBlock::iterator NewBBI = MBBI;
|
||||||
++NumLDRD2LDM;
|
bool isT2 = Opcode == ARM::t2LDRDi8 || Opcode == ARM::t2STRDi8;
|
||||||
} else {
|
bool isLd = Opcode == ARM::LDRD || Opcode == ARM::t2LDRDi8;
|
||||||
BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(NewOpc))
|
bool EvenDeadKill = isLd ?
|
||||||
.addReg(BaseReg, getKillRegState(BaseKill))
|
MI->getOperand(0).isDead() : MI->getOperand(0).isKill();
|
||||||
.addImm(Pred).addReg(PredReg)
|
bool EvenUndef = MI->getOperand(0).isUndef();
|
||||||
.addReg(EvenReg,
|
bool OddDeadKill = isLd ?
|
||||||
getKillRegState(EvenDeadKill) | getUndefRegState(EvenUndef))
|
MI->getOperand(1).isDead() : MI->getOperand(1).isKill();
|
||||||
.addReg(OddReg,
|
bool OddUndef = MI->getOperand(1).isUndef();
|
||||||
getKillRegState(OddDeadKill) | getUndefRegState(OddUndef));
|
bool BaseKill = BaseOp.isKill();
|
||||||
++NumSTRD2STM;
|
bool BaseUndef = BaseOp.isUndef();
|
||||||
}
|
bool OffKill = isT2 ? false : MI->getOperand(3).isKill();
|
||||||
NewBBI = std::prev(MBBI);
|
bool OffUndef = isT2 ? false : MI->getOperand(3).isUndef();
|
||||||
|
int OffImm = getMemoryOpOffset(MI);
|
||||||
|
unsigned PredReg = 0;
|
||||||
|
ARMCC::CondCodes Pred = getInstrPredicate(MI, PredReg);
|
||||||
|
|
||||||
|
if (OddRegNum > EvenRegNum && OffImm == 0) {
|
||||||
|
// Ascending register numbers and no offset. It's safe to change it to a
|
||||||
|
// ldm or stm.
|
||||||
|
unsigned NewOpc = (isLd)
|
||||||
|
? (isT2 ? ARM::t2LDMIA : ARM::LDMIA)
|
||||||
|
: (isT2 ? ARM::t2STMIA : ARM::STMIA);
|
||||||
|
if (isLd) {
|
||||||
|
BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(NewOpc))
|
||||||
|
.addReg(BaseReg, getKillRegState(BaseKill))
|
||||||
|
.addImm(Pred).addReg(PredReg)
|
||||||
|
.addReg(EvenReg, getDefRegState(isLd) | getDeadRegState(EvenDeadKill))
|
||||||
|
.addReg(OddReg, getDefRegState(isLd) | getDeadRegState(OddDeadKill));
|
||||||
|
++NumLDRD2LDM;
|
||||||
} else {
|
} else {
|
||||||
// Split into two instructions.
|
BuildMI(MBB, MBBI, MBBI->getDebugLoc(), TII->get(NewOpc))
|
||||||
unsigned NewOpc = (isLd)
|
.addReg(BaseReg, getKillRegState(BaseKill))
|
||||||
? (isT2 ? (OffImm < 0 ? ARM::t2LDRi8 : ARM::t2LDRi12) : ARM::LDRi12)
|
.addImm(Pred).addReg(PredReg)
|
||||||
: (isT2 ? (OffImm < 0 ? ARM::t2STRi8 : ARM::t2STRi12) : ARM::STRi12);
|
.addReg(EvenReg,
|
||||||
// Be extra careful for thumb2. t2LDRi8 can't reference a zero offset,
|
getKillRegState(EvenDeadKill) | getUndefRegState(EvenUndef))
|
||||||
// so adjust and use t2LDRi12 here for that.
|
.addReg(OddReg,
|
||||||
unsigned NewOpc2 = (isLd)
|
getKillRegState(OddDeadKill) | getUndefRegState(OddUndef));
|
||||||
? (isT2 ? (OffImm+4 < 0 ? ARM::t2LDRi8 : ARM::t2LDRi12) : ARM::LDRi12)
|
++NumSTRD2STM;
|
||||||
: (isT2 ? (OffImm+4 < 0 ? ARM::t2STRi8 : ARM::t2STRi12) : ARM::STRi12);
|
|
||||||
DebugLoc dl = MBBI->getDebugLoc();
|
|
||||||
// If this is a load and base register is killed, it may have been
|
|
||||||
// re-defed by the load, make sure the first load does not clobber it.
|
|
||||||
if (isLd &&
|
|
||||||
(BaseKill || OffKill) &&
|
|
||||||
(TRI->regsOverlap(EvenReg, BaseReg))) {
|
|
||||||
assert(!TRI->regsOverlap(OddReg, BaseReg));
|
|
||||||
InsertLDR_STR(MBB, MBBI, OffImm+4, isLd, dl, NewOpc2,
|
|
||||||
OddReg, OddDeadKill, false,
|
|
||||||
BaseReg, false, BaseUndef, false, OffUndef,
|
|
||||||
Pred, PredReg, TII, isT2);
|
|
||||||
NewBBI = std::prev(MBBI);
|
|
||||||
InsertLDR_STR(MBB, MBBI, OffImm, isLd, dl, NewOpc,
|
|
||||||
EvenReg, EvenDeadKill, false,
|
|
||||||
BaseReg, BaseKill, BaseUndef, OffKill, OffUndef,
|
|
||||||
Pred, PredReg, TII, isT2);
|
|
||||||
} else {
|
|
||||||
if (OddReg == EvenReg && EvenDeadKill) {
|
|
||||||
// If the two source operands are the same, the kill marker is
|
|
||||||
// probably on the first one. e.g.
|
|
||||||
// t2STRDi8 %R5<kill>, %R5, %R9<kill>, 0, 14, %reg0
|
|
||||||
EvenDeadKill = false;
|
|
||||||
OddDeadKill = true;
|
|
||||||
}
|
|
||||||
// Never kill the base register in the first instruction.
|
|
||||||
if (EvenReg == BaseReg)
|
|
||||||
EvenDeadKill = false;
|
|
||||||
InsertLDR_STR(MBB, MBBI, OffImm, isLd, dl, NewOpc,
|
|
||||||
EvenReg, EvenDeadKill, EvenUndef,
|
|
||||||
BaseReg, false, BaseUndef, false, OffUndef,
|
|
||||||
Pred, PredReg, TII, isT2);
|
|
||||||
NewBBI = std::prev(MBBI);
|
|
||||||
InsertLDR_STR(MBB, MBBI, OffImm+4, isLd, dl, NewOpc2,
|
|
||||||
OddReg, OddDeadKill, OddUndef,
|
|
||||||
BaseReg, BaseKill, BaseUndef, OffKill, OffUndef,
|
|
||||||
Pred, PredReg, TII, isT2);
|
|
||||||
}
|
|
||||||
if (isLd)
|
|
||||||
++NumLDRD2LDR;
|
|
||||||
else
|
|
||||||
++NumSTRD2STR;
|
|
||||||
}
|
}
|
||||||
|
NewBBI = std::prev(MBBI);
|
||||||
MBB.erase(MI);
|
} else {
|
||||||
MBBI = NewBBI;
|
// Split into two instructions.
|
||||||
return true;
|
unsigned NewOpc = (isLd)
|
||||||
|
? (isT2 ? (OffImm < 0 ? ARM::t2LDRi8 : ARM::t2LDRi12) : ARM::LDRi12)
|
||||||
|
: (isT2 ? (OffImm < 0 ? ARM::t2STRi8 : ARM::t2STRi12) : ARM::STRi12);
|
||||||
|
// Be extra careful for thumb2. t2LDRi8 can't reference a zero offset,
|
||||||
|
// so adjust and use t2LDRi12 here for that.
|
||||||
|
unsigned NewOpc2 = (isLd)
|
||||||
|
? (isT2 ? (OffImm+4 < 0 ? ARM::t2LDRi8 : ARM::t2LDRi12) : ARM::LDRi12)
|
||||||
|
: (isT2 ? (OffImm+4 < 0 ? ARM::t2STRi8 : ARM::t2STRi12) : ARM::STRi12);
|
||||||
|
DebugLoc dl = MBBI->getDebugLoc();
|
||||||
|
// If this is a load and base register is killed, it may have been
|
||||||
|
// re-defed by the load, make sure the first load does not clobber it.
|
||||||
|
if (isLd &&
|
||||||
|
(BaseKill || OffKill) &&
|
||||||
|
(TRI->regsOverlap(EvenReg, BaseReg))) {
|
||||||
|
assert(!TRI->regsOverlap(OddReg, BaseReg));
|
||||||
|
InsertLDR_STR(MBB, MBBI, OffImm+4, isLd, dl, NewOpc2,
|
||||||
|
OddReg, OddDeadKill, false,
|
||||||
|
BaseReg, false, BaseUndef, false, OffUndef,
|
||||||
|
Pred, PredReg, TII, isT2);
|
||||||
|
NewBBI = std::prev(MBBI);
|
||||||
|
InsertLDR_STR(MBB, MBBI, OffImm, isLd, dl, NewOpc,
|
||||||
|
EvenReg, EvenDeadKill, false,
|
||||||
|
BaseReg, BaseKill, BaseUndef, OffKill, OffUndef,
|
||||||
|
Pred, PredReg, TII, isT2);
|
||||||
|
} else {
|
||||||
|
if (OddReg == EvenReg && EvenDeadKill) {
|
||||||
|
// If the two source operands are the same, the kill marker is
|
||||||
|
// probably on the first one. e.g.
|
||||||
|
// t2STRDi8 %R5<kill>, %R5, %R9<kill>, 0, 14, %reg0
|
||||||
|
EvenDeadKill = false;
|
||||||
|
OddDeadKill = true;
|
||||||
|
}
|
||||||
|
// Never kill the base register in the first instruction.
|
||||||
|
if (EvenReg == BaseReg)
|
||||||
|
EvenDeadKill = false;
|
||||||
|
InsertLDR_STR(MBB, MBBI, OffImm, isLd, dl, NewOpc,
|
||||||
|
EvenReg, EvenDeadKill, EvenUndef,
|
||||||
|
BaseReg, false, BaseUndef, false, OffUndef,
|
||||||
|
Pred, PredReg, TII, isT2);
|
||||||
|
NewBBI = std::prev(MBBI);
|
||||||
|
InsertLDR_STR(MBB, MBBI, OffImm+4, isLd, dl, NewOpc2,
|
||||||
|
OddReg, OddDeadKill, OddUndef,
|
||||||
|
BaseReg, BaseKill, BaseUndef, OffKill, OffUndef,
|
||||||
|
Pred, PredReg, TII, isT2);
|
||||||
|
}
|
||||||
|
if (isLd)
|
||||||
|
++NumLDRD2LDR;
|
||||||
|
else
|
||||||
|
++NumSTRD2STR;
|
||||||
}
|
}
|
||||||
return false;
|
|
||||||
|
MBB.erase(MI);
|
||||||
|
MBBI = NewBBI;
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
/// An optimization pass to turn multiple LDR / STR ops of the same base and
|
/// An optimization pass to turn multiple LDR / STR ops of the same base and
|
||||||
|
@@ -6,23 +6,24 @@
|
|||||||
|
|
||||||
; Magic ARM pair hints works best with linearscan / fast.
|
; Magic ARM pair hints works best with linearscan / fast.
|
||||||
|
|
||||||
; Cortex-M3 errata 602117: LDRD with base in list may result in incorrect base
|
|
||||||
; register when interrupted or faulted.
|
|
||||||
|
|
||||||
@b = external global i64*
|
@b = external global i64*
|
||||||
|
|
||||||
define i64 @t(i64 %a) nounwind readonly {
|
; We use the following two to force values into specific registers.
|
||||||
entry:
|
declare i64* @get_ptr()
|
||||||
; A8-LABEL: t:
|
declare void @use_i64(i64 %v)
|
||||||
; A8: ldrd r2, r3, [r2]
|
|
||||||
|
|
||||||
; M3-LABEL: t:
|
define void @test_ldrd(i64 %a) nounwind readonly {
|
||||||
; M3-NOT: ldrd
|
; CHECK-LABEL: test_ldrd:
|
||||||
|
; CHECK: bl{{x?}} _get_ptr
|
||||||
%0 = load i64*, i64** @b, align 4
|
; A8: ldrd r0, r1, [r0]
|
||||||
%1 = load i64, i64* %0, align 4
|
; Cortex-M3 errata 602117: LDRD with base in list may result in incorrect base
|
||||||
%2 = mul i64 %1, %a
|
; register when interrupted or faulted.
|
||||||
ret i64 %2
|
; M3-NOT: ldrd r[[REGNUM:[0-9]+]], {{r[0-9]+}}, [r[[REGNUM]]]
|
||||||
|
; CHECK: bl{{x?}} _use_i64
|
||||||
|
%ptr = call i64* @get_ptr()
|
||||||
|
%v = load i64, i64* %ptr, align 8
|
||||||
|
call void @use_i64(i64 %v)
|
||||||
|
ret void
|
||||||
}
|
}
|
||||||
|
|
||||||
; rdar://10435045 mixed LDRi8/LDRi12
|
; rdar://10435045 mixed LDRi8/LDRi12
|
||||||
|
@@ -109,7 +109,7 @@ entry:
|
|||||||
define double @load_d(double* %a) {
|
define double @load_d(double* %a) {
|
||||||
entry:
|
entry:
|
||||||
; CHECK-LABEL: load_d:
|
; CHECK-LABEL: load_d:
|
||||||
; NONE: ldrd r0, r1, [r0]
|
; NONE: ldm r0, {r0, r1}
|
||||||
; HARD: vldr d0, [r0]
|
; HARD: vldr d0, [r0]
|
||||||
%0 = load double, double* %a, align 8
|
%0 = load double, double* %a, align 8
|
||||||
ret double %0
|
ret double %0
|
||||||
|
Reference in New Issue
Block a user