mirror of
https://github.com/RPCS3/llvm-mirror.git
synced 2024-11-22 18:54:02 +01:00
f1bab75f10
Remove the InsertionPoint argument from SlotIndexes::insertMBBInMaps because it was confusing: what does it mean to insert a new block between two instructions, in the middle of an existing block? Instead, support the case that MachineBasicBlock::splitAt really needs, where the new block contains some instructions that are already in the maps because they have been moved there from the tail of the previous block. In all other use cases the new block is empty. Based on work by Carl Ritson! Differential Revision: https://reviews.llvm.org/D94311
657 lines
19 KiB
C++
657 lines
19 KiB
C++
#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/IR/LegacyPassManager.h"
|
|
#include "llvm/InitializePasses.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 "gtest/gtest.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, None, None,
|
|
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) : 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);
|
|
}
|
|
|
|
/**
|
|
* 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);
|
|
}
|
|
|
|
static void liveIntervalTest(StringRef MIRFunc, LiveIntervalTest T) {
|
|
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));
|
|
|
|
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 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 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 0, %57, 0, undef %70:vgpr_32, 0, %125.sub1, 0, 0, implicit $mode, implicit $exec
|
|
%124.sub0:vreg_128 = nofpexcept V_MAD_F32 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 = WWM %4:sreg_32, implicit $exec
|
|
%5:sreg_32 = S_FLBIT_I32_B32 %9.sub0:sreg_64
|
|
early-clobber %9.sub1:sreg_64 = 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, 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);
|
|
});
|
|
}
|
|
|
|
int main(int argc, char **argv) {
|
|
::testing::InitGoogleTest(&argc, argv);
|
|
initLLVM();
|
|
return RUN_ALL_TESTS();
|
|
}
|