llvm-project/llvm/unittests/MI/LiveIntervalTest.cpp

390 lines
11 KiB
C++

#include "gtest/gtest.h"
#include "llvm/ADT/STLExtras.h"
#include "llvm/CodeGen/LiveIntervalAnalysis.h"
#include "llvm/CodeGen/MIRParser/MIRParser.h"
#include "llvm/CodeGen/MachineFunction.h"
#include "llvm/CodeGen/MachineModuleInfo.h"
#include "llvm/CodeGen/MachineRegisterInfo.h"
#include "llvm/CodeGen/Passes.h"
#include "llvm/Support/MemoryBuffer.h"
#include "llvm/Support/SourceMgr.h"
#include "llvm/Support/TargetRegistry.h"
#include "llvm/Support/TargetSelect.h"
#include "llvm/Target/TargetMachine.h"
#include "llvm/Target/TargetOptions.h"
#include "llvm/Target/TargetRegisterInfo.h"
#include "llvm/IR/LegacyPassManager.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<TargetMachine> createTargetMachine() {
Triple TargetTriple("amdgcn--");
std::string Error;
const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error);
if (!T)
return nullptr;
TargetOptions Options;
return std::unique_ptr<TargetMachine>(
T->createTargetMachine("AMDGPU", "", "", Options, None,
CodeModel::Default, CodeGenOpt::Aggressive));
}
std::unique_ptr<Module> parseMIR(LLVMContext &Context,
legacy::PassManagerBase &PM, std::unique_ptr<MIRParser> &MIR,
const TargetMachine &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->parseLLVMModule();
if (!M)
return nullptr;
M->setDataLayout(TM.createDataLayout());
Function *F = M->getFunction(FuncName);
if (!F)
return nullptr;
MachineModuleInfo *MMI = new MachineModuleInfo(&TM);
MMI->setMachineFunctionInitializer(MIR.get());
PM.add(MMI);
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) : MachineFunctionPass(ID), T(T) {
initializeTestPassPass(*PassRegistry::getPassRegistry());
}
bool runOnMachineFunction(MachineFunction &MF) override {
LiveIntervals &LIS = getAnalysis<LiveIntervals>();
T(MF, LIS);
EXPECT_TRUE(MF.verify(this));
return true;
}
void getAnalysisUsage(AnalysisUsage &AU) const override {
AU.setPreservesAll();
AU.addRequired<LiveIntervals>();
AU.addPreserved<LiveIntervals>();
MachineFunctionPass::getAnalysisUsage(AU);
}
private:
LiveIntervalTest T;
};
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);
}
static void liveIntervalTest(StringRef MIRFunc, LiveIntervalTest T) {
LLVMContext Context;
std::unique_ptr<TargetMachine> 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(
"---\n"
"...\n"
"name: func\n"
"registers:\n"
" - { id: 0, class: sreg_64 }\n"
"body: |\n"
" bb.0:\n"
) + Twine(MIRFunc) + Twine("...\n")).toNullTerminatedStringRef(S);
std::unique_ptr<MIRParser> MIR;
std::unique_ptr<Module> M = parseMIR(Context, PM, MIR, *TM, MIRString,
"func");
PM.add(new TestPass(T));
PM.run(*M);
}
} // End of anonymous namespace.
char TestPass::ID = 0;
INITIALIZE_PASS(TestPass, "testpass", "testpass", false, false)
TEST(LiveIntervalTest, MoveUpDef) {
// Value defined.
liveIntervalTest(
" S_NOP 0\n"
" S_NOP 0\n"
" early-clobber %0 = IMPLICIT_DEF\n"
" S_NOP 0, implicit %0\n",
[](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 2, 1);
});
}
TEST(LiveIntervalTest, MoveUpRedef) {
liveIntervalTest(
" %0 = IMPLICIT_DEF\n"
" S_NOP 0\n"
" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
" S_NOP 0, implicit %0\n",
[](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 2, 1);
});
}
TEST(LiveIntervalTest, MoveUpEarlyDef) {
liveIntervalTest(
" S_NOP 0\n"
" S_NOP 0\n"
" early-clobber %0 = IMPLICIT_DEF\n"
" S_NOP 0, implicit %0\n",
[](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 2, 1);
});
}
TEST(LiveIntervalTest, MoveUpEarlyRedef) {
liveIntervalTest(
" %0 = IMPLICIT_DEF\n"
" S_NOP 0\n"
" early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
" S_NOP 0, implicit %0\n",
[](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 2, 1);
});
}
TEST(LiveIntervalTest, MoveUpKill) {
liveIntervalTest(
" %0 = IMPLICIT_DEF\n"
" S_NOP 0\n"
" S_NOP 0, implicit %0\n",
[](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 2, 1);
});
}
TEST(LiveIntervalTest, MoveUpKillFollowing) {
liveIntervalTest(
" %0 = IMPLICIT_DEF\n"
" S_NOP 0\n"
" S_NOP 0, implicit %0\n"
" S_NOP 0, implicit %0\n",
[](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(
" S_NOP 0\n"
" early-clobber %0 = IMPLICIT_DEF\n"
" S_NOP 0\n"
" S_NOP 0, implicit %0\n",
[](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, MoveDownRedef) {
liveIntervalTest(
" %0 = IMPLICIT_DEF\n"
" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
" S_NOP 0\n"
" S_NOP 0, implicit %0\n",
[](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, MoveDownEarlyDef) {
liveIntervalTest(
" S_NOP 0\n"
" early-clobber %0 = IMPLICIT_DEF\n"
" S_NOP 0\n"
" S_NOP 0, implicit %0\n",
[](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, MoveDownEarlyRedef) {
liveIntervalTest(
" %0 = IMPLICIT_DEF\n"
" early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
" S_NOP 0\n"
" S_NOP 0, implicit %0\n",
[](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, MoveDownKill) {
liveIntervalTest(
" %0 = IMPLICIT_DEF\n"
" S_NOP 0, implicit %0\n"
" S_NOP 0\n",
[](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, MoveDownKillFollowing) {
liveIntervalTest(
" %0 = IMPLICIT_DEF\n"
" S_NOP 0\n"
" S_NOP 0, implicit %0\n"
" S_NOP 0, implicit %0\n",
[](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 1, 2);
});
}
TEST(LiveIntervalTest, MoveUndefUse) {
liveIntervalTest(
" %0 = IMPLICIT_DEF\n"
" S_NOP 0, implicit undef %0\n"
" S_NOP 0, implicit %0\n"
" S_NOP 0\n",
[](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 guarntee that this valno wasn't
// used in other segments.
liveIntervalTest(
" successors: %bb.1, %bb.2\n"
" %0 = IMPLICIT_DEF\n"
" S_CBRANCH_VCCNZ %bb.2, implicit undef %vcc\n"
" S_BRANCH %bb.1\n"
" bb.2:\n"
" S_NOP 0, implicit %0\n"
" bb.1:\n"
" successors: %bb.2\n"
" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n"
" S_BRANCH %bb.2\n",
[](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 2, 0, 2);
});
}
TEST(LiveIntervalTest, MoveOverUndefUse0) {
// findLastUseBefore() used by handleMoveUp() must ignore undef operands.
liveIntervalTest(
" %0 = IMPLICIT_DEF\n"
" S_NOP 0\n"
" S_NOP 0, implicit undef %0\n"
" %0 = IMPLICIT_DEF implicit %0(tied-def 0)\n",
[](MachineFunction &MF, LiveIntervals &LIS) {
testHandleMove(MF, LIS, 3, 1);
});
}
TEST(LiveIntervalTest, MoveOverUndefUse1) {
// findLastUseBefore() used by handleMoveUp() must ignore undef operands.
liveIntervalTest(
" %sgpr0 = IMPLICIT_DEF\n"
" S_NOP 0\n"
" S_NOP 0, implicit undef %sgpr0\n"
" %sgpr0 = IMPLICIT_DEF implicit %sgpr0(tied-def 0)\n",
[](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(
" successors: %bb.1, %bb.2\n"
" %0 = IMPLICIT_DEF\n"
" S_CBRANCH_VCCNZ %bb.2, implicit undef %vcc\n"
" S_BRANCH %bb.1\n"
" bb.2:\n"
" successors: %bb.1\n"
" S_NOP 0, implicit %0.sub0\n"
" S_NOP 0, implicit %0.sub1\n"
" S_NOP 0\n"
" undef %0.sub0 = IMPLICIT_DEF\n"
" %0.sub1 = IMPLICIT_DEF\n"
" bb.1:\n"
" S_NOP 0, implicit %0\n",
[](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);
});
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
initLLVM();
return RUN_ALL_TESTS();
}