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/Support/raw_ostream.h>
22 #include <llvm/Support/Host.h>
23 #include <llvm/Support/TargetSelect.h>
24 #include <llvm/IR/Mangler.h>
25 #include "llvm/IR/LegacyPassNameParser.h"
26 #include <llvm/ExecutionEngine/ExecutionEngine.h>
27 #include "llvm/ExecutionEngine/Orc/CompileUtils.h"
28 #include "llvm/ExecutionEngine/Orc/IRCompileLayer.h"
29 #include "llvm/ExecutionEngine/Orc/LambdaResolver.h"
30 #include "llvm/ExecutionEngine/RTDyldMemoryManager.h"
31 #include "llvm/ExecutionEngine/Orc/RTDyldObjectLinkingLayer.h"
32 #include "llvm/ExecutionEngine/JITSymbol.h"
33 #include "llvm/Transforms/Scalar.h"
34 #include "llvm/CodeGen/GCs.h"
38 #include <mono/utils/mono-dl.h>
41 using namespace llvm::orc
;
43 extern cl::opt
<bool> EnableMonoEH
;
44 extern cl::opt
<std::string
> MonoEHFrameSymbol
;
47 mono_llvm_set_unhandled_exception_handler (void)
52 static std::vector
<T
> singletonSet(T t
) {
54 Vec
.push_back(std::move(t
));
61 extern void *memset(void *, int, size_t);
62 void bzero (void *to
, size_t count
) { memset (to
, 0, count
); }
66 static AllocCodeMemoryCb
*alloc_code_mem_cb
;
68 class MonoJitMemoryManager
: public RTDyldMemoryManager
71 ~MonoJitMemoryManager() override
;
73 uint8_t *allocateDataSection(uintptr_t Size
,
76 StringRef SectionName
,
77 bool IsReadOnly
) override
;
79 uint8_t *allocateCodeSection(uintptr_t Size
,
82 StringRef SectionName
) override
;
84 bool finalizeMemory(std::string
*ErrMsg
= nullptr) override
;
87 MonoJitMemoryManager::~MonoJitMemoryManager()
92 MonoJitMemoryManager::allocateDataSection(uintptr_t Size
,
95 StringRef SectionName
,
100 // FIXME: Use a mempool
101 if (Alignment
== 32) {
103 res
= (uint8_t*)malloc (Size
+ 32);
104 res
+= (GPOINTER_TO_UINT (res
) % 32);
106 res
= (uint8_t*)malloc (Size
);
109 g_assert (GPOINTER_TO_UINT (res
) % Alignment
== 0);
110 memset (res
, 0, Size
);
115 MonoJitMemoryManager::allocateCodeSection(uintptr_t Size
,
118 StringRef SectionName
)
120 return alloc_code_mem_cb (NULL
, Size
);
124 MonoJitMemoryManager::finalizeMemory(std::string
*ErrMsg
)
129 #if LLVM_API_VERSION >= 900
132 std::shared_ptr
<MonoJitMemoryManager
> mmgr
;
133 ExecutionSession execution_session
;
134 std::map
<VModuleKey
, std::shared_ptr
<SymbolResolver
>> resolvers
;
135 TargetMachine
*target_machine
;
136 LegacyRTDyldObjectLinkingLayer object_layer
;
137 LegacyIRCompileLayer
<decltype(object_layer
), SimpleCompiler
> compile_layer
;
138 DataLayout data_layout
;
140 MonoLLVMJIT (TargetMachine
*tm
)
141 : mmgr (std::make_shared
<MonoJitMemoryManager
>())
142 , target_machine (tm
)
144 AcknowledgeORCv1Deprecation
, execution_session
,
145 [this] (VModuleKey k
) {
146 return LegacyRTDyldObjectLinkingLayer::Resources
{
147 mmgr
, resolvers
[k
] };
150 AcknowledgeORCv1Deprecation
, object_layer
,
151 SimpleCompiler
{*target_machine
})
152 , data_layout (target_machine
->createDataLayout())
154 compile_layer
.setNotifyCompiled ([] (VModuleKey
, std::unique_ptr
<Module
> module
) {
160 add_module (std::unique_ptr
<Module
> m
)
162 auto k
= execution_session
.allocateVModule();
163 auto lookup_name
= [this] (const std::string
&namestr
) {
164 auto jit_sym
= compile_layer
.findSymbol(namestr
, false);
168 auto namebuf
= namestr
.c_str();
169 JITSymbolFlags flags
{};
170 if (!strcmp(namebuf
, "___bzero")) {
171 return JITSymbol
{(uint64_t)(gssize
)(void*)bzero
, flags
};
173 auto current
= mono_dl_open (NULL
, 0, NULL
);
175 auto name
= namebuf
[0] == '_' ? namebuf
+ 1 : namebuf
;
177 auto err
= mono_dl_symbol (current
, name
, &sym
);
179 outs () << "R: " << namestr
<< "\n";
182 return JITSymbol
{(uint64_t)(gssize
)sym
, flags
};
184 auto on_error
= [] (Error err
) {
185 outs () << "R2: " << err
<< "\n";
188 auto resolver
= createLegacyLookupResolver (execution_session
,
189 lookup_name
, on_error
);
190 resolvers
[k
] = std::move (resolver
);
191 auto err
= compile_layer
.addModule (k
, std::move(m
));
193 outs () << "addModule error: " << err
<< "\n";
200 mangle (const std::string
&name
)
203 raw_string_ostream out
{ret
};
204 Mangler::getNameWithPrefix (out
, name
, data_layout
);
209 mangle (const GlobalValue
*gv
)
212 raw_string_ostream out
{ret
};
213 Mangler
{}.getNameWithPrefix (out
, gv
, false);
219 Function
*func
, int nvars
, LLVMValueRef
*callee_vars
,
220 gpointer
*callee_addrs
, gpointer
*eh_frame
)
222 auto module
= func
->getParent ();
223 module
->setDataLayout (data_layout
);
224 // The lifetime of this module is managed by the C API, and the
225 // `unique_ptr` created here will be released in the
226 // NotifyCompiled callback.
227 auto k
= add_module (std::unique_ptr
<Module
>(module
));
228 auto bodysym
= compile_layer
.findSymbolIn (k
, mangle (func
), false);
229 auto bodyaddr
= bodysym
.getAddress ();
231 for (int i
= 0; i
< nvars
; ++i
) {
232 auto var
= unwrap
<GlobalVariable
> (callee_vars
[i
]);
233 auto sym
= compile_layer
.findSymbolIn (k
, mangle (var
->getName ()), true);
234 auto addr
= sym
.getAddress ();
235 g_assert ((bool)addr
);
236 callee_addrs
[i
] = (gpointer
)addr
.get ();
238 auto ehsym
= compile_layer
.findSymbolIn (k
, "mono_eh_frame", false);
239 auto ehaddr
= ehsym
.getAddress ();
240 g_assert ((bool)ehaddr
);
241 *eh_frame
= (gpointer
)ehaddr
.get ();
242 return (gpointer
)bodyaddr
.get ();
247 init_mono_llvm_jit ()
252 make_mono_llvm_jit (TargetMachine
*target_machine
)
254 return new MonoLLVMJIT
{target_machine
};
257 #elif LLVM_API_VERSION > 600
259 // The OptimizationList is automatically populated with registered Passes by the
262 static cl::list
<const PassInfo
*, bool, PassNameParser
>
263 PassList(cl::desc("Optimizations available:"));
267 /* We use our own trampoline infrastructure instead of the Orc one */
268 typedef RTDyldObjectLinkingLayer ObjLayerT
;
269 typedef IRCompileLayer
<ObjLayerT
, SimpleCompiler
> CompileLayerT
;
270 typedef CompileLayerT::ModuleHandleT ModuleHandleT
;
272 MonoLLVMJIT (TargetMachine
*TM
, MonoJitMemoryManager
*mm
)
273 : TM(TM
), ObjectLayer([=] { return std::shared_ptr
<RuntimeDyld::MemoryManager
> (mm
); }),
274 CompileLayer (ObjectLayer
, SimpleCompiler (*TM
)),
280 void initPassManager () {
281 PassRegistry
®istry
= *PassRegistry::getPassRegistry();
282 initializeCore(registry
);
283 initializeScalarOpts(registry
);
284 initializeInstCombine(registry
);
285 initializeTarget(registry
);
286 linkCoreCLRGC(); // Mono uses built-in "coreclr" GCStrategy
288 // FIXME: find optimal mono specific order of passes
289 // see https://llvm.org/docs/Frontend/PerformanceTips.html#pass-ordering
290 // the following order is based on a stripped version of "OPT -O2"
291 const char *default_opts
= " -simplifycfg -sroa -lower-expect -instcombine -licm -simplifycfg -lcssa -indvars -loop-deletion -gvn -memcpyopt -sccp -bdce -instcombine -dse -simplifycfg";
292 const char *opts
= g_getenv ("MONO_LLVM_OPT");
295 else if (opts
[0] == '+') // Append passes to the default order if starts with '+', overwrite otherwise
296 opts
= g_strdup_printf ("%s %s", default_opts
, opts
+ 1);
297 else if (opts
[0] != ' ') // pass order has to start with a leading whitespace
298 opts
= g_strdup_printf (" %s", opts
);
300 char **args
= g_strsplit (opts
, " ", -1);
301 llvm::cl::ParseCommandLineOptions (g_strv_length (args
), args
, "");
303 for (size_t i
= 0; i
< PassList
.size(); i
++) {
304 Pass
*pass
= PassList
[i
]->getNormalCtor()();
305 if (pass
->getPassKind () == llvm::PT_Function
|| pass
->getPassKind () == llvm::PT_Loop
) {
308 printf("Opt pass is ignored: %s\n", args
[i
+ 1]);
311 // -place-safepoints pass is mandatory
312 fpm
.add (createPlaceSafepointsPass ());
315 fpm
.doInitialization();
318 ModuleHandleT
addModule(Function
*F
, std::shared_ptr
<Module
> M
) {
319 auto Resolver
= createLambdaResolver(
320 [&](const std::string
&Name
) {
321 const char *name
= Name
.c_str ();
322 JITSymbolFlags flags
= JITSymbolFlags ();
323 if (!strcmp (name
, "___bzero"))
324 return JITSymbol((uint64_t)(gssize
)(void*)bzero
, flags
);
329 current
= mono_dl_open (NULL
, 0, NULL
);
332 err
= mono_dl_symbol (current
, name
+ 1, &symbol
);
334 err
= mono_dl_symbol (current
, name
, &symbol
);
335 mono_dl_close (current
);
337 outs () << "R: " << Name
<< "\n";
339 return JITSymbol((uint64_t)(gssize
)symbol
, flags
);
341 [](const std::string
&S
) {
342 outs () << "R2: " << S
<< "\n";
347 auto m
= CompileLayer
.addModule(M
, std::move(Resolver
));
352 std::string
mangle(const std::string
&Name
) {
353 std::string MangledName
;
355 raw_string_ostream
MangledNameStream(MangledName
);
356 Mangler::getNameWithPrefix(MangledNameStream
, Name
,
357 TM
->createDataLayout());
362 std::string
mangle(const GlobalValue
*GV
) {
363 std::string MangledName
;
367 raw_string_ostream
MangledNameStream(MangledName
);
368 Mang
.getNameWithPrefix(MangledNameStream
, GV
, false);
373 gpointer
compile (Function
*F
, int nvars
, LLVMValueRef
*callee_vars
, gpointer
*callee_addrs
, gpointer
*eh_frame
) {
374 F
->getParent ()->setDataLayout (TM
->createDataLayout ());
376 // TODO: run module wide optimizations, e.g. remove dead globals/functions
377 // Orc uses a shared_ptr to refer to modules so we have to save them ourselves to keep a ref
378 std::shared_ptr
<Module
> m (F
->getParent ());
379 modules
.push_back (m
);
380 auto ModuleHandle
= addModule (F
, m
);
381 auto BodySym
= CompileLayer
.findSymbolIn(ModuleHandle
, mangle (F
), false);
382 auto BodyAddr
= BodySym
.getAddress();
385 for (int i
= 0; i
< nvars
; ++i
) {
386 GlobalVariable
*var
= unwrap
<GlobalVariable
>(callee_vars
[i
]);
388 auto sym
= CompileLayer
.findSymbolIn (ModuleHandle
, mangle (var
->getName ()), true);
389 auto addr
= sym
.getAddress ();
390 g_assert ((bool)addr
);
391 callee_addrs
[i
] = (gpointer
)addr
.get ();
394 auto ehsym
= CompileLayer
.findSymbolIn(ModuleHandle
, "mono_eh_frame", false);
395 auto ehaddr
= ehsym
.getAddress ();
396 g_assert ((bool)ehaddr
);
397 *eh_frame
= (gpointer
)ehaddr
.get ();
398 return (gpointer
)BodyAddr
.get ();
403 ObjLayerT ObjectLayer
;
404 CompileLayerT CompileLayer
;
405 std::vector
<std::shared_ptr
<Module
>> modules
;
406 legacy::FunctionPassManager fpm
;
409 static MonoJitMemoryManager
*mono_mm
;
412 init_mono_llvm_jit ()
414 mono_mm
= new MonoJitMemoryManager ();
418 make_mono_llvm_jit (TargetMachine
*target_machine
)
420 return new MonoLLVMJIT(target_machine
, mono_mm
);
425 static MonoLLVMJIT
*jit
;
428 mono_llvm_create_ee (AllocCodeMemoryCb
*alloc_cb
, FunctionEmittedCb
*emitted_cb
, ExceptionTableCb
*exception_cb
, LLVMExecutionEngineRef
*ee
)
430 alloc_code_mem_cb
= alloc_cb
;
432 InitializeNativeTarget ();
433 InitializeNativeTargetAsmPrinter();
436 MonoEHFrameSymbol
= "mono_eh_frame";
439 if (mono_use_fast_math
) {
441 opts
.NoInfsFPMath
= true;
442 opts
.NoNaNsFPMath
= true;
443 opts
.NoSignedZerosFPMath
= true;
444 opts
.NoTrappingFPMath
= true;
445 opts
.UnsafeFPMath
= true;
446 opts
.AllowFPOpFusion
= FPOpFusion::Fast
;
447 EB
.setTargetOptions (opts
);
450 EB
.setOptLevel(CodeGenOpt::Aggressive
);
451 EB
.setMCPU(sys::getHostCPUName());
452 auto TM
= EB
.selectTarget ();
455 init_mono_llvm_jit ();
456 jit
= make_mono_llvm_jit (TM
);
462 * mono_llvm_compile_method:
464 * Compile METHOD to native code. Compute the addresses of the variables in CALLEE_VARS and store them into
465 * CALLEE_ADDRS. Return the EH frame address in EH_FRAME.
468 mono_llvm_compile_method (MonoEERef mono_ee
, LLVMValueRef method
, int nvars
, LLVMValueRef
*callee_vars
, gpointer
*callee_addrs
, gpointer
*eh_frame
)
470 return jit
->compile (unwrap
<Function
> (method
), nvars
, callee_vars
, callee_addrs
, eh_frame
);
474 mono_llvm_dispose_ee (MonoEERef
*eeref
)
478 #else /* MONO_CROSS_COMPILE or LLVM_API_VERSION < 600 */
481 mono_llvm_set_unhandled_exception_handler (void)
486 mono_llvm_create_ee (AllocCodeMemoryCb
*alloc_cb
, FunctionEmittedCb
*emitted_cb
, ExceptionTableCb
*exception_cb
, LLVMExecutionEngineRef
*ee
)
488 g_error ("LLVM JIT not supported on this platform.");
493 mono_llvm_compile_method (MonoEERef mono_ee
, LLVMValueRef method
, int nvars
, LLVMValueRef
*callee_vars
, gpointer
*callee_addrs
, gpointer
*eh_frame
)
495 g_assert_not_reached ();
500 mono_llvm_dispose_ee (MonoEERef
*eeref
)
502 g_assert_not_reached ();
505 #endif /* !MONO_CROSS_COMPILE */