Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license
2 : :
3 : : // Function multi-versioning
4 : : // LLVM pass to clone function for different archs
5 : :
6 : : #include "llvm-version.h"
7 : : #include "passes.h"
8 : :
9 : : #include <llvm-c/Core.h>
10 : : #include <llvm-c/Types.h>
11 : :
12 : : #include <llvm/Pass.h>
13 : : #include <llvm/ADT/Statistic.h>
14 : : #include <llvm/IR/Module.h>
15 : : #include <llvm/IR/LegacyPassManager.h>
16 : : #include <llvm/IR/Function.h>
17 : : #include <llvm/IR/Instructions.h>
18 : : #include <llvm/IR/Constants.h>
19 : : #include <llvm/IR/LLVMContext.h>
20 : : #include <llvm/Analysis/LoopInfo.h>
21 : : #include <llvm/Analysis/CallGraph.h>
22 : : #include <llvm/IR/LegacyPassManager.h>
23 : : #include <llvm/IR/IRBuilder.h>
24 : : #include <llvm/IR/DebugInfoMetadata.h>
25 : : #include <llvm/IR/Verifier.h>
26 : : #include <llvm/Transforms/Utils/Cloning.h>
27 : :
28 : : #include "julia.h"
29 : : #include "julia_internal.h"
30 : : #include "processor.h"
31 : : #include "support/dtypes.h"
32 : :
33 : : #include <map>
34 : : #include <memory>
35 : : #include <set>
36 : : #include <vector>
37 : :
38 : : #include "codegen_shared.h"
39 : : #include "julia_assert.h"
40 : :
41 : : #define DEBUG_TYPE "julia_multiversioning"
42 : : #undef DEBUG
43 : :
44 : : using namespace llvm;
45 : :
46 : : extern Optional<bool> always_have_fma(Function&);
47 : :
48 : : namespace {
49 : : constexpr uint32_t clone_mask =
50 : : JL_TARGET_CLONE_LOOP | JL_TARGET_CLONE_SIMD | JL_TARGET_CLONE_MATH | JL_TARGET_CLONE_CPU;
51 : :
52 : : // Treat identical mapping as missing and return `def` in that case.
53 : : // We mainly need this to identify cloned function using value map after LLVM cloning
54 : : // functions fills the map with identity entries.
55 : : template<typename T>
56 : 0 : Value *map_get(T &&vmap, Value *key, Value *def=nullptr)
57 : : {
58 : 0 : auto val = vmap.lookup(key);
59 [ # # # # : 0 : if (!val || key == val)
# # ]
60 : 0 : return def;
61 : 0 : return val;
62 : : }
63 : :
64 : : // Iterate through uses of a particular type.
65 : : // Recursively scan through `ConstantExpr` and `ConstantAggregate` use.
66 : : template<typename U>
67 : : struct ConstantUses {
68 : : template<typename T>
69 : : struct Info {
70 : : Use *use;
71 : : T *val;
72 : : // If `samebits == true`, the offset the original value appears in the constant.
73 : : size_t offset;
74 : : // This specify whether the original value appears in the current value in exactly
75 : : // the same bit pattern (with possibly an offset determined by `offset`).
76 : : bool samebits;
77 : 0 : Info(Use *use, T *val, size_t offset, bool samebits) :
78 : : use(use),
79 : : val(val),
80 : : offset(offset),
81 : 0 : samebits(samebits)
82 : : {
83 : 0 : }
84 : 0 : Info(Use *use, size_t offset, bool samebits) :
85 : : use(use),
86 : 0 : val(cast<T>(use->getUser())),
87 : : offset(offset),
88 : 0 : samebits(samebits)
89 : : {
90 : 0 : }
91 : : };
92 : : using UseInfo = Info<U>;
93 : : struct Frame : Info<Constant> {
94 : : template<typename... Args>
95 : 0 : Frame(Args &&... args) :
96 : 0 : Info<Constant>(std::forward<Args>(args)...),
97 [ # # ]: 0 : cur(this->val->use_empty() ? nullptr : &*this->val->use_begin()),
98 [ # # ]: 0 : _next(cur ? cur->getNext() : nullptr)
99 : : {
100 : 0 : }
101 : : private:
102 : 0 : void next()
103 : : {
104 : 0 : cur = _next;
105 [ # # ]: 0 : if (!cur)
106 : 0 : return;
107 : 0 : _next = cur->getNext();
108 : : }
109 : : Use *cur;
110 : : Use *_next;
111 : : friend struct ConstantUses;
112 : : };
113 : 0 : ConstantUses(Constant *c, Module &M)
114 : 0 : : stack{Frame(nullptr, c, 0u, true)},
115 : 0 : M(M)
116 : : {
117 : 0 : forward();
118 : 0 : }
119 : 0 : UseInfo get_info() const
120 : : {
121 : 0 : auto &top = stack.back();
122 : 0 : return UseInfo(top.cur, top.offset, top.samebits);
123 : : }
124 : 0 : const SmallVector<Frame, 4> &get_stack() const
125 : : {
126 : 0 : return stack;
127 : : }
128 : 0 : void next()
129 : : {
130 : 0 : stack.back().next();
131 : 0 : forward();
132 : 0 : }
133 : 0 : bool done()
134 : : {
135 : 0 : return stack.empty();
136 : : }
137 : : private:
138 : : void forward();
139 : : SmallVector<Frame, 4> stack;
140 : : Module &M;
141 : : };
142 : :
143 : : template<typename U>
144 : 0 : void ConstantUses<U>::forward()
145 : : {
146 [ # # ]: 0 : assert(!stack.empty());
147 : 0 : auto frame = &stack.back();
148 : 0 : const DataLayout &DL = M.getDataLayout();
149 : 0 : auto pop = [&] {
150 : 0 : stack.pop_back();
151 [ # # # # ]: 0 : if (stack.empty()) {
152 : 0 : return false;
153 : : }
154 : 0 : frame = &stack.back();
155 : 0 : return true;
156 : : };
157 : 0 : auto push = [&] (Use *use, Constant *c, size_t offset, bool samebits) {
158 : 0 : stack.emplace_back(use, c, offset, samebits);
159 : 0 : frame = &stack.back();
160 : : };
161 : 0 : auto handle_constaggr = [&] (Use *use, ConstantAggregate *aggr) {
162 [ # # # # ]: 0 : if (!frame->samebits) {
163 : 0 : push(use, aggr, 0, false);
164 : 0 : return;
165 : : }
166 [ # # # # ]: 0 : if (auto strct = dyn_cast<ConstantStruct>(aggr)) {
167 : 0 : auto layout = DL.getStructLayout(strct->getType());
168 : 0 : push(use, strct, frame->offset + layout->getElementOffset(use->getOperandNo()), true);
169 : : }
170 [ # # # # ]: 0 : else if (auto ary = dyn_cast<ConstantArray>(aggr)) {
171 : 0 : auto elty = ary->getType()->getElementType();
172 : 0 : push(use, ary, frame->offset + DL.getTypeAllocSize(elty) * use->getOperandNo(), true);
173 : : }
174 [ # # # # ]: 0 : else if (auto vec = dyn_cast<ConstantVector>(aggr)) {
175 : 0 : auto elty = vec->getType()->getElementType();
176 : 0 : push(use, vec, frame->offset + DL.getTypeAllocSize(elty) * use->getOperandNo(), true);
177 : : }
178 : : else {
179 : 0 : jl_safe_printf("Unknown ConstantAggregate:\n");
180 : 0 : llvm_dump(aggr);
181 : 0 : abort();
182 : : }
183 : : };
184 : 0 : auto handle_constexpr = [&] (Use *use, ConstantExpr *expr) {
185 [ # # # # ]: 0 : if (!frame->samebits) {
186 : 0 : push(use, expr, 0, false);
187 : 0 : return;
188 : : }
189 : 0 : auto opcode = expr->getOpcode();
190 [ # # # # : 0 : if (opcode == Instruction::PtrToInt || opcode == Instruction::IntToPtr ||
# # # # #
# # # ]
191 [ # # # # ]: 0 : opcode == Instruction::AddrSpaceCast || opcode == Instruction::BitCast) {
192 : 0 : push(use, expr, frame->offset, true);
193 : : }
194 : : else {
195 : 0 : push(use, expr, 0, false);
196 : : }
197 : : };
198 : 0 : while (true) {
199 : 0 : auto use = frame->cur;
200 [ # # ]: 0 : if (!use) {
201 [ # # ]: 0 : if (!pop())
202 : 0 : return;
203 : 0 : continue;
204 : : }
205 : 0 : auto user = use->getUser();
206 [ # # ]: 0 : if (isa<U>(user))
207 : 0 : return;
208 : 0 : frame->next();
209 [ # # ]: 0 : if (auto aggr = dyn_cast<ConstantAggregate>(user)) {
210 : 0 : handle_constaggr(use, aggr);
211 : : }
212 [ # # ]: 0 : else if (auto expr = dyn_cast<ConstantExpr>(user)) {
213 : 0 : handle_constexpr(use, expr);
214 : : }
215 : : }
216 : : }
217 : :
218 : : struct CloneCtx {
219 : : struct Target {
220 : : int idx;
221 : : uint32_t flags;
222 : : std::unique_ptr<ValueToValueMapTy> vmap; // ValueToValueMapTy is not movable....
223 : : // function ids that needs relocation to be initialized
224 : : std::set<uint32_t> relocs{};
225 : 1 : Target(int idx, const jl_target_spec_t &spec) :
226 : : idx(idx),
227 : 1 : flags(spec.flags),
228 : 1 : vmap(new ValueToValueMapTy)
229 : : {
230 : 1 : }
231 : : };
232 : : struct Group : Target {
233 : : std::vector<Target> clones;
234 : : std::set<uint32_t> clone_fs;
235 : 1 : Group(int base, const jl_target_spec_t &spec) :
236 : : Target(base, spec),
237 : : clones{},
238 : 1 : clone_fs{}
239 : 1 : {}
240 : 29064 : Function *base_func(Function *orig_f) const
241 : : {
242 [ + - ]: 29064 : if (idx == 0)
243 : 29064 : return orig_f;
244 : 0 : return cast<Function>(vmap->lookup(orig_f));
245 : : }
246 : : };
247 : : CloneCtx(Module &M, function_ref<LoopInfo&(Function&)> GetLI, function_ref<CallGraph&()> GetCG, bool allow_bad_fvars);
248 : : void clone_bases();
249 : : void collect_func_infos();
250 : : void clone_all_partials();
251 : : void fix_gv_uses();
252 : : void fix_inst_uses();
253 : : void emit_metadata();
254 : : private:
255 : : void prepare_vmap(ValueToValueMapTy &vmap);
256 : : bool is_vector(FunctionType *ty) const;
257 : : void clone_function(Function *F, Function *new_f, ValueToValueMapTy &vmap);
258 : : uint32_t collect_func_info(Function &F);
259 : : void check_partial(Group &grp, Target &tgt);
260 : : void clone_partial(Group &grp, Target &tgt);
261 : : void add_features(Function *F, StringRef name, StringRef features, uint32_t flags) const;
262 : : template<typename T>
263 : : T *add_comdat(T *G) const;
264 : : uint32_t get_func_id(Function *F);
265 : : template<typename Stack>
266 : : Constant *rewrite_gv_init(const Stack& stack);
267 : : template<typename Stack>
268 : : Value *rewrite_inst_use(const Stack& stack, Value *replace, Instruction *insert_before);
269 : : std::pair<uint32_t,GlobalVariable*> get_reloc_slot(Function *F);
270 : : Constant *get_ptrdiff32(Constant *ptr, Constant *base) const;
271 : : template<typename T>
272 : : Constant *emit_offset_table(const std::vector<T*> &vars, StringRef name) const;
273 : : void rewrite_alias(GlobalAlias *alias, Function* F);
274 : :
275 : : MDNode *tbaa_const;
276 : : std::vector<jl_target_spec_t> specs;
277 : : std::vector<Group> groups{};
278 : : std::vector<Function*> fvars;
279 : : std::vector<Constant*> gvars;
280 : : Module &M;
281 : : function_ref<LoopInfo&(Function&)> GetLI;
282 : : function_ref<CallGraph&()> GetCG;
283 : :
284 : : // Map from original functiton to one based index in `fvars`
285 : : std::map<const Function*,uint32_t> func_ids{};
286 : : std::vector<Function*> orig_funcs{};
287 : : std::vector<uint32_t> func_infos{};
288 : : std::set<Function*> cloned{};
289 : : // GV addresses and their corresponding function id (i.e. 0-based index in `fvars`)
290 : : std::vector<std::pair<Constant*,uint32_t>> gv_relocs{};
291 : : // Mapping from function id (i.e. 0-based index in `fvars`) to GVs to be initialized.
292 : : std::map<uint32_t,GlobalVariable*> const_relocs;
293 : : // Functions that were referred to by a global alias, and might not have other uses.
294 : : std::set<uint32_t> alias_relocs;
295 : : bool has_veccall{false};
296 : : bool has_cloneall{false};
297 : : bool allow_bad_fvars{false};
298 : : };
299 : :
300 : : template<typename T>
301 : 2 : static inline std::vector<T*> consume_gv(Module &M, const char *name, bool allow_bad_fvars)
302 : : {
303 : : // Get information about sysimg export functions from the two global variables.
304 : : // Strip them from the Module so that it's easier to handle the uses.
305 : 2 : GlobalVariable *gv = M.getGlobalVariable(name);
306 [ + - + - ]: 2 : assert(gv && gv->hasInitializer());
307 : 2 : auto *ary = cast<ConstantArray>(gv->getInitializer());
308 : 2 : unsigned nele = ary->getNumOperands();
309 : 2 : std::vector<T*> res(nele);
310 : 2 : unsigned i = 0;
311 [ + + ]: 43963 : while (i < nele) {
312 : 43961 : llvm::Value *val = ary->getOperand(i)->stripPointerCasts();
313 [ - + - - : 43961 : if (allow_bad_fvars && (!isa<T>(val) || (isa<Function>(val) && cast<Function>(val)->isDeclaration()))) {
- - - - -
+ ]
314 : : // Shouldn't happen in regular use, but can happen in bugpoint.
315 : 0 : nele--;
316 : 0 : continue;
317 : : }
318 : 43961 : res[i++] = cast<T>(val);
319 : : }
320 : 2 : res.resize(nele);
321 [ - + ]: 2 : assert(gv->use_empty());
322 : 2 : gv->eraseFromParent();
323 [ + - ]: 2 : if (ary->use_empty())
324 : 2 : ary->destroyConstant();
325 : 2 : return res;
326 : : }
327 : :
328 : : // Collect basic information about targets and functions.
329 : 1 : CloneCtx::CloneCtx(Module &M, function_ref<LoopInfo&(Function&)> GetLI, function_ref<CallGraph&()> GetCG, bool allow_bad_fvars)
330 : 1 : : tbaa_const(tbaa_make_child_with_context(M.getContext(), "jtbaa_const", nullptr, true).first),
331 : : specs(jl_get_llvm_clone_targets()),
332 : : fvars(consume_gv<Function>(M, "jl_sysimg_fvars", allow_bad_fvars)),
333 : : gvars(consume_gv<Constant>(M, "jl_sysimg_gvars", false)),
334 : : M(M),
335 : : GetLI(GetLI),
336 : : GetCG(GetCG),
337 : 1 : allow_bad_fvars(allow_bad_fvars)
338 : : {
339 : 1 : groups.emplace_back(0, specs[0]);
340 : 1 : uint32_t ntargets = specs.size();
341 [ - + ]: 1 : for (uint32_t i = 1; i < ntargets; i++) {
342 : 0 : auto &spec = specs[i];
343 [ # # ]: 0 : if (spec.flags & JL_TARGET_CLONE_ALL) {
344 : 0 : has_cloneall = true;
345 : 0 : groups.emplace_back(i, spec);
346 : : }
347 : : else {
348 : 0 : auto base = spec.base;
349 : 0 : bool found = false;
350 [ # # ]: 0 : for (auto &grp: groups) {
351 [ # # ]: 0 : if (grp.idx == base) {
352 : 0 : found = true;
353 : 0 : grp.clones.emplace_back(i, spec);
354 : 0 : break;
355 : : }
356 : : }
357 : : (void)found;
358 : : }
359 : : }
360 : 1 : uint32_t nfvars = fvars.size();
361 [ + + ]: 28496 : for (uint32_t i = 0; i < nfvars; i++)
362 : 28495 : func_ids[fvars[i]] = i + 1;
363 [ + + ]: 29206 : for (auto &F: M) {
364 [ + + ]: 29205 : if (F.empty())
365 : 141 : continue;
366 : 29064 : orig_funcs.push_back(&F);
367 : : }
368 : 1 : }
369 : :
370 : 0 : void CloneCtx::prepare_vmap(ValueToValueMapTy &vmap)
371 : : {
372 : : // Workaround LLVM `CloneFunctionInfo` bug (?) pre-5.0
373 : : // The `DICompileUnit`s are being cloned but are not added to the `llvm.dbg.cu` metadata
374 : : // which triggers assertions when generating native code/in the verifier.
375 : : // Fix this by forcing an identical mapping for all `DICompileUnit` recorded.
376 : : // The `DISubprogram` cloning on LLVM 5.0 handles this
377 : : // but it doesn't hurt to enforce the identity either.
378 : 0 : auto &MD = vmap.MD();
379 [ # # ]: 0 : for (auto cu: M.debug_compile_units()) {
380 : 0 : MD[cu].reset(cu);
381 : : }
382 : 0 : }
383 : :
384 : 0 : void CloneCtx::clone_function(Function *F, Function *new_f, ValueToValueMapTy &vmap)
385 : : {
386 : 0 : Function::arg_iterator DestI = new_f->arg_begin();
387 [ # # ]: 0 : for (Function::const_arg_iterator J = F->arg_begin(); J != F->arg_end(); ++J) {
388 : 0 : DestI->setName(J->getName());
389 : 0 : vmap[&*J] = &*DestI++;
390 : : }
391 : 0 : SmallVector<ReturnInst*,8> Returns;
392 : : #if JL_LLVM_VERSION >= 130000
393 : : // We are cloning into the same module
394 : 0 : CloneFunctionInto(new_f, F, vmap, CloneFunctionChangeType::GlobalChanges, Returns);
395 : : #else
396 : : CloneFunctionInto(new_f, F, vmap, true, Returns);
397 : : #endif
398 : 0 : }
399 : :
400 : : // Clone all clone_all targets. Makes sure that the base targets are all available.
401 : 1 : void CloneCtx::clone_bases()
402 : : {
403 [ + - ]: 1 : if (!has_cloneall)
404 : 1 : return;
405 : 0 : uint32_t ngrps = groups.size();
406 [ # # ]: 0 : for (uint32_t gid = 1; gid < ngrps; gid++) {
407 : 0 : auto &grp = groups[gid];
408 : 0 : auto suffix = ".clone_" + std::to_string(grp.idx);
409 : 0 : auto &vmap = *grp.vmap;
410 : : // Fill in old->new mapping. We need to do this before cloning the function so that
411 : : // the intra target calls are automatically fixed up on cloning.
412 [ # # ]: 0 : for (auto F: orig_funcs) {
413 : 0 : Function *new_f = Function::Create(F->getFunctionType(), F->getLinkage(),
414 : 0 : F->getName() + suffix, &M);
415 : 0 : new_f->copyAttributesFrom(F);
416 : 0 : vmap[F] = new_f;
417 : : }
418 : 0 : prepare_vmap(vmap);
419 [ # # ]: 0 : for (auto F: orig_funcs) {
420 : 0 : clone_function(F, cast<Function>(vmap.lookup(F)), vmap);
421 : : }
422 : : }
423 : : }
424 : :
425 : 287603 : bool CloneCtx::is_vector(FunctionType *ty) const
426 : : {
427 [ + + ]: 287603 : if (ty->getReturnType()->isVectorTy())
428 : 4 : return true;
429 [ + + ]: 929011 : for (auto arg: ty->params()) {
430 [ - + ]: 641412 : if (arg->isVectorTy()) {
431 : 0 : return true;
432 : : }
433 : : }
434 : 287599 : return false;
435 : : }
436 : :
437 : 29064 : uint32_t CloneCtx::collect_func_info(Function &F)
438 : : {
439 : 29064 : uint32_t flag = 0;
440 [ + + ]: 29064 : if (!GetLI(F).empty())
441 : 3917 : flag |= JL_TARGET_CLONE_LOOP;
442 [ + + ]: 29064 : if (is_vector(F.getFunctionType())) {
443 : 2 : flag |= JL_TARGET_CLONE_SIMD;
444 : 2 : has_veccall = true;
445 : : }
446 [ + + ]: 370672 : for (auto &bb: F) {
447 [ + + ]: 5530030 : for (auto &I: bb) {
448 [ + + ]: 5188420 : if (auto call = dyn_cast<CallInst>(&I)) {
449 [ + + ]: 258539 : if (is_vector(call->getFunctionType())) {
450 : 2 : has_veccall = true;
451 : 2 : flag |= JL_TARGET_CLONE_SIMD;
452 : : }
453 [ + + ]: 258539 : if (auto callee = call->getCalledFunction()) {
454 : 208253 : auto name = callee->getName();
455 [ + - + + : 208253 : if (name.startswith("llvm.muladd.") || name.startswith("llvm.fma.")) {
+ + ]
456 : 18 : flag |= JL_TARGET_CLONE_MATH;
457 : : }
458 [ + + ]: 208235 : else if (name.startswith("julia.cpu.")) {
459 [ + - ]: 22 : if (name.startswith("julia.cpu.have_fma.")) {
460 : : // for some platforms we know they always do (or don't) support
461 : : // FMA. in those cases we don't need to clone the function.
462 [ + - ]: 22 : if (!always_have_fma(*callee).hasValue())
463 : 22 : flag |= JL_TARGET_CLONE_CPU;
464 : : } else {
465 : 0 : flag |= JL_TARGET_CLONE_CPU;
466 : : }
467 : : }
468 : : }
469 : : }
470 [ + + ]: 4929880 : else if (auto store = dyn_cast<StoreInst>(&I)) {
471 [ + + ]: 291833 : if (store->getValueOperand()->getType()->isVectorTy()) {
472 : 2 : flag |= JL_TARGET_CLONE_SIMD;
473 : : }
474 : : }
475 [ + + ]: 4638050 : else if (I.getType()->isVectorTy()) {
476 : 2 : flag |= JL_TARGET_CLONE_SIMD;
477 : : }
478 [ + + ]: 5188420 : if (auto mathOp = dyn_cast<FPMathOperator>(&I)) {
479 [ + + ]: 1344 : if (mathOp->getFastMathFlags().any()) {
480 : 56 : flag |= JL_TARGET_CLONE_MATH;
481 : : }
482 : : }
483 [ + + + + : 5188420 : if (has_veccall && (flag & JL_TARGET_CLONE_SIMD) && (flag & JL_TARGET_CLONE_MATH)) {
- + ]
484 : 0 : return flag;
485 : : }
486 : : }
487 : : }
488 : 29064 : return flag;
489 : : }
490 : :
491 : 1 : void CloneCtx::collect_func_infos()
492 : : {
493 : 1 : uint32_t nfuncs = orig_funcs.size();
494 : 1 : func_infos.resize(nfuncs);
495 [ + + ]: 29065 : for (uint32_t i = 0; i < nfuncs; i++) {
496 : 29064 : func_infos[i] = collect_func_info(*orig_funcs[i]);
497 : : }
498 : 1 : }
499 : :
500 : 1 : void CloneCtx::clone_all_partials()
501 : : {
502 : : // First decide what to clone
503 : : // Do this before actually cloning the functions
504 : : // so that the call graph is easier to understand
505 [ + + ]: 2 : for (auto &grp: groups) {
506 [ - + ]: 1 : for (auto &tgt: grp.clones) {
507 : 0 : check_partial(grp, tgt);
508 : : }
509 : : }
510 [ + + ]: 2 : for (auto &grp: groups) {
511 [ - + ]: 1 : for (auto &tgt: grp.clones)
512 : 0 : clone_partial(grp, tgt);
513 : : // Also set feature strings for base target functions
514 : : // now that all the actual cloning is done.
515 : 1 : auto &base_spec = specs[grp.idx];
516 [ + + ]: 29065 : for (auto orig_f: orig_funcs) {
517 : 29064 : add_features(grp.base_func(orig_f), base_spec.cpu_name,
518 : 29064 : base_spec.cpu_features, base_spec.flags);
519 : : }
520 : : }
521 : 1 : func_infos.clear(); // We don't need this anymore
522 : 1 : }
523 : :
524 : 0 : void CloneCtx::check_partial(Group &grp, Target &tgt)
525 : : {
526 : 0 : auto flag = specs[tgt.idx].flags & clone_mask;
527 : 0 : auto suffix = ".clone_" + std::to_string(tgt.idx);
528 : 0 : auto &vmap = *tgt.vmap;
529 : 0 : uint32_t nfuncs = func_infos.size();
530 : :
531 : 0 : std::set<Function*> all_origs;
532 : : // Use a simple heuristic to decide which function we need to clone.
533 [ # # ]: 0 : for (uint32_t i = 0; i < nfuncs; i++) {
534 [ # # ]: 0 : if (!(func_infos[i] & flag))
535 : 0 : continue;
536 : 0 : auto orig_f = orig_funcs[i];
537 : : // Fill in old->new mapping. We need to do this before cloning the function so that
538 : : // the intra target calls are automatically fixed up on cloning.
539 : 0 : auto F = grp.base_func(orig_f);
540 : 0 : Function *new_f = Function::Create(F->getFunctionType(), F->getLinkage(),
541 : 0 : F->getName() + suffix, &M);
542 : 0 : new_f->copyAttributesFrom(F);
543 : 0 : vmap[F] = new_f;
544 [ # # ]: 0 : if (!has_cloneall)
545 : 0 : cloned.insert(orig_f);
546 : 0 : grp.clone_fs.insert(i);
547 : 0 : all_origs.insert(orig_f);
548 : : }
549 [ # # ]: 0 : std::set<Function*> sets[2]{all_origs, std::set<Function*>{}};
550 : 0 : auto *cur_set = &sets[0];
551 : 0 : auto *next_set = &sets[1];
552 : : // Reduce dispatch by expand the cloning set to functions that are directly called by
553 : : // and calling cloned functions.
554 : 0 : auto &graph = GetCG();
555 [ # # ]: 0 : while (!cur_set->empty()) {
556 [ # # ]: 0 : for (auto orig_f: *cur_set) {
557 : : // Use the uncloned function since it's already in the call graph
558 : 0 : auto node = graph[orig_f];
559 [ # # ]: 0 : for (const auto &I: *node) {
560 : 0 : auto child_node = I.second;
561 : 0 : auto orig_child_f = child_node->getFunction();
562 [ # # ]: 0 : if (!orig_child_f)
563 : 0 : continue;
564 : : // Already cloned
565 [ # # ]: 0 : if (all_origs.count(orig_child_f))
566 : 0 : continue;
567 : 0 : bool calling_clone = false;
568 [ # # ]: 0 : for (const auto &I2: *child_node) {
569 : 0 : auto orig_child_f2 = I2.second->getFunction();
570 [ # # ]: 0 : if (!orig_child_f2)
571 : 0 : continue;
572 [ # # ]: 0 : if (all_origs.count(orig_child_f2)) {
573 : 0 : calling_clone = true;
574 : 0 : break;
575 : : }
576 : : }
577 [ # # ]: 0 : if (!calling_clone)
578 : 0 : continue;
579 : 0 : next_set->insert(orig_child_f);
580 : 0 : all_origs.insert(orig_child_f);
581 : 0 : auto child_f = grp.base_func(orig_child_f);
582 : 0 : Function *new_f = Function::Create(child_f->getFunctionType(),
583 : : child_f->getLinkage(),
584 : 0 : child_f->getName() + suffix, &M);
585 : 0 : new_f->copyAttributesFrom(child_f);
586 : 0 : vmap[child_f] = new_f;
587 : : }
588 : : }
589 : 0 : std::swap(cur_set, next_set);
590 : 0 : next_set->clear();
591 : : }
592 [ # # ]: 0 : for (uint32_t i = 0; i < nfuncs; i++) {
593 : : // Only need to handle expanded functions
594 [ # # ]: 0 : if (func_infos[i] & flag)
595 : 0 : continue;
596 : 0 : auto orig_f = orig_funcs[i];
597 [ # # ]: 0 : if (all_origs.count(orig_f)) {
598 [ # # ]: 0 : if (!has_cloneall)
599 : 0 : cloned.insert(orig_f);
600 : 0 : grp.clone_fs.insert(i);
601 : : }
602 : : }
603 : 0 : }
604 : :
605 : 0 : void CloneCtx::clone_partial(Group &grp, Target &tgt)
606 : : {
607 : 0 : auto &spec = specs[tgt.idx];
608 : 0 : auto &vmap = *tgt.vmap;
609 : 0 : uint32_t nfuncs = orig_funcs.size();
610 : 0 : prepare_vmap(vmap);
611 [ # # ]: 0 : for (uint32_t i = 0; i < nfuncs; i++) {
612 : 0 : auto orig_f = orig_funcs[i];
613 : 0 : auto F = grp.base_func(orig_f);
614 [ # # ]: 0 : if (auto new_v = map_get(vmap, F)) {
615 : 0 : auto new_f = cast<Function>(new_v);
616 [ # # ]: 0 : assert(new_f != F);
617 : 0 : clone_function(F, new_f, vmap);
618 : : // We can set the feature strings now since no one is going to
619 : : // clone these functions again.
620 : 0 : add_features(new_f, spec.cpu_name, spec.cpu_features, spec.flags);
621 : : }
622 : : }
623 : 0 : }
624 : :
625 : 29064 : void CloneCtx::add_features(Function *F, StringRef name, StringRef features, uint32_t flags) const
626 : : {
627 : 29064 : auto attr = F->getFnAttribute("target-features");
628 [ - + ]: 29064 : if (attr.isStringAttribute()) {
629 : 0 : std::string new_features(attr.getValueAsString());
630 : 0 : new_features += ",";
631 : 0 : new_features += features;
632 : 0 : F->addFnAttr("target-features", new_features);
633 : : }
634 : : else {
635 : 29064 : F->addFnAttr("target-features", features);
636 : : }
637 : 29064 : F->addFnAttr("target-cpu", name);
638 [ + - ]: 29064 : if (!F->hasFnAttribute(Attribute::OptimizeNone)) {
639 [ - + ]: 29064 : if (flags & JL_TARGET_OPTSIZE) {
640 : 0 : F->addFnAttr(Attribute::OptimizeForSize);
641 : : }
642 [ - + ]: 29064 : else if (flags & JL_TARGET_MINSIZE) {
643 : 0 : F->addFnAttr(Attribute::MinSize);
644 : : }
645 : : }
646 : 29064 : }
647 : :
648 : 0 : uint32_t CloneCtx::get_func_id(Function *F)
649 : : {
650 : 0 : auto &ref = func_ids[F];
651 [ # # ]: 0 : if (!ref) {
652 [ # # # # : 0 : if (allow_bad_fvars && F->isDeclaration()) {
# # ]
653 : : // This should never happen in regular use, but can happen if
654 : : // bugpoint deletes the function. Just do something here to
655 : : // allow bugpoint to proceed.
656 : 0 : return (uint32_t)-1;
657 : : }
658 : 0 : fvars.push_back(F);
659 : 0 : ref = fvars.size();
660 : : }
661 : 0 : return ref - 1;
662 : : }
663 : :
664 : : template<typename Stack>
665 : 0 : Constant *CloneCtx::rewrite_gv_init(const Stack& stack)
666 : : {
667 : : // Null initialize so that LLVM put it in the correct section.
668 : 0 : SmallVector<Constant*, 8> args;
669 : 0 : Constant *res = ConstantPointerNull::get(cast<PointerType>(stack[0].val->getType()));
670 : 0 : uint32_t nlevel = stack.size();
671 [ # # ]: 0 : for (uint32_t i = 1; i < nlevel; i++) {
672 : 0 : auto &frame = stack[i];
673 : 0 : auto val = frame.val;
674 : 0 : Use *use = frame.use;
675 : 0 : unsigned idx = use->getOperandNo();
676 : 0 : unsigned nargs = val->getNumOperands();
677 : 0 : args.resize(nargs);
678 [ # # ]: 0 : for (unsigned j = 0; j < nargs; j++) {
679 [ # # ]: 0 : if (idx == j) {
680 : 0 : args[j] = res;
681 : : }
682 : : else {
683 : 0 : args[j] = cast<Constant>(val->getOperand(j));
684 : : }
685 : : }
686 [ # # ]: 0 : if (auto expr = dyn_cast<ConstantExpr>(val)) {
687 : 0 : res = expr->getWithOperands(args);
688 : : }
689 [ # # ]: 0 : else if (auto ary = dyn_cast<ConstantArray>(val)) {
690 : 0 : res = ConstantArray::get(ary->getType(), args);
691 : : }
692 [ # # ]: 0 : else if (auto strct = dyn_cast<ConstantStruct>(val)) {
693 : 0 : res = ConstantStruct::get(strct->getType(), args);
694 : : }
695 [ # # ]: 0 : else if (isa<ConstantVector>(val)) {
696 : 0 : res = ConstantVector::get(args);
697 : : }
698 : : else {
699 : 0 : jl_safe_printf("Unknown const use.");
700 : 0 : llvm_dump(val);
701 : 0 : abort();
702 : : }
703 : : }
704 : 0 : return res;
705 : : }
706 : :
707 : : // replace an alias to a function with a trampoline and (uninitialized) global variable slot
708 : 0 : void CloneCtx::rewrite_alias(GlobalAlias *alias, Function *F)
709 : : {
710 [ # # ]: 0 : assert(!is_vector(F->getFunctionType()));
711 : :
712 : : Function *trampoline =
713 : 0 : Function::Create(F->getFunctionType(), alias->getLinkage(), "", &M);
714 : 0 : trampoline->copyAttributesFrom(F);
715 : 0 : trampoline->takeName(alias);
716 : 0 : alias->eraseFromParent();
717 : :
718 : : uint32_t id;
719 : : GlobalVariable *slot;
720 : 0 : std::tie(id, slot) = get_reloc_slot(F);
721 [ # # ]: 0 : for (auto &grp: groups) {
722 : 0 : grp.relocs.insert(id);
723 [ # # ]: 0 : for (auto &tgt: grp.clones) {
724 : 0 : tgt.relocs.insert(id);
725 : : }
726 : : }
727 : 0 : alias_relocs.insert(id);
728 : :
729 : 0 : auto BB = BasicBlock::Create(F->getContext(), "top", trampoline);
730 : 0 : IRBuilder<> irbuilder(BB);
731 : :
732 : 0 : auto ptr = irbuilder.CreateLoad(F->getType(), slot);
733 : 0 : ptr->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
734 : 0 : ptr->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(F->getContext(), None));
735 : :
736 : 0 : std::vector<Value *> Args;
737 [ # # ]: 0 : for (auto &arg : trampoline->args())
738 : 0 : Args.push_back(&arg);
739 : 0 : auto call = irbuilder.CreateCall(F->getFunctionType(), ptr, makeArrayRef(Args));
740 [ # # ]: 0 : if (F->isVarArg())
741 : : #if (defined(_CPU_ARM_) || defined(_CPU_PPC_) || defined(_CPU_PPC64_))
742 : : abort(); // musttail support is very bad on ARM, PPC, PPC64 (as of LLVM 3.9)
743 : : #else
744 : 0 : call->setTailCallKind(CallInst::TCK_MustTail);
745 : : #endif
746 : : else
747 : 0 : call->setTailCallKind(CallInst::TCK_Tail);
748 : :
749 [ # # ]: 0 : if (F->getReturnType() == Type::getVoidTy(F->getContext()))
750 : 0 : irbuilder.CreateRetVoid();
751 : : else
752 : 0 : irbuilder.CreateRet(call);
753 : 0 : }
754 : :
755 : 1 : void CloneCtx::fix_gv_uses()
756 : : {
757 : 0 : auto single_pass = [&] (Function *orig_f) {
758 : 0 : bool changed = false;
759 [ # # ]: 0 : for (auto uses = ConstantUses<GlobalValue>(orig_f, M); !uses.done(); uses.next()) {
760 : 0 : changed = true;
761 : 0 : auto &stack = uses.get_stack();
762 : 0 : auto info = uses.get_info();
763 : : // We only support absolute pointer relocation.
764 [ # # ]: 0 : assert(info.samebits);
765 : : GlobalVariable *val;
766 [ # # ]: 0 : if (auto alias = dyn_cast<GlobalAlias>(info.val)) {
767 : 0 : rewrite_alias(alias, orig_f);
768 : 0 : continue;
769 : : }
770 : : else {
771 : 0 : val = cast<GlobalVariable>(info.val);
772 : : }
773 [ # # ]: 0 : assert(info.use->getOperandNo() == 0);
774 [ # # ]: 0 : assert(!val->isConstant());
775 : 0 : auto fid = get_func_id(orig_f);
776 : 0 : auto addr = ConstantExpr::getPtrToInt(val, getSizeTy(val->getContext()));
777 [ # # ]: 0 : if (info.offset)
778 : 0 : addr = ConstantExpr::getAdd(addr, ConstantInt::get(getSizeTy(val->getContext()), info.offset));
779 : 0 : gv_relocs.emplace_back(addr, fid);
780 : 0 : val->setInitializer(rewrite_gv_init(stack));
781 : : }
782 : 0 : return changed;
783 : 1 : };
784 [ + + ]: 29065 : for (auto orig_f: orig_funcs) {
785 [ + - + - : 29064 : if (!has_cloneall && !cloned.count(orig_f))
+ - ]
786 : 29064 : continue;
787 [ # # ]: 0 : while (single_pass(orig_f)) {
788 : : }
789 : : }
790 : 1 : }
791 : :
792 : 0 : std::pair<uint32_t,GlobalVariable*> CloneCtx::get_reloc_slot(Function *F)
793 : : {
794 : : // Null initialize so that LLVM put it in the correct section.
795 : 0 : auto id = get_func_id(F);
796 : 0 : auto &slot = const_relocs[id];
797 [ # # ]: 0 : if (!slot)
798 : 0 : slot = new GlobalVariable(M, F->getType(), false, GlobalVariable::InternalLinkage,
799 : 0 : ConstantPointerNull::get(F->getType()),
800 : 0 : F->getName() + ".reloc_slot");
801 : 0 : return std::make_pair(id, slot);
802 : : }
803 : :
804 : : template<typename Stack>
805 : 0 : Value *CloneCtx::rewrite_inst_use(const Stack& stack, Value *replace, Instruction *insert_before)
806 : : {
807 : 0 : SmallVector<Constant*, 8> args;
808 : 0 : uint32_t nlevel = stack.size();
809 [ # # ]: 0 : for (uint32_t i = 1; i < nlevel; i++) {
810 : 0 : auto &frame = stack[i];
811 : 0 : auto val = frame.val;
812 : 0 : Use *use = frame.use;
813 : 0 : unsigned idx = use->getOperandNo();
814 [ # # ]: 0 : if (auto expr = dyn_cast<ConstantExpr>(val)) {
815 : 0 : auto inst = expr->getAsInstruction();
816 : 0 : inst->replaceUsesOfWith(val->getOperand(idx), replace);
817 : 0 : inst->insertBefore(insert_before);
818 : 0 : replace = inst;
819 : 0 : continue;
820 : : }
821 : 0 : unsigned nargs = val->getNumOperands();
822 : 0 : args.resize(nargs);
823 [ # # ]: 0 : for (unsigned j = 0; j < nargs; j++) {
824 : 0 : auto op = val->getOperand(j);
825 [ # # ]: 0 : if (idx == j) {
826 : 0 : args[j] = UndefValue::get(op->getType());
827 : : }
828 : : else {
829 : 0 : args[j] = cast<Constant>(op);
830 : : }
831 : : }
832 [ # # ]: 0 : if (auto ary = dyn_cast<ConstantArray>(val)) {
833 : 0 : replace = InsertValueInst::Create(ConstantArray::get(ary->getType(), args),
834 : : replace, {idx}, "", insert_before);
835 : : }
836 [ # # ]: 0 : else if (auto strct = dyn_cast<ConstantStruct>(val)) {
837 : 0 : replace = InsertValueInst::Create(ConstantStruct::get(strct->getType(), args),
838 : : replace, {idx}, "", insert_before);
839 : : }
840 [ # # ]: 0 : else if (isa<ConstantVector>(val)) {
841 : 0 : replace = InsertElementInst::Create(ConstantVector::get(args), replace,
842 : 0 : ConstantInt::get(getSizeTy(insert_before->getContext()), idx), "",
843 : : insert_before);
844 : : }
845 : : else {
846 : 0 : jl_safe_printf("Unknown const use.");
847 : 0 : llvm_dump(val);
848 : 0 : abort();
849 : : }
850 : : }
851 : 0 : return replace;
852 : : }
853 : :
854 : 1 : void CloneCtx::fix_inst_uses()
855 : : {
856 : 1 : uint32_t nfuncs = orig_funcs.size();
857 [ + + ]: 2 : for (auto &grp: groups) {
858 : 2 : auto suffix = ".clone_" + std::to_string(grp.idx);
859 [ + + ]: 29065 : for (uint32_t i = 0; i < nfuncs; i++) {
860 [ + - ]: 29064 : if (!grp.clone_fs.count(i))
861 : 29064 : continue;
862 : 0 : auto orig_f = orig_funcs[i];
863 : 0 : auto F = grp.base_func(orig_f);
864 : : bool changed;
865 [ # # ]: 0 : do {
866 : 0 : changed = false;
867 [ # # ]: 0 : for (auto uses = ConstantUses<Instruction>(F, M); !uses.done(); uses.next()) {
868 : 0 : auto info = uses.get_info();
869 : 0 : auto use_i = info.val;
870 : 0 : auto use_f = use_i->getFunction();
871 [ # # ]: 0 : if (!use_f->getName().endswith(suffix))
872 : 0 : continue;
873 : 0 : Instruction *insert_before = use_i;
874 [ # # ]: 0 : if (auto phi = dyn_cast<PHINode>(use_i))
875 : 0 : insert_before = phi->getIncomingBlock(*info.use)->getTerminator();
876 : : uint32_t id;
877 : : GlobalVariable *slot;
878 : 0 : std::tie(id, slot) = get_reloc_slot(orig_f);
879 : 0 : Instruction *ptr = new LoadInst(orig_f->getType(), slot, "", false, insert_before);
880 : 0 : ptr->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
881 : 0 : ptr->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(ptr->getContext(), None));
882 : 0 : use_i->setOperand(info.use->getOperandNo(),
883 : : rewrite_inst_use(uses.get_stack(), ptr,
884 : : insert_before));
885 : :
886 : 0 : grp.relocs.insert(id);
887 [ # # ]: 0 : for (auto &tgt: grp.clones) {
888 : : // The enclosing function of the use is cloned,
889 : : // no need to deal with this use on this target.
890 [ # # ]: 0 : if (map_get(*tgt.vmap, use_f))
891 : 0 : continue;
892 : 0 : tgt.relocs.insert(id);
893 : : }
894 : :
895 : 0 : changed = true;
896 : : }
897 : : } while (changed);
898 : : }
899 : : }
900 : 1 : }
901 : :
902 : : template<typename T>
903 : 8 : inline T *CloneCtx::add_comdat(T *G) const
904 : : {
905 : : #if defined(_OS_WINDOWS_)
906 : : // add __declspec(dllexport) to everything marked for export
907 : : if (G->getLinkage() == GlobalValue::ExternalLinkage)
908 : : G->setDLLStorageClass(GlobalValue::DLLExportStorageClass);
909 : : else
910 : : G->setDLLStorageClass(GlobalValue::DefaultStorageClass);
911 : : #endif
912 : 8 : return G;
913 : : }
914 : :
915 : 43959 : Constant *CloneCtx::get_ptrdiff32(Constant *ptr, Constant *base) const
916 : : {
917 [ + - ]: 43959 : if (ptr->getType()->isPointerTy())
918 : 43959 : ptr = ConstantExpr::getPtrToInt(ptr, getSizeTy(ptr->getContext()));
919 : 43959 : auto ptrdiff = ConstantExpr::getSub(ptr, base);
920 : 43959 : return sizeof(void*) == 8 ? ConstantExpr::getTrunc(ptrdiff, Type::getInt32Ty(ptr->getContext())) : ptrdiff;
921 : : }
922 : :
923 : : template<typename T>
924 : 2 : Constant *CloneCtx::emit_offset_table(const std::vector<T*> &vars, StringRef name) const
925 : : {
926 : 2 : auto T_int32 = Type::getInt32Ty(M.getContext());
927 : 2 : auto T_size = getSizeTy(M.getContext());
928 [ - + ]: 2 : assert(!vars.empty());
929 : 4 : add_comdat(GlobalAlias::create(T_size, 0, GlobalVariable::ExternalLinkage,
930 : 2 : name + "_base",
931 : 2 : ConstantExpr::getBitCast(vars[0], T_size->getPointerTo()), &M));
932 : 2 : auto vbase = ConstantExpr::getPtrToInt(vars[0], T_size);
933 : 2 : uint32_t nvars = vars.size();
934 : 2 : std::vector<Constant*> offsets(nvars + 1);
935 : 2 : offsets[0] = ConstantInt::get(T_int32, nvars);
936 : 2 : offsets[1] = ConstantInt::get(T_int32, 0);
937 [ + + ]: 43961 : for (uint32_t i = 1; i < nvars; i++)
938 : 43959 : offsets[i + 1] = get_ptrdiff32(vars[i], vbase);
939 : 2 : ArrayType *vars_type = ArrayType::get(T_int32, nvars + 1);
940 : 2 : add_comdat(new GlobalVariable(M, vars_type, true,
941 : : GlobalVariable::ExternalLinkage,
942 : 2 : ConstantArray::get(vars_type, offsets),
943 : 2 : name + "_offsets"));
944 : 2 : return vbase;
945 : : }
946 : :
947 : 1 : void CloneCtx::emit_metadata()
948 : : {
949 : 1 : uint32_t nfvars = fvars.size();
950 [ - + - - ]: 1 : if (allow_bad_fvars && nfvars == 0) {
951 : : // Will result in a non-loadable sysimg, but `allow_bad_fvars` is for bugpoint only
952 : 0 : return;
953 : : }
954 : :
955 : : // Store back the information about exported functions.
956 : 1 : auto fbase = emit_offset_table(fvars, "jl_sysimg_fvars");
957 : 1 : auto gbase = emit_offset_table(gvars, "jl_sysimg_gvars");
958 : :
959 : 1 : uint32_t ntargets = specs.size();
960 : 2 : SmallVector<Target*, 8> targets(ntargets);
961 [ + + ]: 2 : for (auto &grp: groups) {
962 : 1 : targets[grp.idx] = &grp;
963 [ - + ]: 1 : for (auto &tgt: grp.clones) {
964 : 0 : targets[tgt.idx] = &tgt;
965 : : }
966 : : }
967 : :
968 : : // Generate `jl_dispatch_target_ids`
969 : : {
970 [ + - ]: 1 : const uint32_t base_flags = has_veccall ? JL_TARGET_VEC_CALL : 0;
971 : 1 : std::vector<uint8_t> data;
972 : 2 : auto push_i32 = [&] (uint32_t v) {
973 : : uint8_t buff[4];
974 : 2 : memcpy(buff, &v, 4);
975 : 2 : data.insert(data.end(), buff, buff + 4);
976 : 3 : };
977 : 1 : push_i32(ntargets);
978 [ + + ]: 2 : for (uint32_t i = 0; i < ntargets; i++) {
979 : 1 : push_i32(base_flags | (specs[i].flags & JL_TARGET_UNKNOWN_NAME));
980 : 1 : auto &specdata = specs[i].data;
981 : 1 : data.insert(data.end(), specdata.begin(), specdata.end());
982 : : }
983 : 1 : auto value = ConstantDataArray::get(M.getContext(), data);
984 : 2 : add_comdat(new GlobalVariable(M, value->getType(), true,
985 : : GlobalVariable::ExternalLinkage,
986 : 1 : value, "jl_dispatch_target_ids"));
987 : : }
988 : :
989 : : // Generate `jl_dispatch_reloc_slots`
990 : 2 : std::set<uint32_t> shared_relocs;
991 : : {
992 : 1 : auto T_int32 = Type::getInt32Ty(M.getContext());
993 : 1 : std::stable_sort(gv_relocs.begin(), gv_relocs.end(),
994 : 0 : [] (const std::pair<Constant*,uint32_t> &lhs,
995 : : const std::pair<Constant*,uint32_t> &rhs) {
996 : 0 : return lhs.second < rhs.second;
997 : : });
998 : 1 : std::vector<Constant*> values{nullptr};
999 : 1 : uint32_t gv_reloc_idx = 0;
1000 : 1 : uint32_t ngv_relocs = gv_relocs.size();
1001 [ + + ]: 28496 : for (uint32_t id = 0; id < nfvars; id++) {
1002 : : // TODO:
1003 : : // explicitly set section? so that we are sure the relocation slots
1004 : : // are in the same section as `gbase`.
1005 : 28495 : auto id_v = ConstantInt::get(T_int32, id);
1006 [ - + - - : 28495 : for (; gv_reloc_idx < ngv_relocs && gv_relocs[gv_reloc_idx].second == id;
- + ]
1007 : : gv_reloc_idx++) {
1008 : 0 : shared_relocs.insert(id);
1009 : 0 : values.push_back(id_v);
1010 : 0 : values.push_back(get_ptrdiff32(gv_relocs[gv_reloc_idx].first, gbase));
1011 : : }
1012 : 28495 : auto it = const_relocs.find(id);
1013 [ - + ]: 28495 : if (it != const_relocs.end()) {
1014 : 0 : values.push_back(id_v);
1015 : 0 : values.push_back(get_ptrdiff32(it->second, gbase));
1016 : : }
1017 [ - + ]: 28495 : if (alias_relocs.find(id) != alias_relocs.end()) {
1018 : 0 : shared_relocs.insert(id);
1019 : : }
1020 : : }
1021 : 1 : values[0] = ConstantInt::get(T_int32, values.size() / 2);
1022 : 1 : ArrayType *vars_type = ArrayType::get(T_int32, values.size());
1023 : 2 : add_comdat(new GlobalVariable(M, vars_type, true, GlobalVariable::ExternalLinkage,
1024 : 2 : ConstantArray::get(vars_type, values),
1025 : 1 : "jl_dispatch_reloc_slots"));
1026 : : }
1027 : :
1028 : : // Generate `jl_dispatch_fvars_idxs` and `jl_dispatch_fvars_offsets`
1029 : : {
1030 : 2 : std::vector<uint32_t> idxs;
1031 : 1 : std::vector<Constant*> offsets;
1032 [ + + ]: 2 : for (uint32_t i = 0; i < ntargets; i++) {
1033 : 1 : auto tgt = targets[i];
1034 : 1 : auto &spec = specs[i];
1035 : 1 : uint32_t len_idx = idxs.size();
1036 : 1 : idxs.push_back(0); // We will fill in the real value later.
1037 : 1 : uint32_t count = 0;
1038 [ - + - - ]: 1 : if (i == 0 || spec.flags & JL_TARGET_CLONE_ALL) {
1039 : 1 : auto grp = static_cast<Group*>(tgt);
1040 : 1 : count = jl_sysimg_tag_mask;
1041 [ + + ]: 28496 : for (uint32_t j = 0; j < nfvars; j++) {
1042 [ + - - + : 28495 : if (shared_relocs.count(j) || tgt->relocs.count(j)) {
- + ]
1043 : 0 : count++;
1044 : 0 : idxs.push_back(j);
1045 : : }
1046 [ - + ]: 28495 : if (i != 0) {
1047 : 0 : offsets.push_back(get_ptrdiff32(grp->base_func(fvars[j]), fbase));
1048 : : }
1049 : 1 : }
1050 : : }
1051 : : else {
1052 : 0 : auto baseidx = spec.base;
1053 : 0 : auto grp = static_cast<Group*>(targets[baseidx]);
1054 : 0 : idxs.push_back(baseidx);
1055 [ # # ]: 0 : for (uint32_t j = 0; j < nfvars; j++) {
1056 : 0 : auto base_f = grp->base_func(fvars[j]);
1057 [ # # # # : 0 : if (shared_relocs.count(j) || tgt->relocs.count(j)) {
# # ]
1058 : 0 : count++;
1059 : 0 : idxs.push_back(jl_sysimg_tag_mask | j);
1060 : 0 : auto f = map_get(*tgt->vmap, base_f, base_f);
1061 : 0 : offsets.push_back(get_ptrdiff32(cast<Function>(f), fbase));
1062 : : }
1063 [ # # ]: 0 : else if (auto f = map_get(*tgt->vmap, base_f)) {
1064 : 0 : count++;
1065 : 0 : idxs.push_back(j);
1066 : 0 : offsets.push_back(get_ptrdiff32(cast<Function>(f), fbase));
1067 : : }
1068 : : }
1069 : : }
1070 : 1 : idxs[len_idx] = count;
1071 : : }
1072 : 1 : auto idxval = ConstantDataArray::get(M.getContext(), idxs);
1073 : 2 : add_comdat(new GlobalVariable(M, idxval->getType(), true,
1074 : : GlobalVariable::ExternalLinkage,
1075 : 1 : idxval, "jl_dispatch_fvars_idxs"));
1076 : 1 : ArrayType *offsets_type = ArrayType::get(Type::getInt32Ty(M.getContext()), offsets.size());
1077 : 2 : add_comdat(new GlobalVariable(M, offsets_type, true,
1078 : : GlobalVariable::ExternalLinkage,
1079 : 2 : ConstantArray::get(offsets_type, offsets),
1080 : 1 : "jl_dispatch_fvars_offsets"));
1081 : : }
1082 : : }
1083 : :
1084 : 2 : static bool runMultiVersioning(Module &M, function_ref<LoopInfo&(Function&)> GetLI, function_ref<CallGraph&()> GetCG, bool allow_bad_fvars)
1085 : : {
1086 : : // Group targets and identify cloning bases.
1087 : : // Also initialize function info maps (we'll update these maps as we go)
1088 : : // Maps that we need includes,
1089 : : //
1090 : : // * Original function -> ID (initialize from `fvars` and allocate ID lazily)
1091 : : // * Cloned function -> Original function (add as we clone functions)
1092 : : // * Original function -> Base function (target specific and updated by LLVM)
1093 : : // * ID -> relocation slots (const).
1094 [ + + ]: 2 : if (M.getName() == "sysimage")
1095 : 1 : return false;
1096 : :
1097 : 1 : GlobalVariable *fvars = M.getGlobalVariable("jl_sysimg_fvars");
1098 : 1 : GlobalVariable *gvars = M.getGlobalVariable("jl_sysimg_gvars");
1099 [ - + - - : 1 : if (allow_bad_fvars && (!fvars || !fvars->hasInitializer() || !isa<ConstantArray>(fvars->getInitializer()) ||
- - - - -
- ]
1100 [ - - - - : 1 : !gvars || !gvars->hasInitializer() || !isa<ConstantArray>(gvars->getInitializer())))
- + ]
1101 : 0 : return false;
1102 : :
1103 : 1 : CloneCtx clone(M, GetLI, GetCG, allow_bad_fvars);
1104 : :
1105 : : // Collect a list of original functions and clone base functions
1106 : 1 : clone.clone_bases();
1107 : :
1108 : : // Collect function info (type of instruction used)
1109 : 1 : clone.collect_func_infos();
1110 : :
1111 : : // If any partially cloned target exist decide which functions to clone for these targets.
1112 : : // Clone functions for each group and collect a list of them.
1113 : : // We can also add feature strings for cloned functions
1114 : : // now that no additional cloning needs to be done.
1115 : 1 : clone.clone_all_partials();
1116 : :
1117 : : // Scan **ALL** cloned functions (including full cloning for base target)
1118 : : // for global variables initialization use.
1119 : : // Replace them with `null` slot to be initialized at runtime and record relocation slot.
1120 : : // These relocations must be initialized for **ALL** targets.
1121 : 1 : clone.fix_gv_uses();
1122 : :
1123 : : // For each group, scan all functions cloned by **PARTIALLY** cloned targets for
1124 : : // instruction use.
1125 : : // A function needs a const relocation slot if it is cloned and is called by a
1126 : : // uncloned function for at least one partially cloned target in the group.
1127 : : // This is also the condition that a use in an uncloned function needs to be replaced with
1128 : : // a slot load (i.e. if both the caller and the callee are always cloned or not cloned
1129 : : // on all targets, the caller site does not need a relocation slot).
1130 : : // A target needs a slot to be initialized iff at least one caller is not initialized.
1131 : 1 : clone.fix_inst_uses();
1132 : :
1133 : : // Store back sysimg information with the correct format.
1134 : : // At this point, we should have fixed up all the uses of the cloned functions
1135 : : // and collected all the shared/target-specific relocations.
1136 : 1 : clone.emit_metadata();
1137 : :
1138 [ - + ]: 1 : assert(!verifyModule(M));
1139 : :
1140 : 1 : return true;
1141 : : }
1142 : :
1143 : : struct MultiVersioningLegacy: public ModulePass {
1144 : : static char ID;
1145 : 1 : MultiVersioningLegacy(bool allow_bad_fvars=false)
1146 : 1 : : ModulePass(ID), allow_bad_fvars(allow_bad_fvars)
1147 : 1 : {}
1148 : :
1149 : : private:
1150 : : bool runOnModule(Module &M) override;
1151 : 1 : void getAnalysisUsage(AnalysisUsage &AU) const override
1152 : : {
1153 : 1 : AU.addRequired<LoopInfoWrapperPass>();
1154 : 1 : AU.addRequired<CallGraphWrapperPass>();
1155 : 1 : AU.addPreserved<LoopInfoWrapperPass>();
1156 : 1 : }
1157 : : bool allow_bad_fvars;
1158 : : };
1159 : :
1160 : 2 : bool MultiVersioningLegacy::runOnModule(Module &M)
1161 : : {
1162 : 29064 : auto GetLI = [this](Function &F) -> LoopInfo & {
1163 : 29064 : return getAnalysis<LoopInfoWrapperPass>(F).getLoopInfo();
1164 : 2 : };
1165 : 0 : auto GetCG = [this]() -> CallGraph & {
1166 : 0 : return getAnalysis<CallGraphWrapperPass>().getCallGraph();
1167 : 2 : };
1168 : 2 : return runMultiVersioning(M, GetLI, GetCG, allow_bad_fvars);
1169 : : }
1170 : :
1171 : :
1172 : : char MultiVersioningLegacy::ID = 0;
1173 : : static RegisterPass<MultiVersioningLegacy> X("JuliaMultiVersioning", "JuliaMultiVersioning Pass",
1174 : : false /* Only looks at CFG */,
1175 : : false /* Analysis Pass */);
1176 : :
1177 : : } // anonymous namespace
1178 : :
1179 : 0 : PreservedAnalyses MultiVersioning::run(Module &M, ModuleAnalysisManager &AM)
1180 : : {
1181 : 0 : auto &FAM = AM.getResult<FunctionAnalysisManagerModuleProxy>(M).getManager();
1182 : 0 : auto GetLI = [&](Function &F) -> LoopInfo & {
1183 : 0 : return FAM.getResult<LoopAnalysis>(F);
1184 : 0 : };
1185 : 0 : auto GetCG = [&]() -> CallGraph & {
1186 : 0 : return AM.getResult<CallGraphAnalysis>(M);
1187 : 0 : };
1188 [ # # ]: 0 : if (runMultiVersioning(M, GetLI, GetCG, external_use)) {
1189 : 0 : auto preserved = PreservedAnalyses::allInSet<CFGAnalyses>();
1190 : 0 : preserved.preserve<LoopAnalysis>();
1191 : 0 : return preserved;
1192 : : }
1193 : 0 : return PreservedAnalyses::all();
1194 : : }
1195 : :
1196 : 1 : Pass *createMultiVersioningPass(bool allow_bad_fvars)
1197 : : {
1198 : 1 : return new MultiVersioningLegacy(allow_bad_fvars);
1199 : : }
1200 : :
1201 : 0 : extern "C" JL_DLLEXPORT void LLVMExtraAddMultiVersioningPass_impl(LLVMPassManagerRef PM)
1202 : : {
1203 : 0 : unwrap(PM)->add(createMultiVersioningPass(false));
1204 : 0 : }
|