Index: docs/MIRLangRef.rst =================================================================== --- docs/MIRLangRef.rst +++ docs/MIRLangRef.rst @@ -71,6 +71,36 @@ specific test directories. They also need to specify a target triple or a target architecture either in the run line or in the embedded LLVM IR module. +Testing Individual APIs +----------------------- + +Generally APIs should be tested with unit tests. However for cases that require +a specific machine intermediate representation as input and only test simple +calls ``llc`` supports the ``execute-test-commands`` pass. + +This pass takes a sequence of expressions specified with the test and executes +them. Example: + +.. code-block:: text + + # RUN: llc -o - %s -mtarget=x86_64-- -run-pass=execute-test-commands | FileCheck %s + --- + name: func + body: | + bb.0: + %eax = MOV32i 7 + %ebx = MOV32i 42 + %ecx = MOV32i 5 + testCommands: move(bb(0).insn(0), bb(0).insn(2)) + ... + +The example executes the ``move`` command; taking the first basic block +(``bb(0)``) and the first instruction in it (``.insn(0)``) and moving it in +front of the third instruction (``bb(0).insn(2)``). + +The list of available functions can be found in the pass implementation in +:file:`tools/llc/ExecuteTestCommands.cpp`. It is easy to add more functions. + Limitations ----------- Index: include/llvm/CodeGen/MIRYamlMapping.h =================================================================== --- include/llvm/CodeGen/MIRYamlMapping.h +++ include/llvm/CodeGen/MIRYamlMapping.h @@ -399,6 +399,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 { @@ -421,6 +424,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) { Index: test/CodeGen/AMDGPU/handlemove.mir =================================================================== --- /dev/null +++ test/CodeGen/AMDGPU/handlemove.mir @@ -0,0 +1,264 @@ +# RUN: llc -o - %s -mtriple=amdgcn -verify-machineinstrs -run-pass=execute-test-commands | FileCheck %s +... +# CHECK-LABEL: name: MoveUpDef +# CHECK: S_NOP 0 +# CHECK: early-clobber %0 = IMPLICIT_DEF +# CHECK: S_NOP 0 +# CHECK: S_NOP 0, implicit %0 +name: MoveUpDef +body: | + bb.0: + S_NOP 0 + S_NOP 0 + early-clobber %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0, implicit %0 +testCommands: move(bb(0).insn(2), bb(0).insn(1)) +--- +... +# CHECK-LABEL: name: MoveUpRedef +# CHECK: %0 = 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: move(bb(0).insn(2), bb(0).insn(1)) +--- +... +# CHECK-LABEL: MoveUpEarlyDef +# CHECK: %0 = IMPLICIT_DEF +# CHECK: early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0) +# CHECK: S_NOP 0 +# CHECK: S_NOP 0, implicit %0 +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: move(bb(0).insn(2), bb(0).insn(1)) +--- +... +# CHECK-LABEL: MoveUpKill +# CHECK: %0 = IMPLICIT_DEF +# CHECK: S_NOP 0, implicit %0 +# CHECK: S_NOP 0 +name: MoveUpKill +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 +testCommands: move(bb(0).insn(2), bb(0).insn(1)) +--- +... +# CHECK-LABEL: MoveUpKillFollowing +# CHECK: %0 = IMPLICIT_DEF +# CHECK: S_NOP 0, implicit %0 +# CHECK: S_NOP 0 +# CHECK: S_NOP 0, implicit %0 +name: MoveUpKillFollowing +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 + S_NOP 0, implicit %0 +testCommands: move(bb(0).insn(2), bb(0).insn(1)) +--- +... +# CHECK-LABEL: MoveDownDef +# CHECK: S_NOP 0 +# CHECK: S_NOP 0 +# CHECK: early-clobber %0 = IMPLICIT_DEF +# CHECK: S_NOP 0, implicit %0 +name: MoveDownDef +body: | + bb.0: + S_NOP 0 + early-clobber %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 +testCommands: move(bb(0).insn(1), bb(0).insn(3)) +--- +... +# CHECK-LABEL: MoveDownRedef +# CHECK: %0 = IMPLICIT_DEF +# CHECK: S_NOP 0 +# CHECK: %0 = IMPLICIT_DEF implicit %0(tied-def 0) +# CHECK: S_NOP 0, implicit %0 +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: move(bb(0).insn(1), bb(0).insn(3)) +--- +... +# CHECK-LABEL: MoveDownEarlyDef +# CHECK: S_NOP 0 +# CHECK: S_NOP 0 +# CHECK: early-clobber %0 = IMPLICIT_DEF +# CHECK: S_NOP 0, implicit %0 +name: MoveDownEarlyDef +body: | + bb.0: + S_NOP 0 + early-clobber %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0 + S_NOP 0, implicit %0 +testCommands: move(bb(0).insn(1), bb(0).insn(3)) +--- +... +# CHECK-LABEL: MoveDownEarlyRedef +# CHECK: %0 = IMPLICIT_DEF +# CHECK: S_NOP 0 +# CHECK: early-clobber %0 = IMPLICIT_DEF implicit %0(tied-def 0) +# CHECK: S_NOP 0, implicit %0 +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: move(bb(0).insn(1), bb(0).insn(3)) +--- +... +# CHECK-LABEL: MoveDownKill +# CHECK: %0 = IMPLICIT_DEF +# CHECK: S_NOP 0 +# CHECK: S_NOP 0, implicit %0 +# CHECK: S_NOP 0 +name: MoveDownKill +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0, implicit %0 + S_NOP 0 + S_NOP 0 +testCommands: move(bb(0).insn(1), bb(0).insn(3)) +--- +... +# CHECK-LABEL: MoveDownKillFollowing +# CHECK: %0 = IMPLICIT_DEF +# CHECK: S_NOP 0 +# CHECK: S_NOP 0, implicit %0 +# CHECK: S_NOP 0, implicit %0 +name: MoveDownKillFollowing +body: | + bb.0: + %0 : sreg_64 = IMPLICIT_DEF + S_NOP 0, implicit %0 + S_NOP 0 + S_NOP 0, implicit %0 +testCommands: move(bb(0).insn(1), bb(0).insn(3)) +--- +... +# CHECK-LABEL: MoveUndefUse +# CHECK: %0 = IMPLICIT_DEF +# CHECK: S_NOP 0, implicit %0 +# CHECK: S_NOP 0, implicit undef %0 +# CHECK: S_NOP 0 +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: move(bb(0).insn(1), bb(0).insn(3)) +--- +... +# CHECK-LABEL: MoveOverUndefUse0 +# CHECK: %0 = IMPLICIT_DEF +# CHECK: dead %0 = IMPLICIT_DEF implicit %0(tied-def 0) +# CHECK: S_NOP 0 +# CHECK: S_NOP 0, implicit undef %0 +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: move(bb(0).insn(3), bb(0).insn(1)) +--- +... +# CHECK-LABEL: MoveOverUndefUse1 +# CHECK: %sgpr0 = IMPLICIT_DEF +# CHECK: %sgpr0 = IMPLICIT_DEF implicit %sgpr0(tied-def 0) +# CHECK: S_NOP 0 +# CHECK: S_NOP 0, implicit undef %sgpr0 +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: move(bb(0).insn(3), bb(0).insn(1)) +... +--- +# CHECK-LABEL: MoveUpValNos +# CHECK: %0 = IMPLICIT_DEF implicit %0(tied-def 0) +# CHECK-NEXT: %0 = IMPLICIT_DEF implicit %0(tied-def 0) +# CHECK-NEXT: %0 = IMPLICIT_DEF implicit %0(tied-def 0) +name: MoveUpValNos +body: | + bb.0: + successors: %bb.1, %bb.2 + %0 : sreg_64 = IMPLICIT_DEF + S_CBRANCH_VCCNZ %bb.1, implicit undef %vcc + S_BRANCH %bb.2 + bb.1: + S_NOP 0, implicit %0 + bb.2: + successors: %bb.1 + %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.1 + + bb.3: + +testCommands: move(bb(2).insn(2), bb(2).insn(0)) +... +--- +# CHECK-LABEL: SubRegMoveDown +# CHECK: S_NOP 0, implicit %0.sub0 +# CHECK-NEXT: S_NOP 0 +# CHECK-NEXT: %0.sub0 = IMPLICIT_DEF +# CHECK-NEXT: S_NOP 0, implicit %0.sub1 +# CHECK-NEXT: %0.sub1 = IMPLICIT_DEF +name: SubRegMoveDown +body: | + bb.0: + successors: %bb.1, %bb.2 + %0 : sreg_64 = IMPLICIT_DEF + S_CBRANCH_VCCNZ %bb.1, implicit undef %vcc + S_BRANCH %bb.2 + bb.1: + successors: %bb.2 + 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.2: + S_NOP 0, implicit %0 + +testCommands: | + bb(1).insn(3).setIsUndef(0, 0) + ; The scheduler is in the situation where we compute liveness with the + ; undef flag present, but then clear it. + move(bb(1).insn(1), bb(1).insn(4)) +... Index: tools/llc/CMakeLists.txt =================================================================== --- tools/llc/CMakeLists.txt +++ tools/llc/CMakeLists.txt @@ -19,6 +19,8 @@ set(LLVM_NO_DEAD_STRIP 1) add_llvm_tool(llc + ExecuteTestCommands.cpp + TestCommands.cpp llc.cpp DEPENDS Index: tools/llc/ExecuteTestCommands.h =================================================================== --- /dev/null +++ tools/llc/ExecuteTestCommands.h @@ -0,0 +1,18 @@ +//===- ExecuteTestCommands.h - Test Command Execution Pass ------*- 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,228 @@ +//===-- ExecuteTestCommands.cpp - Test Command Execution Pass -------------===// +// +// 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. +/// +/// The supported functions in a script can be found in \see ScriptFunctions. +/// +/// This is how to add a new function: +/// +/// - Implement the new function (look for "ScriptFunctions" in this file) +/// - If it does not exist, implement a call_XXX marshalling function matching +/// the return and argument types of your function. +/// - Add the function to executeFunction() +// +//===----------------------------------------------------------------------===// + +#include "ExecuteTestCommands.h" + +#include "TestCommands.h" +#include "llvm/ADT/SmallVector.h" +#include "llvm/CodeGen/LiveIntervalAnalysis.h" +#include "llvm/CodeGen/MachineFunctionPass.h" +#include "llvm/Pass.h" +#include "llvm/PassRegistry.h" +#include "llvm/Support/Debug.h" + +#define DEBUG_TYPE "execute-test-commands" + +using namespace llvm::testcommands; +using namespace llvm; + +namespace { + +using Value = llvm::testcommands::Value; + +/// Pass that executes simple user provided commands on a machine function. +class ExecuteTestCommands : public MachineFunctionPass { +public: + static char ID; + ExecuteTestCommands() : MachineFunctionPass(ID) {} + + StringRef getPassName() const override { return "Execute Test Commands"; } + + void getAnalysisUsage(AnalysisUsage &AU) const override { + AU.setPreservesCFG(); + AU.addRequired(); + AU.addPreserved(); + AU.addPreserved(); + MachineFunctionPass::getAnalysisUsage(AU); + } + + bool runOnMachineFunction(MachineFunction &MF) override; +}; + +char ExecuteTestCommands::ID; + +bool executeFunction(ExecContext &Context, StringRef FunctionName, + ArrayRef Args) { + FuncMap::iterator I = Funcs.find(FunctionName); + if (I == Funcs.end()) { + errs() << "Error: Unknown function \"" << FunctionName << "\"\n"; + return false; + } + + CallableFunc Func = I->second; + return Func(Context, FunctionName, Args); +} + +/// \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. +/// \{ + +/// Advances \p Input until it points past whitespace and comments. +void skipWS(StringRef &Input) { + Input = Input.ltrim(); + // Skip comment + if (!Input.empty() && Input[0] == ';') { + Input = Input.drop_until([](char c) { return c == '\n'; }); + if (!Input.empty()) + Input = Input.drop_front(1); // skip '\n' + } +} + +bool tryIdentifier(StringRef &Input, StringRef &Result) { + skipWS(Input); + Result = Input.take_while([](char c) { + return ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '_'; + }); + if (Result.empty()) + return false; + Input = Input.drop_front(Result.size()); + return true; +} + +bool tryNumber(StringRef &Input, unsigned &Result) { + skipWS(Input); + unsigned long long a0; + if (consumeUnsignedInteger(Input, 10, a0)) + 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) { + skipWS(Input); + 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 parseExpression(ExecContext &Context, StringRef &Input); + +bool tryCallExpression(ExecContext &Context, StringRef &Input, Value FirstArg, + bool &Found) { + StringRef FunctionName; + if (!tryIdentifier(Input, FunctionName)) { + Found = false; + return true; + } + Found = true; + if (!expectChar(Input, '(')) + return false; + + SmallVector Args; + if (!FirstArg.is()) + Args.push_back(FirstArg); + + while (!tryChar(Input, ')')) { + if (!parseExpression(Context, Input)) + return false; + Args.push_back(Context.Result); + tryChar(Input, ','); + } + + if (!executeFunction(Context, FunctionName, Args)) + return false; + + if (tryChar(Input, '.')) { + bool Found; + if (!tryCallExpression(Context, Input, Context.Result, Found)) + return false; + if (!Found) { + errs() << "Expect call after '.'\n"; + return false; + } + } + + return true; +} + +bool parseExpression(ExecContext &Context, StringRef &Input) { + bool Found; + if (!tryCallExpression(Context, Input, Value::make(), Found)) + return false; + if (Found) + return true; + unsigned Number; + if (tryNumber(Input, Number)) { + Context.Result = Value::make(Number); + return true; + } + errs() << "Expected expression, got \"" << Input << "\"\n"; + return false; +} + +bool ExecuteTestCommands::runOnMachineFunction(MachineFunction &MF) { + const llvm::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(); + + DEBUG(dbgs() << "Execute commands on function " << MF.getName() << ":\n" + << Commands << '\n'); + + ExecContext Context(MF, getAnalysis()); + StringRef Input = Commands; + while (!Input.empty()) { + StringRef Begin = Input; + if (!parseExpression(Context, Input)) { + skipWS(Begin); + errs() << "While parsing '" << Begin << "'\n"; + break; + } + } + 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/TestCommands.h =================================================================== --- /dev/null +++ tools/llc/TestCommands.h @@ -0,0 +1,184 @@ +//===- TestCommands.h - Test Command Definition Tools -----------*- C++ -*-===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file Provides tools to implement test commands. +// +//===----------------------------------------------------------------------===// + +#include "llvm/CodeGen/LiveIntervalAnalysis.h" +#include "llvm/CodeGen/MachineBasicBlock.h" +#include "llvm/CodeGen/MachineOperand.h" + +namespace llvm { +namespace testcommands { + +/// A dynamically typed value. +struct Value { +public: + Value() = delete; + /// Creates a new Value with type \p T. + template static Value make(T Val); + /// Creates a new Value with type void. + static Value make() { return Value(VOID); } + /// Returns true if dynamic value is of type \p T. + template bool is() const; + /// Returns stored value. Undefined if stored value does not have type \p T. + template T get() const; + +private: + enum Type { UNSIGNED, MACHINE_INSTR, MACHINE_BASIC_BLOCK, VOID }; + Value(Type ValueType) : ValueType(ValueType) {} + Type ValueType; + union ValueData { + unsigned Number; + MachineInstr *MI; + MachineBasicBlock *MBB; + } D; +}; + +template<> inline Value Value::make(unsigned Val) { + Value Res(UNSIGNED); + Res.D.Number = Val; + return Res; +} +template<> inline Value +Value::make(MachineBasicBlock &Val) { + Value Res(MACHINE_BASIC_BLOCK); + Res.D.MBB = &Val; + return Res; +} +template<> inline Value Value::make(MachineInstr &Val) { + Value Res(MACHINE_INSTR); + Res.D.MI = &Val; + return Res; +} + +template<> inline bool Value::is() const { return ValueType == VOID; } +template<> inline bool Value::is() const { + return ValueType == UNSIGNED; +} +template<> inline bool Value::is() const { + return ValueType == MACHINE_BASIC_BLOCK; +} +template<> inline bool Value::is() const { + return ValueType == MACHINE_INSTR; +} + +template<> inline unsigned Value::get() const { + assert(is()); + return D.Number; +} +template<> inline MachineBasicBlock &Value::get() const { + assert(is()); + return *D.MBB; +} +template<> inline MachineInstr &Value::get() const { + assert(is()); + return *D.MI; +} + +/// Execution context for the scripting language. +struct ExecContext { + ExecContext(MachineFunction &MF, LiveIntervals &LIS) + : MF(MF), LIS(LIS), Result(Value::make()) {} + MachineFunction &MF; + LiveIntervals &LIS; + + /// Used to store the result after executing an expression. + Value Result; +}; + +/// \defgroup marshalling Marshalling support. +/// \{ + +template +ResT callArgs(ResT(*Func)(ExecContext&), ExecContext &Context, + ArrayRef Args) { + return Func(Context); +} +template +ResT callArgs(ResT(*Func)(ExecContext&,T0), ExecContext &Context, + ArrayRef Args) { + return Func(Context, Args[0].get()); +} +template +ResT callArgs(ResT(*Func)(ExecContext&,T0,T1), ExecContext &Context, + ArrayRef Args) { + return Func(Context, Args[0].get(), Args[1].get()); +} +template +ResT callArgs(ResT(*Func)(ExecContext&,T0,T1,T2), ExecContext &Context, + ArrayRef Args) { + return Func(Context, Args[0].get(), Args[1].get(), Args[2].get()); +} +// Add more callArgs variants as needed. Or find a solution that works with +// variadic templates/parameter packs if you can :) + +template +struct callSetResult { + static void call(ResT(*Func)(ExecContext&, ArgTs...), ExecContext &Context, + ArrayRef Args) { + ResT Result = callArgs(Func, Context, Args); + Context.Result = Value::make(Result); + } +}; +template +struct callSetResult { + static void call(void(*Func)(ExecContext&, ArgTs...), ExecContext &Context, + ArrayRef Args) { + callArgs(Func, Context, Args); + Context.Result = Value::make(); + } +}; + +typedef bool (*CallableFunc)(ExecContext &Context, StringRef Name, + ArrayRef Args); +typedef std::map FuncMap; +extern FuncMap Funcs; + +template struct Function; ///< undefined. + +template +struct Function { + template + struct Register { + /// Creates new Callable helper and registers it with the global map of + /// available functions. + Register(StringRef Name) { + Funcs.insert(std::make_pair(Name, checkAndCall)); + } + static bool checkAndCall(ExecContext &Context, StringRef Name, + ArrayRef Args) { + // Check number of arguments. + const unsigned NArgs = sizeof...(ArgTs); + if (Args.size() != NArgs) { + errs() << "Function '" << Name << " expects " << NArgs + << " arguments, got " << Args.size() << '\n'; + return false; + } + // Check argument types. + unsigned N = 0; + bool TypeChecks[] = { (Args[N++].is())... }; + for (bool C : TypeChecks) { + if (!C) { + errs() << "Arguments for function '" << Name + << "' have the wrong type\n"; + return false; + } + } + callSetResult::call(Func, Context, Args); + return true; + } + }; +}; + +/// \} + +} // end namespace testcommands +} // end namespace llvm Index: tools/llc/TestCommands.cpp =================================================================== --- /dev/null +++ tools/llc/TestCommands.cpp @@ -0,0 +1,86 @@ +//===-- TestCommands.cpp - Test Commands ----------------------------------===// +// +// The LLVM Compiler Infrastructure +// +// This file is distributed under the University of Illinois Open Source +// License. See LICENSE.TXT for details. +// +//===----------------------------------------------------------------------===// +// +/// \file Implements commands available to the ExecuteTestCommands pass. +// +//===----------------------------------------------------------------------===// + +#include "TestCommands.h" + +#include "llvm/ADT/Twine.h" +#include "llvm/Support/Debug.h" +#include "llvm/Support/ErrorHandling.h" + +#define DEBUG_TYPE "execute-test-commands" + +namespace llvm { +namespace testcommands { + +FuncMap Funcs; + +/// @defgroup ScriptFunctions +/// Functions available for calling from scripts. +/// \{ + +/// Returns machine basic block with number \p N. +MachineBasicBlock &bb(ExecContext &Context, unsigned N) { + DEBUG(dbgs() << "bb(" << N << ")\n"); + if (N > Context.MF.getNumBlockIDs()) + report_fatal_error(Twine("Invalid block index ") + Twine(N)); + return *Context.MF.getBlockNumbered(N); +} +Function::Register bbFunc("bb"); + +/// Returns instruction number \p N (counting from 0) in machine basic block +/// \p MBB. +MachineInstr &insn(ExecContext &Context, MachineBasicBlock &MBB, unsigned N) { + DEBUG(dbgs() << "insn(" << N << ")\n"); + unsigned I = 0; + for (MachineInstr &MI : MBB) { + if (I == N) + return MI; + ++I; + } + report_fatal_error(Twine("Invalid instructio index ") + Twine(N)); +} +Function::Register +insnFunc("insn"); + +/// Sets the undef flag on machine operand number \p OperandNum of MachineInstr +/// \p MI to value \p Val. +void setIsUndef(ExecContext &Context, MachineInstr &MI, unsigned OperandNum, + unsigned Val) { + DEBUG(dbgs() << "setIsUndef(" << MI << ", " << OperandNum << "," + << (Val ? "true" : "false") << ")\n"); + MI.getOperand(OperandNum).setIsUndef(Val); +} +Function::Register +setIsUndefFunc("setIsUndef"); + +/// Moves instruction \p From in front of instruction \p To and updates +/// liveness information with LiveIntervalAnalysis::handleMove(). +void move(ExecContext &Context, MachineInstr &From, MachineInstr &To) { + DEBUG(dbgs() << "move:\n" + << "\tInstruction: " << From + << "\tto Before: " << To); + + MachineBasicBlock &MBB = *From.getParent(); + if (To.getParent() != &MBB) + report_fatal_error("Arguments to move must be in the same block"); + // Move the instruction. + MBB.splice(To.getIterator(), &MBB, From.getIterator()); + // Update liveness with handleMove(). + Context.LIS.handleMove(From, true); +} +Function::Register moveFunc("move"); + +/// \} + +} // end namespace testcommands +} // end namespace llvm 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,386 +1,7 @@ -#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). TEST(LiveIntervalTest, SubRegMoveUp) { // handleMoveUp had a bug not updating valno of segment incoming to bb.2 @@ -401,7 +22,5 @@ } int main(int argc, char **argv) { - ::testing::InitGoogleTest(&argc, argv); - initLLVM(); - return RUN_ALL_TESTS(); + return 0; }