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 : 60842 : Value *FinalLowerGC::lowerNewGCFrame(CallInst *target, Function &F)
68 : : {
69 : : assert(target->arg_size() == 1);
70 : 60842 : unsigned nRoots = cast<ConstantInt>(target->getArgOperand(0))->getLimitedValue(INT_MAX);
71 : :
72 : : // Create the GC frame.
73 : : AllocaInst *gcframe = new AllocaInst(
74 : 60842 : T_prjlvalue,
75 : : 0,
76 : 60842 : ConstantInt::get(Type::getInt32Ty(F.getContext()), nRoots + 2),
77 : 60842 : Align(16));
78 : 60842 : gcframe->insertAfter(target);
79 : 60842 : gcframe->takeName(target);
80 : :
81 : : // Zero out the GC frame.
82 : 60842 : BitCastInst *tempSlot_i8 = new BitCastInst(gcframe, Type::getInt8PtrTy(F.getContext()), "");
83 : 60842 : tempSlot_i8->insertAfter(gcframe);
84 : 60842 : Type *argsT[2] = {tempSlot_i8->getType(), Type::getInt32Ty(F.getContext())};
85 : 60842 : Function *memset = Intrinsic::getDeclaration(F.getParent(), Intrinsic::memset, makeArrayRef(argsT));
86 : : Value *args[4] = {
87 : : tempSlot_i8, // dest
88 : 60842 : ConstantInt::get(Type::getInt8Ty(F.getContext()), 0), // val
89 : 60842 : ConstantInt::get(Type::getInt32Ty(F.getContext()), sizeof(jl_value_t*) * (nRoots + 2)), // len
90 : 182526 : ConstantInt::get(Type::getInt1Ty(F.getContext()), 0)}; // volatile
91 : 60842 : CallInst *zeroing = CallInst::Create(memset, makeArrayRef(args));
92 : 60842 : cast<MemSetInst>(zeroing)->setDestAlignment(16);
93 : 60842 : zeroing->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
94 : 60842 : zeroing->insertAfter(tempSlot_i8);
95 : :
96 : 60842 : return gcframe;
97 : : }
98 : :
99 : 60842 : void FinalLowerGC::lowerPushGCFrame(CallInst *target, Function &F)
100 : : {
101 : : assert(target->arg_size() == 2);
102 : 60842 : auto gcframe = target->getArgOperand(0);
103 : 60842 : unsigned nRoots = cast<ConstantInt>(target->getArgOperand(1))->getLimitedValue(INT_MAX);
104 : :
105 : 60842 : IRBuilder<> builder(target->getContext());
106 : 60842 : builder.SetInsertPoint(&*(++BasicBlock::iterator(target)));
107 : 243368 : StoreInst *inst = builder.CreateAlignedStore(
108 : 60842 : ConstantInt::get(getSizeTy(F.getContext()), JL_GC_ENCODE_PUSHARGS(nRoots)),
109 : : builder.CreateBitCast(
110 : 60842 : builder.CreateConstInBoundsGEP1_32(T_prjlvalue, gcframe, 0),
111 : 60842 : getSizeTy(F.getContext())->getPointerTo()),
112 : 60842 : Align(sizeof(void*)));
113 : 60842 : inst->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
114 : 60842 : auto T_ppjlvalue = JuliaType::get_ppjlvalue_ty(F.getContext());
115 : 243368 : inst = builder.CreateAlignedStore(
116 : 60842 : builder.CreateAlignedLoad(T_ppjlvalue, pgcstack, Align(sizeof(void*))),
117 : : builder.CreatePointerCast(
118 : 60842 : builder.CreateConstInBoundsGEP1_32(T_prjlvalue, gcframe, 1),
119 : 60842 : PointerType::get(T_ppjlvalue, 0)),
120 : 60842 : Align(sizeof(void*)));
121 : 60842 : inst->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
122 : 121684 : inst = builder.CreateAlignedStore(
123 : : gcframe,
124 : 60842 : builder.CreateBitCast(pgcstack, PointerType::get(PointerType::get(T_prjlvalue, 0), 0)),
125 : 121684 : Align(sizeof(void*)));
126 : 60842 : }
127 : :
128 : 58182 : void FinalLowerGC::lowerPopGCFrame(CallInst *target, Function &F)
129 : : {
130 : : assert(target->arg_size() == 1);
131 : 58182 : auto gcframe = target->getArgOperand(0);
132 : :
133 : 116364 : IRBuilder<> builder(target->getContext());
134 : 58182 : builder.SetInsertPoint(target);
135 : : Instruction *gcpop =
136 : 58182 : cast<Instruction>(builder.CreateConstInBoundsGEP1_32(T_prjlvalue, gcframe, 1));
137 : 58182 : Instruction *inst = builder.CreateAlignedLoad(T_prjlvalue, gcpop, Align(sizeof(void*)));
138 : 58182 : inst->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
139 : 116364 : inst = builder.CreateAlignedStore(
140 : : inst,
141 : 58182 : builder.CreateBitCast(pgcstack,
142 : 58182 : PointerType::get(T_prjlvalue, 0)),
143 : 58182 : Align(sizeof(void*)));
144 : 58182 : inst->setMetadata(LLVMContext::MD_tbaa, tbaa_gcframe);
145 : 58182 : }
146 : :
147 : 560842 : Value *FinalLowerGC::lowerGetGCFrameSlot(CallInst *target, Function &F)
148 : : {
149 : : assert(target->arg_size() == 2);
150 : 560842 : auto gcframe = target->getArgOperand(0);
151 : 560842 : auto index = target->getArgOperand(1);
152 : :
153 : : // Initialize an IR builder.
154 : 560842 : IRBuilder<> builder(target->getContext());
155 : 560842 : builder.SetInsertPoint(target);
156 : :
157 : : // The first two slots are reserved, so we'll add two to the index.
158 : 560842 : index = builder.CreateAdd(index, ConstantInt::get(Type::getInt32Ty(F.getContext()), 2));
159 : :
160 : : // Lower the intrinsic as a GEP.
161 : 560842 : auto gep = builder.CreateInBoundsGEP(T_prjlvalue, gcframe, index);
162 : 560842 : gep->takeName(target);
163 : 560842 : return gep;
164 : : }
165 : :
166 : 26899 : Value *FinalLowerGC::lowerQueueGCRoot(CallInst *target, Function &F)
167 : : {
168 : : assert(target->arg_size() == 1);
169 : 26899 : target->setCalledFunction(queueRootFunc);
170 : 26899 : return target;
171 : : }
172 : :
173 : 639 : Value *FinalLowerGC::lowerQueueGCBinding(CallInst *target, Function &F)
174 : : {
175 : : assert(target->arg_size() == 1);
176 : 639 : target->setCalledFunction(queueBindingFunc);
177 : 639 : return target;
178 : : }
179 : :
180 : 63997 : Value *FinalLowerGC::lowerGCAllocBytes(CallInst *target, Function &F)
181 : : {
182 : : assert(target->arg_size() == 2);
183 : 63997 : auto sz = (size_t)cast<ConstantInt>(target->getArgOperand(1))->getZExtValue();
184 : : // This is strongly architecture and OS dependent
185 : : int osize;
186 : 63997 : int offset = jl_gc_classify_pools(sz, &osize);
187 : 63997 : IRBuilder<> builder(target);
188 : 63997 : builder.SetCurrentDebugLocation(target->getDebugLoc());
189 : 63997 : auto ptls = target->getArgOperand(0);
190 : : CallInst *newI;
191 [ - + ]: 63997 : if (offset < 0) {
192 : 0 : newI = builder.CreateCall(
193 : : bigAllocFunc,
194 : 0 : { ptls, ConstantInt::get(getSizeTy(F.getContext()), sz + sizeof(void*)) });
195 : : }
196 : : else {
197 : 63997 : auto pool_offs = ConstantInt::get(Type::getInt32Ty(F.getContext()), offset);
198 : 63997 : auto pool_osize = ConstantInt::get(Type::getInt32Ty(F.getContext()), osize);
199 : 63997 : newI = builder.CreateCall(poolAllocFunc, { ptls, pool_offs, pool_osize });
200 : : }
201 : 63997 : newI->setAttributes(newI->getCalledFunction()->getAttributes());
202 : 63997 : newI->takeName(target);
203 : 63997 : return newI;
204 : : }
205 : :
206 : 40403 : bool FinalLowerGC::doInitialization(Module &M) {
207 : : // Initialize platform-agnostic references.
208 : 40403 : initAll(M);
209 : :
210 : : // Initialize platform-specific references.
211 : 40403 : queueRootFunc = getOrDeclare(jl_well_known::GCQueueRoot);
212 : 40403 : queueBindingFunc = getOrDeclare(jl_well_known::GCQueueBinding);
213 : 40403 : poolAllocFunc = getOrDeclare(jl_well_known::GCPoolAlloc);
214 : 40403 : bigAllocFunc = getOrDeclare(jl_well_known::GCBigAlloc);
215 : :
216 : 40403 : GlobalValue *functionList[] = {queueRootFunc, queueBindingFunc, poolAllocFunc, bigAllocFunc};
217 : 40403 : unsigned j = 0;
218 [ + + ]: 202015 : for (unsigned i = 0; i < sizeof(functionList) / sizeof(void*); i++) {
219 [ - + ]: 161612 : if (!functionList[i])
220 : 0 : continue;
221 [ - + ]: 161612 : if (i != j)
222 : 0 : functionList[j] = functionList[i];
223 : 161612 : j++;
224 : : }
225 [ + - ]: 40403 : if (j != 0)
226 : 40403 : appendToCompilerUsed(M, ArrayRef<GlobalValue*>(functionList, j));
227 : 40403 : return true;
228 : : }
229 : :
230 : 40403 : bool FinalLowerGC::doFinalization(Module &M)
231 : : {
232 : 40403 : GlobalValue *functionList[] = {queueRootFunc, queueBindingFunc, poolAllocFunc, bigAllocFunc};
233 : 40403 : queueRootFunc = queueBindingFunc = poolAllocFunc = bigAllocFunc = nullptr;
234 : 40403 : auto used = M.getGlobalVariable("llvm.compiler.used");
235 [ - + ]: 40403 : if (!used)
236 : 0 : return false;
237 : : SmallPtrSet<Constant*, 16> InitAsSet(
238 : : functionList,
239 : 80806 : functionList + sizeof(functionList) / sizeof(void*));
240 : 40403 : bool changed = false;
241 : 80806 : SmallVector<Constant*, 16> init;
242 : 40403 : ConstantArray *CA = cast<ConstantArray>(used->getInitializer());
243 [ + + ]: 202015 : for (auto &Op : CA->operands()) {
244 : 161612 : Constant *C = cast_or_null<Constant>(Op);
245 [ + - ]: 161612 : if (InitAsSet.count(C->stripPointerCasts())) {
246 : 161612 : changed = true;
247 : 161612 : continue;
248 : : }
249 : 0 : init.push_back(C);
250 : : }
251 [ - + ]: 40403 : if (!changed)
252 : 0 : return false;
253 : 40403 : used->eraseFromParent();
254 [ + - ]: 40403 : if (init.empty())
255 : 40403 : 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 : 713219 : static void replaceInstruction(
265 : : Instruction *oldInstruction,
266 : : Value *newInstruction,
267 : : TIterator &it)
268 : : {
269 [ + + ]: 713219 : if (newInstruction != oldInstruction) {
270 : 685681 : oldInstruction->replaceAllUsesWith(newInstruction);
271 : 685681 : it = oldInstruction->eraseFromParent();
272 : : }
273 : : else {
274 : 27538 : ++it;
275 : : }
276 : 713219 : }
277 : :
278 : 137811 : 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 : 137811 : initFunctions(*F.getParent());
283 [ + + ]: 137811 : if (!pgcstack_getter)
284 : 5557 : return false;
285 : :
286 : : // Look for a call to 'julia.get_pgcstack'.
287 : 132254 : pgcstack = getPGCstack(F);
288 [ + + ]: 132254 : if (!pgcstack)
289 : 1183 : return false;
290 : :
291 : : // Acquire intrinsic functions.
292 : 131071 : auto newGCFrameFunc = getOrNull(jl_intrinsics::newGCFrame);
293 : 131071 : auto pushGCFrameFunc = getOrNull(jl_intrinsics::pushGCFrame);
294 : 131071 : auto popGCFrameFunc = getOrNull(jl_intrinsics::popGCFrame);
295 : 131071 : auto getGCFrameSlotFunc = getOrNull(jl_intrinsics::getGCFrameSlot);
296 : 131071 : auto GCAllocBytesFunc = getOrNull(jl_intrinsics::GCAllocBytes);
297 : 131071 : auto queueGCRootFunc = getOrNull(jl_intrinsics::queueGCRoot);
298 : 131071 : auto queueGCBindingFunc = getOrNull(jl_intrinsics::queueGCBinding);
299 : :
300 : : // Lower all calls to supported intrinsics.
301 [ + + ]: 1875880 : for (BasicBlock &BB : F) {
302 [ + + ]: 27203300 : for (auto it = BB.begin(); it != BB.end();) {
303 : 25458500 : auto *CI = dyn_cast<CallInst>(&*it);
304 [ + + ]: 25458500 : if (!CI) {
305 : 23361700 : ++it;
306 : 23361700 : continue;
307 : : }
308 : :
309 : 2096840 : Value *callee = CI->getCalledOperand();
310 : :
311 [ + + ]: 2096840 : if (callee == newGCFrameFunc) {
312 : 60842 : replaceInstruction(CI, lowerNewGCFrame(CI, F), it);
313 : : }
314 [ + + ]: 2036000 : else if (callee == pushGCFrameFunc) {
315 : 60842 : lowerPushGCFrame(CI, F);
316 : 60842 : it = CI->eraseFromParent();
317 : : }
318 [ + + ]: 1975160 : else if (callee == popGCFrameFunc) {
319 : 58182 : lowerPopGCFrame(CI, F);
320 : 58182 : it = CI->eraseFromParent();
321 : : }
322 [ + + ]: 1916970 : else if (callee == getGCFrameSlotFunc) {
323 : 560842 : replaceInstruction(CI, lowerGetGCFrameSlot(CI, F), it);
324 : : }
325 [ + + ]: 1356130 : else if (callee == GCAllocBytesFunc) {
326 : 63997 : replaceInstruction(CI, lowerGCAllocBytes(CI, F), it);
327 : : }
328 [ + + ]: 1292130 : else if (callee == queueGCRootFunc) {
329 : 26899 : replaceInstruction(CI, lowerQueueGCRoot(CI, F), it);
330 : : }
331 [ + + ]: 1265240 : else if (callee == queueGCBindingFunc) {
332 : 639 : replaceInstruction(CI, lowerQueueGCBinding(CI, F), it);
333 : : }
334 : : else {
335 : 1264600 : ++it;
336 : : }
337 : : }
338 : : }
339 : :
340 : 131071 : return true;
341 : : }
342 : :
343 : : struct FinalLowerGCLegacy: public FunctionPass {
344 : : static char ID;
345 : 17 : FinalLowerGCLegacy() : FunctionPass(ID), finalLowerGC(FinalLowerGC()) {}
346 : :
347 : : protected:
348 : 17 : void getAnalysisUsage(AnalysisUsage &AU) const override {
349 : 17 : FunctionPass::getAnalysisUsage(AU);
350 : 17 : }
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 : 137811 : bool FinalLowerGCLegacy::runOnFunction(Function &F) {
361 : 137811 : return finalLowerGC.runOnFunction(F);
362 : : }
363 : :
364 : 40403 : bool FinalLowerGCLegacy::doInitialization(Module &M) {
365 : 40403 : return finalLowerGC.doInitialization(M);
366 : : }
367 : :
368 : 40403 : bool FinalLowerGCLegacy::doFinalization(Module &M) {
369 : 40403 : 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 : 17 : Pass *createFinalLowerGCPass()
394 : : {
395 : 17 : 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 : }
|