Add EngineBuilder to ExecutionEngine in favor of the five optional argument EE::create().

Also a test commit.

llvm-svn: 76276
This commit is contained in:
Reid Kleckner 2009-07-18 00:42:18 +00:00
parent 91ff94d6d9
commit fc8a2d5a83
22 changed files with 249 additions and 93 deletions

View File

@ -89,14 +89,14 @@ module ExecutionEngine: sig
module provider [mp] if successful. Creates a JIT if possible, else falls
back to an interpreter. Raises [Error msg] if an error occurrs. The
execution engine is not garbage collected and must be destroyed with
[dispose ee]. See the function [llvm::ExecutionEngine::create]. *)
[dispose ee]. See the function [llvm::EngineBuilder::create]. *)
val create: Llvm.llmoduleprovider -> t
(** [create_interpreter mp] creates a new interpreter, taking ownership of the
module provider [mp] if successful. Raises [Error msg] if an error
occurrs. The execution engine is not garbage collected and must be
destroyed with [dispose ee].
See the function [llvm::ExecutionEngine::create]. *)
See the function [llvm::EngineBuilder::create]. *)
val create_interpreter: Llvm.llmoduleprovider -> t
(** [create_jit mp] creates a new JIT (just-in-time compiler), taking
@ -104,7 +104,7 @@ module ExecutionEngine: sig
a JIT which favors code quality over compilation speed. Raises [Error msg]
if an error occurrs. The execution engine is not garbage collected and
must be destroyed with [dispose ee].
See the function [llvm::ExecutionEngine::create]. *)
See the function [llvm::EngineBuilder::create]. *)
val create_jit: Llvm.llmoduleprovider -> t
(** [create_fast_jit mp] creates a new JIT (just-in-time compiler) which
@ -112,7 +112,7 @@ module ExecutionEngine: sig
module provider [mp] if successful. Raises [Error msg] if an error
occurrs. The execution engine is not garbage collected and must be
destroyed with [dispose ee].
See the function [llvm::ExecutionEngine::create]. *)
See the function [llvm::EngineBuilder::create]. *)
val create_fast_jit: Llvm.llmoduleprovider -> t
(** [dispose ee] releases the memory used by the execution engine and must be

View File

@ -352,7 +352,7 @@
<p>to your linker options. This is required for adding the relevant
LLVM object code to the executable. Not doing this will result on
some methods returning NULL (<i>ExecutionEngine::create</i>, for
some methods returning NULL (<i>EngineBuilder::create</i>, for
instance).</p>
</div>

View File

@ -299,7 +299,7 @@ by adding a global variable and a call in <tt>main</tt>:</p>
int main() {
..
<b>// Create the JIT.
TheExecutionEngine = ExecutionEngine::create(TheModule);</b>
TheExecutionEngine = EngineBuilder(TheModule).create();</b>
..
}
</pre>
@ -1078,7 +1078,7 @@ int main() {
TheModule = new Module("my cool jit", getGlobalContext());
// Create the JIT.
TheExecutionEngine = ExecutionEngine::create(TheModule);
TheExecutionEngine = EngineBuilder(TheModule).create();
{
ExistingModuleProvider OurModuleProvider(TheModule);

View File

@ -1712,7 +1712,7 @@ int main() {
TheModule = new Module("my cool jit", getGlobalContext());
// Create the JIT.
TheExecutionEngine = ExecutionEngine::create(TheModule);
TheExecutionEngine = EngineBuilder(TheModule).create();
{
ExistingModuleProvider OurModuleProvider(TheModule);

View File

@ -1751,7 +1751,7 @@ int main() {
TheModule = new Module("my cool jit", getGlobalContext());
// Create the JIT.
TheExecutionEngine = ExecutionEngine::create(TheModule);
TheExecutionEngine = EngineBuilder(TheModule).create();
{
ExistingModuleProvider OurModuleProvider(TheModule);

View File

@ -2103,7 +2103,7 @@ int main() {
TheModule = new Module("my cool jit", getGlobalContext());
// Create the JIT.
TheExecutionEngine = ExecutionEngine::create(TheModule);
TheExecutionEngine = EngineBuilder(TheModule).create();
{
ExistingModuleProvider OurModuleProvider(TheModule);

View File

@ -141,8 +141,7 @@ int main(int argc, char **argv) {
InitializeNativeTarget();
std::cout << "------- Running JIT -------\n";
ExistingModuleProvider *mp = new ExistingModuleProvider(mod);
ExecutionEngine *ee = ExecutionEngine::create(mp, false);
ExecutionEngine *ee = EngineBuilder(mod).create();
std::vector<GenericValue> args;
Function *brainf_func = mod->getFunction("brainf");
GenericValue gv = ee->runFunction(brainf_func, args);

View File

@ -100,8 +100,7 @@ int main(int argc, char **argv) {
Function *FibF = CreateFibFunction(M, Context);
// Now we going to create JIT
ExistingModuleProvider *MP = new ExistingModuleProvider(M);
ExecutionEngine *EE = ExecutionEngine::create(MP, false);
ExecutionEngine *EE = EngineBuilder(M).create();
errs() << "verifying... ";
if (verifyModule(*M)) {

View File

@ -104,8 +104,7 @@ int main() {
ReturnInst::Create(Add1CallRes, BB);
// Now we create the JIT.
ExistingModuleProvider* MP = new ExistingModuleProvider(M);
ExecutionEngine* EE = ExecutionEngine::create(MP, false);
ExecutionEngine* EE = EngineBuilder(M).create();
outs() << "We just constructed this LLVM module:\n\n" << *M;
outs() << "\n\nRunning foo: ";

View File

@ -1103,7 +1103,7 @@ int main() {
TheModule = new Module("my cool jit", Context);
// Create the JIT.
TheExecutionEngine = ExecutionEngine::create(TheModule);
TheExecutionEngine = EngineBuilder(TheModule).create();
{
ExistingModuleProvider OurModuleProvider(TheModule);
@ -1138,4 +1138,3 @@ int main() {
return 0;
}

View File

@ -242,8 +242,7 @@ int main() {
Function* fibF = CreateFibFunction( M );
// Now we create the JIT.
ExistingModuleProvider* MP = new ExistingModuleProvider(M);
ExecutionEngine* EE = ExecutionEngine::create(MP, false);
ExecutionEngine* EE = EngineBuilder(M).create();
//~ std::cout << "We just constructed this LLVM module:\n\n" << *M;
//~ std::cout << "\n\nRunning foo: " << std::flush;

View File

@ -71,6 +71,8 @@ class ExecutionEngine {
bool SymbolSearchingDisabled;
bool DlsymStubsEnabled;
friend class EngineBuilder; // To allow access to JITCtor and InterpCtor.
protected:
/// Modules - This is a list of ModuleProvider's that we are JIT'ing from. We
/// use a smallvector to optimize for the case where there is only one module.
@ -86,10 +88,13 @@ protected:
// To avoid having libexecutionengine depend on the JIT and interpreter
// libraries, the JIT and Interpreter set these functions to ctor pointers
// at startup time if they are linked in.
typedef ExecutionEngine *(*EECtorFn)(ModuleProvider*, std::string*,
CodeGenOpt::Level OptLevel,
bool GVsWithCode);
static EECtorFn JITCtor, InterpCtor;
static ExecutionEngine *(*JITCtor)(ModuleProvider *MP,
std::string *ErrorStr,
JITMemoryManager *JMM,
CodeGenOpt::Level OptLevel,
bool GVsWithCode);
static ExecutionEngine *(*InterpCtor)(ModuleProvider *MP,
std::string *ErrorStr);
/// LazyFunctionCreator - If an unknown function is needed, this function
/// pointer is invoked to create it. If this returns null, the JIT will abort.
@ -372,6 +377,96 @@ protected:
const Type *Ty);
};
namespace EngineKind {
// These are actually bitmasks that get or-ed together.
enum Kind {
JIT = 0x1,
Interpreter = 0x2
};
const static Kind Either = (Kind)(JIT | Interpreter);
}
/// EngineBuilder - Builder class for ExecutionEngines. Use this by
/// stack-allocating a builder, chaining the various set* methods, and
/// terminating it with a .create() call.
class EngineBuilder {
private:
ModuleProvider *MP;
EngineKind::Kind WhichEngine;
std::string *ErrorStr;
CodeGenOpt::Level OptLevel;
JITMemoryManager *JMM;
bool AllocateGVsWithCode;
/// InitEngine - Does the common initialization of default options.
///
void InitEngine() {
WhichEngine = EngineKind::Either;
ErrorStr = NULL;
OptLevel = CodeGenOpt::Default;
JMM = NULL;
AllocateGVsWithCode = false;
}
public:
/// EngineBuilder - Constructor for EngineBuilder. If create() is called and
/// is successful, the created engine takes ownership of the module
/// provider.
EngineBuilder(ModuleProvider *mp) : MP(mp) {
InitEngine();
}
/// EngineBuilder - Overloaded constructor that automatically creates an
/// ExistingModuleProvider for an existing module.
EngineBuilder(Module *m);
/// setEngineKind - Controls whether the user wants the interpreter, the JIT,
/// or whichever engine works. This option defaults to EngineKind::Either.
EngineBuilder &setEngineKind(EngineKind::Kind w) {
WhichEngine = w;
return *this;
}
/// setJITMemoryManager - Sets the memory manager to use. This allows
/// clients to customize their memory allocation policies. If create() is
/// called and is successful, the created engine takes ownership of the
/// memory manager. This option defaults to NULL.
EngineBuilder &setJITMemoryManager(JITMemoryManager *jmm) {
JMM = jmm;
return *this;
}
/// setErrorStr - Set the error string to write to on error. This option
/// defaults to NULL.
EngineBuilder &setErrorStr(std::string *e) {
ErrorStr = e;
return *this;
}
/// setOptLevel - Set the optimization level for the JIT. This option
/// defaults to CodeGenOpt::Default.
EngineBuilder &setOptLevel(CodeGenOpt::Level l) {
OptLevel = l;
return *this;
}
/// setAllocateGVsWithCode - Sets whether global values should be allocated
/// into the same buffer as code. For most applications this should be set
/// to false. Allocating globals with code breaks freeMachineCodeForFunction
/// and is probably unsafe and bad for performance. However, we have clients
/// who depend on this behavior, so we must support it. This option defaults
/// to false so that users of the new API can safely use the new memory
/// manager and free machine code.
EngineBuilder &setAllocateGVsWithCode(bool a) {
AllocateGVsWithCode = a;
return *this;
}
ExecutionEngine *create();
};
} // End llvm namespace
#endif

View File

@ -35,8 +35,13 @@ using namespace llvm;
STATISTIC(NumInitBytes, "Number of bytes of global vars initialized");
STATISTIC(NumGlobals , "Number of global vars initialized");
ExecutionEngine::EECtorFn ExecutionEngine::JITCtor = 0;
ExecutionEngine::EECtorFn ExecutionEngine::InterpCtor = 0;
ExecutionEngine *(*ExecutionEngine::JITCtor)(ModuleProvider *MP,
std::string *ErrorStr,
JITMemoryManager *JMM,
CodeGenOpt::Level OptLevel,
bool GVsWithCode) = 0;
ExecutionEngine *(*ExecutionEngine::InterpCtor)(ModuleProvider *MP,
std::string *ErrorStr) = 0;
ExecutionEngine::EERegisterFn ExecutionEngine::ExceptionTableRegister = 0;
@ -382,28 +387,62 @@ ExecutionEngine *ExecutionEngine::create(ModuleProvider *MP,
std::string *ErrorStr,
CodeGenOpt::Level OptLevel,
bool GVsWithCode) {
ExecutionEngine *EE = 0;
return EngineBuilder(MP)
.setEngineKind(ForceInterpreter
? EngineKind::Interpreter
: EngineKind::JIT)
.setErrorStr(ErrorStr)
.setOptLevel(OptLevel)
.setAllocateGVsWithCode(GVsWithCode)
.create();
}
ExecutionEngine *ExecutionEngine::create(Module *M) {
return EngineBuilder(M).create();
}
/// EngineBuilder - Overloaded constructor that automatically creates an
/// ExistingModuleProvider for an existing module.
EngineBuilder::EngineBuilder(Module *m) : MP(new ExistingModuleProvider(m)) {
InitEngine();
}
ExecutionEngine *EngineBuilder::create() {
// Make sure we can resolve symbols in the program as well. The zero arg
// to the function tells DynamicLibrary to load the program, not a library.
if (sys::DynamicLibrary::LoadLibraryPermanently(0, ErrorStr))
return 0;
// Unless the interpreter was explicitly selected, try making a JIT.
if (!ForceInterpreter && JITCtor)
EE = JITCtor(MP, ErrorStr, OptLevel, GVsWithCode);
// If the user specified a memory manager but didn't specify which engine to
// create, we assume they only want the JIT, and we fail if they only want
// the interpreter.
if (JMM) {
if (WhichEngine & EngineKind::JIT) {
WhichEngine = EngineKind::JIT;
} else {
*ErrorStr = "Cannot create an interpreter with a memory manager.";
}
}
// If we can't make a JIT, make an interpreter instead.
if (EE == 0 && InterpCtor)
EE = InterpCtor(MP, ErrorStr, OptLevel, GVsWithCode);
ExecutionEngine *EE = 0;
// Unless the interpreter was explicitly selected or the JIT is not linked,
// try making a JIT.
if (WhichEngine & EngineKind::JIT && ExecutionEngine::JITCtor) {
EE = ExecutionEngine::JITCtor(MP, ErrorStr, JMM, OptLevel,
AllocateGVsWithCode);
}
// If we can't make a JIT and we didn't request one specifically, try making
// an interpreter instead.
if (WhichEngine & EngineKind::Interpreter && EE == 0 &&
ExecutionEngine::InterpCtor) {
EE = ExecutionEngine::InterpCtor(MP, ErrorStr);
}
return EE;
}
ExecutionEngine *ExecutionEngine::create(Module *M) {
return create(new ExistingModuleProvider(M));
}
/// getPointerToGlobal - This returns the address of the specified global
/// value. This may involve code generation if it's a function.
///

View File

@ -91,7 +91,10 @@ int LLVMCreateExecutionEngine(LLVMExecutionEngineRef *OutEE,
LLVMModuleProviderRef MP,
char **OutError) {
std::string Error;
if (ExecutionEngine *EE = ExecutionEngine::create(unwrap(MP), false, &Error)){
EngineBuilder builder(unwrap(MP));
builder.setEngineKind(EngineKind::Either)
.setErrorStr(&Error);
if (ExecutionEngine *EE = builder.create()){
*OutEE = wrap(EE);
return 0;
}
@ -103,8 +106,10 @@ int LLVMCreateInterpreter(LLVMExecutionEngineRef *OutInterp,
LLVMModuleProviderRef MP,
char **OutError) {
std::string Error;
if (ExecutionEngine *Interp =
ExecutionEngine::create(unwrap(MP), true, &Error)) {
EngineBuilder builder(unwrap(MP));
builder.setEngineKind(EngineKind::Interpreter)
.setErrorStr(&Error);
if (ExecutionEngine *Interp = builder.create()) {
*OutInterp = wrap(Interp);
return 0;
}
@ -117,9 +122,11 @@ int LLVMCreateJITCompiler(LLVMExecutionEngineRef *OutJIT,
unsigned OptLevel,
char **OutError) {
std::string Error;
if (ExecutionEngine *JIT =
ExecutionEngine::create(unwrap(MP), false, &Error,
(CodeGenOpt::Level)OptLevel)) {
EngineBuilder builder(unwrap(MP));
builder.setEngineKind(EngineKind::JIT)
.setErrorStr(&Error)
.setOptLevel((CodeGenOpt::Level)OptLevel);
if (ExecutionEngine *JIT = builder.create()) {
*OutJIT = wrap(JIT);
return 0;
}

View File

@ -33,9 +33,7 @@ extern "C" void LLVMLinkInInterpreter() { }
/// create - Create a new interpreter object. This can never fail.
///
ExecutionEngine *Interpreter::create(ModuleProvider *MP, std::string* ErrStr,
CodeGenOpt::Level OptLevel, /*unused*/
bool GVsWithCode /* unused */) {
ExecutionEngine *Interpreter::create(ModuleProvider *MP, std::string* ErrStr) {
// Tell this ModuleProvide to materialize and release the module
if (!MP->materializeModule(ErrStr))
// We got an error, just return 0

View File

@ -108,9 +108,7 @@ public:
/// create - Create an interpreter ExecutionEngine. This can never fail.
///
static ExecutionEngine *create(ModuleProvider *M, std::string *ErrorStr = 0,
CodeGenOpt::Level = CodeGenOpt::Default,
bool GVsWithCode = true);
static ExecutionEngine *create(ModuleProvider *M, std::string *ErrorStr = 0);
/// run - Start execution with the specified function and arguments.
///

View File

@ -199,14 +199,31 @@ ExecutionEngine *ExecutionEngine::createJIT(ModuleProvider *MP,
JITMemoryManager *JMM,
CodeGenOpt::Level OptLevel,
bool GVsWithCode) {
ExecutionEngine *EE = JIT::createJIT(MP, ErrorStr, JMM, OptLevel,
GVsWithCode);
if (!EE) return 0;
return JIT::createJIT(MP, ErrorStr, JMM, OptLevel, GVsWithCode);
}
ExecutionEngine *JIT::createJIT(ModuleProvider *MP,
std::string *ErrorStr,
JITMemoryManager *JMM,
CodeGenOpt::Level OptLevel,
bool GVsWithCode) {
// Make sure we can resolve symbols in the program as well. The zero arg
// to the function tells DynamicLibrary to load the program, not a library.
sys::DynamicLibrary::LoadLibraryPermanently(0, ErrorStr);
return EE;
if (sys::DynamicLibrary::LoadLibraryPermanently(0, ErrorStr))
return 0;
// Pick a target either via -march or by guessing the native arch.
TargetMachine *TM = JIT::selectTarget(MP, ErrorStr);
if (!TM || (ErrorStr && ErrorStr->length() > 0)) return 0;
// If the target supports JIT code generation, create a the JIT.
if (TargetJITInfo *TJ = TM->getJITInfo()) {
return new JIT(MP, *TM, *TJ, JMM, OptLevel, GVsWithCode);
} else {
if (ErrorStr)
*ErrorStr = "target does not support JIT code generation";
return 0;
}
}
JIT::JIT(ModuleProvider *MP, TargetMachine &tm, TargetJITInfo &tji,

View File

@ -79,11 +79,13 @@ public:
/// create - Create an return a new JIT compiler if there is one available
/// for the current target. Otherwise, return null.
///
static ExecutionEngine *create(ModuleProvider *MP, std::string *Err,
static ExecutionEngine *create(ModuleProvider *MP,
std::string *Err,
JITMemoryManager *JMM,
CodeGenOpt::Level OptLevel =
CodeGenOpt::Default,
bool AllocateGVsWithCode = true) {
return createJIT(MP, Err, 0, OptLevel, AllocateGVsWithCode);
bool GVsWithCode = true) {
return ExecutionEngine::createJIT(MP, Err, JMM, OptLevel, GVsWithCode);
}
virtual void addModuleProvider(ModuleProvider *MP);
@ -152,18 +154,22 @@ public:
/// addPendingFunction - while jitting non-lazily, a called but non-codegen'd
/// function was encountered. Add it to a pending list to be processed after
/// the current function.
///
///
void addPendingFunction(Function *F);
/// getCodeEmitter - Return the code emitter this JIT is emitting into.
///
JITCodeEmitter *getCodeEmitter() const { return JCE; }
/// selectTarget - Pick a target either via -march or by guessing the native
/// arch. Add any CPU features specified via -mcpu or -mattr.
static TargetMachine *selectTarget(ModuleProvider *MP, std::string *Err);
static ExecutionEngine *createJIT(ModuleProvider *MP,
std::string *Err,
std::string *ErrorStr,
JITMemoryManager *JMM,
CodeGenOpt::Level OptLevel,
bool AllocateGVsWithCode);
bool GVsWithCode);
// Run the JIT on F and return information about the generated code
void runJITOnFunction(Function *F, MachineCodeInfo *MCI = 0);

View File

@ -38,13 +38,9 @@ MAttrs("mattr",
cl::desc("Target specific attributes (-mattr=help for details)"),
cl::value_desc("a1,+a2,-a3,..."));
/// createInternal - Create an return a new JIT compiler if there is one
/// available for the current target. Otherwise, return null.
///
ExecutionEngine *JIT::createJIT(ModuleProvider *MP, std::string *ErrorStr,
JITMemoryManager *JMM,
CodeGenOpt::Level OptLevel,
bool AllocateGVsWithCode) {
/// selectTarget - Pick a target either via -march or by guessing the native
/// arch. Add any CPU features specified via -mcpu or -mattr.
TargetMachine *JIT::selectTarget(ModuleProvider *MP, std::string *ErrorStr) {
const Target *TheTarget = 0;
if (MArch.empty()) {
std::string Error;
@ -90,12 +86,5 @@ ExecutionEngine *JIT::createJIT(ModuleProvider *MP, std::string *ErrorStr,
TargetMachine *Target =
TheTarget->createTargetMachine(*MP->getModule(), FeaturesStr);
assert(Target && "Could not allocate target machine!");
// If the target supports JIT code generation, return a new JIT now.
if (TargetJITInfo *TJ = Target->getJITInfo())
return new JIT(MP, *Target, *TJ, JMM, OptLevel, AllocateGVsWithCode);
if (ErrorStr)
*ErrorStr = "target does not support JIT code generation";
return 0;
return Target;
}

View File

@ -131,6 +131,12 @@ int main(int argc, char **argv, char * const *envp) {
exit(1);
}
EngineBuilder builder(MP);
builder.setErrorStr(&ErrorMsg)
.setEngineKind(ForceInterpreter
? EngineKind::Interpreter
: EngineKind::JIT);
// If we are supposed to override the target triple, do so now.
if (!TargetTriple.empty())
Mod->setTargetTriple(TargetTriple);
@ -146,8 +152,9 @@ int main(int argc, char **argv, char * const *envp) {
case '2': OLvl = CodeGenOpt::Default; break;
case '3': OLvl = CodeGenOpt::Aggressive; break;
}
EE = ExecutionEngine::create(MP, ForceInterpreter, &ErrorMsg, OLvl);
builder.setOptLevel(OLvl);
EE = builder.create();
if (!EE) {
if (!ErrorMsg.empty())
errs() << argv[0] << ": error creating EE: " << ErrorMsg << "\n";

View File

@ -66,7 +66,9 @@ class JITEventListenerTest : public testing::Test {
protected:
JITEventListenerTest()
: M(new Module("module", getGlobalContext())),
EE(ExecutionEngine::createJIT(new ExistingModuleProvider(M))) {
EE(EngineBuilder(M)
.setEngineToCreate(EngineBuilder::ENG_JIT)
.create()) {
}
Module *M;
@ -232,7 +234,7 @@ TEST_F(JITEventListenerTest, MatchesMachineCodeInfo) {
class JITEnvironment : public testing::Environment {
virtual void SetUp() {
// Required for ExecutionEngine::createJIT to create a JIT.
// Required to create a JIT.
InitializeNativeTarget();
}
};

View File

@ -1,4 +1,4 @@
//===- JITEmitter.cpp - Unit tests for the JIT code emitter ---------------===//
//===- JITTest.cpp - Unit tests for the JIT -------------------------------===//
//
// The LLVM Compiler Infrastructure
//
@ -18,6 +18,7 @@
#include "llvm/Function.h"
#include "llvm/GlobalValue.h"
#include "llvm/GlobalVariable.h"
#include "llvm/LLVMContext.h"
#include "llvm/Module.h"
#include "llvm/ModuleProvider.h"
#include "llvm/Support/IRBuilder.h"
@ -60,12 +61,13 @@ TEST(JIT, GlobalInFunction) {
// memory is more easily tested.
MemMgr->setPoisonMemory(true);
std::string Error;
OwningPtr<ExecutionEngine> JIT(ExecutionEngine::createJIT(
MP,
&Error,
MemMgr,
CodeGenOpt::Default,
false)); // This last argument enables the fix.
OwningPtr<ExecutionEngine> JIT(EngineBuilder(MP)
.setEnginePreference(EngineBuilder::JITONLY)
.setErrorStr(&Error)
.setJITMemoryManager(MemMgr)
// The next line enables the fix:
.setAllocateGVsWithCode(false)
.create());
ASSERT_EQ(Error, "");
// Create a global variable.
@ -115,11 +117,12 @@ TEST(JIT, GlobalInFunction) {
EXPECT_EQ(3, *GPtr);
}
// TODO(rnk): This seems to only run once for both tests, which is unexpected.
// That works just fine, but we shouldn't duplicate the code.
// This code is copied from JITEventListenerTest, but it only runs once for all
// the tests in this directory. Everything seems fine, but that's strange
// behavior.
class JITEnvironment : public testing::Environment {
virtual void SetUp() {
// Required for ExecutionEngine::createJIT to create a JIT.
// Required to create a JIT.
InitializeNativeTarget();
}
};