blob: c2d6b7660c892e7bae966ef635cb991f81ced5c7 [file] [log] [blame]
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/LiveIntervals.h"
#include "llvm/CodeGen/MIRParser/MIRParser.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/TargetRegisterInfo.h"
#include "llvm/CodeGen/TargetSubtargetInfo.h"
#include "llvm/IR/LegacyPassManager.h"
#include "llvm/InitializePasses.h"
#include "llvm/MC/TargetRegistry.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include "gtest/gtest.h"
#include "../lib/CodeGen/RegisterCoalescer.h"
using namespace llvm;
namespace llvm {
void initializeTestPassPass(PassRegistry &);
}
namespace {
void initLLVM() {
InitializeAllTargets();
InitializeAllTargetMCs();
InitializeAllAsmPrinters();
InitializeAllAsmParsers();
PassRegistry *Registry = PassRegistry::getPassRegistry();
initializeCore(*Registry);
initializeCodeGen(*Registry);
}
/// Create a TargetMachine. As we lack a dedicated always available target for
/// unittests, we go for "AMDGPU" to be able to test normal and subregister
/// liveranges.
std::unique_ptr<LLVMTargetMachine> createTargetMachine() {
Triple TargetTriple("amdgcn--");
std::string Error;
const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);
if (!T)
return nullptr;
TargetOptions Options;
return std::unique_ptr<LLVMTargetMachine>(static_cast<LLVMTargetMachine *>(
T->createTargetMachine("AMDGPU", "gfx900", "", Options, std::nullopt,
std::nullopt, CodeGenOpt::Aggressive)));
}
std::unique_ptr<Module> parseMIR(LLVMContext &Context,
legacy::PassManagerBase &PM, std::unique_ptr<MIRParser> &MIR,
const LLVMTargetMachine &TM, StringRef MIRCode, const char *FuncName) {
SMDiagnostic Diagnostic;
std::unique_ptr<MemoryBuffer> MBuffer = MemoryBuffer::getMemBuffer(MIRCode);
MIR = createMIRParser(std::move(MBuffer), Context);
if (!MIR)
return nullptr;
std::unique_ptr<Module> M = MIR->parseIRModule();
if (!M)
return nullptr;
M->setDataLayout(TM.createDataLayout());
MachineModuleInfoWrapperPass *MMIWP = new MachineModuleInfoWrapperPass(&TM);
if (MIR->parseMachineFunctions(*M, MMIWP->getMMI()))
return nullptr;
PM.add(MMIWP);
return M;
}
typedef std::function<void(MachineFunction&,LiveIntervals&)> LiveIntervalTest;
struct TestPass : public MachineFunctionPass {
static char ID;
TestPass() : MachineFunctionPass(ID) {
// We should never call this but always use PM.add(new TestPass(...))
abort();
}
TestPass(LiveIntervalTest T, bool ShouldPass)
: MachineFunctionPass(ID), T(T), ShouldPass(ShouldPass) {
initializeTestPassPass(*PassRegistry::getPassRegistry());
}
bool runOnMachineFunction(MachineFunction &MF) override {
LiveIntervals &LIS = getAnalysis<LiveIntervals>();
T(MF, LIS);
EXPECT_EQ(MF.verify(this, /* Banner */ nullptr, /* AbortOnError */ false),
ShouldPass);
return true;
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
AU.addRequired<LiveIntervals>();
AU.addPreserved<LiveIntervals>();
MachineFunctionPass::getAnalysisUsage(AU);
}
private:
LiveIntervalTest T;
bool ShouldPass;
};
static MachineInstr &getMI(MachineFunction &MF, unsigned At,
unsigned BlockNum) {
MachineBasicBlock &MBB = *MF.getBlockNumbered(BlockNum);
unsigned I = 0;
for (MachineInstr &MI : MBB) {
if (I == At)
return MI;
++I;
}
llvm_unreachable("Instruction not found");
}
/**
* Move instruction number \p From in front of instruction number \p To and
* update affected liveness intervals with LiveIntervalAnalysis::handleMove().
*/
static void testHandleMove(MachineFunction &MF, LiveIntervals &LIS,
unsigned From, unsigned To, unsigned BlockNum = 0) {
MachineInstr &FromInstr = getMI(MF, From, BlockNum);
MachineInstr &ToInstr = getMI(MF, To, BlockNum);
MachineBasicBlock &MBB = *FromInstr.getParent();
MBB.splice(ToInstr.getIterator(), &MBB, FromInstr.getIterator());
LIS.handleMove(FromInstr, true);
}
/**
* Move instructions numbered \p From inclusive through instruction number
* \p To into a newly formed bundle and update affected liveness intervals
* with LiveIntervalAnalysis::handleMoveIntoNewBundle().
*/
static void testHandleMoveIntoNewBundle(MachineFunction &MF, LiveIntervals &LIS,
unsigned From, unsigned To,
unsigned BlockNum = 0) {
MachineInstr &FromInstr = getMI(MF, From, BlockNum);
MachineInstr &ToInstr = getMI(MF, To, BlockNum);
MachineBasicBlock &MBB = *FromInstr.getParent();
MachineBasicBlock::instr_iterator I = FromInstr.getIterator();
// Build bundle
finalizeBundle(MBB, I, std::next(ToInstr.getIterator()));
// Update LiveIntervals
MachineBasicBlock::instr_iterator BundleStart = std::prev(I);
LIS.handleMoveIntoNewBundle(*BundleStart, true);
}
/**
* Split block numbered \p BlockNum at instruction \p SplitAt using
* MachineBasicBlock::splitAt updating liveness intervals.
*/
static void testSplitAt(MachineFunction &MF, LiveIntervals &LIS,
unsigned SplitAt, unsigned BlockNum) {
MachineInstr &SplitInstr = getMI(MF, SplitAt, BlockNum);
MachineBasicBlock &MBB = *SplitInstr.getParent();
// Split block and update live intervals
MBB.splitAt(SplitInstr, false, &LIS);
}
/**
* Helper function to test for interference between a hard register and a
* virtual register live ranges.
*/
static bool checkRegUnitInterference(LiveIntervals &LIS,
const TargetRegisterInfo &TRI,
const LiveInterval &VirtReg,
MCRegister PhysReg) {
if (VirtReg.empty())
return false;
CoalescerPair CP(VirtReg.reg(), PhysReg, TRI);
for (MCRegUnitIterator Units(PhysReg, &TRI); Units.isValid(); ++Units) {
const LiveRange &UnitRange = LIS.getRegUnit(*Units);
if (VirtReg.overlaps(UnitRange, CP, *LIS.getSlotIndexes()))
return true;
}
return false;
}
static void liveIntervalTest(StringRef MIRFunc, LiveIntervalTest T,
bool ShouldPass = true) {
LLVMContext Context;
std::unique_ptr<LLVMTargetMachine> TM = createTargetMachine();
// This test is designed for the X86 backend; stop if it is not available.
if (!TM)
return;
legacy::PassManager PM;
SmallString<160> S;
StringRef MIRString = (Twine(R"MIR(
---
...
name: func
registers:
- { id: 0, class: sreg_64 }
body: |
bb.0:
)MIR") + Twine(MIRFunc) + Twine("...\n")).toNullTerminatedStringRef(S);
std::unique_ptr<MIRParser> MIR;
std::unique_ptr<Module> M = parseMIR(Context, PM, MIR, *TM, MIRString,
"func");
ASSERT_TRUE(M);
PM.add(new TestPass(T, ShouldPass));
PM.run(*M);
}
} // End of anonymous namespace.
char TestPass::ID = 0;
INITIALIZE_PASS(TestPass, "testpass", "testpass", false, false)
TEST(LiveIntervalTest, MoveUpDef) {
// Value defined.
liveIntervalTest(R"MIR(
S_NOP 0
S_NOP 0
early-clobber %0 = IMPLICIT_DEF
S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 2, 1);
});
}
TEST(LiveIntervalTest, MoveUpRedef) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
S_NOP 0
%0 = IMPLICIT_DEF implicit %0(tied-def 0)
S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 2, 1);
});
}
TEST(LiveIntervalTest, MoveUpEarlyDef) {
liveIntervalTest(R"MIR(
S_NOP 0
S_NOP 0
early-clobber %0 = IMPLICIT_DEF
S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 2, 1);
});
}
TEST(LiveIntervalTest, MoveUpEarlyRedef) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
S_NOP 0
early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0)
S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 2, 1);
});
}
TEST(LiveIntervalTest, MoveUpKill) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
S_NOP 0
S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 2, 1);
});
}
TEST(LiveIntervalTest, MoveUpKillFollowing) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
S_NOP 0
S_NOP 0, implicit %0
S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 2, 1);
});
}
// TODO: Construct a situation where we have intervals following a hole
// while still having connected components.
TEST(LiveIntervalTest, MoveDownDef) {
// Value defined.
liveIntervalTest(R"MIR(
S_NOP 0
early-clobber %0 = IMPLICIT_DEF
S_NOP 0
S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, MoveDownRedef) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
%0 = IMPLICIT_DEF implicit %0(tied-def 0)
S_NOP 0
S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, MoveDownEarlyDef) {
liveIntervalTest(R"MIR(
S_NOP 0
early-clobber %0 = IMPLICIT_DEF
S_NOP 0
S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, MoveDownEarlyRedef) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0)
S_NOP 0
S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, MoveDownKill) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
S_NOP 0, implicit %0
S_NOP 0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, MoveDownKillFollowing) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
S_NOP 0
S_NOP 0, implicit %0
S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, MoveUndefUse) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
S_NOP 0, implicit undef %0
S_NOP 0, implicit %0
S_NOP 0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 3);
});
}
TEST(LiveIntervalTest, MoveUpValNos) {
// handleMoveUp() had a bug where it would reuse the value number of the
// destination segment, even though we have no guarantee that this valno
// wasn't used in other segments.
liveIntervalTest(R"MIR(
successors: %bb.1, %bb.2
%0 = IMPLICIT_DEF
S_CBRANCH_VCCNZ %bb.2, implicit undef $vcc
S_BRANCH %bb.1
bb.2:
S_NOP 0, implicit %0
bb.1:
successors: %bb.2
%0 = IMPLICIT_DEF implicit %0(tied-def 0)
%0 = IMPLICIT_DEF implicit %0(tied-def 0)
%0 = IMPLICIT_DEF implicit %0(tied-def 0)
S_BRANCH %bb.2
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 2, 0, 2);
});
}
TEST(LiveIntervalTest, MoveOverUndefUse0) {
// findLastUseBefore() used by handleMoveUp() must ignore undef operands.
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
S_NOP 0
S_NOP 0, implicit undef %0
%0 = IMPLICIT_DEF implicit %0(tied-def 0)
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 3, 1);
});
}
TEST(LiveIntervalTest, MoveOverUndefUse1) {
// findLastUseBefore() used by handleMoveUp() must ignore undef operands.
liveIntervalTest(R"MIR(
$sgpr0 = IMPLICIT_DEF
S_NOP 0
S_NOP 0, implicit undef $sgpr0
$sgpr0 = IMPLICIT_DEF implicit $sgpr0(tied-def 0)
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 3, 1);
});
}
TEST(LiveIntervalTest, SubRegMoveDown) {
// Subregister ranges can have holes inside a basic block. Check for a
// movement of the form 32->150 in a liverange [16, 32) [100,200).
liveIntervalTest(R"MIR(
successors: %bb.1, %bb.2
%0 = IMPLICIT_DEF
S_CBRANCH_VCCNZ %bb.2, implicit undef $vcc
S_BRANCH %bb.1
bb.2:
successors: %bb.1
S_NOP 0, implicit %0.sub0
S_NOP 0, implicit %0.sub1
S_NOP 0
undef %0.sub0 = IMPLICIT_DEF
%0.sub1 = IMPLICIT_DEF
bb.1:
S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
// Scheduler behaviour: Clear def,read-undef flag and move.
MachineInstr &MI = getMI(MF, 3, /*BlockNum=*/1);
MI.getOperand(0).setIsUndef(false);
testHandleMove(MF, LIS, 1, 4, /*BlockNum=*/1);
});
}
TEST(LiveIntervalTest, SubRegMoveUp) {
// handleMoveUp had a bug not updating valno of segment incoming to bb.2
// after swapping subreg definitions.
liveIntervalTest(R"MIR(
successors: %bb.1, %bb.2
undef %0.sub0 = IMPLICIT_DEF
%0.sub1 = IMPLICIT_DEF
S_CBRANCH_VCCNZ %bb.2, implicit undef $vcc
S_BRANCH %bb.1
bb.1:
S_NOP 0, implicit %0.sub1
bb.2:
S_NOP 0, implicit %0.sub1
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 0);
});
}
TEST(LiveIntervalTest, DeadSubRegMoveUp) {
// handleMoveUp had a bug where moving a dead subreg def into the middle of
// an earlier segment resulted in an invalid live range.
liveIntervalTest(R"MIR(
undef %125.sub0:vreg_128 = V_MOV_B32_e32 0, implicit $exec
%125.sub1:vreg_128 = COPY %125.sub0
%125.sub2:vreg_128 = COPY %125.sub0
undef %51.sub0:vreg_128 = V_MOV_B32_e32 898625526, implicit $exec
%51.sub1:vreg_128 = COPY %51.sub0
%51.sub2:vreg_128 = COPY %51.sub0
%52:vgpr_32 = V_MOV_B32_e32 986714345, implicit $exec
%54:vgpr_32 = V_MOV_B32_e32 1742342378, implicit $exec
%57:vgpr_32 = V_MOV_B32_e32 3168768712, implicit $exec
%59:vgpr_32 = V_MOV_B32_e32 1039972644, implicit $exec
%60:vgpr_32 = nofpexcept V_MAD_F32_e64 0, %52, 0, undef %61:vgpr_32, 0, %59, 0, 0, implicit $mode, implicit $exec
%63:vgpr_32 = nofpexcept V_ADD_F32_e32 %51.sub3, undef %64:vgpr_32, implicit $mode, implicit $exec
dead %66:vgpr_32 = nofpexcept V_MAD_F32_e64 0, %60, 0, undef %67:vgpr_32, 0, %125.sub2, 0, 0, implicit $mode, implicit $exec
undef %124.sub1:vreg_128 = nofpexcept V_MAD_F32_e64 0, %57, 0, undef %70:vgpr_32, 0, %125.sub1, 0, 0, implicit $mode, implicit $exec
%124.sub0:vreg_128 = nofpexcept V_MAD_F32_e64 0, %54, 0, undef %73:vgpr_32, 0, %125.sub0, 0, 0, implicit $mode, implicit $exec
dead undef %125.sub3:vreg_128 = nofpexcept V_MAC_F32_e32 %63, undef %76:vgpr_32, %125.sub3, implicit $mode, implicit $exec
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 15, 12);
});
}
TEST(LiveIntervalTest, EarlyClobberSubRegMoveUp) {
// handleMoveUp had a bug where moving an early-clobber subreg def into the
// middle of an earlier segment resulted in an invalid live range.
liveIntervalTest(R"MIR(
%4:sreg_32 = IMPLICIT_DEF
%6:sreg_32 = IMPLICIT_DEF
undef early-clobber %9.sub0:sreg_64 = STRICT_WWM %4:sreg_32, implicit $exec
%5:sreg_32 = S_FLBIT_I32_B32 %9.sub0:sreg_64
early-clobber %9.sub1:sreg_64 = STRICT_WWM %6:sreg_32, implicit $exec
%7:sreg_32 = S_FLBIT_I32_B32 %9.sub1:sreg_64
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 4, 3);
});
}
TEST(LiveIntervalTest, TestMoveSubRegDefAcrossUseDef) {
liveIntervalTest(R"MIR(
%1:vreg_64 = IMPLICIT_DEF
bb.1:
%2:vgpr_32 = V_MOV_B32_e32 2, implicit $exec
%3:vgpr_32 = V_ADD_U32_e32 %2, %1.sub0, implicit $exec
undef %1.sub0:vreg_64 = V_ADD_U32_e32 %2, %2, implicit $exec
%1.sub1:vreg_64 = COPY %2
S_NOP 0, implicit %1.sub1
S_BRANCH %bb.1
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
MachineInstr &UndefSubregDef = getMI(MF, 2, 1);
// The scheduler clears undef from subregister defs before moving
UndefSubregDef.getOperand(0).setIsUndef(false);
testHandleMove(MF, LIS, 3, 1, 1);
});
}
TEST(LiveIntervalTest, TestMoveSubRegDefAcrossUseDefMulti) {
liveIntervalTest(R"MIR(
%1:vreg_96 = IMPLICIT_DEF
bb.1:
%2:vgpr_32 = V_MOV_B32_e32 2, implicit $exec
%3:vgpr_32 = V_ADD_U32_e32 %2, %1.sub0, implicit $exec
undef %1.sub0:vreg_96 = V_ADD_U32_e32 %2, %2, implicit $exec
%1.sub1:vreg_96 = COPY %2
%1.sub2:vreg_96 = COPY %2
S_NOP 0, implicit %1.sub1, implicit %1.sub2
S_BRANCH %bb.1
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
MachineInstr &UndefSubregDef = getMI(MF, 2, 1);
// The scheduler clears undef from subregister defs before moving
UndefSubregDef.getOperand(0).setIsUndef(false);
testHandleMove(MF, LIS, 4, 1, 1);
});
}
TEST(LiveIntervalTest, TestMoveSubRegUseAcrossMainRangeHole) {
liveIntervalTest(R"MIR(
%1:sgpr_128 = IMPLICIT_DEF
bb.1:
%2:sgpr_32 = COPY %1.sub2
%3:sgpr_32 = COPY %1.sub1
%1.sub2 = COPY %2
undef %1.sub0 = IMPLICIT_DEF
%1.sub2 = IMPLICIT_DEF
S_CBRANCH_SCC1 %bb.1, implicit undef $scc
S_BRANCH %bb.2
bb.2:
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
MachineInstr &MI = getMI(MF, 3, /*BlockNum=*/1);
MI.getOperand(0).setIsUndef(false);
testHandleMove(MF, LIS, 4, 3, 1);
testHandleMove(MF, LIS, 1, 4, 1);
});
}
TEST(LiveIntervalTest, TestMoveSubRegsOfOneReg) {
liveIntervalTest(R"MIR(
INLINEASM &"", 0, 1835018, def undef %4.sub0:vreg_64, 1835018, def undef %4.sub1:vreg_64
%1:vreg_64 = COPY %4
undef %2.sub0:vreg_64 = V_MOV_B32_e32 0, implicit $exec
%2.sub1:vreg_64 = COPY %2.sub0
%3:vreg_64 = COPY %2
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 4);
testHandleMove(MF, LIS, 0, 3);
});
}
TEST(LiveIntervalTest, BundleUse) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
S_NOP 0
S_NOP 0, implicit %0
S_NOP 0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMoveIntoNewBundle(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, BundleDef) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
S_NOP 0
S_NOP 0, implicit %0
S_NOP 0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMoveIntoNewBundle(MF, LIS, 0, 1);
});
}
TEST(LiveIntervalTest, BundleRedef) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
S_NOP 0
%0 = IMPLICIT_DEF implicit %0(tied-def 0)
S_NOP 0, implicit %0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMoveIntoNewBundle(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, BundleInternalUse) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
S_NOP 0
S_NOP 0, implicit %0
S_NOP 0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMoveIntoNewBundle(MF, LIS, 0, 2);
});
}
TEST(LiveIntervalTest, BundleUndefUse) {
liveIntervalTest(R"MIR(
%0 = IMPLICIT_DEF
S_NOP 0
S_NOP 0, implicit undef %0
S_NOP 0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMoveIntoNewBundle(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, BundleSubRegUse) {
liveIntervalTest(R"MIR(
successors: %bb.1, %bb.2
undef %0.sub0 = IMPLICIT_DEF
%0.sub1 = IMPLICIT_DEF
S_CBRANCH_VCCNZ %bb.2, implicit undef $vcc
S_BRANCH %bb.1
bb.1:
S_NOP 0
S_NOP 0, implicit %0.sub1
bb.2:
S_NOP 0, implicit %0.sub1
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMoveIntoNewBundle(MF, LIS, 0, 1, 1);
});
}
TEST(LiveIntervalTest, BundleSubRegDef) {
liveIntervalTest(R"MIR(
successors: %bb.1, %bb.2
undef %0.sub0 = IMPLICIT_DEF
%0.sub1 = IMPLICIT_DEF
S_CBRANCH_VCCNZ %bb.2, implicit undef $vcc
S_BRANCH %bb.1
bb.1:
S_NOP 0
S_NOP 0, implicit %0.sub1
bb.2:
S_NOP 0, implicit %0.sub1
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMoveIntoNewBundle(MF, LIS, 0, 1, 0);
});
}
TEST(LiveIntervalTest, SplitAtOneInstruction) {
liveIntervalTest(R"MIR(
successors: %bb.1
%0 = IMPLICIT_DEF
S_BRANCH %bb.1
bb.1:
S_NOP 0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testSplitAt(MF, LIS, 1, 0);
});
}
TEST(LiveIntervalTest, SplitAtMultiInstruction) {
liveIntervalTest(R"MIR(
successors: %bb.1
%0 = IMPLICIT_DEF
S_NOP 0
S_NOP 0
S_NOP 0
S_NOP 0
S_BRANCH %bb.1
bb.1:
S_NOP 0
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
testSplitAt(MF, LIS, 0, 0);
});
}
TEST(LiveIntervalTest, RepairIntervals) {
liveIntervalTest(R"MIR(
%1:sgpr_32 = IMPLICIT_DEF
dead %2:sgpr_32 = COPY undef %3.sub0:sgpr_128
undef %4.sub2:sgpr_128 = COPY %1:sgpr_32
%5:sgpr_32 = COPY %4.sub2:sgpr_128
)MIR", [](MachineFunction &MF, LiveIntervals &LIS) {
MachineInstr &Instr1 = getMI(MF, 1, 0);
MachineInstr &Instr2 = getMI(MF, 2, 0);
MachineInstr &Instr3 = getMI(MF, 3, 0);
LIS.RemoveMachineInstrFromMaps(Instr2);
MachineBasicBlock *MBB = Instr1.getParent();
SmallVector<Register> OrigRegs{
Instr1.getOperand(0).getReg(),
Instr2.getOperand(0).getReg(),
Instr2.getOperand(1).getReg(),
};
LIS.repairIntervalsInRange(MBB, Instr2, Instr3, OrigRegs);
});
}
TEST(LiveIntervalTest, AdjacentIntervals) {
liveIntervalTest(
R"MIR(
successors: %bb.1, %bb.2
$vgpr1 = IMPLICIT_DEF
S_NOP 0, implicit $vgpr1
%1:vgpr_32 = IMPLICIT_DEF
%2:vgpr_32 = IMPLICIT_DEF
S_CBRANCH_VCCNZ %bb.2, implicit undef $vcc
S_BRANCH %bb.1
bb.1:
$vgpr0, dead renamable $vcc = V_ADD_CO_U32_e64 %1, %2, 0, implicit $exec
S_NOP 0, implicit $vgpr0
S_BRANCH %bb.3
bb.2:
$vgpr0 = IMPLICIT_DEF
$vgpr1, dead renamable $vcc = V_ADD_CO_U32_e64 %1, %2, 0, implicit $exec
S_NOP 0, implicit $vgpr0, implicit $vgpr1
S_BRANCH %bb.3
bb.3:
)MIR",
[](MachineFunction &MF, LiveIntervals &LIS) {
const auto &R1 =
LIS.getInterval(getMI(MF, 2, 0).getOperand(0).getReg());
const auto &R2 =
LIS.getInterval(getMI(MF, 3, 0).getOperand(0).getReg());
MCRegister V1 = getMI(MF, 1, 2).getOperand(0).getReg().asMCReg();
ASSERT_FALSE(checkRegUnitInterference(
LIS, *MF.getSubtarget().getRegisterInfo(), R1, V1));
ASSERT_FALSE(checkRegUnitInterference(
LIS, *MF.getSubtarget().getRegisterInfo(), R2, V1));
});
}
TEST(LiveIntervalTest, LiveThroughSegments) {
liveIntervalTest(
R"MIR(
%0 = IMPLICIT_DEF
S_BRANCH %bb.2
bb.1:
S_NOP 0, implicit %0
S_ENDPGM 0
bb.2:
S_BRANCH %bb.1
)MIR",
[](MachineFunction &MF, LiveIntervals &LIS) {
MachineInstr &ImpDef = getMI(MF, 0, 0);
MachineInstr &Nop = getMI(MF, 0, 1);
LiveInterval &LI = LIS.getInterval(ImpDef.getOperand(0).getReg());
SlotIndex OrigIdx = LIS.getInstructionIndex(ImpDef).getRegSlot();
LiveInterval::iterator FirstSeg = LI.FindSegmentContaining(OrigIdx);
// %0 is live through bb.2. Move its def into bb.1 and update LIS but do
// not remove the segment for bb.2. This should cause machine
// verification to fail.
LIS.RemoveMachineInstrFromMaps(ImpDef);
ImpDef.moveBefore(&Nop);
LIS.InsertMachineInstrInMaps(ImpDef);
SlotIndex NewIdx = LIS.getInstructionIndex(ImpDef).getRegSlot();
FirstSeg->start = NewIdx;
FirstSeg->valno->def = NewIdx;
},
false);
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
initLLVM();
return RUN_ALL_TESTS();
}