Skip to content

Commit

Permalink
[Sparc] Add errata workaround pass for GR712RC and UT700 (llvm#103843)
Browse files Browse the repository at this point in the history
This patch adds a pass that provides workarounds for the errata
described in GRLIB-TN-0009, GRLIB-TN-0010, GRLIB-TN-0011, GRLIB-TN-0012,
and GRLIB-TN-0013, that are applicable to the GR712RC and UT700. The
documents are available for download from here:

https://www.gaisler.com/index.php/information/app-tech-notes

The pass will detect certain sensitive instruction sequences and prevent
them from occurring by inserting NOP instruction. Below is an overview
of each of the workarounds. A similar implementation is available in
GCC.

GRLIB-TN-0009:

* Insert NOPs to prevent the sequence (stb/sth/st/stf) -> (single
non-store/load instruction) -> (any store)

* Insert NOPs to prevent the sequence (std/stdf) -> (any store)

GRLIB-TN-0010:

* Insert a NOP between load instruction and atomic instruction (swap and
casa).

* Insert a NOP at branch target if load in delay slot and atomic
instruction at branch target.

* Do not allow functions to begin with atomic instruction.

GRLIB-TN-0011:

* Insert .p2align 4 before atomic instructions (swap and casa).

GRLIB-TN-0012:

* Place a NOP at the branch target of an integer branch if it is a
floating-point operation or a floating-point branch.

GRLIB-TN-0013:

* Prevent (div/sqrt) instructions in the delay slot.

* Insert NOPs to prevent the sequence (div/sqrt) -> (two or three
floating point operations or loads) -> (div/sqrt).

* Do not insert NOPs if any of the floating point operations have a
dependency on the destination register of the first (div/sqrt).

* Do not insert NOPs if one of the floating point operations is a
(div/sqrt).

* Insert NOPs to prevent (div/sqrt) followed by a branch.
  • Loading branch information
doac committed Aug 19, 2024
1 parent 82fdfd4 commit 7faf1a0
Show file tree
Hide file tree
Showing 12 changed files with 678 additions and 0 deletions.
14 changes: 14 additions & 0 deletions llvm/lib/Target/Sparc/DelaySlotFiller.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -282,6 +282,20 @@ bool Filler::delayHasHazard(MachineBasicBlock::iterator candidate,
Opcode >= SP::FDIVD && Opcode <= SP::FSQRTD)
return true;

if (Subtarget->fixTN0009() && candidate->mayStore())
return true;

if (Subtarget->fixTN0013()) {
switch (Opcode) {
case SP::FDIVS:
case SP::FDIVD:
case SP::FSQRTS:
case SP::FSQRTD:
return true;
default:
break;
}
}

return false;
}
Expand Down
35 changes: 35 additions & 0 deletions llvm/lib/Target/Sparc/LeonFeatures.td
Original file line number Diff line number Diff line change
Expand Up @@ -61,3 +61,38 @@ def FixAllFDIVSQRT : SubtargetFeature<
def LeonCycleCounter
: SubtargetFeature<"leoncyclecounter", "HasLeonCycleCounter", "true",
"Use the Leon cycle counter register">;

def FixTN0009 : SubtargetFeature<
"fix-tn0009",
"FixTN0009",
"true",
"Enable workaround for errata described in GRLIB-TN-0009"
>;

def FixTN0010 : SubtargetFeature<
"fix-tn0010",
"FixTN0010",
"true",
"Enable workaround for errata described in GRLIB-TN-0010"
>;

def FixTN0011 : SubtargetFeature<
"fix-tn0011",
"FixTN0011",
"true",
"Enable workaround for errata described in GRLIB-TN-0011"
>;

def FixTN0012 : SubtargetFeature<
"fix-tn0012",
"FixTN0012",
"true",
"Enable workaround for errata described in GRLIB-TN-0012"
>;

def FixTN0013 : SubtargetFeature<
"fix-tn0013",
"FixTN0013",
"true",
"Enable workaround for errata described in GRLIB-TN-0013"
>;
304 changes: 304 additions & 0 deletions llvm/lib/Target/Sparc/LeonPasses.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,310 @@

using namespace llvm;

char ErrataWorkaround::ID = 0;

ErrataWorkaround::ErrataWorkaround() : MachineFunctionPass(ID) {
initializeErrataWorkaroundPass(*PassRegistry::getPassRegistry());
}

INITIALIZE_PASS(ErrataWorkaround, "errata-workaround", "Errata workaround pass",
false, false)

// Move iterator to the next instruction in the function, ignoring
// meta instructions and inline assembly. Returns false when reaching
// the end of the function.
bool ErrataWorkaround::moveNext(MachineBasicBlock::iterator &I) {

MachineBasicBlock *MBB = I->getParent();

do {
I++;

while (I == MBB->end()) {
if (MBB->getFallThrough() == nullptr)
return false;
MBB = MBB->getFallThrough();
I = MBB->begin();
}
} while (I->isMetaInstruction() || I->isInlineAsm());

return true;
}

void ErrataWorkaround::insertNop(MachineBasicBlock::iterator I) {
BuildMI(*I->getParent(), I, I->getDebugLoc(), TII->get(SP::NOP));
}

bool ErrataWorkaround::isFloat(MachineBasicBlock::iterator I) {
if (I->getNumOperands() == 0)
return false;

if (!I->getOperand(0).isReg())
return false;

unsigned reg = I->getOperand(0).getReg();

if (!SP::FPRegsRegClass.contains(reg) && !SP::DFPRegsRegClass.contains(reg))
return false;

return true;
}

bool ErrataWorkaround::isDivSqrt(MachineBasicBlock::iterator I) {
switch (I->getOpcode()) {
case SP::FDIVS:
case SP::FDIVD:
case SP::FSQRTS:
case SP::FSQRTD:
return true;
}
return false;
}

// Prevents the following code sequence from being generated:
// (stb/sth/st/stf) -> (single non-store/load instruction) -> (any store)
// If the sequence is detected a NOP instruction is inserted after
// the first store instruction.
bool ErrataWorkaround::checkSeqTN0009A(MachineBasicBlock::iterator I) {
switch (I->getOpcode()) {
case SP::STrr:
case SP::STri:
case SP::STBrr:
case SP::STBri:
case SP::STHrr:
case SP::STHri:
case SP::STFrr:
case SP::STFri:
break;
default:
return false;
}

MachineBasicBlock::iterator MI = I;
if (!moveNext(MI))
return false;

if (MI->mayStore() || MI->mayLoad())
return false;

MachineBasicBlock::iterator PatchHere = MI;

if (!moveNext(MI))
return false;

if (!MI->mayStore())
return false;

insertNop(PatchHere);
return true;
}

// Prevents the following code sequence from being generated:
// (std/stdf) -> (any store)
// If the sequence is detected a NOP instruction is inserted after
// the first store instruction.
bool ErrataWorkaround::checkSeqTN0009B(MachineBasicBlock::iterator I) {

switch (I->getOpcode()) {
case SP::STDrr:
case SP::STDri:
case SP::STDFrr:
case SP::STDFri:
break;
default:
return false;
}

MachineBasicBlock::iterator MI = I;

if (!moveNext(MI))
return false;

if (!MI->mayStore())
return false;

insertNop(MI);
return true;
}

// Insert a NOP at branch target if load in delay slot and atomic
// instruction at branch target. Also insert a NOP between load
// instruction and atomic instruction (swap or casa).
bool ErrataWorkaround::checkSeqTN0010(MachineBasicBlock::iterator I) {

// Check for load instruction or branch bundled with load instruction
if (!I->mayLoad())
return false;

// Check for branch to atomic instruction with load in delay slot
if (I->isBranch()) {
MachineBasicBlock *TargetMBB = I->getOperand(0).getMBB();
MachineBasicBlock::iterator MI = TargetMBB->begin();

while (MI != TargetMBB->end() && MI->isMetaInstruction())
MI++;

if (MI == TargetMBB->end())
return false;

switch (MI->getOpcode()) {
case SP::SWAPrr:
case SP::SWAPri:
case SP::CASArr:
insertNop(MI);
break;
default:
break;
}
}

// Check for load followed by atomic instruction
MachineBasicBlock::iterator MI = I;
if (!moveNext(MI))
return false;

switch (MI->getOpcode()) {
case SP::SWAPrr:
case SP::SWAPri:
case SP::CASArr:
break;
default:
return false;
}
insertNop(MI);
return true;
}

// Do not allow functions to begin with an atomic instruction
bool ErrataWorkaround::checkSeqTN0010First(MachineBasicBlock &MBB) {
MachineBasicBlock::iterator I = MBB.begin();
while (I != MBB.end() && I->isMetaInstruction())
I++;
switch (I->getOpcode()) {
case SP::SWAPrr:
case SP::SWAPri:
case SP::CASArr:
break;
default:
return false;
}
insertNop(I);
return true;
}

// Inserts a NOP instruction at the target of an integer branch if the
// target is a floating-point instruction or floating-point branch.
bool ErrataWorkaround::checkSeqTN0012(MachineBasicBlock::iterator I) {

if (I->getOpcode() != SP::BCOND && I->getOpcode() != SP::BCONDA)
return false;

MachineBasicBlock *TargetMBB = I->getOperand(0).getMBB();
MachineBasicBlock::iterator MI = TargetMBB->begin();

while (MI != TargetMBB->end() && MI->isMetaInstruction())
MI++;

if (MI == TargetMBB->end())
return false;

if (!isFloat(MI) && MI->getOpcode() != SP::FBCOND)
return false;

insertNop(MI);
return true;
}

// Prevents the following code sequence from being generated:
// (div/sqrt) -> (2 to 3 floating-point operations or loads) -> (div/sqrt)
// If the sequence is detected one or two NOP instruction are inserted after
// the first div/sqrt instruction. No NOPs are inserted if one of the floating-
// point instructions in the middle of the sequence is a (div/sqrt), or if
// they have dependency on the destination register of the first (div/sqrt).
//
// The function also prevents the following code sequence from being generated,
// (div/sqrt) -> (branch), by inserting a NOP instruction after the (div/sqrt).
bool ErrataWorkaround::checkSeqTN0013(MachineBasicBlock::iterator I) {

if (!isDivSqrt(I))
return false;

unsigned dstReg = I->getOperand(0).getReg();

MachineBasicBlock::iterator MI = I;
if (!moveNext(MI))
return false;

if (MI->isBranch()) {
insertNop(MI);
return true;
}

MachineBasicBlock::iterator PatchHere = MI;

unsigned fpFound = 0;
for (unsigned i = 0; i < 4; i++) {

if (!isFloat(MI)) {
if (!moveNext(MI))
return false;
continue;
}

if (MI->readsRegister(dstReg, TRI))
return false;

if (isDivSqrt(MI)) {
if (i < 2)
return false;
if (fpFound < 2)
return false;

insertNop(PatchHere);
if (i == 2)
insertNop(PatchHere);
return true;
}

fpFound++;
if (!moveNext(MI))
return false;
}

return false;
}

bool ErrataWorkaround::runOnMachineFunction(MachineFunction &MF) {
bool Changed = false;
ST = &MF.getSubtarget<SparcSubtarget>();

if (!(ST->fixTN0009() || ST->fixTN0010() || ST->fixTN0012() ||
ST->fixTN0013()))
return false;

TII = ST->getInstrInfo();
TRI = ST->getRegisterInfo();

if (ST->fixTN0010())
Changed |= checkSeqTN0010First(MF.front());

for (auto &MBB : MF) {
for (auto &I : MBB) {
if (ST->fixTN0009()) {
Changed |= checkSeqTN0009A(I);
Changed |= checkSeqTN0009B(I);
}
if (ST->fixTN0010())
Changed |= checkSeqTN0010(I);
if (ST->fixTN0012())
Changed |= checkSeqTN0012(I);
if (ST->fixTN0013())
Changed |= checkSeqTN0013(I);
}
}
return Changed;
}

LEONMachineFunctionPass::LEONMachineFunctionPass(char &ID)
: MachineFunctionPass(ID) {}

Expand Down
Loading

0 comments on commit 7faf1a0

Please sign in to comment.