Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license
2 : :
3 : : #include "llvm-version.h"
4 : : #include "platform.h"
5 : :
6 : : // target support
7 : : #include <llvm/ADT/Triple.h>
8 : : #include <llvm/Analysis/TargetLibraryInfo.h>
9 : : #include <llvm/Analysis/TargetTransformInfo.h>
10 : : #include <llvm/IR/DataLayout.h>
11 : : #if JL_LLVM_VERSION >= 140000
12 : : #include <llvm/MC/TargetRegistry.h>
13 : : #else
14 : : #include <llvm/Support/TargetRegistry.h>
15 : : #endif
16 : : #include <llvm/Target/TargetMachine.h>
17 : :
18 : : // analysis passes
19 : : #include <llvm/Analysis/Passes.h>
20 : : #include <llvm/Analysis/BasicAliasAnalysis.h>
21 : : #include <llvm/Analysis/TypeBasedAliasAnalysis.h>
22 : : #include <llvm/Analysis/ScopedNoAliasAA.h>
23 : : #include <llvm/IR/PassManager.h>
24 : : #include <llvm/IR/Verifier.h>
25 : : #include <llvm/Transforms/IPO.h>
26 : : #include <llvm/Transforms/Scalar.h>
27 : : #include <llvm/Transforms/Vectorize.h>
28 : : #include <llvm/Transforms/Instrumentation/AddressSanitizer.h>
29 : : #include <llvm/Transforms/Instrumentation/ThreadSanitizer.h>
30 : : #include <llvm/Transforms/Scalar/GVN.h>
31 : : #include <llvm/Transforms/IPO/AlwaysInliner.h>
32 : : #include <llvm/Transforms/InstCombine/InstCombine.h>
33 : : #include <llvm/Transforms/Scalar/InstSimplifyPass.h>
34 : : #include <llvm/Transforms/Utils/SimplifyCFGOptions.h>
35 : : #include <llvm/Passes/PassBuilder.h>
36 : : #include <llvm/Passes/PassPlugin.h>
37 : : #if defined(USE_POLLY)
38 : : #include <polly/RegisterPasses.h>
39 : : #include <polly/LinkAllPasses.h>
40 : : #include <polly/CodeGen/CodegenCleanup.h>
41 : : #if defined(USE_POLLY_ACC)
42 : : #include <polly/Support/LinkGPURuntime.h>
43 : : #endif
44 : : #endif
45 : :
46 : : // for outputting code
47 : : #include <llvm/Bitcode/BitcodeWriter.h>
48 : : #include <llvm/Bitcode/BitcodeWriterPass.h>
49 : : #include "llvm/Object/ArchiveWriter.h"
50 : : #include <llvm/IR/IRPrintingPasses.h>
51 : :
52 : : #include <llvm/IR/LegacyPassManagers.h>
53 : : #include <llvm/Transforms/Utils/Cloning.h>
54 : :
55 : :
56 : : using namespace llvm;
57 : :
58 : : #include "julia.h"
59 : : #include "julia_internal.h"
60 : : #include "jitlayers.h"
61 : : #include "julia_assert.h"
62 : :
63 : : template<class T> // for GlobalObject's
64 : 81720 : static T *addComdat(T *G)
65 : : {
66 : : #if defined(_OS_WINDOWS_)
67 : : if (!G->isDeclaration()) {
68 : : // add __declspec(dllexport) to everything marked for export
69 : : if (G->getLinkage() == GlobalValue::ExternalLinkage)
70 : : G->setDLLStorageClass(GlobalValue::DLLExportStorageClass);
71 : : else
72 : : G->setDLLStorageClass(GlobalValue::DefaultStorageClass);
73 : : }
74 : : #endif
75 : 81720 : return G;
76 : : }
77 : :
78 : :
79 : : typedef struct {
80 : : orc::ThreadSafeModule M;
81 : : std::vector<GlobalValue*> jl_sysimg_fvars;
82 : : std::vector<GlobalValue*> jl_sysimg_gvars;
83 : : std::map<jl_code_instance_t*, std::tuple<uint32_t, uint32_t>> jl_fvar_map;
84 : : std::map<void*, int32_t> jl_value_to_llvm; // uses 1-based indexing
85 : : } jl_native_code_desc_t;
86 : :
87 : : extern "C" JL_DLLEXPORT
88 : 111997 : void jl_get_function_id_impl(void *native_code, jl_code_instance_t *codeinst,
89 : : int32_t *func_idx, int32_t *specfunc_idx)
90 : : {
91 : 111997 : jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code;
92 [ + + ]: 111997 : if (data) {
93 : : // get the function index in the fvar lookup table
94 : 68290 : auto it = data->jl_fvar_map.find(codeinst);
95 [ + + ]: 68290 : if (it != data->jl_fvar_map.end()) {
96 : 14900 : std::tie(*func_idx, *specfunc_idx) = it->second;
97 : : }
98 : : }
99 : 111997 : }
100 : :
101 : : extern "C" JL_DLLEXPORT
102 : 2278290 : int32_t jl_get_llvm_gv_impl(void *native_code, jl_value_t *p)
103 : : {
104 : : // map a jl_value_t memory location to a GlobalVariable
105 : 2278290 : jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code;
106 [ + + ]: 2278290 : if (data) {
107 : 1228330 : auto it = data->jl_value_to_llvm.find(p);
108 [ + + ]: 1228330 : if (it != data->jl_value_to_llvm.end()) {
109 : 18372 : return it->second;
110 : : }
111 : : }
112 : 2259920 : return 0;
113 : : }
114 : :
115 : : extern "C" JL_DLLEXPORT
116 : 0 : LLVMOrcThreadSafeModuleRef jl_get_llvm_module_impl(void *native_code)
117 : : {
118 : 0 : jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code;
119 [ # # ]: 0 : if (data)
120 : 0 : return wrap(&data->M);
121 : : else
122 : 0 : return NULL;
123 : : }
124 : :
125 : : extern "C" JL_DLLEXPORT
126 : 0 : GlobalValue* jl_get_llvm_function_impl(void *native_code, uint32_t idx)
127 : : {
128 : 0 : jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code;
129 [ # # ]: 0 : if (data)
130 : 0 : return data->jl_sysimg_fvars[idx];
131 : : else
132 : 0 : return NULL;
133 : : }
134 : :
135 : :
136 : 2 : static void emit_offset_table(Module &mod, const std::vector<GlobalValue*> &vars, StringRef name, Type *T_psize)
137 : : {
138 : : // Emit a global variable with all the variable addresses.
139 : : // The cloning pass will convert them into offsets.
140 [ - + ]: 2 : assert(!vars.empty());
141 : 2 : size_t nvars = vars.size();
142 : 2 : std::vector<Constant*> addrs(nvars);
143 [ + + ]: 43963 : for (size_t i = 0; i < nvars; i++) {
144 : 43961 : Constant *var = vars[i];
145 : 43961 : addrs[i] = ConstantExpr::getBitCast(var, T_psize);
146 : : }
147 : 2 : ArrayType *vars_type = ArrayType::get(T_psize, nvars);
148 : 2 : new GlobalVariable(mod, vars_type, true,
149 : : GlobalVariable::ExternalLinkage,
150 : 4 : ConstantArray::get(vars_type, addrs),
151 : 2 : name);
152 : 2 : }
153 : :
154 : 1734410 : static bool is_safe_char(unsigned char c)
155 : : {
156 [ + + + + ]: 1734410 : return ('0' <= c && c <= '9') ||
157 [ + + + + ]: 1338500 : ('A' <= c && c <= 'Z') ||
158 [ + + + + ]: 1274250 : ('a' <= c && c <= 'z') ||
159 [ + + + + ]: 3537590 : (c == '_' || c == '$') ||
160 [ + + + - ]: 1803180 : (c >= 128 && c < 255);
161 : : }
162 : :
163 : : static const char hexchars[16] = {
164 : : '0', '1', '2', '3', '4', '5', '6', '7',
165 : : '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
166 : :
167 : : static const char *const common_names[256] = {
168 : : // 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, a, b, c, d, e, f
169 : : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x00
170 : : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x10
171 : : "SP", "NOT", "DQT", "YY", 0, "REM", "AND", "SQT", // 0x20
172 : : "LPR", "RPR", "MUL", "SUM", 0, "SUB", "DOT", "DIV", // 0x28
173 : : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "COL", 0, "LT", "EQ", "GT", "QQ", // 0x30
174 : : "AT", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x40
175 : : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "LBR", "RDV", "RBR", "POW", 0, // 0x50
176 : : "TIC", 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, // 0x60
177 : : 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, "LCR", "OR", "RCR", "TLD", "DEL", // 0x70
178 : : 0 }; // remainder is filled with zeros, though are also all safe characters
179 : :
180 : : // reversibly removes special characters from the name of GlobalObjects,
181 : : // which might cause them to be treated special by LLVM or the system linker
182 : : // the only non-identifier characters we allow to appear are '.' and '$',
183 : : // and all of UTF-8 above code-point 128 (except 255)
184 : : // most are given "friendly" abbreviations
185 : : // the remaining few will print as hex
186 : : // e.g. mangles "llvm.a≠a$a!a##" as "llvmDOT.a≠a$aNOT.aYY.YY."
187 : 81717 : static void makeSafeName(GlobalObject &G)
188 : : {
189 : 81717 : StringRef Name = G.getName();
190 : 163434 : SmallVector<char, 32> SafeName;
191 [ + + ]: 1816130 : for (unsigned char c : Name.bytes()) {
192 [ + + ]: 1734410 : if (is_safe_char(c)) {
193 : 1665750 : SafeName.push_back(c);
194 : : }
195 : : else {
196 [ + - ]: 68662 : if (common_names[c]) {
197 : 68662 : SafeName.push_back(common_names[c][0]);
198 : 68662 : SafeName.push_back(common_names[c][1]);
199 [ + + ]: 68662 : if (common_names[c][2])
200 : 46704 : SafeName.push_back(common_names[c][2]);
201 : : }
202 : : else {
203 : 0 : SafeName.push_back(hexchars[(c >> 4) & 0xF]);
204 : 0 : SafeName.push_back(hexchars[c & 0xF]);
205 : : }
206 : 68662 : SafeName.push_back('.');
207 : : }
208 : : }
209 [ + + ]: 81717 : if (SafeName.size() != Name.size())
210 : 39228 : G.setName(StringRef(SafeName.data(), SafeName.size()));
211 : 81717 : }
212 : :
213 : 38025 : static void jl_ci_cache_lookup(const jl_cgparams_t &cgparams, jl_method_instance_t *mi, size_t world, jl_code_instance_t **ci_out, jl_code_info_t **src_out)
214 : : {
215 : 38025 : jl_value_t *ci = cgparams.lookup(mi, world, world);
216 : : JL_GC_PROMISE_ROOTED(ci);
217 : 38025 : jl_code_instance_t *codeinst = NULL;
218 [ + + ]: 38025 : if (ci != jl_nothing) {
219 : 37875 : codeinst = (jl_code_instance_t*)ci;
220 : 37875 : *src_out = (jl_code_info_t*)codeinst->inferred;
221 : 37875 : jl_method_t *def = codeinst->def->def.method;
222 [ + + ]: 37875 : if ((jl_value_t*)*src_out == jl_nothing)
223 : 1312 : *src_out = NULL;
224 [ + + + - ]: 37875 : if (*src_out && jl_is_method(def))
225 : 36563 : *src_out = jl_uncompress_ir(def, codeinst, (jl_array_t*)*src_out);
226 : : }
227 [ + + - + ]: 38025 : if (*src_out == NULL || !jl_is_code_info(*src_out)) {
228 [ - + ]: 1462 : if (cgparams.lookup != jl_rettype_inferred) {
229 : 0 : jl_error("Refusing to automatically run type inference with custom cache lookup.");
230 : : }
231 : : else {
232 : 1462 : *src_out = jl_type_infer(mi, world, 0);
233 [ + - ]: 1462 : if (*src_out) {
234 : 1462 : codeinst = jl_get_method_inferred(mi, (*src_out)->rettype, (*src_out)->min_world, (*src_out)->max_world);
235 [ + - + + ]: 1462 : if ((*src_out)->inferred && !codeinst->inferred)
236 : 7 : codeinst->inferred = jl_nothing;
237 : : }
238 : : }
239 : : }
240 : 38025 : *ci_out = codeinst;
241 : 38025 : }
242 : :
243 : : // takes the running content that has collected in the shadow module and dump it to disk
244 : : // this builds the object file portion of the sysimage files for fast startup, and can
245 : : // also be used be extern consumers like GPUCompiler.jl to obtain a module containing
246 : : // all reachable & inferrrable functions. The `policy` flag switches between the default
247 : : // mode `0`, the extern mode `1`, and imaging mode `2`.
248 : : extern "C" JL_DLLEXPORT
249 : 3 : void *jl_create_native_impl(jl_array_t *methods, LLVMOrcThreadSafeModuleRef llvmmod, const jl_cgparams_t *cgparams, int _policy)
250 : : {
251 [ + - ]: 3 : if (cgparams == NULL)
252 : 3 : cgparams = &jl_default_cgparams;
253 : 3 : jl_native_code_desc_t *data = new jl_native_code_desc_t;
254 : 3 : CompilationPolicy policy = (CompilationPolicy) _policy;
255 [ - + - - ]: 3 : bool imaging = imaging_default() || policy == CompilationPolicy::ImagingMode;
256 : 6 : jl_workqueue_t emitted;
257 : 3 : jl_method_instance_t *mi = NULL;
258 : 3 : jl_code_info_t *src = NULL;
259 : 3 : JL_GC_PUSH1(&src);
260 : 3 : JL_LOCK(&jl_codegen_lock);
261 : 3 : orc::ThreadSafeContext ctx;
262 : 3 : orc::ThreadSafeModule backing;
263 [ + - ]: 3 : if (!llvmmod) {
264 : 3 : ctx = jl_ExecutionEngine->acquireContext();
265 : 3 : backing = jl_create_llvm_module("text", ctx, imaging);
266 : : }
267 [ - + ]: 3 : orc::ThreadSafeModule &clone = llvmmod ? *unwrap(llvmmod) : backing;
268 : 6 : auto ctxt = clone.getContext();
269 : 6 : jl_codegen_params_t params(ctxt);
270 : 3 : params.params = cgparams;
271 : 3 : uint64_t compiler_start_time = 0;
272 : 3 : uint8_t measure_compile_time_enabled = jl_atomic_load_relaxed(&jl_measure_compile_time_enabled);
273 [ - + ]: 3 : if (measure_compile_time_enabled)
274 : 0 : compiler_start_time = jl_hrtime();
275 : :
276 : 3 : params.imaging = imaging;
277 : :
278 : : // compile all methods for the current world and type-inference world
279 : 3 : size_t compile_for[] = { jl_typeinf_world, jl_atomic_load_acquire(&jl_world_counter) };
280 [ + + ]: 9 : for (int worlds = 0; worlds < 2; worlds++) {
281 : 6 : params.world = compile_for[worlds];
282 [ - + ]: 6 : if (!params.world)
283 : 0 : continue;
284 : : // Don't emit methods for the typeinf_world with extern policy
285 [ - + - - ]: 6 : if (policy != CompilationPolicy::Default && params.world == jl_typeinf_world)
286 : 0 : continue;
287 : : size_t i, l;
288 [ + + ]: 53548 : for (i = 0, l = jl_array_len(methods); i < l; i++) {
289 : : // each item in this list is either a MethodInstance indicating something
290 : : // to compile, or an svec(rettype, sig) describing a C-callable alias to create.
291 : 53542 : jl_value_t *item = jl_array_ptr_ref(methods, i);
292 [ - + ]: 53542 : if (jl_is_simplevector(item)) {
293 [ # # ]: 0 : if (worlds == 1)
294 : 0 : jl_compile_extern_c(wrap(&clone), ¶ms, NULL, jl_svecref(item, 0), jl_svecref(item, 1));
295 : 0 : continue;
296 : : }
297 : 53542 : mi = (jl_method_instance_t*)item;
298 : 53542 : src = NULL;
299 : : // if this method is generally visible to the current compilation world,
300 : : // and this is either the primary world, or not applicable in the primary world
301 : : // then we want to compile and emit this
302 [ + + + + ]: 53542 : if (mi->def.method->primary_world <= params.world && params.world <= mi->def.method->deleted_world) {
303 : : // find and prepare the source code to compile
304 : 38025 : jl_code_instance_t *codeinst = NULL;
305 : 38025 : jl_ci_cache_lookup(*cgparams, mi, params.world, &codeinst, &src);
306 [ + - + + : 38025 : if (src && !emitted.count(codeinst)) {
+ + ]
307 : : // now add it to our compilation results
308 : : JL_GC_PROMISE_ROOTED(codeinst->rettype);
309 : : orc::ThreadSafeModule result_m = jl_create_llvm_module(name_from_method_instance(codeinst->def),
310 : 27198 : params.tsctx, params.imaging,
311 : : clone.getModuleUnlocked()->getDataLayout(),
312 : 54396 : Triple(clone.getModuleUnlocked()->getTargetTriple()));
313 : 54396 : jl_llvm_functions_t decls = jl_emit_code(result_m, mi, src, codeinst->rettype, params);
314 [ + - ]: 27198 : if (result_m)
315 : 27198 : emitted[codeinst] = {std::move(result_m), std::move(decls)};
316 : : }
317 : : }
318 : : }
319 : :
320 : : // finally, make sure all referenced methods also get compiled or fixed up
321 : 6 : jl_compile_workqueue(emitted, *clone.getModuleUnlocked(), params, policy);
322 : : }
323 : 3 : JL_GC_POP();
324 : :
325 : : // process the globals array, before jl_merge_module destroys them
326 : 3 : std::vector<std::string> gvars;
327 : :
328 [ + + ]: 25271 : for (auto &global : params.globals) {
329 : 25268 : gvars.push_back(std::string(global.second->getName()));
330 : 25268 : data->jl_value_to_llvm[global.first] = gvars.size();
331 : : }
332 : :
333 : : // clones the contents of the module `m` to the shadow_output collector
334 : : // while examining and recording what kind of function pointer we have
335 [ + + ]: 27336 : for (auto &def : emitted) {
336 : 27333 : jl_merge_module(clone, std::move(std::get<0>(def.second)));
337 : 27333 : jl_code_instance_t *this_code = def.first;
338 : 27333 : jl_llvm_functions_t decls = std::get<1>(def.second);
339 : 27333 : StringRef func = decls.functionObject;
340 : 27333 : StringRef cfunc = decls.specFunctionObject;
341 : 27333 : uint32_t func_id = 0;
342 : 27333 : uint32_t cfunc_id = 0;
343 [ + + ]: 27333 : if (func == "jl_fptr_args") {
344 : 2790 : func_id = -1;
345 : : }
346 [ - + ]: 24543 : else if (func == "jl_fptr_sparam") {
347 : 0 : func_id = -2;
348 : : }
349 : : else {
350 : : //Safe b/c context is locked by params
351 : 24543 : data->jl_sysimg_fvars.push_back(cast<Function>(clone.getModuleUnlocked()->getNamedValue(func)));
352 : 24543 : func_id = data->jl_sysimg_fvars.size();
353 : : }
354 [ + - ]: 27333 : if (!cfunc.empty()) {
355 : : //Safe b/c context is locked by params
356 : 27333 : data->jl_sysimg_fvars.push_back(cast<Function>(clone.getModuleUnlocked()->getNamedValue(cfunc)));
357 : 27333 : cfunc_id = data->jl_sysimg_fvars.size();
358 : : }
359 : 27333 : data->jl_fvar_map[this_code] = std::make_tuple(func_id, cfunc_id);
360 : : }
361 [ + - ]: 3 : if (params._shared_module) {
362 : 3 : jl_merge_module(clone, std::move(params._shared_module));
363 : : }
364 : :
365 : : // now get references to the globals in the merged module
366 : : // and set them to be internalized and initialized at startup
367 [ + + ]: 25271 : for (auto &global : gvars) {
368 : : //Safe b/c context is locked by params
369 : 25268 : GlobalVariable *G = cast<GlobalVariable>(clone.getModuleUnlocked()->getNamedValue(global));
370 : 25268 : G->setInitializer(ConstantPointerNull::get(cast<PointerType>(G->getValueType())));
371 : 25268 : G->setLinkage(GlobalVariable::InternalLinkage);
372 : 25268 : data->jl_sysimg_gvars.push_back(G);
373 : : }
374 : :
375 : : //Safe b/c context is locked by params
376 : : #if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_)
377 : : // setting the function personality enables stack unwinding and catching exceptions
378 : : // so make sure everything has something set
379 : : Type *T_int32 = Type::getInt32Ty(clone.getModuleUnlocked()->getContext());
380 : : Function *juliapersonality_func =
381 : : Function::Create(FunctionType::get(T_int32, true),
382 : : Function::ExternalLinkage, "__julia_personality", clone.getModuleUnlocked());
383 : : juliapersonality_func->setDLLStorageClass(GlobalValue::DLLImportStorageClass);
384 : : #endif
385 : :
386 : : // move everything inside, now that we've merged everything
387 : : // (before adding the exported headers)
388 [ + - ]: 3 : if (policy == CompilationPolicy::Default) {
389 : : //Safe b/c context is locked by params
390 [ + + ]: 82050 : for (GlobalObject &G : clone.getModuleUnlocked()->global_objects()) {
391 [ + + ]: 82047 : if (!G.isDeclaration()) {
392 : 81717 : G.setLinkage(Function::InternalLinkage);
393 : 81717 : makeSafeName(G);
394 : 81717 : addComdat(&G);
395 : : #if defined(_OS_WINDOWS_) && defined(_CPU_X86_64_)
396 : : // Add unwind exception personalities to functions to handle async exceptions
397 : : if (Function *F = dyn_cast<Function>(&G))
398 : : F->setPersonalityFn(juliapersonality_func);
399 : : #endif
400 : : }
401 : : }
402 : : }
403 : :
404 : 3 : data->M = std::move(clone);
405 [ - + ]: 3 : if (measure_compile_time_enabled)
406 : 0 : jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, (jl_hrtime() - compiler_start_time));
407 [ + - ]: 3 : if (ctx.getContext()) {
408 : 3 : jl_ExecutionEngine->releaseContext(std::move(ctx));
409 : : }
410 : 3 : JL_UNLOCK(&jl_codegen_lock); // Might GC
411 : 3 : return (void*)data;
412 : : }
413 : :
414 : :
415 : 2 : static void emit_result(std::vector<NewArchiveMember> &Archive, SmallVectorImpl<char> &OS,
416 : : StringRef Name, std::vector<std::string> &outputs)
417 : : {
418 : 2 : outputs.push_back({ OS.data(), OS.size() });
419 : 2 : Archive.push_back(NewArchiveMember(MemoryBufferRef(outputs.back(), Name)));
420 : 2 : OS.clear();
421 : 2 : }
422 : :
423 : 1 : static object::Archive::Kind getDefaultForHost(Triple &triple)
424 : : {
425 [ - + ]: 1 : if (triple.isOSDarwin())
426 : 0 : return object::Archive::K_DARWIN;
427 : 1 : return object::Archive::K_GNU;
428 : : }
429 : :
430 : : typedef Error ArchiveWriterError;
431 : 0 : static void reportWriterError(const ErrorInfoBase &E)
432 : : {
433 : 0 : std::string err = E.message();
434 : 0 : jl_safe_printf("ERROR: failed to emit output file %s\n", err.c_str());
435 : 0 : }
436 : :
437 : :
438 : : // takes the running content that has collected in the shadow module and dump it to disk
439 : : // this builds the object file portion of the sysimage files for fast startup
440 : : extern "C" JL_DLLEXPORT
441 : 1 : void jl_dump_native_impl(void *native_code,
442 : : const char *bc_fname, const char *unopt_bc_fname, const char *obj_fname,
443 : : const char *asm_fname,
444 : : const char *sysimg_data, size_t sysimg_len)
445 : : {
446 : : JL_TIMING(NATIVE_DUMP);
447 : 1 : jl_native_code_desc_t *data = (jl_native_code_desc_t*)native_code;
448 : 2 : auto TSCtx = data->M.getContext();
449 : 2 : auto lock = TSCtx.getLock();
450 : 1 : LLVMContext &Context = *TSCtx.getContext();
451 : : // We don't want to use MCJIT's target machine because
452 : : // it uses the large code model and we may potentially
453 : : // want less optimizations there.
454 : 2 : Triple TheTriple = Triple(jl_ExecutionEngine->getTargetTriple());
455 : : // make sure to emit the native object format, even if FORCE_ELF was set in codegen
456 : : #if defined(_OS_WINDOWS_)
457 : : TheTriple.setObjectFormat(Triple::COFF);
458 : : #elif defined(_OS_DARWIN_)
459 : : TheTriple.setObjectFormat(Triple::MachO);
460 : : TheTriple.setOS(llvm::Triple::MacOSX);
461 : : #endif
462 : : std::unique_ptr<TargetMachine> TM(
463 : 1 : jl_ExecutionEngine->getTarget().createTargetMachine(
464 : : TheTriple.getTriple(),
465 : : jl_ExecutionEngine->getTargetCPU(),
466 : : jl_ExecutionEngine->getTargetFeatureString(),
467 : : jl_ExecutionEngine->getTargetOptions(),
468 : : #if defined(_OS_LINUX_) || defined(_OS_FREEBSD_)
469 : 2 : Reloc::PIC_,
470 : : #else
471 : : Optional<Reloc::Model>(),
472 : : #endif
473 : : #if defined(_CPU_PPC_) || defined(_CPU_PPC64_)
474 : : // On PPC the small model is limited to 16bit offsets
475 : : CodeModel::Medium,
476 : : #else
477 : : // Use small model so that we can use signed 32bits offset in the function and GV tables
478 : 2 : CodeModel::Small,
479 : : #endif
480 : : CodeGenOpt::Aggressive // -O3 TODO: respect command -O0 flag?
481 : 2 : ));
482 : :
483 : :
484 : : // set up optimization passes
485 : 2 : SmallVector<char, 0> bc_Buffer;
486 : 2 : SmallVector<char, 0> obj_Buffer;
487 : 2 : SmallVector<char, 0> asm_Buffer;
488 : 2 : SmallVector<char, 0> unopt_bc_Buffer;
489 : 2 : raw_svector_ostream bc_OS(bc_Buffer);
490 : 2 : raw_svector_ostream obj_OS(obj_Buffer);
491 : 2 : raw_svector_ostream asm_OS(asm_Buffer);
492 : 2 : raw_svector_ostream unopt_bc_OS(unopt_bc_Buffer);
493 : 2 : std::vector<NewArchiveMember> bc_Archive;
494 : 2 : std::vector<NewArchiveMember> obj_Archive;
495 : 2 : std::vector<NewArchiveMember> asm_Archive;
496 : 2 : std::vector<NewArchiveMember> unopt_bc_Archive;
497 : 2 : std::vector<std::string> outputs;
498 : :
499 : 2 : legacy::PassManager preopt, postopt;
500 : :
501 [ - + ]: 1 : if (unopt_bc_fname)
502 : 0 : preopt.add(createBitcodeWriterPass(unopt_bc_OS));
503 : :
504 : : //Is this necessary for TM?
505 : : // addTargetPasses(&postopt, TM->getTargetTriple(), TM->getTargetIRAnalysis());
506 [ - + ]: 1 : if (bc_fname)
507 : 0 : postopt.add(createBitcodeWriterPass(bc_OS));
508 [ + - ]: 1 : if (obj_fname)
509 [ - + ]: 1 : if (TM->addPassesToEmitFile(postopt, obj_OS, nullptr, CGFT_ObjectFile, false))
510 : 0 : jl_safe_printf("ERROR: target does not support generation of object files\n");
511 [ - + ]: 1 : if (asm_fname)
512 [ # # ]: 0 : if (TM->addPassesToEmitFile(postopt, asm_OS, nullptr, CGFT_AssemblyFile, false))
513 : 0 : jl_safe_printf("ERROR: target does not support generation of object files\n");
514 : :
515 : 2 : legacy::PassManager optimizer;
516 [ + - - + : 1 : if (bc_fname || obj_fname || asm_fname) {
- - ]
517 : 1 : addTargetPasses(&optimizer, TM->getTargetTriple(), TM->getTargetIRAnalysis());
518 : 1 : addOptimizationPasses(&optimizer, jl_options.opt_level, true, true);
519 : 1 : addMachinePasses(&optimizer, jl_options.opt_level);
520 : : }
521 : :
522 : : // Reset the target triple to make sure it matches the new target machine
523 : 1 : auto dataM = data->M.getModuleUnlocked();
524 : 1 : dataM->setTargetTriple(TM->getTargetTriple().str());
525 : 1 : dataM->setDataLayout(jl_create_datalayout(*TM));
526 : : Type *T_size;
527 : : if (sizeof(size_t) == 8)
528 : 1 : T_size = Type::getInt64Ty(Context);
529 : : else
530 : : T_size = Type::getInt32Ty(Context);
531 : 1 : Type *T_psize = T_size->getPointerTo();
532 : :
533 : : // add metadata information
534 [ + - ]: 1 : if (imaging_default()) {
535 : 1 : emit_offset_table(*dataM, data->jl_sysimg_gvars, "jl_sysimg_gvars", T_psize);
536 : 1 : emit_offset_table(*dataM, data->jl_sysimg_fvars, "jl_sysimg_fvars", T_psize);
537 : :
538 : : // reflect the address of the jl_RTLD_DEFAULT_handle variable
539 : : // back to the caller, so that we can check for consistency issues
540 : 1 : GlobalValue *jlRTLD_DEFAULT_var = jl_emit_RTLD_DEFAULT_var(dataM);
541 : 2 : addComdat(new GlobalVariable(*dataM,
542 : 1 : jlRTLD_DEFAULT_var->getType(),
543 : : true,
544 : : GlobalVariable::ExternalLinkage,
545 : : jlRTLD_DEFAULT_var,
546 : 1 : "jl_RTLD_DEFAULT_handle_pointer"));
547 : : }
548 : :
549 : : // do the actual work
550 : 2 : auto add_output = [&] (Module &M, StringRef unopt_bc_Name, StringRef bc_Name, StringRef obj_Name, StringRef asm_Name) {
551 : 2 : preopt.run(M);
552 : 2 : optimizer.run(M);
553 : 2 : postopt.run(M);
554 [ - + ]: 2 : if (unopt_bc_fname)
555 : 0 : emit_result(unopt_bc_Archive, unopt_bc_Buffer, unopt_bc_Name, outputs);
556 [ - + ]: 2 : if (bc_fname)
557 : 0 : emit_result(bc_Archive, bc_Buffer, bc_Name, outputs);
558 [ + - ]: 2 : if (obj_fname)
559 : 2 : emit_result(obj_Archive, obj_Buffer, obj_Name, outputs);
560 [ - + ]: 2 : if (asm_fname)
561 : 0 : emit_result(asm_Archive, asm_Buffer, asm_Name, outputs);
562 : 3 : };
563 : :
564 : 1 : add_output(*dataM, "unopt.bc", "text.bc", "text.o", "text.s");
565 : :
566 : 3 : orc::ThreadSafeModule sysimage(std::make_unique<Module>("sysimage", Context), TSCtx);
567 : 1 : auto sysimageM = sysimage.getModuleUnlocked();
568 : 1 : sysimageM->setTargetTriple(dataM->getTargetTriple());
569 : 1 : sysimageM->setDataLayout(dataM->getDataLayout());
570 : : #if JL_LLVM_VERSION >= 130000
571 : 1 : sysimageM->setStackProtectorGuard(dataM->getStackProtectorGuard());
572 : 1 : sysimageM->setOverrideStackAlignment(dataM->getOverrideStackAlignment());
573 : : #endif
574 : 1 : data->M = orc::ThreadSafeModule(); // free memory for data->M
575 : :
576 [ + - ]: 1 : if (sysimg_data) {
577 : 1 : Constant *data = ConstantDataArray::get(Context,
578 : : ArrayRef<uint8_t>((const unsigned char*)sysimg_data, sysimg_len));
579 : 2 : addComdat(new GlobalVariable(*sysimageM, data->getType(), false,
580 : : GlobalVariable::ExternalLinkage,
581 : 2 : data, "jl_system_image_data"))->setAlignment(Align(64));
582 : 1 : Constant *len = ConstantInt::get(T_size, sysimg_len);
583 : 2 : addComdat(new GlobalVariable(*sysimageM, len->getType(), true,
584 : : GlobalVariable::ExternalLinkage,
585 : 1 : len, "jl_system_image_size"));
586 : : }
587 : 1 : add_output(*sysimageM, "data.bc", "data.bc", "data.o", "data.s");
588 : :
589 : 1 : object::Archive::Kind Kind = getDefaultForHost(TheTriple);
590 [ - + ]: 1 : if (unopt_bc_fname)
591 : 0 : handleAllErrors(writeArchive(unopt_bc_fname, unopt_bc_Archive, true,
592 : : Kind, true, false), reportWriterError);
593 [ - + ]: 1 : if (bc_fname)
594 : 0 : handleAllErrors(writeArchive(bc_fname, bc_Archive, true,
595 : : Kind, true, false), reportWriterError);
596 [ + - ]: 1 : if (obj_fname)
597 : 1 : handleAllErrors(writeArchive(obj_fname, obj_Archive, true,
598 : : Kind, true, false), reportWriterError);
599 [ - + ]: 1 : if (asm_fname)
600 : 0 : handleAllErrors(writeArchive(asm_fname, asm_Archive, true,
601 : : Kind, true, false), reportWriterError);
602 : :
603 [ + - ]: 1 : delete data;
604 : 1 : }
605 : :
606 : 1076 : void addTargetPasses(legacy::PassManagerBase *PM, const Triple &triple, TargetIRAnalysis analysis)
607 : : {
608 : 1076 : PM->add(new TargetLibraryInfoWrapperPass(triple));
609 : 1076 : PM->add(createTargetTransformInfoWrapperPass(std::move(analysis)));
610 : 1076 : }
611 : :
612 : :
613 : 1052 : void addMachinePasses(legacy::PassManagerBase *PM, int optlevel)
614 : : {
615 : : // TODO: don't do this on CPUs that natively support Float16
616 : 1052 : PM->add(createDemoteFloat16Pass());
617 [ + + ]: 1052 : if (optlevel > 1)
618 : 574 : PM->add(createGVNPass());
619 : 1052 : }
620 : :
621 : : // this defines the set of optimization passes defined for Julia at various optimization levels.
622 : : // it assumes that the TLI and TTI wrapper passes have already been added.
623 : 1052 : void addOptimizationPasses(legacy::PassManagerBase *PM, int opt_level,
624 : : bool lower_intrinsics, bool dump_native,
625 : : bool external_use)
626 : : {
627 : : // Note: LLVM 12 disabled the hoisting of common instruction
628 : : // before loop vectorization (https://reviews.llvm.org/D84108).
629 : : //
630 : : // TODO: CommonInstruction hoisting/sinking enables AllocOpt
631 : : // to merge allocations and sometimes eliminate them,
632 : : // since AllocOpt does not handle PhiNodes.
633 : : // Enable this instruction hoisting because of this and Union benchmarks.
634 : 1052 : auto basicSimplifyCFGOptions = SimplifyCFGOptions()
635 : 1052 : .convertSwitchRangeToICmp(true)
636 : 1052 : .convertSwitchToLookupTable(true)
637 : 1052 : .forwardSwitchCondToPhi(true);
638 : 1052 : auto aggressiveSimplifyCFGOptions = SimplifyCFGOptions()
639 : 1052 : .convertSwitchRangeToICmp(true)
640 : 1052 : .convertSwitchToLookupTable(true)
641 : 1052 : .forwardSwitchCondToPhi(true)
642 : : //These mess with loop rotation, so only do them after that
643 : 1052 : .hoistCommonInsts(true)
644 : : // Causes an SRET assertion error in late-gc-lowering
645 : : // .sinkCommonInsts(true)
646 : : ;
647 : : #ifdef JL_DEBUG_BUILD
648 : 1052 : PM->add(createGCInvariantVerifierPass(true));
649 : 1052 : PM->add(createVerifierPass());
650 : : #endif
651 : :
652 : 1052 : PM->add(createConstantMergePass());
653 [ + + ]: 1052 : if (opt_level < 2) {
654 [ + + ]: 478 : if (!dump_native) {
655 : : // we won't be multiversioning, so lower CPU feature checks early on
656 : : // so that we can avoid an additional CFG simplification pass at the end.
657 : 477 : PM->add(createCPUFeaturesPass());
658 [ + + ]: 477 : if (opt_level == 1)
659 : 418 : PM->add(createInstSimplifyLegacyPass());
660 : : }
661 : 478 : PM->add(createCFGSimplificationPass(basicSimplifyCFGOptions));
662 [ + + ]: 478 : if (opt_level == 1) {
663 : 418 : PM->add(createSROAPass());
664 : 418 : PM->add(createInstructionCombiningPass());
665 : 418 : PM->add(createEarlyCSEPass());
666 : : // maybe add GVN?
667 : : // also try GVNHoist and GVNSink
668 : : }
669 : 478 : PM->add(createMemCpyOptPass());
670 : 478 : PM->add(createAlwaysInlinerLegacyPass()); // Respect always_inline
671 : 478 : PM->add(createLowerSimdLoopPass()); // Annotate loop marked with "loopinfo" as LLVM parallel loop
672 [ + - ]: 478 : if (lower_intrinsics) {
673 : 478 : PM->add(createBarrierNoopPass());
674 : 478 : PM->add(createLowerExcHandlersPass());
675 : 478 : PM->add(createGCInvariantVerifierPass(false));
676 : 478 : PM->add(createRemoveNIPass());
677 : 478 : PM->add(createLateLowerGCFramePass());
678 : 478 : PM->add(createFinalLowerGCPass());
679 : 478 : PM->add(createLowerPTLSPass(dump_native));
680 : : }
681 : : else {
682 : 0 : PM->add(createRemoveNIPass());
683 : : }
684 : 478 : PM->add(createLowerSimdLoopPass()); // Annotate loop marked with "loopinfo" as LLVM parallel loop
685 [ + + ]: 478 : if (dump_native) {
686 : 1 : PM->add(createMultiVersioningPass(external_use));
687 : 1 : PM->add(createCPUFeaturesPass());
688 : : // minimal clean-up to get rid of CPU feature checks
689 [ - + ]: 1 : if (opt_level == 1) {
690 : 0 : PM->add(createInstSimplifyLegacyPass());
691 : 0 : PM->add(createCFGSimplificationPass(basicSimplifyCFGOptions));
692 : : }
693 : : }
694 : : #if defined(_COMPILER_ASAN_ENABLED_)
695 : : PM->add(createAddressSanitizerFunctionPass());
696 : : #endif
697 : : #if defined(_COMPILER_MSAN_ENABLED_)
698 : : PM->add(createMemorySanitizerPass(true));
699 : : #endif
700 : : #if defined(_COMPILER_TSAN_ENABLED_)
701 : : PM->add(createThreadSanitizerLegacyPassPass());
702 : : #endif
703 : 478 : return;
704 : : }
705 : 574 : PM->add(createPropagateJuliaAddrspaces());
706 : 574 : PM->add(createScopedNoAliasAAWrapperPass());
707 : 574 : PM->add(createTypeBasedAAWrapperPass());
708 [ + + ]: 574 : if (opt_level >= 3) {
709 : 2 : PM->add(createBasicAAWrapperPass());
710 : : }
711 : :
712 : 574 : PM->add(createCFGSimplificationPass(basicSimplifyCFGOptions));
713 : 574 : PM->add(createDeadCodeEliminationPass());
714 : 574 : PM->add(createSROAPass());
715 : :
716 : : //PM->add(createMemCpyOptPass());
717 : :
718 : 574 : PM->add(createAlwaysInlinerLegacyPass()); // Respect always_inline
719 : :
720 : : // Running `memcpyopt` between this and `sroa` seems to give `sroa` a hard time
721 : : // merging the `alloca` for the unboxed data and the `alloca` created by the `alloc_opt`
722 : : // pass.
723 : 574 : PM->add(createAllocOptPass());
724 : : // consider AggressiveInstCombinePass at optlevel > 2
725 : 574 : PM->add(createInstructionCombiningPass());
726 : 574 : PM->add(createCFGSimplificationPass(basicSimplifyCFGOptions));
727 [ - + ]: 574 : if (dump_native)
728 : 0 : PM->add(createMultiVersioningPass(external_use));
729 : 574 : PM->add(createCPUFeaturesPass());
730 : 574 : PM->add(createSROAPass());
731 : 574 : PM->add(createInstSimplifyLegacyPass());
732 : 574 : PM->add(createJumpThreadingPass());
733 : 574 : PM->add(createCorrelatedValuePropagationPass());
734 : :
735 : 574 : PM->add(createReassociatePass());
736 : :
737 : 574 : PM->add(createEarlyCSEPass());
738 : :
739 : : // Load forwarding above can expose allocations that aren't actually used
740 : : // remove those before optimizing loops.
741 : 574 : PM->add(createAllocOptPass());
742 : 574 : PM->add(createLoopRotatePass());
743 : : // moving IndVarSimplify here prevented removing the loop in perf_sumcartesian(10:-1:1)
744 : : #ifdef USE_POLLY
745 : : // LCSSA (which has already run at this point due to the dependencies of the
746 : : // above passes) introduces redundant phis that hinder Polly. Therefore we
747 : : // run InstCombine here to remove them.
748 : : PM->add(createInstructionCombiningPass());
749 : : PM->add(polly::createCodePreparationPass());
750 : : polly::registerPollyPasses(*PM);
751 : : PM->add(polly::createCodegenCleanupPass());
752 : : #endif
753 : : // LoopRotate strips metadata from terminator, so run LowerSIMD afterwards
754 : 574 : PM->add(createLowerSimdLoopPass()); // Annotate loop marked with "loopinfo" as LLVM parallel loop
755 : 574 : PM->add(createLICMPass());
756 : 574 : PM->add(createJuliaLICMPass());
757 : 574 : PM->add(createLoopUnswitchPass());
758 : 574 : PM->add(createLICMPass());
759 : 574 : PM->add(createJuliaLICMPass());
760 : 574 : PM->add(createInductiveRangeCheckEliminationPass()); // Must come before indvars
761 : : // Subsequent passes not stripping metadata from terminator
762 : 574 : PM->add(createInstSimplifyLegacyPass());
763 : 574 : PM->add(createLoopIdiomPass());
764 : 574 : PM->add(createIndVarSimplifyPass());
765 : 574 : PM->add(createLoopDeletionPass());
766 : 574 : PM->add(createSimpleLoopUnrollPass());
767 : :
768 : : // Run our own SROA on heap objects before LLVM's
769 : 574 : PM->add(createAllocOptPass());
770 : : // Re-run SROA after loop-unrolling (useful for small loops that operate,
771 : : // over the structure of an aggregate)
772 : 574 : PM->add(createSROAPass());
773 : : // might not be necessary:
774 : 574 : PM->add(createInstSimplifyLegacyPass());
775 : :
776 : 574 : PM->add(createGVNPass());
777 : 574 : PM->add(createMemCpyOptPass());
778 : 574 : PM->add(createSCCPPass());
779 : :
780 : : //These next two passes must come before IRCE to eliminate the bounds check in #43308
781 : 574 : PM->add(createCorrelatedValuePropagationPass());
782 : 574 : PM->add(createDeadCodeEliminationPass());
783 : :
784 : 574 : PM->add(createInductiveRangeCheckEliminationPass()); // Must come between the two GVN passes
785 : :
786 : : // Run instcombine after redundancy elimination to exploit opportunities
787 : : // opened up by them.
788 : : // This needs to be InstCombine instead of InstSimplify to allow
789 : : // loops over Union-typed arrays to vectorize.
790 : 574 : PM->add(createInstructionCombiningPass());
791 : 574 : PM->add(createJumpThreadingPass());
792 [ + + ]: 574 : if (opt_level >= 3) {
793 : 2 : PM->add(createGVNPass()); // Must come after JumpThreading and before LoopVectorize
794 : : }
795 : 574 : PM->add(createDeadStoreEliminationPass());
796 : : // see if all of the constant folding has exposed more loops
797 : : // to simplification and deletion
798 : : // this helps significantly with cleaning up iteration
799 : 574 : PM->add(createCFGSimplificationPass(aggressiveSimplifyCFGOptions));
800 : :
801 : : // More dead allocation (store) deletion before loop optimization
802 : : // consider removing this:
803 : : // Moving this after aggressive CFG simplification helps deallocate when allocations are hoisted
804 : 574 : PM->add(createAllocOptPass());
805 : 574 : PM->add(createLoopDeletionPass());
806 : 574 : PM->add(createInstructionCombiningPass());
807 : 574 : PM->add(createLoopVectorizePass());
808 : 574 : PM->add(createLoopLoadEliminationPass());
809 : : // Cleanup after LV pass
810 : 574 : PM->add(createInstructionCombiningPass());
811 : 574 : PM->add(createCFGSimplificationPass( // Aggressive CFG simplification
812 : : aggressiveSimplifyCFGOptions
813 : 574 : ));
814 : 574 : PM->add(createSLPVectorizerPass());
815 : : // might need this after LLVM 11:
816 : : //PM->add(createVectorCombinePass());
817 : :
818 : 574 : PM->add(createAggressiveDCEPass());
819 : :
820 [ + - ]: 574 : if (lower_intrinsics) {
821 : : // LowerPTLS removes an indirect call. As a result, it is likely to trigger
822 : : // LLVM's devirtualization heuristics, which would result in the entire
823 : : // pass pipeline being re-executed. Prevent this by inserting a barrier.
824 : 574 : PM->add(createBarrierNoopPass());
825 : 574 : PM->add(createLowerExcHandlersPass());
826 : 574 : PM->add(createGCInvariantVerifierPass(false));
827 : : // Needed **before** LateLowerGCFrame on LLVM < 12
828 : : // due to bug in `CreateAlignmentAssumption`.
829 : 574 : PM->add(createRemoveNIPass());
830 : 574 : PM->add(createLateLowerGCFramePass());
831 : 574 : PM->add(createFinalLowerGCPass());
832 : : // We need these two passes and the instcombine below
833 : : // after GC lowering to let LLVM do some constant propagation on the tags.
834 : : // and remove some unnecessary write barrier checks.
835 : 574 : PM->add(createGVNPass());
836 : 574 : PM->add(createSCCPPass());
837 : : // Remove dead use of ptls
838 : 574 : PM->add(createDeadCodeEliminationPass());
839 : 574 : PM->add(createLowerPTLSPass(dump_native));
840 : 574 : PM->add(createInstructionCombiningPass());
841 : : // Clean up write barrier and ptls lowering
842 : 574 : PM->add(createCFGSimplificationPass());
843 : : }
844 : : else {
845 : 0 : PM->add(createRemoveNIPass());
846 : : }
847 : 574 : PM->add(createCombineMulAddPass());
848 : 574 : PM->add(createDivRemPairsPass());
849 : : #if defined(_COMPILER_ASAN_ENABLED_)
850 : : PM->add(createAddressSanitizerFunctionPass());
851 : : #endif
852 : : #if defined(_COMPILER_MSAN_ENABLED_)
853 : : PM->add(createMemorySanitizerPass(true));
854 : : #endif
855 : : #if defined(_COMPILER_TSAN_ENABLED_)
856 : : PM->add(createThreadSanitizerLegacyPassPass());
857 : : #endif
858 : : }
859 : :
860 : : // An LLVM module pass that just runs all julia passes in order. Useful for
861 : : // debugging
862 : : template <int OptLevel, bool dump_native>
863 : : class JuliaPipeline : public Pass {
864 : : public:
865 : : static char ID;
866 : : // A bit of a hack, but works
867 : : struct TPMAdapter : public PassManagerBase {
868 : : PMTopLevelManager *TPM;
869 : 0 : TPMAdapter(PMTopLevelManager *TPM) : TPM(TPM) {}
870 : 0 : void add(Pass *P) { TPM->schedulePass(P); }
871 : : };
872 : 0 : void preparePassManager(PMStack &Stack) override {
873 : 0 : (void)jl_init_llvm();
874 : 0 : PMTopLevelManager *TPM = Stack.top()->getTopLevelManager();
875 : 0 : TPMAdapter Adapter(TPM);
876 : 0 : addTargetPasses(&Adapter, jl_ExecutionEngine->getTargetTriple(), jl_ExecutionEngine->getTargetIRAnalysis());
877 : 0 : addOptimizationPasses(&Adapter, OptLevel, true, dump_native, true);
878 : 0 : addMachinePasses(&Adapter, OptLevel);
879 : 0 : }
880 : 0 : JuliaPipeline() : Pass(PT_PassManager, ID) {}
881 : 0 : Pass *createPrinterPass(raw_ostream &O, const std::string &Banner) const override {
882 : 0 : return createPrintModulePass(O, Banner);
883 : : }
884 : : };
885 : : template<> char JuliaPipeline<0,false>::ID = 0;
886 : : template<> char JuliaPipeline<2,false>::ID = 0;
887 : : template<> char JuliaPipeline<3,false>::ID = 0;
888 : : template<> char JuliaPipeline<0,true>::ID = 0;
889 : : template<> char JuliaPipeline<2,true>::ID = 0;
890 : : template<> char JuliaPipeline<3,true>::ID = 0;
891 : : static RegisterPass<JuliaPipeline<0,false>> X("juliaO0", "Runs the entire julia pipeline (at -O0)", false, false);
892 : : static RegisterPass<JuliaPipeline<2,false>> Y("julia", "Runs the entire julia pipeline (at -O2)", false, false);
893 : : static RegisterPass<JuliaPipeline<3,false>> Z("juliaO3", "Runs the entire julia pipeline (at -O3)", false, false);
894 : :
895 : : static RegisterPass<JuliaPipeline<0,true>> XS("juliaO0-sysimg", "Runs the entire julia pipeline (at -O0/sysimg mode)", false, false);
896 : : static RegisterPass<JuliaPipeline<2,true>> YS("julia-sysimg", "Runs the entire julia pipeline (at -O2/sysimg mode)", false, false);
897 : : static RegisterPass<JuliaPipeline<3,true>> ZS("juliaO3-sysimg", "Runs the entire julia pipeline (at -O3/sysimg mode)", false, false);
898 : :
899 : : extern "C" JL_DLLEXPORT
900 : 0 : void jl_add_optimization_passes_impl(LLVMPassManagerRef PM, int opt_level, int lower_intrinsics) {
901 : 0 : addOptimizationPasses(unwrap(PM), opt_level, lower_intrinsics);
902 : 0 : }
903 : :
904 : : // new pass manager plugin
905 : :
906 : : // NOTE: Instead of exporting all the constructors in passes.h we could
907 : : // forward the callbacks to the respective passes. LLVM seems to prefer this,
908 : : // and when we add the full pass builder having them directly will be helpful.
909 : 0 : static void registerCallbacks(PassBuilder &PB) {
910 : 0 : PB.registerPipelineParsingCallback(
911 : 0 : [](StringRef Name, FunctionPassManager &PM,
912 : : ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {
913 [ # # ]: 0 : if (Name == "DemoteFloat16") {
914 : 0 : PM.addPass(DemoteFloat16());
915 : 0 : return true;
916 : : }
917 [ # # ]: 0 : if (Name == "CombineMulAdd") {
918 : 0 : PM.addPass(CombineMulAdd());
919 : 0 : return true;
920 : : }
921 [ # # ]: 0 : if (Name == "LateLowerGCFrame") {
922 : 0 : PM.addPass(LateLowerGC());
923 : 0 : return true;
924 : : }
925 [ # # ]: 0 : if (Name == "AllocOpt") {
926 : 0 : PM.addPass(AllocOptPass());
927 : 0 : return true;
928 : : }
929 [ # # ]: 0 : if (Name == "PropagateJuliaAddrspaces") {
930 : 0 : PM.addPass(PropagateJuliaAddrspacesPass());
931 : 0 : return true;
932 : : }
933 [ # # ]: 0 : if (Name == "LowerExcHandlers") {
934 : 0 : PM.addPass(LowerExcHandlers());
935 : 0 : return true;
936 : : }
937 [ # # ]: 0 : if (Name == "GCInvariantVerifier") {
938 : : // TODO: Parse option and allow users to set `Strong`
939 : 0 : PM.addPass(GCInvariantVerifierPass());
940 : 0 : return true;
941 : : }
942 : 0 : return false;
943 : : });
944 : :
945 : 0 : PB.registerPipelineParsingCallback(
946 : 0 : [](StringRef Name, ModulePassManager &PM,
947 : : ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {
948 [ # # ]: 0 : if (Name == "CPUFeatures") {
949 : 0 : PM.addPass(CPUFeatures());
950 : 0 : return true;
951 : : }
952 [ # # ]: 0 : if (Name == "RemoveNI") {
953 : 0 : PM.addPass(RemoveNI());
954 : 0 : return true;
955 : : }
956 [ # # ]: 0 : if (Name == "LowerSIMDLoop") {
957 : 0 : PM.addPass(LowerSIMDLoop());
958 : 0 : return true;
959 : : }
960 [ # # ]: 0 : if (Name == "FinalLowerGC") {
961 : 0 : PM.addPass(FinalLowerGCPass());
962 : 0 : return true;
963 : : }
964 [ # # ]: 0 : if (Name == "RemoveJuliaAddrspaces") {
965 : 0 : PM.addPass(RemoveJuliaAddrspacesPass());
966 : 0 : return true;
967 : : }
968 [ # # ]: 0 : if (Name == "MultiVersioning") {
969 : 0 : PM.addPass(MultiVersioning());
970 : 0 : return true;
971 : : }
972 [ # # ]: 0 : if (Name == "LowerPTLS") {
973 : 0 : PM.addPass(LowerPTLSPass());
974 : 0 : return true;
975 : : }
976 : 0 : return false;
977 : : });
978 : :
979 : 0 : PB.registerPipelineParsingCallback(
980 : 0 : [](StringRef Name, LoopPassManager &PM,
981 : : ArrayRef<PassBuilder::PipelineElement> InnerPipeline) {
982 [ # # ]: 0 : if (Name == "JuliaLICM") {
983 : 0 : PM.addPass(JuliaLICMPass());
984 : 0 : return true;
985 : : }
986 : 0 : return false;
987 : : });
988 : 0 : }
989 : :
990 : : extern "C" JL_DLLEXPORT ::llvm::PassPluginLibraryInfo
991 : 0 : llvmGetPassPluginInfo() {
992 : 0 : return {LLVM_PLUGIN_API_VERSION, "Julia", "1", registerCallbacks};
993 : : }
994 : :
995 : : // --- native code info, and dump function to IR and ASM ---
996 : : // Get pointer to llvm::Function instance, compiling if necessary
997 : : // for use in reflection from Julia.
998 : : // this is paired with jl_dump_function_ir, jl_dump_function_asm, jl_dump_method_asm in particular ways:
999 : : // misuse will leak memory or cause read-after-free
1000 : : extern "C" JL_DLLEXPORT
1001 : 106 : void jl_get_llvmf_defn_impl(jl_llvmf_dump_t* dump, jl_method_instance_t *mi, size_t world, char getwrapper, char optimize, const jl_cgparams_t params)
1002 : : {
1003 [ + - + + ]: 106 : if (jl_is_method(mi->def.method) && mi->def.method->source == NULL &&
1004 [ - + ]: 2 : mi->def.method->generator == NULL) {
1005 : : // not a generic function
1006 : 0 : dump->F = NULL;
1007 : 0 : return;
1008 : : }
1009 : :
1010 : : // get the source code for this function
1011 : 106 : jl_value_t *jlrettype = (jl_value_t*)jl_any_type;
1012 : 106 : jl_code_info_t *src = NULL;
1013 : 106 : JL_GC_PUSH2(&src, &jlrettype);
1014 [ + - + + : 106 : if (jl_is_method(mi->def.method) && mi->def.method->source != NULL && jl_ir_flag_inferred((jl_array_t*)mi->def.method->source)) {
+ + + + ]
1015 : 2 : src = (jl_code_info_t*)mi->def.method->source;
1016 [ + - + - ]: 2 : if (src && !jl_is_code_info(src))
1017 : 2 : src = jl_uncompress_ir(mi->def.method, NULL, (jl_array_t*)src);
1018 : : } else {
1019 : 104 : jl_value_t *ci = jl_rettype_inferred(mi, world, world);
1020 [ + + ]: 104 : if (ci != jl_nothing) {
1021 : 53 : jl_code_instance_t *codeinst = (jl_code_instance_t*)ci;
1022 : 53 : src = (jl_code_info_t*)codeinst->inferred;
1023 [ + + + - : 53 : if ((jl_value_t*)src != jl_nothing && !jl_is_code_info(src) && jl_is_method(mi->def.method))
+ - ]
1024 : 33 : src = jl_uncompress_ir(mi->def.method, codeinst, (jl_array_t*)src);
1025 : 53 : jlrettype = codeinst->rettype;
1026 : : }
1027 [ + + + + ]: 104 : if (!src || (jl_value_t*)src == jl_nothing) {
1028 : 71 : src = jl_type_infer(mi, world, 0);
1029 [ + + ]: 71 : if (src)
1030 : 70 : jlrettype = src->rettype;
1031 [ + - ]: 1 : else if (jl_is_method(mi->def.method)) {
1032 [ - + ]: 1 : src = mi->def.method->generator ? jl_code_for_staged(mi) : (jl_code_info_t*)mi->def.method->source;
1033 [ + - + - : 1 : if (src && !jl_is_code_info(src) && jl_is_method(mi->def.method))
+ - ]
1034 : 1 : src = jl_uncompress_ir(mi->def.method, NULL, (jl_array_t*)src);
1035 : : }
1036 : : // TODO: use mi->uninferred
1037 : : }
1038 : : }
1039 : :
1040 : : // emit this function into a new llvm module
1041 [ + - + - ]: 106 : if (src && jl_is_code_info(src)) {
1042 : 106 : JL_LOCK(&jl_codegen_lock);
1043 : 106 : auto ctx = jl_ExecutionEngine->getContext();
1044 : 106 : jl_codegen_params_t output(*ctx);
1045 : 106 : output.world = world;
1046 : 106 : output.params = ¶ms;
1047 : 106 : orc::ThreadSafeModule m = jl_create_llvm_module(name_from_method_instance(mi), output.tsctx, output.imaging);
1048 : 106 : uint64_t compiler_start_time = 0;
1049 : 106 : uint8_t measure_compile_time_enabled = jl_atomic_load_relaxed(&jl_measure_compile_time_enabled);
1050 [ - + ]: 106 : if (measure_compile_time_enabled)
1051 : 0 : compiler_start_time = jl_hrtime();
1052 : 106 : auto decls = jl_emit_code(m, mi, src, jlrettype, output);
1053 : :
1054 : 106 : Function *F = NULL;
1055 [ + - ]: 106 : if (m) {
1056 : : // if compilation succeeded, prepare to return the result
1057 : : // For imaging mode, global constants are currently private without initializer
1058 : : // which isn't legal. Convert them to extern linkage so that the code can compile
1059 : : // and will better match what's actually in sysimg.
1060 [ - + ]: 106 : for (auto &global : output.globals)
1061 : 0 : global.second->setLinkage(GlobalValue::ExternalLinkage);
1062 [ + + ]: 106 : if (optimize) {
1063 : 198 : legacy::PassManager PM;
1064 : 99 : addTargetPasses(&PM, jl_ExecutionEngine->getTargetTriple(), jl_ExecutionEngine->getTargetIRAnalysis());
1065 : 99 : addOptimizationPasses(&PM, jl_options.opt_level);
1066 : 99 : addMachinePasses(&PM, jl_options.opt_level);
1067 : : //Safe b/c context lock is held by output
1068 : 99 : PM.run(*m.getModuleUnlocked());
1069 : : }
1070 : : const std::string *fname;
1071 [ + + + + : 106 : if (decls.functionObject == "jl_fptr_args" || decls.functionObject == "jl_fptr_sparam")
+ + ]
1072 : 8 : getwrapper = false;
1073 [ + - ]: 106 : if (!getwrapper)
1074 : 106 : fname = &decls.specFunctionObject;
1075 : : else
1076 : 0 : fname = &decls.functionObject;
1077 : 106 : F = cast<Function>(m.getModuleUnlocked()->getNamedValue(*fname));
1078 : : }
1079 : 106 : JL_GC_POP();
1080 [ - + ]: 106 : if (measure_compile_time_enabled)
1081 : 0 : jl_atomic_fetch_add_relaxed(&jl_cumulative_compile_time, (jl_hrtime() - compiler_start_time));
1082 : 106 : JL_UNLOCK(&jl_codegen_lock); // Might GC
1083 [ + - ]: 106 : if (F) {
1084 : 106 : dump->TSM = wrap(new orc::ThreadSafeModule(std::move(m)));
1085 : 106 : dump->F = wrap(F);
1086 : 106 : return;
1087 : : }
1088 : : }
1089 : :
1090 : 0 : const char *mname = name_from_method_instance(mi);
1091 : 0 : jl_errorf("unable to compile source for function %s", mname);
1092 : : }
|