[llvm] [Sparc] Add errata workaround pass for GR712RC and UT700 (PR #103843)
Daniel Cederman via llvm-commits
llvm-commits at lists.llvm.org
Thu Aug 15 01:51:16 PDT 2024
https://github.com/doac updated https://github.com/llvm/llvm-project/pull/103843
>From 1df70727715e62fba374875efb7b4a68903b2a8f Mon Sep 17 00:00:00 2001
From: Daniel Cederman <cederman at gaisler.com>
Date: Wed, 23 Aug 2023 11:07:42 +0200
Subject: [PATCH 1/3] [Sparc] Add errata workaround pass for GR712RC and UT700
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.
---
llvm/lib/Target/Sparc/DelaySlotFiller.cpp | 14 +
llvm/lib/Target/Sparc/LeonFeatures.td | 35 +++
llvm/lib/Target/Sparc/LeonPasses.cpp | 298 +++++++++++++++++++
llvm/lib/Target/Sparc/LeonPasses.h | 23 ++
llvm/lib/Target/Sparc/Sparc.h | 1 +
llvm/lib/Target/Sparc/SparcAsmPrinter.cpp | 6 +
llvm/lib/Target/Sparc/SparcTargetMachine.cpp | 2 +
llvm/test/CodeGen/SPARC/tn0009.mir | 26 ++
llvm/test/CodeGen/SPARC/tn0010.mir | 79 +++++
llvm/test/CodeGen/SPARC/tn0011.ll | 26 ++
llvm/test/CodeGen/SPARC/tn0012.mir | 65 ++++
llvm/test/CodeGen/SPARC/tn0013.mir | 93 ++++++
12 files changed, 668 insertions(+)
create mode 100644 llvm/test/CodeGen/SPARC/tn0009.mir
create mode 100644 llvm/test/CodeGen/SPARC/tn0010.mir
create mode 100644 llvm/test/CodeGen/SPARC/tn0011.ll
create mode 100644 llvm/test/CodeGen/SPARC/tn0012.mir
create mode 100644 llvm/test/CodeGen/SPARC/tn0013.mir
diff --git a/llvm/lib/Target/Sparc/DelaySlotFiller.cpp b/llvm/lib/Target/Sparc/DelaySlotFiller.cpp
index 7e129101fefc79..a74bfe490b9018 100644
--- a/llvm/lib/Target/Sparc/DelaySlotFiller.cpp
+++ b/llvm/lib/Target/Sparc/DelaySlotFiller.cpp
@@ -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;
}
diff --git a/llvm/lib/Target/Sparc/LeonFeatures.td b/llvm/lib/Target/Sparc/LeonFeatures.td
index 75273eff186867..ac2ad5a577f4b3 100644
--- a/llvm/lib/Target/Sparc/LeonFeatures.td
+++ b/llvm/lib/Target/Sparc/LeonFeatures.td
@@ -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"
+>;
diff --git a/llvm/lib/Target/Sparc/LeonPasses.cpp b/llvm/lib/Target/Sparc/LeonPasses.cpp
index 45a46c131d21ea..9bede058ebea57 100644
--- a/llvm/lib/Target/Sparc/LeonPasses.cpp
+++ b/llvm/lib/Target/Sparc/LeonPasses.cpp
@@ -19,6 +19,304 @@
using namespace llvm;
+char ErrataWorkaround::ID = 0;
+
+ErrataWorkaround::ErrataWorkaround() : MachineFunctionPass(ID) {
+ initializeErrataWorkaroundPass(*PassRegistry::getPassRegistry());
+}
+
+INITIALIZE_PASS(ErrataWorkaround, "errata-workaround", "Errata workaround pass",
+ false, false)
+
+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;
+}
+
+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) {}
diff --git a/llvm/lib/Target/Sparc/LeonPasses.h b/llvm/lib/Target/Sparc/LeonPasses.h
index 9bc4569a12984a..d61178406f3c23 100644
--- a/llvm/lib/Target/Sparc/LeonPasses.h
+++ b/llvm/lib/Target/Sparc/LeonPasses.h
@@ -37,6 +37,29 @@ class LLVM_LIBRARY_VISIBILITY LEONMachineFunctionPass
}
};
+class LLVM_LIBRARY_VISIBILITY ErrataWorkaround : public MachineFunctionPass {
+public:
+ const SparcSubtarget *ST;
+ const TargetInstrInfo *TII;
+ const TargetRegisterInfo *TRI;
+ static char ID;
+
+ ErrataWorkaround();
+ bool runOnMachineFunction(MachineFunction &MF) override;
+ bool checkSeqTN0009A(MachineBasicBlock::iterator I);
+ bool checkSeqTN0009B(MachineBasicBlock::iterator I);
+ bool checkSeqTN0010First(MachineBasicBlock &MBB);
+ bool checkSeqTN0010(MachineBasicBlock::iterator I);
+ bool checkSeqTN0012(MachineBasicBlock::iterator I);
+ bool checkSeqTN0013(MachineBasicBlock::iterator I);
+
+ bool moveNext(MachineBasicBlock::iterator &I);
+ bool isFloat(MachineBasicBlock::iterator I);
+ bool isDivSqrt(MachineBasicBlock::iterator I);
+ void insertNop(MachineBasicBlock::iterator I);
+ StringRef getPassName() const override { return "Errata workaround pass"; };
+};
+
class LLVM_LIBRARY_VISIBILITY InsertNOPLoad : public LEONMachineFunctionPass {
public:
static char ID;
diff --git a/llvm/lib/Target/Sparc/Sparc.h b/llvm/lib/Target/Sparc/Sparc.h
index 33a803469603b3..afcbc473295411 100644
--- a/llvm/lib/Target/Sparc/Sparc.h
+++ b/llvm/lib/Target/Sparc/Sparc.h
@@ -32,6 +32,7 @@ FunctionPass *createSparcDelaySlotFillerPass();
void LowerSparcMachineInstrToMCInst(const MachineInstr *MI, MCInst &OutMI,
AsmPrinter &AP);
void initializeSparcDAGToDAGISelLegacyPass(PassRegistry &);
+void initializeErrataWorkaroundPass(PassRegistry &);
} // namespace llvm
namespace llvm {
diff --git a/llvm/lib/Target/Sparc/SparcAsmPrinter.cpp b/llvm/lib/Target/Sparc/SparcAsmPrinter.cpp
index 6855471840e9db..837d1443bf95c2 100644
--- a/llvm/lib/Target/Sparc/SparcAsmPrinter.cpp
+++ b/llvm/lib/Target/Sparc/SparcAsmPrinter.cpp
@@ -277,6 +277,12 @@ void SparcAsmPrinter::emitInstruction(const MachineInstr *MI) {
case TargetOpcode::DBG_VALUE:
// FIXME: Debug Value.
return;
+ case SP::CASArr:
+ case SP::SWAPrr:
+ case SP::SWAPri:
+ if (MF->getSubtarget<SparcSubtarget>().fixTN0011())
+ OutStreamer->emitCodeAlignment(Align(16), &getSubtargetInfo());
+ break;
case SP::GETPCX:
LowerGETPCXAndEmitMCInsts(MI, getSubtargetInfo());
return;
diff --git a/llvm/lib/Target/Sparc/SparcTargetMachine.cpp b/llvm/lib/Target/Sparc/SparcTargetMachine.cpp
index ea403230bea893..fec2d3a35ae6d2 100644
--- a/llvm/lib/Target/Sparc/SparcTargetMachine.cpp
+++ b/llvm/lib/Target/Sparc/SparcTargetMachine.cpp
@@ -29,6 +29,7 @@ extern "C" LLVM_EXTERNAL_VISIBILITY void LLVMInitializeSparcTarget() {
PassRegistry &PR = *PassRegistry::getPassRegistry();
initializeSparcDAGToDAGISelLegacyPass(PR);
+ initializeErrataWorkaroundPass(PR);
}
static cl::opt<bool>
@@ -193,6 +194,7 @@ void SparcPassConfig::addPreEmitPass(){
addPass(new InsertNOPLoad());
addPass(new DetectRoundChange());
addPass(new FixAllFDIVSQRT());
+ addPass(new ErrataWorkaround());
}
void SparcV8TargetMachine::anchor() { }
diff --git a/llvm/test/CodeGen/SPARC/tn0009.mir b/llvm/test/CodeGen/SPARC/tn0009.mir
new file mode 100644
index 00000000000000..f3807940cd30ec
--- /dev/null
+++ b/llvm/test/CodeGen/SPARC/tn0009.mir
@@ -0,0 +1,26 @@
+# RUN: llc %s -mattr=+fix-tn0009 -march=sparc -run-pass=errata-workaround -o - \
+# RUN: | FileCheck %s
+
+---
+# CHECK: STrr $o1, $g0, $o0
+# CHECK-NEXT: NOP
+# CHECK-NEXT: NOP
+# CHECK-NEXT: STrr $o1, $g0, $o0
+name: seqa
+body: |
+ bb.0.entry:
+ STrr $o1, $g0, $o0
+ NOP
+ STrr $o1, $g0, $o0
+
+---
+# CHECK: STDrr $i0, $g0, $i0_i1
+# CHECK-NEXT: NOP
+# CHECK-NEXT: STDrr $i0, $g0, $i0_i1
+name: seqb
+body: |
+ bb.0.entry:
+ STDrr $i0, $g0, $i0_i1
+ STDrr $i0, $g0, $i0_i1
+
+...
diff --git a/llvm/test/CodeGen/SPARC/tn0010.mir b/llvm/test/CodeGen/SPARC/tn0010.mir
new file mode 100644
index 00000000000000..416dd740cf21ca
--- /dev/null
+++ b/llvm/test/CodeGen/SPARC/tn0010.mir
@@ -0,0 +1,79 @@
+# RUN: llc %s -mattr=+fix-tn0010 -march=sparc -run-pass=errata-workaround -o - \
+# RUN: | FileCheck %s
+
+---
+# CHECK: LDrr
+# CHECK-NEXT: NOP
+# CHECK-NEXT: CASArr
+# CHECK: LDrr
+# CHECK-NEXT: NOP
+# CHECK-NEXT: SWAPrr
+# CHECK: LDrr
+# CHECK-NEXT: NOP
+# CHECK-NEXT: SWAPri
+name: er-5-1
+registers:
+ - { id: 0, class: intregs }
+body: |
+ bb.0.entry:
+ %0 = LDrr $o0, $g0
+ %0 = CASArr $o2, $o3, %0, 10
+ %0 = LDrr $g0, $o0
+ %0 = SWAPrr $o2, $o3, %0
+ %0 = LDrr $g0, $o0
+ %0 = SWAPri $o2, 10, %0
+
+---
+# CHECK: bb.2:
+# CHECK-NEXT: NOP
+# CHECK-NEXT: CASArr
+name: er-5-2
+registers:
+ - { id: 0, class: intregs }
+body: |
+ bb.0.entry:
+ successors: %bb.1, %bb.2
+ BCOND %bb.2, 10, implicit $icc {
+ %0 = LDrr $g0, $o0
+ }
+
+ bb.1.entry:
+ successors: %bb.2
+ NOP
+
+ bb.2:
+ %0 = CASArr $o1, $o2, %0, 10
+
+---
+# CHECK: bb.0.entry:
+# CHECK-NEXT: NOP
+# CHECK-NEXT: CASArr
+name: er-5-3
+registers:
+ - { id: 0, class: intregs }
+body: |
+ bb.0.entry:
+ %0 = CASArr $o1, $o2, %0, 10
+
+---
+# CHECK: bb.1.entry:
+# CHECK: NOP
+# CHECK-NEXT: CASArr
+name: er-5-4
+registers:
+ - { id: 0, class: intregs }
+body: |
+ bb.0.entry:
+ successors: %bb.1, %bb.2
+ BCOND %bb.2, 10, implicit $icc {
+ %0 = LDrr $g0, $o0
+ }
+
+ bb.1.entry:
+ successors: %bb.2
+ %0 = CASArr $o1, $o2, %0, 10
+
+ bb.2:
+ NOP
+
+...
diff --git a/llvm/test/CodeGen/SPARC/tn0011.ll b/llvm/test/CodeGen/SPARC/tn0011.ll
new file mode 100644
index 00000000000000..e13379eb54f4a9
--- /dev/null
+++ b/llvm/test/CodeGen/SPARC/tn0011.ll
@@ -0,0 +1,26 @@
+; RUN: llc < %s -mtriple=sparc -mattr=fix-tn0011,hasleoncasa | FileCheck %s
+
+; CHECK: .p2align 4
+; CHECK-NEXT: casa
+define i32 @test_casarr(i32* %p, i32 %v) {
+entry:
+ %0 = atomicrmw nand i32* %p, i32 %v seq_cst
+ ret i32 %0
+}
+
+; CHECK: .p2align 4
+; CHECK-NEXT: swap
+define i32 @test_swaprr(i32* %p, i32 %v) {
+entry:
+ %0 = atomicrmw xchg i32* %p, i32 %v seq_cst
+ ret i32 %0
+}
+
+; CHECK: .p2align 4
+; CHECK-NEXT: swap
+define i32 @test_swapri(i32* %p, i32 %v) {
+entry:
+ %1 = getelementptr inbounds i32, ptr %p, i32 1
+ %2 = atomicrmw xchg ptr %1, i32 %v seq_cst, align 4
+ ret i32 %2
+}
diff --git a/llvm/test/CodeGen/SPARC/tn0012.mir b/llvm/test/CodeGen/SPARC/tn0012.mir
new file mode 100644
index 00000000000000..f6c55b0d9a770d
--- /dev/null
+++ b/llvm/test/CodeGen/SPARC/tn0012.mir
@@ -0,0 +1,65 @@
+# RUN: llc %s -mattr=+fix-tn0012 -march=sparc -run-pass=errata-workaround -o - \
+# RUN: | FileCheck %s
+
+---
+# CHECK: bb.2:
+# CHECK: NOP
+# CHECK-NEXT: $f0 = FADDS $f0, $f0
+name: er-5-1
+body: |
+ bb.0.entry:
+ successors: %bb.1, %bb.2
+ BCOND %bb.2, 10, implicit $icc {
+ NOP
+ }
+
+ bb.1.entry:
+ successors: %bb.2
+ NOP
+
+ bb.2:
+ $f0 = FADDS $f0, $f0
+
+---
+# CHECK: bb.2:
+# CHECK: NOP
+# CHECK-NEXT: FBCOND %bb.3, 22, implicit $fcc0 {
+name: er-5-2
+body: |
+ bb.0.entry:
+ successors: %bb.2, %bb.1
+ BCOND %bb.1, 10, implicit $icc {
+ NOP
+ }
+
+ bb.2.entry:
+ successors: %bb.1
+ NOP
+
+ bb.1:
+ successors: %bb.3
+ FBCOND %bb.3, 22, implicit $fcc0 {
+ NOP
+ }
+ bb.3:
+ NOP
+
+---
+# CHECK: bb.2:
+# CHECK: NOP
+# CHECK-NEXT: FCMPS $f0, $f0, implicit-def $fcc0
+name: er-5-3
+body: |
+ bb.0.entry:
+ successors: %bb.1, %bb.2
+ BCOND %bb.2, 10, implicit $icc {
+ NOP
+ }
+
+ bb.1.entry:
+ successors: %bb.2
+ NOP
+
+ bb.2:
+ FCMPS $f0, $f0, implicit-def $fcc0
+...
diff --git a/llvm/test/CodeGen/SPARC/tn0013.mir b/llvm/test/CodeGen/SPARC/tn0013.mir
new file mode 100644
index 00000000000000..068bd0f02b3482
--- /dev/null
+++ b/llvm/test/CodeGen/SPARC/tn0013.mir
@@ -0,0 +1,93 @@
+# RUN: llc %s -mattr=+fix-tn0013 -march=sparc -run-pass=errata-workaround -o - \
+# RUN: | FileCheck %s
+
+---
+# CHECK: $f0 = FSQRTS $f0
+# CHECK-NEXT: NOP
+# CHECK-NEXT: NOP
+# CHECK-NEXT: $f3 = FADDS $f1, $f2
+# CHECK-NEXT: $f3 = FADDS $f1, $f2
+# CHECK-NEXT: $f0 = FDIVS $f4, $f5
+name: er-8-1
+body: |
+ bb.0.entry:
+ $f0 = FSQRTS $f0
+ $f3 = FADDS $f1, $f2
+ $f3 = FADDS $f1, $f2
+ $f0 = FDIVS $f4, $f5
+
+---
+# CHECK: $f0 = FDIVS $f0, $f0
+# CHECK-NEXT: NOP
+# CHECK-NEXT: $f3 = FADDS $f1, $f2
+# CHECK-NEXT: $f3 = FADDS $f1, $f2
+# CHECK-NEXT: $f3 = FADDS $f1, $f2
+# CHECK-NEXT: $f0 = FSQRTS $f4
+name: er-8-2
+body: |
+ bb.0.entry:
+ $f0 = FDIVS $f0, $f0
+ $f3 = FADDS $f1, $f2
+ $f3 = FADDS $f1, $f2
+ $f3 = FADDS $f1, $f2
+ $f0 = FSQRTS $f4
+
+---
+# CHECK-NOT: NOP
+name: er-9
+body: |
+ bb.0.entry:
+ $f0 = FSQRTS $f0
+ $f3 = FADDS $f1, $f2
+ $f3 = FADDS $f0, $f2
+ $f3 = FADDS $f1, $f2
+ $f0 = FSQRTS $f0
+
+---
+# CHECK-NOT: NOP
+name: er-10
+body: |
+ bb.0.entry:
+ $f0 = FSQRTS $f0
+ $f4 = FSQRTS $f4
+ $f3 = FADDS $f1, $f2
+ $f0 = FSQRTS $f0
+
+---
+# CHECK: er-11
+# CHECK: $f0 = FSQRTS $f0
+# CHECK-NEXT: NOP
+# CHECK: $f0 = FDIVS $f0, $f0
+# CHECK-NEXT: NOP
+name: er-11
+body: |
+ bb.0.entry:
+ successors: %bb.3, %bb.1
+ $f0 = FSQRTS $f0
+ FBCOND %bb.3, 22, implicit $fcc0 {
+ NOP
+ }
+ bb.1:
+ successors: %bb.3, %bb.4
+ $f0 = FDIVS $f0, $f0
+ BCOND %bb.4, 10, implicit $icc {
+ NOP
+ }
+ bb.3:
+ NOP
+ bb.4:
+ NOP
+
+---
+# CHECK: $f1 = FDIVS $f0, $f1
+# CHECK-NEXT: NOP
+# CHECK-NEXT: STri $i6, -84, $i2
+name: er-8-3
+body: |
+ bb.0.entry:
+ $f1 = FDIVS $f0, $f1
+ STri $i6, -84, $i2
+ $f0 = LDFri $i6, -84
+ $f0 = FITOS $f0
+ $f5 = FDIVS $f4, $f0
+...
>From 95af8d5595922fb2ba2ed33385d793c086b5f9c8 Mon Sep 17 00:00:00 2001
From: Daniel Cederman <cederman at gaisler.com>
Date: Thu, 15 Aug 2024 09:26:55 +0200
Subject: [PATCH 2/3] [Sparc] Make internal functions protected
---
llvm/lib/Target/Sparc/LeonPasses.h | 12 ++++++++----
1 file changed, 8 insertions(+), 4 deletions(-)
diff --git a/llvm/lib/Target/Sparc/LeonPasses.h b/llvm/lib/Target/Sparc/LeonPasses.h
index d61178406f3c23..261b283fadaeb3 100644
--- a/llvm/lib/Target/Sparc/LeonPasses.h
+++ b/llvm/lib/Target/Sparc/LeonPasses.h
@@ -38,14 +38,11 @@ class LLVM_LIBRARY_VISIBILITY LEONMachineFunctionPass
};
class LLVM_LIBRARY_VISIBILITY ErrataWorkaround : public MachineFunctionPass {
-public:
+protected:
const SparcSubtarget *ST;
const TargetInstrInfo *TII;
const TargetRegisterInfo *TRI;
- static char ID;
- ErrataWorkaround();
- bool runOnMachineFunction(MachineFunction &MF) override;
bool checkSeqTN0009A(MachineBasicBlock::iterator I);
bool checkSeqTN0009B(MachineBasicBlock::iterator I);
bool checkSeqTN0010First(MachineBasicBlock &MBB);
@@ -57,6 +54,13 @@ class LLVM_LIBRARY_VISIBILITY ErrataWorkaround : public MachineFunctionPass {
bool isFloat(MachineBasicBlock::iterator I);
bool isDivSqrt(MachineBasicBlock::iterator I);
void insertNop(MachineBasicBlock::iterator I);
+
+public:
+ static char ID;
+
+ ErrataWorkaround();
+ bool runOnMachineFunction(MachineFunction &MF) override;
+
StringRef getPassName() const override { return "Errata workaround pass"; };
};
>From f4b4effa6b24e78f3dafaf8096dc7bd24e9b1bb1 Mon Sep 17 00:00:00 2001
From: Daniel Cederman <cederman at gaisler.com>
Date: Thu, 15 Aug 2024 09:27:29 +0200
Subject: [PATCH 3/3] [Sparc] Add comments describing checkSeqTN0010 and
moveNext
---
llvm/lib/Target/Sparc/LeonPasses.cpp | 6 ++++++
1 file changed, 6 insertions(+)
diff --git a/llvm/lib/Target/Sparc/LeonPasses.cpp b/llvm/lib/Target/Sparc/LeonPasses.cpp
index 9bede058ebea57..018d8126f7a7e9 100644
--- a/llvm/lib/Target/Sparc/LeonPasses.cpp
+++ b/llvm/lib/Target/Sparc/LeonPasses.cpp
@@ -28,6 +28,9 @@ ErrataWorkaround::ErrataWorkaround() : MachineFunctionPass(ID) {
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();
@@ -142,6 +145,9 @@ bool ErrataWorkaround::checkSeqTN0009B(MachineBasicBlock::iterator I) {
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
More information about the llvm-commits
mailing list