2 // jit-llvm.cpp: Support code for using LLVM as a JIT backend
4 // (C) 2009-2011 Novell, Inc.
5 // Copyright 2011-2015 Xamarin, Inc (http://www.xamarin.com)
7 // Licensed under the MIT license. See LICENSE file in the project root for full license information.
12 #include <llvm-c/Core.h>
13 #include <llvm-c/ExecutionEngine.h>
15 #include "mini-llvm-cpp.h"
16 #include "mini-runtime.h"
19 #if defined(MONO_ARCH_LLVM_JIT_SUPPORTED) && !defined(MONO_CROSS_COMPILE) && LLVM_API_VERSION > 600
21 #include <llvm/ADT/SmallVector.h>
22 #include <llvm/Support/raw_ostream.h>
23 #include <llvm/Support/Host.h>
24 #include <llvm/Support/Memory.h>
25 #include <llvm/Support/TargetSelect.h>
26 #include <llvm/IR/Mangler.h>
27 #include "llvm/IR/LegacyPassManager.h"
28 #include "llvm/IR/LegacyPassNameParser.h"
29 #include <llvm/ExecutionEngine/ExecutionEngine.h>
30 #include "llvm/ExecutionEngine/Orc/CompileUtils.h"
31 #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
32 #include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
33 #include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
34 #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
35 #include "llvm/ExecutionEngine/JITSymbol.h"
36 #include "llvm/Transforms/Scalar.h"
38 #if LLVM_API_VERSION >= 800
39 #include "llvm/CodeGen/BuiltinGCs.h"
41 #include "llvm/CodeGen/GCs.h"
46 #include <mono/utils/mono-dl.h>
49 using namespace llvm::orc
;
51 extern cl::opt
<bool> EnableMonoEH
;
52 extern cl::opt
<std::string
> MonoEHFrameSymbol
;
55 mono_llvm_set_unhandled_exception_handler (void)
59 // noop function that merely ensures that certain symbols are not eliminated
60 // from the resulting binary.
63 #if LLVM_API_VERSION >= 800
64 llvm::linkAllBuiltinGCs();
66 llvm::linkCoreCLRGC(); // Mono uses built-in "coreclr" GCStrategy
71 static std::vector
<T
> singletonSet(T t
) {
73 Vec
.push_back(std::move(t
));
80 extern void *memset(void *, int, size_t);
81 void bzero (void *to
, size_t count
) { memset (to
, 0, count
); }
85 static MonoNativeTlsKey current_cfg_tls_id
;
87 static unsigned char *
88 alloc_code (LLVMValueRef function
, int size
)
90 auto cfg
= (MonoCompile
*)mono_native_tls_get_value (current_cfg_tls_id
);
92 return (unsigned char *)mono_mem_manager_code_reserve (cfg
->mem_manager
, size
);
95 class MonoJitMemoryManager
: public RTDyldMemoryManager
98 ~MonoJitMemoryManager() override
;
100 uint8_t *allocateDataSection(uintptr_t Size
,
103 StringRef SectionName
,
104 bool IsReadOnly
) override
;
106 uint8_t *allocateCodeSection(uintptr_t Size
,
109 StringRef SectionName
) override
;
111 bool finalizeMemory(std::string
*ErrMsg
= nullptr) override
;
113 SmallVector
<sys::MemoryBlock
, 16> PendingCodeMem
;
116 MonoJitMemoryManager::~MonoJitMemoryManager()
121 MonoJitMemoryManager::allocateDataSection(uintptr_t Size
,
124 StringRef SectionName
,
129 // FIXME: Use a mempool
132 res
= (uint8_t*)malloc (Size
+ Alignment
);
133 res
= (uint8_t*)ALIGN_PTR_TO(res
, Alignment
);
135 g_assert (GPOINTER_TO_UINT (res
) % Alignment
== 0);
136 memset (res
, 0, Size
);
141 MonoJitMemoryManager::allocateCodeSection(uintptr_t Size
,
144 StringRef SectionName
)
146 uint8_t *mem
= alloc_code (NULL
, Size
);
147 PendingCodeMem
.push_back (sys::MemoryBlock ((void *)mem
, Size
));
152 MonoJitMemoryManager::finalizeMemory(std::string
*ErrMsg
)
154 for (sys::MemoryBlock
&Block
: PendingCodeMem
) {
155 #if LLVM_API_VERSION >= 900
156 sys::Memory::InvalidateInstructionCache (Block
.base (), Block
.allocatedSize ());
158 sys::Memory::InvalidateInstructionCache (Block
.base (), Block
.size ());
161 PendingCodeMem
.clear ();
165 #if defined(TARGET_AMD64) || defined(TARGET_X86)
166 #define NO_CALL_FRAME_OPT " -no-x86-call-frame-opt"
168 #define NO_CALL_FRAME_OPT ""
171 // The OptimizationList is automatically populated with registered Passes by the
174 static cl::list
<const PassInfo
*, bool, PassNameParser
>
175 PassList(cl::desc("Optimizations available:"));
178 init_function_pass_manager (legacy::FunctionPassManager
&fpm
)
180 auto reg
= PassRegistry::getPassRegistry ();
181 for (size_t i
= 0; i
< PassList
.size(); i
++) {
182 Pass
*pass
= PassList
[i
]->getNormalCtor()();
183 if (pass
->getPassKind () == llvm::PT_Function
|| pass
->getPassKind () == llvm::PT_Loop
) {
186 auto info
= reg
->getPassInfo (pass
->getPassID());
187 auto name
= info
->getPassArgument ();
188 printf("Opt pass is ignored: %.*s\n", (int) name
.size(), name
.data());
191 // -place-safepoints pass is mandatory
192 fpm
.add (createPlaceSafepointsPass ());
194 fpm
.doInitialization();
197 #if LLVM_API_VERSION >= 900
200 std::shared_ptr
<MonoJitMemoryManager
> mmgr
;
201 ExecutionSession execution_session
;
202 std::map
<VModuleKey
, std::shared_ptr
<SymbolResolver
>> resolvers
;
203 TargetMachine
*target_machine
;
204 LegacyRTDyldObjectLinkingLayer object_layer
;
205 LegacyIRCompileLayer
<decltype(object_layer
), SimpleCompiler
> compile_layer
;
206 DataLayout data_layout
;
207 legacy::FunctionPassManager fpm
;
209 MonoLLVMJIT (TargetMachine
*tm
, Module
*pgo_module
)
210 : mmgr (std::make_shared
<MonoJitMemoryManager
>())
211 , target_machine (tm
)
213 AcknowledgeORCv1Deprecation
, execution_session
,
214 [this] (VModuleKey k
) {
215 return LegacyRTDyldObjectLinkingLayer::Resources
{
216 mmgr
, resolvers
[k
] };
219 AcknowledgeORCv1Deprecation
, object_layer
,
220 SimpleCompiler
{*target_machine
})
221 , data_layout (target_machine
->createDataLayout())
224 compile_layer
.setNotifyCompiled ([] (VModuleKey
, std::unique_ptr
<Module
> module
) {
227 init_function_pass_manager (fpm
);
231 add_module (std::unique_ptr
<Module
> m
)
233 auto k
= execution_session
.allocateVModule();
234 auto lookup_name
= [this] (const std::string
&namestr
) {
235 auto jit_sym
= compile_layer
.findSymbol(namestr
, false);
239 auto namebuf
= namestr
.c_str();
240 JITSymbolFlags flags
{};
241 if (!strcmp(namebuf
, "___bzero")) {
242 return JITSymbol
{(uint64_t)(gssize
)(void*)bzero
, flags
};
244 auto current
= mono_dl_open (NULL
, 0, NULL
);
246 auto name
= namebuf
[0] == '_' ? namebuf
+ 1 : namebuf
;
248 auto err
= mono_dl_symbol (current
, name
, &sym
);
250 outs () << "R: " << namestr
<< " " << err
<< "\n";
253 return JITSymbol
{(uint64_t)(gssize
)sym
, flags
};
255 auto on_error
= [] (Error err
) {
256 outs () << "R2: " << err
<< "\n";
259 auto resolver
= createLegacyLookupResolver (execution_session
,
260 lookup_name
, on_error
);
261 resolvers
[k
] = std::move (resolver
);
262 auto err
= compile_layer
.addModule (k
, std::move(m
));
264 outs () << "addModule error: " << err
<< "\n";
271 mangle (llvm::StringRef name
)
274 raw_string_ostream out
{ret
};
275 Mangler::getNameWithPrefix (out
, name
, data_layout
);
280 mangle (const GlobalValue
*gv
)
283 raw_string_ostream out
{ret
};
284 Mangler
{}.getNameWithPrefix (out
, gv
, false);
290 Function
*func
, int nvars
, LLVMValueRef
*callee_vars
,
291 gpointer
*callee_addrs
, gpointer
*eh_frame
)
293 auto module
= func
->getParent ();
294 module
->setDataLayout (data_layout
);
296 // The lifetime of this module is managed by Mono, not LLVM, so
297 // the `unique_ptr` created here will be released in the
298 // NotifyCompiled callback.
299 auto k
= add_module (std::unique_ptr
<Module
>(module
));
300 auto bodysym
= compile_layer
.findSymbolIn (k
, mangle (func
), false);
301 auto bodyaddr
= bodysym
.getAddress ();
303 g_assert_not_reached();
304 for (int i
= 0; i
< nvars
; ++i
) {
305 auto var
= unwrap
<GlobalVariable
> (callee_vars
[i
]);
306 auto sym
= compile_layer
.findSymbolIn (k
, mangle (var
->getName ()), true);
307 auto addr
= sym
.getAddress ();
308 g_assert ((bool)addr
);
309 callee_addrs
[i
] = (gpointer
)addr
.get ();
311 auto ehsym
= compile_layer
.findSymbolIn (k
, "mono_eh_frame", false);
312 auto ehaddr
= ehsym
.getAddress ();
313 g_assert ((bool)ehaddr
);
314 *eh_frame
= (gpointer
)ehaddr
.get ();
315 return (gpointer
)bodyaddr
.get ();
320 make_mono_llvm_jit (TargetMachine
*target_machine
, llvm::Module
*pgo_module
)
322 return new MonoLLVMJIT
{target_machine
, pgo_module
};
325 #elif LLVM_API_VERSION > 600
329 /* We use our own trampoline infrastructure instead of the Orc one */
330 typedef RTDyldObjectLinkingLayer ObjLayerT
;
331 typedef IRCompileLayer
<ObjLayerT
, SimpleCompiler
> CompileLayerT
;
332 typedef CompileLayerT::ModuleHandleT ModuleHandleT
;
334 MonoLLVMJIT (TargetMachine
*TM
, MonoJitMemoryManager
*mm
)
335 : TM(TM
), ObjectLayer([=] { return std::shared_ptr
<RuntimeDyld::MemoryManager
> (mm
); }),
336 CompileLayer (ObjectLayer
, SimpleCompiler (*TM
)),
340 init_function_pass_manager (fpm
);
343 ModuleHandleT
addModule(Function
*F
, std::shared_ptr
<Module
> M
) {
344 auto Resolver
= createLambdaResolver(
345 [&](const std::string
&Name
) {
346 const char *name
= Name
.c_str ();
347 JITSymbolFlags flags
= JITSymbolFlags ();
348 if (!strcmp (name
, "___bzero"))
349 return JITSymbol((uint64_t)(gssize
)(void*)bzero
, flags
);
354 current
= mono_dl_open (NULL
, 0, NULL
);
357 err
= mono_dl_symbol (current
, name
+ 1, &symbol
);
359 err
= mono_dl_symbol (current
, name
, &symbol
);
360 mono_dl_close (current
);
362 outs () << "R: " << Name
<< "\n";
364 return JITSymbol((uint64_t)(gssize
)symbol
, flags
);
366 [](const std::string
&S
) {
367 outs () << "R2: " << S
<< "\n";
372 auto m
= CompileLayer
.addModule(M
, std::move(Resolver
));
377 std::string
mangle(const std::string
&Name
) {
378 std::string MangledName
;
380 raw_string_ostream
MangledNameStream(MangledName
);
381 Mangler::getNameWithPrefix(MangledNameStream
, Name
,
382 TM
->createDataLayout());
387 std::string
mangle(const GlobalValue
*GV
) {
388 std::string MangledName
;
392 raw_string_ostream
MangledNameStream(MangledName
);
393 Mang
.getNameWithPrefix(MangledNameStream
, GV
, false);
398 gpointer
compile (Function
*F
, int nvars
, LLVMValueRef
*callee_vars
, gpointer
*callee_addrs
, gpointer
*eh_frame
) {
399 F
->getParent ()->setDataLayout (TM
->createDataLayout ());
401 // TODO: run module wide optimizations, e.g. remove dead globals/functions
402 // Orc uses a shared_ptr to refer to modules so we have to save them ourselves to keep a ref
403 std::shared_ptr
<Module
> m (F
->getParent ());
404 modules
.push_back (m
);
405 auto ModuleHandle
= addModule (F
, m
);
406 auto BodySym
= CompileLayer
.findSymbolIn(ModuleHandle
, mangle (F
), false);
407 auto BodyAddr
= BodySym
.getAddress();
409 g_assert_not_reached ();
411 for (int i
= 0; i
< nvars
; ++i
) {
412 GlobalVariable
*var
= unwrap
<GlobalVariable
>(callee_vars
[i
]);
414 auto sym
= CompileLayer
.findSymbolIn (ModuleHandle
, mangle (var
->getName ()), true);
415 auto addr
= sym
.getAddress ();
416 g_assert ((bool)addr
);
417 callee_addrs
[i
] = (gpointer
)addr
.get ();
420 auto ehsym
= CompileLayer
.findSymbolIn(ModuleHandle
, "mono_eh_frame", false);
421 auto ehaddr
= ehsym
.getAddress ();
422 g_assert ((bool)ehaddr
);
423 *eh_frame
= (gpointer
)ehaddr
.get ();
424 return (gpointer
)BodyAddr
.get ();
429 ObjLayerT ObjectLayer
;
430 CompileLayerT CompileLayer
;
431 std::vector
<std::shared_ptr
<Module
>> modules
;
432 legacy::FunctionPassManager fpm
;
435 static MonoJitMemoryManager
*mono_mm
;
438 make_mono_llvm_jit (TargetMachine
*target_machine
, llvm::Module
*)
440 mono_mm
= new MonoJitMemoryManager ();
441 return new MonoLLVMJIT(target_machine
, mono_mm
);
446 static llvm::Module
*dummy_pgo_module
= nullptr;
447 static MonoLLVMJIT
*jit
;
450 init_passes_and_options ()
452 PassRegistry
®istry
= *PassRegistry::getPassRegistry();
453 initializeCore(registry
);
454 initializeScalarOpts(registry
);
455 initializeInstCombine(registry
);
456 initializeTarget(registry
);
457 initializeLoopIdiomRecognizeLegacyPassPass(registry
);
459 // FIXME: find optimal mono specific order of passes
460 // see https://llvm.org/docs/Frontend/PerformanceTips.html#pass-ordering
461 // the following order is based on a stripped version of "OPT -O2"
462 const char *default_opts
= " -simplifycfg -sroa -lower-expect -instcombine -sroa -jump-threading -loop-rotate -licm -simplifycfg -lcssa -loop-idiom -indvars -loop-deletion -gvn -memcpyopt -sccp -bdce -instcombine -dse -simplifycfg -enable-implicit-null-checks -sroa -instcombine" NO_CALL_FRAME_OPT
;
463 const char *opts
= g_getenv ("MONO_LLVM_OPT");
466 else if (opts
[0] == '+') // Append passes to the default order if starts with '+', overwrite otherwise
467 opts
= g_strdup_printf ("%s %s", default_opts
, opts
+ 1);
468 else if (opts
[0] != ' ') // pass order has to start with a leading whitespace
469 opts
= g_strdup_printf (" %s", opts
);
471 char **args
= g_strsplit (opts
, " ", -1);
472 llvm::cl::ParseCommandLineOptions (g_strv_length (args
), args
, "");
477 mono_llvm_jit_init ()
479 if (jit
!= nullptr) return;
483 mono_native_tls_alloc (¤t_cfg_tls_id
, NULL
);
485 InitializeNativeTarget ();
486 InitializeNativeTargetAsmPrinter();
489 MonoEHFrameSymbol
= "mono_eh_frame";
492 if (mono_use_fast_math
) {
494 opts
.NoInfsFPMath
= true;
495 opts
.NoNaNsFPMath
= true;
496 opts
.NoSignedZerosFPMath
= true;
497 opts
.NoTrappingFPMath
= true;
498 opts
.UnsafeFPMath
= true;
499 opts
.AllowFPOpFusion
= FPOpFusion::Fast
;
500 EB
.setTargetOptions (opts
);
503 EB
.setOptLevel (CodeGenOpt::Aggressive
);
504 EB
.setMCPU (sys::getHostCPUName ());
507 EB
.setMArch ("x86-64");
511 EB
.setMArch ("aarch64");
515 g_assert_not_reached ();
518 llvm::StringMap
<bool> cpu_features
;
519 // Why 76? LLVM 9 supports 76 different x86 feature strings. This
520 // requires around 1216 bytes of data in the local activation record.
521 // It'd be possible to stream entries to setMAttrs using
522 // llvm::map_range and llvm::make_filter_range, but llvm::map_range
523 // isn't available in LLVM 6, and it's not worth writing a small
524 // single-purpose one here.
525 llvm::SmallVector
<llvm::StringRef
, 76> supported_features
;
526 if (llvm::sys::getHostCPUFeatures (cpu_features
)) {
527 for (const auto &feature
: cpu_features
) {
529 supported_features
.push_back (feature
.first ());
531 EB
.setMAttrs (supported_features
);
534 auto TM
= EB
.selectTarget ();
536 dummy_pgo_module
= unwrap (LLVMModuleCreateWithName("dummy-pgo-module"));
537 init_passes_and_options ();
538 jit
= make_mono_llvm_jit (TM
, dummy_pgo_module
);
542 mono_llvm_create_ee (LLVMExecutionEngineRef
*ee
)
548 * mono_llvm_compile_method:
550 * Compile METHOD to native code. Compute the addresses of the variables in CALLEE_VARS and store them into
551 * CALLEE_ADDRS. Return the EH frame address in EH_FRAME.
554 mono_llvm_compile_method (MonoEERef mono_ee
, MonoCompile
*cfg
, LLVMValueRef method
, int nvars
, LLVMValueRef
*callee_vars
, gpointer
*callee_addrs
, gpointer
*eh_frame
)
556 mono_native_tls_set_value (current_cfg_tls_id
, cfg
);
557 auto ret
= jit
->compile (unwrap
<Function
> (method
), nvars
, callee_vars
, callee_addrs
, eh_frame
);
558 mono_native_tls_set_value (current_cfg_tls_id
, nullptr);
563 mono_llvm_dispose_ee (MonoEERef
*eeref
)
567 #else /* MONO_CROSS_COMPILE or LLVM_API_VERSION < 600 */
570 mono_llvm_set_unhandled_exception_handler (void)
575 mono_llvm_jit_init ()
580 mono_llvm_create_ee (LLVMExecutionEngineRef
*ee
)
582 g_error ("LLVM JIT not supported on this platform.");
587 mono_llvm_compile_method (MonoEERef mono_ee
, MonoCompile
*cfg
, LLVMValueRef method
, int nvars
, LLVMValueRef
*callee_vars
, gpointer
*callee_addrs
, gpointer
*eh_frame
)
589 g_assert_not_reached ();
594 mono_llvm_dispose_ee (MonoEERef
*eeref
)
596 g_assert_not_reached ();
599 #endif /* !MONO_CROSS_COMPILE */