//===- MCJITTestBase.h - Common base class for MCJIT Unit tests -*- C++ -*-===// // // The LLVM Compiler Infrastructure // // This file is distributed under the University of Illinois Open Source // License. See LICENSE.TXT for details. // //===----------------------------------------------------------------------===// // // This class implements common functionality required by the MCJIT unit tests, // as well as logic to skip tests on unsupported architectures and operating // systems. // //===----------------------------------------------------------------------===// #ifndef LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTBASE_H #define LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTBASE_H #include "MCJITTestAPICommon.h" #include "llvm/Config/config.h" #include "llvm/ExecutionEngine/ExecutionEngine.h" #include "llvm/ExecutionEngine/SectionMemoryManager.h" #include "llvm/IR/Function.h" #include "llvm/IR/IRBuilder.h" #include "llvm/IR/LLVMContext.h" #include "llvm/IR/Module.h" #include "llvm/IR/TypeBuilder.h" #include "llvm/Support/CodeGen.h" namespace llvm { /// Helper class that can build very simple Modules class TrivialModuleBuilder { protected: LLVMContext Context; IRBuilder<> Builder; std::string BuilderTriple; TrivialModuleBuilder(const std::string &Triple) : Builder(Context), BuilderTriple(Triple) {} Module *createEmptyModule(StringRef Name = StringRef()) { Module * M = new Module(Name, Context); M->setTargetTriple(Triple::normalize(BuilderTriple)); return M; } template<typename FuncType> Function *startFunction(Module *M, StringRef Name) { Function *Result = Function::Create( TypeBuilder<FuncType, false>::get(Context), GlobalValue::ExternalLinkage, Name, M); BasicBlock *BB = BasicBlock::Create(Context, Name, Result); Builder.SetInsertPoint(BB); return Result; } void endFunctionWithRet(Function *Func, Value *RetValue) { Builder.CreateRet(RetValue); } // Inserts a simple function that invokes Callee and takes the same arguments: // int Caller(...) { return Callee(...); } template<typename Signature> Function *insertSimpleCallFunction(Module *M, Function *Callee) { Function *Result = startFunction<Signature>(M, "caller"); SmallVector<Value*, 1> CallArgs; for (Argument &A : Result->args()) CallArgs.push_back(&A); Value *ReturnCode = Builder.CreateCall(Callee, CallArgs); Builder.CreateRet(ReturnCode); return Result; } // Inserts a function named 'main' that returns a uint32_t: // int32_t main() { return X; } // where X is given by returnCode Function *insertMainFunction(Module *M, uint32_t returnCode) { Function *Result = startFunction<int32_t(void)>(M, "main"); Value *ReturnVal = ConstantInt::get(Context, APInt(32, returnCode)); endFunctionWithRet(Result, ReturnVal); return Result; } // Inserts a function // int32_t add(int32_t a, int32_t b) { return a + b; } // in the current module and returns a pointer to it. Function *insertAddFunction(Module *M, StringRef Name = "add") { Function *Result = startFunction<int32_t(int32_t, int32_t)>(M, Name); Function::arg_iterator args = Result->arg_begin(); Value *Arg1 = &*args; Value *Arg2 = &*++args; Value *AddResult = Builder.CreateAdd(Arg1, Arg2); endFunctionWithRet(Result, AddResult); return Result; } // Inserts a declaration to a function defined elsewhere template <typename FuncType> Function *insertExternalReferenceToFunction(Module *M, StringRef Name) { Function *Result = Function::Create( TypeBuilder<FuncType, false>::get(Context), GlobalValue::ExternalLinkage, Name, M); return Result; } // Inserts an declaration to a function defined elsewhere Function *insertExternalReferenceToFunction(Module *M, StringRef Name, FunctionType *FuncTy) { Function *Result = Function::Create(FuncTy, GlobalValue::ExternalLinkage, Name, M); return Result; } // Inserts an declaration to a function defined elsewhere Function *insertExternalReferenceToFunction(Module *M, Function *Func) { Function *Result = Function::Create(Func->getFunctionType(), GlobalValue::ExternalLinkage, Func->getName(), M); return Result; } // Inserts a global variable of type int32 // FIXME: make this a template function to support any type GlobalVariable *insertGlobalInt32(Module *M, StringRef name, int32_t InitialValue) { Type *GlobalTy = TypeBuilder<types::i<32>, true>::get(Context); Constant *IV = ConstantInt::get(Context, APInt(32, InitialValue)); GlobalVariable *Global = new GlobalVariable(*M, GlobalTy, false, GlobalValue::ExternalLinkage, IV, name); return Global; } // Inserts a function // int32_t recursive_add(int32_t num) { // if (num == 0) { // return num; // } else { // int32_t recursive_param = num - 1; // return num + Helper(recursive_param); // } // } // NOTE: if Helper is left as the default parameter, Helper == recursive_add. Function *insertAccumulateFunction(Module *M, Function *Helper = nullptr, StringRef Name = "accumulate") { Function *Result = startFunction<int32_t(int32_t)>(M, Name); if (!Helper) Helper = Result; BasicBlock *BaseCase = BasicBlock::Create(Context, "", Result); BasicBlock *RecursiveCase = BasicBlock::Create(Context, "", Result); // if (num == 0) Value *Param = &*Result->arg_begin(); Value *Zero = ConstantInt::get(Context, APInt(32, 0)); Builder.CreateCondBr(Builder.CreateICmpEQ(Param, Zero), BaseCase, RecursiveCase); // return num; Builder.SetInsertPoint(BaseCase); Builder.CreateRet(Param); // int32_t recursive_param = num - 1; // return Helper(recursive_param); Builder.SetInsertPoint(RecursiveCase); Value *One = ConstantInt::get(Context, APInt(32, 1)); Value *RecursiveParam = Builder.CreateSub(Param, One); Value *RecursiveReturn = Builder.CreateCall(Helper, RecursiveParam); Value *Accumulator = Builder.CreateAdd(Param, RecursiveReturn); Builder.CreateRet(Accumulator); return Result; } // Populates Modules A and B: // Module A { Extern FB1, Function FA which calls FB1 }, // Module B { Extern FA, Function FB1, Function FB2 which calls FA }, void createCrossModuleRecursiveCase(std::unique_ptr<Module> &A, Function *&FA, std::unique_ptr<Module> &B, Function *&FB1, Function *&FB2) { // Define FB1 in B. B.reset(createEmptyModule("B")); FB1 = insertAccumulateFunction(B.get(), nullptr, "FB1"); // Declare FB1 in A (as an external). A.reset(createEmptyModule("A")); Function *FB1Extern = insertExternalReferenceToFunction(A.get(), FB1); // Define FA in A (with a call to FB1). FA = insertAccumulateFunction(A.get(), FB1Extern, "FA"); // Declare FA in B (as an external) Function *FAExtern = insertExternalReferenceToFunction(B.get(), FA); // Define FB2 in B (with a call to FA) FB2 = insertAccumulateFunction(B.get(), FAExtern, "FB2"); } // Module A { Function FA }, // Module B { Extern FA, Function FB which calls FA }, // Module C { Extern FB, Function FC which calls FB }, void createThreeModuleChainedCallsCase(std::unique_ptr<Module> &A, Function *&FA, std::unique_ptr<Module> &B, Function *&FB, std::unique_ptr<Module> &C, Function *&FC) { A.reset(createEmptyModule("A")); FA = insertAddFunction(A.get()); B.reset(createEmptyModule("B")); Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA); FB = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(B.get(), FAExtern_in_B); C.reset(createEmptyModule("C")); Function *FBExtern_in_C = insertExternalReferenceToFunction(C.get(), FB); FC = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(C.get(), FBExtern_in_C); } // Module A { Function FA }, // Populates Modules A and B: // Module B { Function FB } void createTwoModuleCase(std::unique_ptr<Module> &A, Function *&FA, std::unique_ptr<Module> &B, Function *&FB) { A.reset(createEmptyModule("A")); FA = insertAddFunction(A.get()); B.reset(createEmptyModule("B")); FB = insertAddFunction(B.get()); } // Module A { Function FA }, // Module B { Extern FA, Function FB which calls FA } void createTwoModuleExternCase(std::unique_ptr<Module> &A, Function *&FA, std::unique_ptr<Module> &B, Function *&FB) { A.reset(createEmptyModule("A")); FA = insertAddFunction(A.get()); B.reset(createEmptyModule("B")); Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA); FB = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(B.get(), FAExtern_in_B); } // Module A { Function FA }, // Module B { Extern FA, Function FB which calls FA }, // Module C { Extern FB, Function FC which calls FA }, void createThreeModuleCase(std::unique_ptr<Module> &A, Function *&FA, std::unique_ptr<Module> &B, Function *&FB, std::unique_ptr<Module> &C, Function *&FC) { A.reset(createEmptyModule("A")); FA = insertAddFunction(A.get()); B.reset(createEmptyModule("B")); Function *FAExtern_in_B = insertExternalReferenceToFunction(B.get(), FA); FB = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(B.get(), FAExtern_in_B); C.reset(createEmptyModule("C")); Function *FAExtern_in_C = insertExternalReferenceToFunction(C.get(), FA); FC = insertSimpleCallFunction<int32_t(int32_t, int32_t)>(C.get(), FAExtern_in_C); } }; class MCJITTestBase : public MCJITTestAPICommon, public TrivialModuleBuilder { protected: MCJITTestBase() : TrivialModuleBuilder(HostTriple), OptLevel(CodeGenOpt::None), CodeModel(CodeModel::Small), MArch(""), MM(new SectionMemoryManager) { // The architectures below are known to be compatible with MCJIT as they // are copied from test/ExecutionEngine/MCJIT/lit.local.cfg and should be // kept in sync. SupportedArchs.push_back(Triple::aarch64); SupportedArchs.push_back(Triple::arm); SupportedArchs.push_back(Triple::mips); SupportedArchs.push_back(Triple::mipsel); SupportedArchs.push_back(Triple::mips64); SupportedArchs.push_back(Triple::mips64el); SupportedArchs.push_back(Triple::x86); SupportedArchs.push_back(Triple::x86_64); // Some architectures have sub-architectures in which tests will fail, like // ARM. These two vectors will define if they do have sub-archs (to avoid // extra work for those who don't), and if so, if they are listed to work HasSubArchs.push_back(Triple::arm); SupportedSubArchs.push_back("armv6"); SupportedSubArchs.push_back("armv7"); UnsupportedEnvironments.push_back(Triple::Cygnus); } void createJIT(std::unique_ptr<Module> M) { // Due to the EngineBuilder constructor, it is required to have a Module // in order to construct an ExecutionEngine (i.e. MCJIT) assert(M != 0 && "a non-null Module must be provided to create MCJIT"); EngineBuilder EB(std::move(M)); std::string Error; TheJIT.reset(EB.setEngineKind(EngineKind::JIT) .setMCJITMemoryManager(std::move(MM)) .setErrorStr(&Error) .setOptLevel(CodeGenOpt::None) .setMArch(MArch) .setMCPU(sys::getHostCPUName()) //.setMAttrs(MAttrs) .create()); // At this point, we cannot modify the module any more. assert(TheJIT.get() != NULL && "error creating MCJIT with EngineBuilder"); } CodeGenOpt::Level OptLevel; CodeModel::Model CodeModel; StringRef MArch; SmallVector<std::string, 1> MAttrs; std::unique_ptr<ExecutionEngine> TheJIT; std::unique_ptr<RTDyldMemoryManager> MM; std::unique_ptr<Module> M; }; } // namespace llvm #endif // LLVM_UNITTESTS_EXECUTIONENGINE_MCJIT_MCJITTESTBASE_H