From 7faf1a0868cb6a6a05e454044239292c454e0a71 Mon Sep 17 00:00:00 2001 From: Daniel Cederman Date: Mon, 19 Aug 2024 07:59:58 +0200 Subject: [PATCH] [Sparc] Add errata workaround pass for GR712RC and UT700 (#103843) 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 | 304 +++++++++++++++++++ llvm/lib/Target/Sparc/LeonPasses.h | 27 ++ 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, 678 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..018d8126f7a7e9 100644 --- a/llvm/lib/Target/Sparc/LeonPasses.cpp +++ b/llvm/lib/Target/Sparc/LeonPasses.cpp @@ -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(); + + 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..261b283fadaeb3 100644 --- a/llvm/lib/Target/Sparc/LeonPasses.h +++ b/llvm/lib/Target/Sparc/LeonPasses.h @@ -37,6 +37,33 @@ class LLVM_LIBRARY_VISIBILITY LEONMachineFunctionPass } }; +class LLVM_LIBRARY_VISIBILITY ErrataWorkaround : public MachineFunctionPass { +protected: + const SparcSubtarget *ST; + const TargetInstrInfo *TII; + const TargetRegisterInfo *TRI; + + 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); + +public: + static char ID; + + ErrataWorkaround(); + bool runOnMachineFunction(MachineFunction &MF) override; + + 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 182779fa93e503..87d76ebf268c41 100644 --- a/llvm/lib/Target/Sparc/SparcAsmPrinter.cpp +++ b/llvm/lib/Target/Sparc/SparcAsmPrinter.cpp @@ -276,6 +276,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().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 @@ -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 +...