llvm-mirror/unittests/MI/LiveIntervalTest.cpp
Stanislav Mekhanoshin 634b6f1d8b LIS: fix handleMove to properly extend main range
handleMoveDown or handleMoveUp cannot properly repair a main
range of a LiveInterval since they only get LiveRange. There
is a problem if certain use has moved few segments away and
there is a hole in the main range in between of these two
locations. We may get a SubRange with a very extended Segment
spanning several Segments of the main range and also spanning
that hole. If that happens then we end up with the main range
not covering its SubRange which is an error.

It might be possible to attempt fixing the main range in place
just between of the old and new index by extending all of its
Segments in between, but it is unclear this logic will be
faster than just straight constructMainRangeFromSubranges,
which itself is pretty cheap since it only contains interval
logic. That will also require shrinkToUses() call after which
is probably even more expensive.

In the test second move is from 64B to 92B for the sub1.
Subrange is correctly fixed:

L000000000000000C [16r,32B:0)[32B,92r:1)  0@16r 1@32B-phi

But the main range has a hole in between 80d and 88r after
updateRange():

%1 [16r,32B:0)[32B,80r:4)[80r,80d:3)[88r,96r:1)[96r,160B:2)

Since source position is 64B this segment is not even considered
by the updateRange().

Differential Revision: https://reviews.llvm.org/D82916
2020-07-07 11:52:32 -07:00

616 lines
18 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);
}
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);
});
}
int main(int argc, char **argv) {
::testing::InitGoogleTest(&argc, argv);
initLLVM();
return RUN_ALL_TESTS();
}