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 "passes.h"
5 : :
6 : : #include <llvm/IR/LegacyPassManager.h>
7 : : #include <llvm/IR/Function.h>
8 : : #include <llvm/IR/IntrinsicInst.h>
9 : : #include <llvm/IR/Module.h>
10 : : #include <llvm/IR/IRBuilder.h>
11 : : #include <llvm/Pass.h>
12 : : #include <llvm/Support/Debug.h>
13 : : #include <llvm/Transforms/Utils/ModuleUtils.h>
14 : :
15 : : #include "codegen_shared.h"
16 : : #include "julia.h"
17 : : #include "julia_internal.h"
18 : : #include "llvm-pass-helpers.h"
19 : :
20 : : #define DEBUG_TYPE "final_gc_lowering"
21 : :
22 : : using namespace llvm;
23 : :
24 : : // The final GC lowering pass. This pass lowers platform-agnostic GC
25 : : // intrinsics to platform-dependent instruction sequences. The
26 : : // intrinsics it targets are those produced by the late GC frame
27 : : // lowering pass.
28 : : //
29 : : // This pass targets typical back-ends for which the standard Julia
30 : : // runtime library is available. Atypical back-ends should supply
31 : : // their own lowering pass.
32 : :
33 : : struct FinalLowerGC: private JuliaPassContext {
34 : : bool runOnFunction(Function &F);
35 : : bool doInitialization(Module &M);
36 : : bool doFinalization(Module &M);
37 : :
38 : : private:
39 : : Function *queueRootFunc;
40 : : Function *queueBindingFunc;
41 : : Function *poolAllocFunc;
42 : : Function *bigAllocFunc;
43 : : Instruction *pgcstack;
44 : :
45 : : // Lowers a `julia.new_gc_frame` intrinsic.
46 : : Value *lowerNewGCFrame(CallInst *target, Function &F);
47 : :
48 : : // Lowers a `julia.push_gc_frame` intrinsic.
49 : : void lowerPushGCFrame(CallInst *target, Function &F);
50 : :
51 : : // Lowers a `julia.pop_gc_frame` intrinsic.
52 : : void lowerPopGCFrame(CallInst *target, Function &F);
53 : :
54 : : // Lowers a `julia.get_gc_frame_slot` intrinsic.
55 : : Value *lowerGetGCFrameSlot(CallInst *target, Function &F);
56 : :
57 : : // Lowers a `julia.gc_alloc_bytes` intrinsic.
58 : : Value *lowerGCAllocBytes(CallInst *target, Function &F);
59 : :
60 : : // Lowers a `julia.queue_gc_root` intrinsic.
61 : : Value *lowerQueueGCRoot(CallInst *target, Function &F);
62 : :
63 : : // Lowers a `julia.queue_gc_binding` intrinsic.
64 : : Value *lowerQueueGCBinding(CallInst *target, Function &F);
65 : : };
66 : :
67 : 279033 : Value *FinalLowerGC::lowerNewGCFrame(CallInst *target, Function &F)
68 : : {
69 : : assert(target->arg_size() == 1);
70 : 279033 : unsigned nRoots = cast<ConstantInt>(target->getArgOperand(0))->getLimitedValue(INT_MAX);
71 : :
72 : : // Create the GC frame.
73 : : AllocaInst *gcframe = new AllocaInst(
74 : 279033 : T_prjlvalue,
75 : : 0,
76 : 279033 : ConstantInt::get(Type::getInt32Ty(F.getContext()), nRoots + 2),
77 : 279033 : Align(16));
78 : 279033 : gcframe->insertAfter(target);
79 : 279033 : gcframe->takeName(target);
80 : :
81 : : // Zero out the GC frame.
82 : 279033 : BitCastInst *tempSlot_i8 = new BitCastInst(gcframe, Type::getInt8PtrTy(F.getContext()), "");
83 : 279033 : tempSlot_i8->insertAfter(gcframe);
84 : 279033 : Type *argsT[2] = {tempSlot_i8->getType(), Type::getInt32Ty(F.getContext())};
85 : 279033 : Function *memset = Intrinsic::getDeclaration(F.getParent(), Intrinsic::memset, makeArrayRef(argsT));
86 : : Value *args[4] = {
87 : : tempSlot_i8, // dest
88 : 279033 : ConstantInt::get(Type::getInt8Ty(F.getContext()), 0), // val
89 : 279033 : ConstantInt::get(Type::getInt32Ty(F.getContext()), sizeof(jl_value_t*) * (nRoots + 2)), // len
90 : 837099 : ConstantInt::get(Type::getInt1Ty(F.getContext()), 0)}; // volatile
91 : 279033 : CallInst *zeroing = CallInst::Create(memset, makeArrayRef(args));
92 : 279033 : cast<MemSetInst>(zeroing)->setDestAlignment(16);
93 : 279033 : zeroing->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
94 : 279033 : zeroing->insertAfter(tempSlot_i8);
95 : :
96 : 279033 : return gcframe;
97 : : }
98 : :
99 : 279033 : void FinalLowerGC::lowerPushGCFrame(CallInst *target, Function &F)
100 : : {
101 : : assert(target->arg_size() == 2);
102 : 279033 : auto gcframe = target->getArgOperand(0);
103 : 279033 : unsigned nRoots = cast<ConstantInt>(target->getArgOperand(1))->getLimitedValue(INT_MAX);
104 : :
105 : 279033 : IRBuilder<> builder(target->getContext());
106 : 279033 : builder.SetInsertPoint(&*(++BasicBlock::iterator(target)));
107 : 1116130 : StoreInst *inst = builder.CreateAlignedStore(
108 : 279033 : ConstantInt::get(getSizeTy(F.getContext()), JL_GC_ENCODE_PUSHARGS(nRoots)),
109 : : builder.CreateBitCast(
110 : 279033 : builder.CreateConstInBoundsGEP1_32(T_prjlvalue, gcframe, 0),
111 : 279033 : getSizeTy(F.getContext())->getPointerTo()),
112 : 279033 : Align(sizeof(void*)));
113 : 279033 : inst->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
114 : 279033 : auto T_ppjlvalue = JuliaType::get_ppjlvalue_ty(F.getContext());
115 : 1116130 : inst = builder.CreateAlignedStore(
116 : 279033 : builder.CreateAlignedLoad(T_ppjlvalue, pgcstack, Align(sizeof(void*))),
117 : : builder.CreatePointerCast(
118 : 279033 : builder.CreateConstInBoundsGEP1_32(T_prjlvalue, gcframe, 1),
119 : 279033 : PointerType::get(T_ppjlvalue, 0)),
120 : 279033 : Align(sizeof(void*)));
121 : 279033 : inst->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
122 : 558066 : inst = builder.CreateAlignedStore(
123 : : gcframe,
124 : 279033 : builder.CreateBitCast(pgcstack, PointerType::get(PointerType::get(T_prjlvalue, 0), 0)),
125 : 558066 : Align(sizeof(void*)));
126 : 279033 : }
127 : :
128 : 258682 : void FinalLowerGC::lowerPopGCFrame(CallInst *target, Function &F)
129 : : {
130 : : assert(target->arg_size() == 1);
131 : 258682 : auto gcframe = target->getArgOperand(0);
132 : :
133 : 517364 : IRBuilder<> builder(target->getContext());
134 : 258682 : builder.SetInsertPoint(target);
135 : : Instruction *gcpop =
136 : 258682 : cast<Instruction>(builder.CreateConstInBoundsGEP1_32(T_prjlvalue, gcframe, 1));
137 : 258682 : Instruction *inst = builder.CreateAlignedLoad(T_prjlvalue, gcpop, Align(sizeof(void*)));
138 : 258682 : inst->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
139 : 517364 : inst = builder.CreateAlignedStore(
140 : : inst,
141 : 258682 : builder.CreateBitCast(pgcstack,
142 : 258682 : PointerType::get(T_prjlvalue, 0)),
143 : 258682 : Align(sizeof(void*)));
144 : 258682 : inst->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
145 : 258682 : }
146 : :
147 : 2331730 : Value *FinalLowerGC::lowerGetGCFrameSlot(CallInst *target, Function &F)
148 : : {
149 : : assert(target->arg_size() == 2);
150 : 2331730 : auto gcframe = target->getArgOperand(0);
151 : 2331730 : auto index = target->getArgOperand(1);
152 : :
153 : : // Initialize an IR builder.
154 : 2331730 : IRBuilder<> builder(target->getContext());
155 : 2331730 : builder.SetInsertPoint(target);
156 : :
157 : : // The first two slots are reserved, so we'll add two to the index.
158 : 2331730 : index = builder.CreateAdd(index, ConstantInt::get(Type::getInt32Ty(F.getContext()), 2));
159 : :
160 : : // Lower the intrinsic as a GEP.
161 : 2331730 : auto gep = builder.CreateInBoundsGEP(T_prjlvalue, gcframe, index);
162 : 2331730 : gep->takeName(target);
163 : 2331730 : return gep;
164 : : }
165 : :
166 : 72769 : Value *FinalLowerGC::lowerQueueGCRoot(CallInst *target, Function &F)
167 : : {
168 : : assert(target->arg_size() == 1);
169 : 72769 : target->setCalledFunction(queueRootFunc);
170 : 72769 : return target;
171 : : }
172 : :
173 : 1848 : Value *FinalLowerGC::lowerQueueGCBinding(CallInst *target, Function &F)
174 : : {
175 : : assert(target->arg_size() == 1);
176 : 1848 : target->setCalledFunction(queueBindingFunc);
177 : 1848 : return target;
178 : : }
179 : :
180 : 405301 : Value *FinalLowerGC::lowerGCAllocBytes(CallInst *target, Function &F)
181 : : {
182 : : assert(target->arg_size() == 2);
183 : 405301 : auto sz = (size_t)cast<ConstantInt>(target->getArgOperand(1))->getZExtValue();
184 : : // This is strongly architecture and OS dependent
185 : : int osize;
186 : 405301 : int offset = jl_gc_classify_pools(sz, &osize);
187 : 405301 : IRBuilder<> builder(target);
188 : 405301 : builder.SetCurrentDebugLocation(target->getDebugLoc());
189 : 405301 : auto ptls = target->getArgOperand(0);
190 : : CallInst *newI;
191 [ + + ]: 405301 : if (offset < 0) {
192 : 48 : newI = builder.CreateCall(
193 : : bigAllocFunc,
194 : 24 : { ptls, ConstantInt::get(getSizeTy(F.getContext()), sz + sizeof(void*)) });
195 : : }
196 : : else {
197 : 405277 : auto pool_offs = ConstantInt::get(Type::getInt32Ty(F.getContext()), offset);
198 : 405277 : auto pool_osize = ConstantInt::get(Type::getInt32Ty(F.getContext()), osize);
199 : 405277 : newI = builder.CreateCall(poolAllocFunc, { ptls, pool_offs, pool_osize });
200 : : }
201 : 405301 : newI->setAttributes(newI->getCalledFunction()->getAttributes());
202 : 405301 : newI->takeName(target);
203 : 405301 : return newI;
204 : : }
205 : :
206 : 334505 : bool FinalLowerGC::doInitialization(Module &M) {
207 : : // Initialize platform-agnostic references.
208 : 334505 : initAll(M);
209 : :
210 : : // Initialize platform-specific references.
211 : 334505 : queueRootFunc = getOrDeclare(jl_well_known::GCQueueRoot);
212 : 334505 : queueBindingFunc = getOrDeclare(jl_well_known::GCQueueBinding);
213 : 334505 : poolAllocFunc = getOrDeclare(jl_well_known::GCPoolAlloc);
214 : 334505 : bigAllocFunc = getOrDeclare(jl_well_known::GCBigAlloc);
215 : :
216 : 334505 : GlobalValue *functionList[] = {queueRootFunc, queueBindingFunc, poolAllocFunc, bigAllocFunc};
217 : 334505 : unsigned j = 0;
218 [ + + ]: 1672520 : for (unsigned i = 0; i < sizeof(functionList) / sizeof(void*); i++) {
219 [ - + ]: 1338020 : if (!functionList[i])
220 : 0 : continue;
221 [ - + ]: 1338020 : if (i != j)
222 : 0 : functionList[j] = functionList[i];
223 : 1338020 : j++;
224 : : }
225 [ + - ]: 334505 : if (j != 0)
226 : 334505 : appendToCompilerUsed(M, ArrayRef<GlobalValue*>(functionList, j));
227 : 334505 : return true;
228 : : }
229 : :
230 : 334505 : bool FinalLowerGC::doFinalization(Module &M)
231 : : {
232 : 334505 : GlobalValue *functionList[] = {queueRootFunc, queueBindingFunc, poolAllocFunc, bigAllocFunc};
233 : 334505 : queueRootFunc = queueBindingFunc = poolAllocFunc = bigAllocFunc = nullptr;
234 : 334505 : auto used = M.getGlobalVariable("llvm.compiler.used");
235 [ - + ]: 334505 : if (!used)
236 : 0 : return false;
237 : : SmallPtrSet<Constant*, 16> InitAsSet(
238 : : functionList,
239 : 669010 : functionList + sizeof(functionList) / sizeof(void*));
240 : 334505 : bool changed = false;
241 : 669010 : SmallVector<Constant*, 16> init;
242 : 334505 : ConstantArray *CA = cast<ConstantArray>(used->getInitializer());
243 [ + + ]: 1672520 : for (auto &Op : CA->operands()) {
244 : 1338020 : Constant *C = cast_or_null<Constant>(Op);
245 [ + - ]: 1338020 : if (InitAsSet.count(C->stripPointerCasts())) {
246 : 1338020 : changed = true;
247 : 1338020 : continue;
248 : : }
249 : 0 : init.push_back(C);
250 : : }
251 [ - + ]: 334505 : if (!changed)
252 : 0 : return false;
253 : 334505 : used->eraseFromParent();
254 [ + - ]: 334505 : if (init.empty())
255 : 334505 : return true;
256 : 0 : ArrayType *ATy = ArrayType::get(Type::getInt8PtrTy(M.getContext()), init.size());
257 : 0 : used = new GlobalVariable(M, ATy, false, GlobalValue::AppendingLinkage,
258 : 0 : ConstantArray::get(ATy, init), "llvm.compiler.used");
259 : 0 : used->setSection("llvm.metadata");
260 : 0 : return true;
261 : : }
262 : :
263 : : template<typename TIterator>
264 : 3090680 : static void replaceInstruction(
265 : : Instruction *oldInstruction,
266 : : Value *newInstruction,
267 : : TIterator &it)
268 : : {
269 [ + + ]: 3090680 : if (newInstruction != oldInstruction) {
270 : 3016060 : oldInstruction->replaceAllUsesWith(newInstruction);
271 : 3016060 : it = oldInstruction->eraseFromParent();
272 : : }
273 : : else {
274 : 74617 : ++it;
275 : : }
276 : 3090680 : }
277 : :
278 : 689606 : bool FinalLowerGC::runOnFunction(Function &F)
279 : : {
280 : : LLVM_DEBUG(dbgs() << "FINAL GC LOWERING: Processing function " << F.getName() << "\n");
281 : : // Check availability of functions again since they might have been deleted.
282 : 689606 : initFunctions(*F.getParent());
283 [ + + ]: 689606 : if (!pgcstack_getter)
284 : 3252 : return false;
285 : :
286 : : // Look for a call to 'julia.get_pgcstack'.
287 : 686354 : pgcstack = getPGCstack(F);
288 [ + + ]: 686354 : if (!pgcstack)
289 : 725 : return false;
290 : :
291 : : // Acquire intrinsic functions.
292 : 685629 : auto newGCFrameFunc = getOrNull(jl_intrinsics::newGCFrame);
293 : 685629 : auto pushGCFrameFunc = getOrNull(jl_intrinsics::pushGCFrame);
294 : 685629 : auto popGCFrameFunc = getOrNull(jl_intrinsics::popGCFrame);
295 : 685629 : auto getGCFrameSlotFunc = getOrNull(jl_intrinsics::getGCFrameSlot);
296 : 685629 : auto GCAllocBytesFunc = getOrNull(jl_intrinsics::GCAllocBytes);
297 : 685629 : auto queueGCRootFunc = getOrNull(jl_intrinsics::queueGCRoot);
298 : 685629 : auto queueGCBindingFunc = getOrNull(jl_intrinsics::queueGCBinding);
299 : :
300 : : // Lower all calls to supported intrinsics.
301 [ + + ]: 7311820 : for (BasicBlock &BB : F) {
302 [ + + ]: 77964400 : for (auto it = BB.begin(); it != BB.end();) {
303 : 71338200 : auto *CI = dyn_cast<CallInst>(&*it);
304 [ + + ]: 71338200 : if (!CI) {
305 : 61236900 : ++it;
306 : 61236900 : continue;
307 : : }
308 : :
309 : 10101400 : Value *callee = CI->getCalledOperand();
310 : :
311 [ + + ]: 10101400 : if (callee == newGCFrameFunc) {
312 : 279033 : replaceInstruction(CI, lowerNewGCFrame(CI, F), it);
313 : : }
314 [ + + ]: 9822340 : else if (callee == pushGCFrameFunc) {
315 : 279033 : lowerPushGCFrame(CI, F);
316 : 279033 : it = CI->eraseFromParent();
317 : : }
318 [ + + ]: 9543310 : else if (callee == popGCFrameFunc) {
319 : 258682 : lowerPopGCFrame(CI, F);
320 : 258682 : it = CI->eraseFromParent();
321 : : }
322 [ + + ]: 9284620 : else if (callee == getGCFrameSlotFunc) {
323 : 2331730 : replaceInstruction(CI, lowerGetGCFrameSlot(CI, F), it);
324 : : }
325 [ + + ]: 6952900 : else if (callee == GCAllocBytesFunc) {
326 : 405301 : replaceInstruction(CI, lowerGCAllocBytes(CI, F), it);
327 : : }
328 [ + + ]: 6547600 : else if (callee == queueGCRootFunc) {
329 : 72769 : replaceInstruction(CI, lowerQueueGCRoot(CI, F), it);
330 : : }
331 [ + + ]: 6474830 : else if (callee == queueGCBindingFunc) {
332 : 1848 : replaceInstruction(CI, lowerQueueGCBinding(CI, F), it);
333 : : }
334 : : else {
335 : 6472980 : ++it;
336 : : }
337 : : }
338 : : }
339 : :
340 : 685629 : return true;
341 : : }
342 : :
343 : : struct FinalLowerGCLegacy: public FunctionPass {
344 : : static char ID;
345 : 1052 : FinalLowerGCLegacy() : FunctionPass(ID), finalLowerGC(FinalLowerGC()) {}
346 : :
347 : : protected:
348 : 1052 : void getAnalysisUsage(AnalysisUsage &AU) const override {
349 : 1052 : FunctionPass::getAnalysisUsage(AU);
350 : 1052 : }
351 : :
352 : : private:
353 : : bool runOnFunction(Function &F) override;
354 : : bool doInitialization(Module &M) override;
355 : : bool doFinalization(Module &M) override;
356 : :
357 : : FinalLowerGC finalLowerGC;
358 : : };
359 : :
360 : 689606 : bool FinalLowerGCLegacy::runOnFunction(Function &F) {
361 : 689606 : return finalLowerGC.runOnFunction(F);
362 : : }
363 : :
364 : 334505 : bool FinalLowerGCLegacy::doInitialization(Module &M) {
365 : 334505 : return finalLowerGC.doInitialization(M);
366 : : }
367 : :
368 : 334505 : bool FinalLowerGCLegacy::doFinalization(Module &M) {
369 : 334505 : return finalLowerGC.doFinalization(M);
370 : : }
371 : :
372 : :
373 : 0 : PreservedAnalyses FinalLowerGCPass::run(Module &M, ModuleAnalysisManager &AM)
374 : : {
375 : 0 : auto finalLowerGC = FinalLowerGC();
376 : 0 : bool modified = false;
377 : 0 : modified |= finalLowerGC.doInitialization(M);
378 [ # # ]: 0 : for (auto &F : M.functions()) {
379 [ # # ]: 0 : if (F.isDeclaration())
380 : 0 : continue;
381 : 0 : modified |= finalLowerGC.runOnFunction(F);
382 : : }
383 : 0 : modified |= finalLowerGC.doFinalization(M);
384 [ # # ]: 0 : if (modified) {
385 : 0 : return PreservedAnalyses::allInSet<CFGAnalyses>();
386 : : }
387 : 0 : return PreservedAnalyses::all();
388 : : }
389 : :
390 : : char FinalLowerGCLegacy::ID = 0;
391 : : static RegisterPass<FinalLowerGCLegacy> X("FinalLowerGC", "Final GC intrinsic lowering pass", false, false);
392 : :
393 : 1052 : Pass *createFinalLowerGCPass()
394 : : {
395 : 1052 : return new FinalLowerGCLegacy();
396 : : }
397 : :
398 : 0 : extern "C" JL_DLLEXPORT void LLVMExtraAddFinalLowerGCPass_impl(LLVMPassManagerRef PM)
399 : : {
400 : 0 : unwrap(PM)->add(createFinalLowerGCPass());
401 : 0 : }
|