Index: include/llvm/CodeGen/MIRYamlMapping.h =================================================================== --- include/llvm/CodeGen/MIRYamlMapping.h +++ include/llvm/CodeGen/MIRYamlMapping.h @@ -398,6 +398,9 @@ std::vector Constants; /// Constant pool. MachineJumpTable JumpTableInfo; BlockStringValue Body; + /// The testCommands block is syntactic sugar to produce an llc-test-commands + /// function attribute for the llc ExecutTestCommands pass. + BlockStringValue TestCommands; }; template <> struct MappingTraits { @@ -419,6 +422,8 @@ if (!YamlIO.outputting() || !MF.JumpTableInfo.Entries.empty()) YamlIO.mapOptional("jumpTable", MF.JumpTableInfo); YamlIO.mapOptional("body", MF.Body); + if (!YamlIO.outputting()) + YamlIO.mapOptional("testCommands", MF.TestCommands); } }; Index: lib/CodeGen/MIRParser/MIRParser.cpp =================================================================== --- lib/CodeGen/MIRParser/MIRParser.cpp +++ lib/CodeGen/MIRParser/MIRParser.cpp @@ -145,7 +145,7 @@ SMRange SourceRange); /// Create an empty function with the given name. - void createDummyFunction(StringRef Name, Module &M); + Function &createDummyFunction(StringRef Name, Module &M); void initNames2RegClasses(const MachineFunction &MF); void initNames2RegBanks(const MachineFunction &MF); @@ -265,21 +265,30 @@ if (Functions.find(FunctionName) != Functions.end()) return error(Twine("redefinition of machine function '") + FunctionName + "'"); - Functions.insert(std::make_pair(FunctionName, std::move(MF))); + + Function *F; if (NoLLVMIR) - createDummyFunction(FunctionName, M); - else if (!M.getFunction(FunctionName)) + F = &createDummyFunction(FunctionName, M); + else if (!(F = M.getFunction(FunctionName))) return error(Twine("function '") + FunctionName + "' isn't defined in the provided LLVM IR"); + + // Syntactic sugar for lit/llc tests. + const std::string &TestCommands = MF->TestCommands.Value.Value; + if (!TestCommands.empty()) + F->addFnAttr("llc-test-commands", TestCommands); + + Functions.insert(std::make_pair(FunctionName, std::move(MF))); return false; } -void MIRParserImpl::createDummyFunction(StringRef Name, Module &M) { +Function &MIRParserImpl::createDummyFunction(StringRef Name, Module &M) { auto &Context = M.getContext(); Function *F = cast(M.getOrInsertFunction( Name, FunctionType::get(Type::getVoidTy(Context), false))); BasicBlock *BB = BasicBlock::Create(Context, "entry", F); new UnreachableInst(Context, BB); + return *F; } static bool isSSA(const MachineFunction &MF) { @@ -324,6 +333,7 @@ if (It == Functions.end()) return error(Twine("no machine function information for function '") + MF.getName() + "' in the MIR file"); + // TODO: Recreate the machine function. initNames2RegClasses(MF); initNames2RegBanks(MF); Index: test/CodeGen/AMDGPU/handlemove.mir =================================================================== --- /dev/null +++ test/CodeGen/AMDGPU/handlemove.mir @@ -0,0 +1,189 @@ +# RUN: llc -o - %s -mtriple=amdgcn -verify-machineinstrs -run-pass=execute-test-commands | FileCheck %s +... +name: MoveUpDef +body: | + bb.0: + S_NOP 0 + S_NOP 0 + early-clobber %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0, implicit %0 +testCommands: getMBB(0).handleMove(2,1) +--- +... +# CHECK-LABEL: name: MoveUpRedef +# CHECK: %0 : sreg_64 = IMPLICIT_DEF +# CHECK: %0 = IMPLICIT_DEF implicit %0(tied-def 0) +# CHECK: S_NOP 0 +# CHECK: S_NOP 0, implicit %0 +name: MoveUpRedef +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0 + %0 = IMPLICIT_DEF implicit %0(tied-def 0) + S_NOP 0, implicit %0 +testCommands: getMBB(0).handleMove(2,1) +--- +... +name: MoveUpEarlyDef +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0 + early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0) + S_NOP 0, implicit %0 +testCommands: getMBB(0).handleMove(2,1) +--- +... +name: MoveUpKill +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 +testCommands: getMBB(0).handleMove(2,1) +--- +... +name: MoveUpKillFollowing +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 + S_NOP 0, implicit %0 +testCommands: getMBB(0).handleMove(2,1)" +--- +... +name: MoveDownDef +body: | + bb.0: + S_NOP 0 + early-clobber %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 +testCommands: getMBB(0).handleMove(1,2) +--- +... +name: MoveDownRedef +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + %0 = IMPLICIT_DEF implicit %0(tied-def 0) + S_NOP 0 + S_NOP 0, implicit %0 +testCommands: getMBB(0).handleMove(1,2) +--- +... +name: MoveDownEarlyDef +body: | + bb.0: + S_NOP 0 + early-clobber %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 +testCommands: getMBB(0).handleMove(1,2) +--- +... +name: MoveDownEarlyRedef +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0) + S_NOP 0 + S_NOP 0, implicit %0 +testCommands: getMBB(0).handleMove(1,2) +--- +... +name: MoveDownKill +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0, implicit %0 + S_NOP 0 +testCommands: getMBB(0).handleMove(1,2) +--- +... +name: MoveDownKillFollowing +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 + S_NOP 0, implicit %0 +testCommands: getMBB(0).handleMove(1,2) +--- +... +name: MoveUndefUse +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0, implicit undef %0 + S_NOP 0, implicit %0 + S_NOP 0 +testCommands: getMBB(0).handleMove(1,3) +--- +... +name: MoveOverUndefUse0 +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit undef %0 + %0 = IMPLICIT_DEF implicit %0(tied-def 0) +testCommands: getMBB(0).handleMove(3,1) +--- +... +name: MoveOverUndefUse1 +body: | + bb.0: + %sgpr0 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit undef %sgpr0 + %sgpr0 = IMPLICIT_DEF implicit %sgpr0(tied-def 0) +testCommands: getMBB(0).handleMove(3,1) +... +--- +name: MoveUpValNos +body: | + bb.0: + successors: %bb.1, %bb.2 + %0 : sreg_64 = 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 + + bb.3: + +testCommands: getMBB(2).handleMove(2,0) +... +--- +name: SubRegMoveDown +body: | + bb.0: + successors: %bb.1, %bb.2 + %0 : sreg_64 = 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 + +testCommands: | + getMBB(1).getMI(0).setIsUndef(0) + ; The scheduler is in the situation where we compute liveness with the + ; undef flag present, but then clear it. + getMBB(1).handleMove(1,4) +... Index: tools/llc/CMakeLists.txt =================================================================== --- tools/llc/CMakeLists.txt +++ tools/llc/CMakeLists.txt @@ -20,6 +20,7 @@ add_llvm_tool(llc llc.cpp + ExecuteTestCommands.cpp DEPENDS intrinsics_gen Index: tools/llc/ExecuteTestCommands.h =================================================================== --- /dev/null +++ tools/llc/ExecuteTestCommands.h @@ -0,0 +1,18 @@ +//===- llvm/Pass.h - Base class for Passes ----------------------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +#ifndef LLVM_LLC_EXECUTETESTCOMMANDS_H +#define LLVM_LLC_EXECUTETESTCOMMANDS_H + +namespace llvm { +class PassRegistry; + +void initializeExecuteTestCommandsPass(PassRegistry &Registry); +} // End llvm namespace + +#endif Index: tools/llc/ExecuteTestCommands.cpp =================================================================== --- /dev/null +++ tools/llc/ExecuteTestCommands.cpp @@ -0,0 +1,340 @@ +//===-- llc.cpp - Implement the LLVM Native Code Generator ----------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file Implements a that executes a simple scripting language to perform +/// user specified transformation on a machine function. This enables testing +/// of single transformation APIs without relying on a complete pass with +/// related anlysis. +// +//===----------------------------------------------------------------------===// + +#include "ExecuteTestCommands.h" + +#include "llvm/Support/Debug.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/LiveIntervalAnalysis.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/CodeGen/MachineOperand.h" +#include "llvm/Pass.h" +#include "llvm/PassRegistry.h" + +#define DEBUG_TYPE "execute-test-commands" + +using namespace llvm; + +namespace { + +/// A dynamically typed value in the minimalistic scripting language. +struct Value { +public: + enum ValueKind { + INTEGER, + INSTR, + BLOCK, + NONE + }; + static Value integer(unsigned Val) { + Value Res(INTEGER); + Res.D.Integer = Val; + return Res; + } + static Value instr(MachineInstr &MI) { + Value Res(INSTR); + Res.D.MI = &MI; + return Res; + } + static Value block(MachineBasicBlock &MBB) { + Value Res(BLOCK); + Res.D.MBB = &MBB; + return Res; + } + static Value none() { + return Value(NONE); + } + ValueKind getKind() const { return Kind; } + + unsigned getInteger() const { + assert(Kind == INTEGER); + return D.Integer; + } + MachineInstr &getInstr() const { + assert(Kind == INSTR); + return *D.MI; + } + MachineBasicBlock &getBlock() const { + assert(Kind == BLOCK); + return *D.MBB; + } + +private: + Value(ValueKind Kind) : Kind(Kind) {} + ValueKind Kind; + union ValueData { + unsigned Integer; + MachineInstr *MI; + MachineBasicBlock *MBB; + } D; +}; + +/// Pass that executes simple user provided commands on a machine function. +class ExecuteTestCommands : public MachineFunctionPass { +public: + static char ID; + ExecuteTestCommands() : MachineFunctionPass(ID) {} + + bool runOnMachineFunction(MachineFunction &MF) override; + StringRef getPassName() const override { return "Execute Test Commands"; } + + bool parseExecuteCommand(StringRef &Input, Value &Result, Value This) const; + bool getMBB(const SmallVectorImpl &Args, Value &Result) const; + bool getMI(const SmallVectorImpl &Args, Value &Result) const; + bool setIsUndef(const SmallVectorImpl &Args, Value &Result) const; + bool handleMove(const SmallVectorImpl &Args, Value &Result) const; + bool executeCommand(StringRef CommandName, const SmallVectorImpl &Args, + Value &Result) const; + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addRequired(); + AU.addPreserved(); + AU.addPreserved(); + MachineFunctionPass::getAnalysisUsage(AU); + } + +private: + MachineFunction *MF; + LiveIntervals *LIS; +}; + +char ExecuteTestCommands::ID; + +MachineInstr &getMIAtIdx(MachineBasicBlock &MBB, unsigned Idx) { + unsigned I = 0; + for (MachineInstr &MI : MBB) { + if (I == Idx) + return MI; + ++I; + } + report_fatal_error("Instruction not found"); +} + +bool ExecuteTestCommands::getMBB(const SmallVectorImpl &Args, + Value &Result) const { + if (Args.size() != 1 || Args[0].getKind() != Value::INTEGER) + return false; + + unsigned Idx = Args[0].getInteger(); + DEBUG(dbgs() << "getMBB(" << Idx << ")\n"); + if (Idx > MF->getNumBlockIDs()) { + errs() << "Invalid block index\n"; + return false; + } + MachineBasicBlock *MBB = MF->getBlockNumbered(Idx); + Result = Value::block(*MBB); + return true; +} + +bool ExecuteTestCommands::getMI(const SmallVectorImpl &Args, + Value &Result) const { + if (Args.size() != 2 || Args[0].getKind() != Value::BLOCK || + Args[1].getKind() != Value::INTEGER) + return false; + + MachineBasicBlock &MBB = Args[0].getBlock(); + unsigned Idx = Args[1].getInteger(); + DEBUG(dbgs() << "getMI(" << Idx << ")\n"); + MachineInstr &MI = getMIAtIdx(MBB, Idx); + Result = Value::instr(MI); + return true; +} + +bool ExecuteTestCommands::setIsUndef(const SmallVectorImpl &Args, + Value &Result) const { + if (Args.size() != 3 || Args[0].getKind() != Value::INSTR || + Args[1].getKind() != Value::INTEGER || + Args[2].getKind() != Value::INTEGER) + return false; + + MachineInstr &MI = Args[0].getInstr(); + unsigned OperandNum = Args[1].getInteger(); + bool SetUndef = Args[1].getInteger() != 0; + DEBUG(dbgs() << "setIsUndef(" << OperandNum << "," + << (SetUndef ? "true" : "false") << ")\n"); + if (OperandNum >= MI.getNumOperands()) { + errs() << "Error: Invalid operand number\n"; + return false; + } + MachineOperand &MO = MI.getOperand(OperandNum); + if (!MO.isReg()) { + errs() << "Error: Operand is not a register operand\n"; + return false; + } + MO.setIsUndef(SetUndef); + return true; +} + +bool ExecuteTestCommands::handleMove(const SmallVectorImpl &Args, + Value &Result) const { + if (Args.size() != 3 || Args[0].getKind() != Value::BLOCK || + Args[1].getKind() != Value::INTEGER || + Args[2].getKind() != Value::INTEGER) + return false; + + MachineBasicBlock &MBB = Args[0].getBlock(); + unsigned FromIdx = Args[1].getInteger(); + unsigned ToIdx = Args[2].getInteger(); + MachineInstr &FromInstr = getMIAtIdx(MBB, FromIdx); + MachineInstr &ToInstr = getMIAtIdx(MBB, ToIdx); + Result = Value::none(); + + DEBUG(dbgs() << "handleMove:\n" + << "\tInstruction: " << FromInstr + << "\tto Before: " << ToInstr); + + // Move the instruction. + MBB.splice(ToInstr.getIterator(), &MBB, FromInstr.getIterator()); + // Update liveness with handleMove(). + LIS->handleMove(FromInstr, true); + return true; +} + +bool ExecuteTestCommands::executeCommand(StringRef CommandName, + const SmallVectorImpl &Args, + Value &Result) const { + if (CommandName == "getMBB") { + return getMBB(Args, Result); + } else if (CommandName == "getMI") { + return getMI(Args, Result); + } else if (CommandName == "handleMove") { + return handleMove(Args, Result); + } + errs() << "Error: Unknown command \"" << CommandName << "\"\n"; + return false; +} + +/// \defgroup ParsingHelpers Helper functions to facility parsing. +/// The functions detect elements at the beginning of the Input StringRef +/// parameter and update the StringRef to a later position when consuming the +/// input. Elements are separated by spaces, all parsing functions skip spaces +/// before looking for the elements. +/// The tryXXX() functions check for a given sequence and if it is present +/// consume it and return true, otherwise false. +/// The expectXXX() functions check for a given sequence and if it is present +/// consume it and return true, otherwise they print and error message and +/// return false. +/// \{ + +bool expectIdentifier(StringRef &Input, StringRef &Result) { + Input = Input.ltrim(); + Result = Input.take_while([](char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; + }); + if (Result.empty()) { + errs() << "Expected identifier, got \"" << Input << "\"\n"; + return false; + } + Input = Input.drop_front(Result.size()); + return true; +} + +bool expectUnsigned(StringRef &Input, unsigned &Result) { + Input = Input.ltrim(); + unsigned long long a0; + if (consumeUnsignedInteger(Input, 10, a0)) { + errs() << "Expected number, got \"" << Input << "\"\n"; + return false; + } + Result = (unsigned)a0; + if ((unsigned long long)Result != a0) { + errs() << "Number overflow\n"; + return false; + } + return true; +} + +bool tryChar(StringRef &Input, char C) { + Input = Input.ltrim(); + if (Input.empty() || Input[0] != C) + return false; + Input = Input.drop_front(1); + return true; +} + +bool expectChar(StringRef &Input, char C) { + if (!tryChar(Input, C)) { + errs() << "Expected '" << C << "', got \"" << Input << "\"\n"; + return false; + } + return true; +} + +bool expectArguments(StringRef &Input, SmallVectorImpl &Args) { + if (!expectChar(Input, '(')) + return false; + + while (!tryChar(Input, ')')) { + unsigned Arg; + if (!expectUnsigned(Input, Arg)) + return false; + Args.push_back(Value::integer(Arg)); + tryChar(Input, ','); + } + return true; +} + +/// \} + +bool ExecuteTestCommands::parseExecuteCommand(StringRef &Input, Value &Result, + Value This) const { + StringRef CommandName; + if (!expectIdentifier(Input, CommandName)) + return false; + SmallVector Args; + if (This.getKind() != Value::NONE) + Args.push_back(This); + if (!expectArguments(Input, Args)) + return false; + + if (!executeCommand(CommandName, Args, Result)) { + errs() << "Error: Failed to execute command\n"; + return false; + } + + if (tryChar(Input, '.')) + return parseExecuteCommand(Input, Result, Result); + + return true; +} + +bool ExecuteTestCommands::runOnMachineFunction(MachineFunction &MF) { + this->MF = &MF; + const Function &F = *MF.getFunction(); + if (!F.hasFnAttribute("llc-test-commands")) { + errs() << "Warning: No test commands for function " << MF.getName() << '\n'; + return false; + } + + StringRef Commands = F.getFnAttribute("llc-test-commands").getValueAsString(); + LIS = &getAnalysis(); + + DEBUG(dbgs() << "Execute commands on function " << MF.getName() << '\n'); + DEBUG(dbgs() << "Commands: " << Commands << '\n'); + + Value Result = Value::none(); + parseExecuteCommand(Commands, Result, Value::none()); + return true; +} + +} // end anonymous namespace + +INITIALIZE_PASS_BEGIN(ExecuteTestCommands, "execute-test-commands", + "Execute Test Commands", false, false) +INITIALIZE_PASS_DEPENDENCY(LiveIntervals) +INITIALIZE_PASS_END(ExecuteTestCommands, "execute-test-commands", + "Execute Test Commands", false, false) Index: tools/llc/llc.cpp =================================================================== --- tools/llc/llc.cpp +++ tools/llc/llc.cpp @@ -13,7 +13,7 @@ // //===----------------------------------------------------------------------===// - +#include "ExecuteTestCommands.h" #include "llvm/ADT/STLExtras.h" #include "llvm/ADT/Triple.h" #include "llvm/Analysis/TargetLibraryInfo.h" @@ -296,6 +296,7 @@ initializeConstantHoistingLegacyPassPass(*Registry); initializeScalarOpts(*Registry); initializeVectorization(*Registry); + initializeExecuteTestCommandsPass(*Registry); // Register the target printer for --version. cl::AddExtraVersionPrinter(TargetRegistry::printRegisteredTargetsForVersion); Index: unittests/MI/LiveIntervalTest.cpp =================================================================== --- unittests/MI/LiveIntervalTest.cpp +++ unittests/MI/LiveIntervalTest.cpp @@ -1,389 +1,8 @@ -#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 createTargetMachine() { - Triple TargetTriple("amdgcn--"); - std::string Error; - const Target *T = TargetRegistry::lookupTarget("", TargetTriple, Error); - if (!T) - return nullptr; - - TargetOptions Options; - return std::unique_ptr( - T->createTargetMachine("AMDGPU", "", "", Options, None, - CodeModel::Default, CodeGenOpt::Aggressive)); -} - -std::unique_ptr parseMIR(LLVMContext &Context, - legacy::PassManagerBase &PM, std::unique_ptr &MIR, - const TargetMachine &TM, StringRef MIRCode, const char *FuncName) { - SMDiagnostic Diagnostic; - std::unique_ptr MBuffer = MemoryBuffer::getMemBuffer(MIRCode); - MIR = createMIRParser(std::move(MBuffer), Context); - if (!MIR) - return nullptr; - - std::unique_ptr 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 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(); - T(MF, LIS); - EXPECT_TRUE(MF.verify(this)); - return true; - } - - void getAnalysisUsage(AnalysisUsage &AU) const override { - AU.setPreservesAll(); - AU.addRequired(); - AU.addPreserved(); - 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 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 MIR; - std::unique_ptr 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(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 guarntee 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); - }); -} +// Empty program. This is a hack to not fail incremental builders. +// Would we just remove this program then the incremental builders would keep +// executing an old version of the test (tests are discovered by search for +// executables, they are not defined based on CMakeLists.txt). int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - initLLVM(); - return RUN_ALL_TESTS(); + return 0; }