390 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			C++
		
	
	
	
			
		
		
	
	
			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();
 | 
						|
}
 |