// This is the method invoked by the EE to Jit code. CorJitResult LLILCJit::compileMethod(ICorJitInfo *JitInfo, CORINFO_METHOD_INFO *MethodInfo, UINT Flags, BYTE **NativeEntry, ULONG *NativeSizeOfCode) { // Bail if input is malformed if (nullptr == JitInfo || nullptr == MethodInfo || nullptr == NativeEntry || nullptr == NativeSizeOfCode) { return CORJIT_INTERNALERROR; } // Prep main outputs *NativeEntry = nullptr; *NativeSizeOfCode = 0; // Set up state for this thread (if necessary) LLILCJitPerThreadState *PerThreadState = State.get(); if (PerThreadState == nullptr) { PerThreadState = new LLILCJitPerThreadState(); State.set(PerThreadState); } // Set up context for this Jit request LLILCJitContext Context = LLILCJitContext(PerThreadState); // Fill in context information from the CLR Context.JitInfo = JitInfo; Context.MethodInfo = MethodInfo; Context.Flags = Flags; JitInfo->getEEInfo(&Context.EEInfo); // Fill in context information from LLVM Context.LLVMContext = &PerThreadState->LLVMContext; std::unique_ptr<Module> M = Context.getModuleForMethod(MethodInfo); Context.CurrentModule = M.get(); Context.CurrentModule->setTargetTriple(LLILC_TARGET_TRIPLE); Context.MethodName = Context.CurrentModule->getModuleIdentifier(); Context.TheABIInfo = ABIInfo::get(*Context.CurrentModule); // Initialize per invocation JIT options. This should be done after the // rest of the Context is filled out as it has dependencies on JitInfo, // Flags and MethodInfo. JitOptions JitOptions(Context); Context.Options = &JitOptions; EngineBuilder Builder(std::move(M)); std::string ErrStr; Builder.setErrorStr(&ErrStr); std::unique_ptr<RTDyldMemoryManager> MM(new EEMemoryManager(&Context)); Builder.setMCJITMemoryManager(std::move(MM)); TargetOptions Options; if (Context.Options->OptLevel != OptLevel::DEBUG_CODE) { Builder.setOptLevel(CodeGenOpt::Level::Default); } else { Builder.setOptLevel(CodeGenOpt::Level::None); Options.NoFramePointerElim = true; } Builder.setTargetOptions(Options); ExecutionEngine *NewEngine = Builder.create(); if (!NewEngine) { errs() << "Could not create ExecutionEngine: " << ErrStr << "\n"; return CORJIT_INTERNALERROR; } // Don't allow the EE to search for external symbols. NewEngine->DisableSymbolSearching(); Context.EE = NewEngine; // Now jit the method. CorJitResult Result = CORJIT_INTERNALERROR; if (Context.Options->DumpLevel == DumpLevel::VERBOSE) { Context.outputDebugMethodName(); } bool HasMethod = this->readMethod(&Context); if (HasMethod) { if (Context.Options->DoInsertStatepoints) { // If using Precise GC, run the GC-Safepoint insertion // and lowering passes before generating code. legacy::PassManager Passes; Passes.add(createPlaceSafepointsPass()); PassManagerBuilder PMBuilder; PMBuilder.OptLevel = 0; // Set optimization level to -O0 PMBuilder.SizeLevel = 0; // so that no additional phases are run. PMBuilder.populateModulePassManager(Passes); Passes.add(createRewriteStatepointsForGCPass(false)); Passes.run(*Context.CurrentModule); } Context.EE->generateCodeForModule(Context.CurrentModule); // You need to pick up the COFFDyld changes from the MS branch of LLVM // or this will fail with an "Incompatible object format!" error // from LLVM's dynamic loader. uint64_t FunctionAddress = Context.EE->getFunctionAddress(Context.MethodName); *NativeEntry = (BYTE *)FunctionAddress; // TODO: ColdCodeSize, or separated code, is not enabled or included. *NativeSizeOfCode = Context.HotCodeSize + Context.ReadOnlyDataSize; // This is a stop-gap point to issue a default stub of GC info. This lets // the CLR consume our methods cleanly. (and the ETW tracing still works) // Down the road this will be superseded by a CLR specific // GCMetadataPrinter instance or similar. this->outputGCInfo(&Context); // Dump out any enabled timing info. TimerGroup::printAll(errs()); // Tell the CLR that we've successfully generated code for this method. Result = CORJIT_OK; } // Clean up a bit delete Context.EE; Context.EE = nullptr; delete Context.TheABIInfo; Context.TheABIInfo = nullptr; return Result; }