text: //===- MCJITTestBase.h - Common base class for MCJIT Unit tests  ----------===//
  //
  //                     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;
  
      Function::arg_iterator arg_iter = Result->arg_begin();
      for(;arg_iter != Result->arg_end(); ++arg_iter)
        CallArgs.push_back(arg_iter);
  
      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 = 0,
                                                StringRef Name = "accumulate") {
      Function *Result = startFunction<int32_t(int32_t)>(M, Name);
      if (Helper == 0)
        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(), 0, "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)
      , RelocModel(Reloc::Default)
      , CodeModel(CodeModel::Default)
      , 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::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");
  
      // The operating systems below are known to be incompatible with MCJIT as
      // they are copied from the test/ExecutionEngine/MCJIT/lit.local.cfg and
      // should be kept in sync.
      UnsupportedOSs.push_back(Triple::Darwin);
  
      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)
                   .setCodeModel(CodeModel::JITDefault)
                   .setRelocationModel(Reloc::Default)
                   .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;
    Reloc::Model RelocModel;
    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
  