[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