Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license
2 : :
3 : : //
4 : : // This file implements common functionality that is useful for the late GC frame
5 : : // lowering and final GC intrinsic lowering passes. See the corresponding header
6 : : // for docs.
7 : :
8 : : #include "llvm-version.h"
9 : :
10 : : #include <llvm/IR/Function.h>
11 : : #include <llvm/IR/Metadata.h>
12 : : #include <llvm/IR/Module.h>
13 : : #include <llvm/IR/Type.h>
14 : :
15 : : #include "codegen_shared.h"
16 : : #include "julia_assert.h"
17 : : #include "llvm-pass-helpers.h"
18 : :
19 : : using namespace llvm;
20 : :
21 : 1207150 : JuliaPassContext::JuliaPassContext()
22 : : : T_prjlvalue(nullptr),
23 : :
24 : : tbaa_gcframe(nullptr), tbaa_tag(nullptr),
25 : :
26 : : pgcstack_getter(nullptr), gc_flush_func(nullptr),
27 : : gc_preserve_begin_func(nullptr), gc_preserve_end_func(nullptr),
28 : : pointer_from_objref_func(nullptr), alloc_obj_func(nullptr),
29 : : typeof_func(nullptr), write_barrier_func(nullptr),
30 : : write_barrier_binding_func(nullptr), call_func(nullptr),
31 : 1207150 : call2_func(nullptr), module(nullptr)
32 : : {
33 : 1207150 : }
34 : :
35 : 3467060 : void JuliaPassContext::initFunctions(Module &M)
36 : : {
37 : 3467060 : module = &M;
38 : 3467060 : LLVMContext &llvmctx = M.getContext();
39 : :
40 : 3467060 : tbaa_gcframe = tbaa_make_child_with_context(llvmctx, "jtbaa_gcframe").first;
41 : : MDNode *tbaa_data;
42 : : MDNode *tbaa_data_scalar;
43 : 3467060 : std::tie(tbaa_data, tbaa_data_scalar) = tbaa_make_child_with_context(llvmctx, "jtbaa_data");
44 : 3467060 : tbaa_tag = tbaa_make_child_with_context(llvmctx, "jtbaa_tag", tbaa_data_scalar).first;
45 : :
46 : 3467060 : pgcstack_getter = M.getFunction("julia.get_pgcstack");
47 : 3467060 : gc_flush_func = M.getFunction("julia.gcroot_flush");
48 : 3467060 : gc_preserve_begin_func = M.getFunction("llvm.julia.gc_preserve_begin");
49 : 3467060 : gc_preserve_end_func = M.getFunction("llvm.julia.gc_preserve_end");
50 : 3467060 : pointer_from_objref_func = M.getFunction("julia.pointer_from_objref");
51 : 3467060 : typeof_func = M.getFunction("julia.typeof");
52 : 3467060 : write_barrier_func = M.getFunction("julia.write_barrier");
53 : 3467060 : write_barrier_binding_func = M.getFunction("julia.write_barrier_binding");
54 : 3467060 : alloc_obj_func = M.getFunction("julia.gc_alloc_obj");
55 : 3467060 : call_func = M.getFunction("julia.call");
56 : 3467060 : call2_func = M.getFunction("julia.call2");
57 : 3467060 : }
58 : :
59 : 2263260 : void JuliaPassContext::initAll(Module &M)
60 : : {
61 : : // First initialize the functions.
62 : 2263260 : initFunctions(M);
63 : :
64 : : // Then initialize types and metadata nodes.
65 : 2263260 : auto &ctx = M.getContext();
66 : :
67 : : // Construct derived types.
68 : 2263260 : T_prjlvalue = JuliaType::get_prjlvalue_ty(ctx);
69 : 2263260 : }
70 : :
71 : 1372710 : llvm::CallInst *JuliaPassContext::getPGCstack(llvm::Function &F) const
72 : : {
73 : 3963750 : for (auto I = F.getEntryBlock().begin(), E = F.getEntryBlock().end();
74 [ + - + + : 6554800 : pgcstack_getter && I != E; ++I) {
+ + ]
75 [ + + ]: 3962300 : if (CallInst *callInst = dyn_cast<CallInst>(&*I)) {
76 [ + + ]: 2106050 : if (callInst->getCalledOperand() == pgcstack_getter) {
77 : 1371260 : return callInst;
78 : : }
79 : : }
80 : : }
81 : 1450 : return nullptr;
82 : : }
83 : :
84 : 9765820 : llvm::Function *JuliaPassContext::getOrNull(
85 : : const jl_intrinsics::IntrinsicDescription &desc) const
86 : : {
87 : 9765820 : return module->getFunction(desc.name);
88 : : }
89 : :
90 : 4966410 : llvm::Function *JuliaPassContext::getOrDeclare(
91 : : const jl_intrinsics::IntrinsicDescription &desc)
92 : : {
93 : 4966410 : auto local = getOrNull(desc);
94 [ + + ]: 4966410 : if (local) {
95 : : // If the function exists already, then we'll
96 : : // just return it.
97 : 2546790 : return local;
98 : : }
99 : : else {
100 : : // Otherwise, we'll declare it and add it to the module.
101 : : // Declare the function.
102 : 2419620 : auto func = desc.declare(*this);
103 : : // Add it to the function list.
104 : 2419620 : module->getFunctionList().push_back(func);
105 : : // Return the newly created function.
106 : 2419620 : return func;
107 : : }
108 : : }
109 : :
110 : : namespace jl_intrinsics {
111 : : static const char *GET_GC_FRAME_SLOT_NAME = "julia.get_gc_frame_slot";
112 : : static const char *GC_ALLOC_BYTES_NAME = "julia.gc_alloc_bytes";
113 : : static const char *NEW_GC_FRAME_NAME = "julia.new_gc_frame";
114 : : static const char *PUSH_GC_FRAME_NAME = "julia.push_gc_frame";
115 : : static const char *POP_GC_FRAME_NAME = "julia.pop_gc_frame";
116 : : static const char *QUEUE_GC_ROOT_NAME = "julia.queue_gc_root";
117 : : static const char *QUEUE_GC_BINDING_NAME = "julia.queue_gc_binding";
118 : :
119 : : // Annotates a function with attributes suitable for GC allocation
120 : : // functions. Specifically, the return value is marked noalias and nonnull.
121 : : // The allocation size is set to the first argument.
122 : 835250 : static Function *addGCAllocAttributes(Function *target, LLVMContext &context)
123 : : {
124 : 835250 : addRetAttr(target, Attribute::NoAlias);
125 : 835250 : addRetAttr(target, Attribute::NonNull);
126 : 835250 : target->addFnAttr(Attribute::getWithAllocSizeArgs(context, 1, None)); // returns %1 bytes
127 : 835250 : return target;
128 : : }
129 : :
130 : : const IntrinsicDescription getGCFrameSlot(
131 : : GET_GC_FRAME_SLOT_NAME,
132 : 226357 : [](const JuliaPassContext &context) {
133 : 905428 : return Function::Create(
134 : : FunctionType::get(
135 : 226357 : PointerType::get(context.T_prjlvalue, 0),
136 : 226357 : {PointerType::get(context.T_prjlvalue, 0), Type::getInt32Ty(context.getLLVMContext())},
137 : : false),
138 : : Function::ExternalLinkage,
139 : 226357 : GET_GC_FRAME_SLOT_NAME);
140 : : });
141 : :
142 : : const IntrinsicDescription GCAllocBytes(
143 : : GC_ALLOC_BYTES_NAME,
144 : 166240 : [](const JuliaPassContext &context) {
145 : 498720 : auto intrinsic = Function::Create(
146 : : FunctionType::get(
147 : 166240 : context.T_prjlvalue,
148 : 166240 : { Type::getInt8PtrTy(context.getLLVMContext()),
149 : 166240 : sizeof(size_t) == sizeof(uint32_t) ?
150 : : Type::getInt32Ty(context.getLLVMContext()) :
151 : : Type::getInt64Ty(context.getLLVMContext()) },
152 : : false),
153 : : Function::ExternalLinkage,
154 : : GC_ALLOC_BYTES_NAME);
155 : :
156 : 166240 : return addGCAllocAttributes(intrinsic, context.getLLVMContext());
157 : : });
158 : :
159 : : const IntrinsicDescription newGCFrame(
160 : : NEW_GC_FRAME_NAME,
161 : 226357 : [](const JuliaPassContext &context) {
162 : 679071 : auto intrinsic = Function::Create(
163 : 452714 : FunctionType::get(PointerType::get(context.T_prjlvalue, 0), {Type::getInt32Ty(context.getLLVMContext())}, false),
164 : : Function::ExternalLinkage,
165 : : NEW_GC_FRAME_NAME);
166 : 226357 : addRetAttr(intrinsic, Attribute::NoAlias);
167 : 226357 : addRetAttr(intrinsic, Attribute::NonNull);
168 : :
169 : 226357 : return intrinsic;
170 : : });
171 : :
172 : : const IntrinsicDescription pushGCFrame(
173 : : PUSH_GC_FRAME_NAME,
174 : 226357 : [](const JuliaPassContext &context) {
175 : 679071 : return Function::Create(
176 : : FunctionType::get(
177 : : Type::getVoidTy(context.getLLVMContext()),
178 : 226357 : {PointerType::get(context.T_prjlvalue, 0), Type::getInt32Ty(context.getLLVMContext())},
179 : : false),
180 : : Function::ExternalLinkage,
181 : 226357 : PUSH_GC_FRAME_NAME);
182 : : });
183 : :
184 : : const IntrinsicDescription popGCFrame(
185 : : POP_GC_FRAME_NAME,
186 : 206719 : [](const JuliaPassContext &context) {
187 : 620157 : return Function::Create(
188 : : FunctionType::get(
189 : : Type::getVoidTy(context.getLLVMContext()),
190 : 206719 : {PointerType::get(context.T_prjlvalue, 0)},
191 : : false),
192 : : Function::ExternalLinkage,
193 : 206719 : POP_GC_FRAME_NAME);
194 : : });
195 : :
196 : : const IntrinsicDescription queueGCRoot(
197 : : QUEUE_GC_ROOT_NAME,
198 : 28654 : [](const JuliaPassContext &context) {
199 : 57308 : auto intrinsic = Function::Create(
200 : : FunctionType::get(
201 : : Type::getVoidTy(context.getLLVMContext()),
202 : 28654 : { context.T_prjlvalue },
203 : : false),
204 : : Function::ExternalLinkage,
205 : : QUEUE_GC_ROOT_NAME);
206 : 28654 : intrinsic->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
207 : 28654 : return intrinsic;
208 : : });
209 : :
210 : : const IntrinsicDescription queueGCBinding(
211 : : QUEUE_GC_BINDING_NAME,
212 : 919 : [](const JuliaPassContext &context) {
213 : 1838 : auto intrinsic = Function::Create(
214 : : FunctionType::get(
215 : : Type::getVoidTy(context.getLLVMContext()),
216 : 919 : { context.T_prjlvalue },
217 : : false),
218 : : Function::ExternalLinkage,
219 : : QUEUE_GC_BINDING_NAME);
220 : 919 : intrinsic->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
221 : 919 : return intrinsic;
222 : : });
223 : : }
224 : :
225 : : namespace jl_well_known {
226 : : static const char *GC_BIG_ALLOC_NAME = XSTR(jl_gc_big_alloc);
227 : : static const char *GC_POOL_ALLOC_NAME = XSTR(jl_gc_pool_alloc);
228 : : static const char *GC_QUEUE_ROOT_NAME = XSTR(jl_gc_queue_root);
229 : : static const char *GC_QUEUE_BINDING_NAME = XSTR(jl_gc_queue_binding);
230 : :
231 : : using jl_intrinsics::addGCAllocAttributes;
232 : :
233 : : const WellKnownFunctionDescription GCBigAlloc(
234 : : GC_BIG_ALLOC_NAME,
235 : 334505 : [](const JuliaPassContext &context) {
236 : 1003520 : auto bigAllocFunc = Function::Create(
237 : : FunctionType::get(
238 : 334505 : context.T_prjlvalue,
239 : 334505 : { Type::getInt8PtrTy(context.getLLVMContext()),
240 : 334505 : sizeof(size_t) == sizeof(uint32_t) ?
241 : : Type::getInt32Ty(context.getLLVMContext()) :
242 : : Type::getInt64Ty(context.getLLVMContext()) },
243 : : false),
244 : : Function::ExternalLinkage,
245 : : GC_BIG_ALLOC_NAME);
246 : :
247 : 334505 : return addGCAllocAttributes(bigAllocFunc, context.getLLVMContext());
248 : : });
249 : :
250 : : const WellKnownFunctionDescription GCPoolAlloc(
251 : : GC_POOL_ALLOC_NAME,
252 : 334505 : [](const JuliaPassContext &context) {
253 : 1003520 : auto poolAllocFunc = Function::Create(
254 : : FunctionType::get(
255 : 334505 : context.T_prjlvalue,
256 : 334505 : { Type::getInt8PtrTy(context.getLLVMContext()), Type::getInt32Ty(context.getLLVMContext()), Type::getInt32Ty(context.getLLVMContext()) },
257 : : false),
258 : : Function::ExternalLinkage,
259 : : GC_POOL_ALLOC_NAME);
260 : :
261 : 334505 : return addGCAllocAttributes(poolAllocFunc, context.getLLVMContext());
262 : : });
263 : :
264 : : const WellKnownFunctionDescription GCQueueBinding(
265 : : GC_QUEUE_BINDING_NAME,
266 : 334505 : [](const JuliaPassContext &context) {
267 : 669010 : auto func = Function::Create(
268 : : FunctionType::get(
269 : : Type::getVoidTy(context.getLLVMContext()),
270 : 334505 : { context.T_prjlvalue },
271 : : false),
272 : : Function::ExternalLinkage,
273 : : GC_QUEUE_BINDING_NAME);
274 : 334505 : func->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
275 : 334505 : return func;
276 : : });
277 : :
278 : : const WellKnownFunctionDescription GCQueueRoot(
279 : : GC_QUEUE_ROOT_NAME,
280 : 334505 : [](const JuliaPassContext &context) {
281 : 669010 : auto func = Function::Create(
282 : : FunctionType::get(
283 : : Type::getVoidTy(context.getLLVMContext()),
284 : 334505 : { context.T_prjlvalue },
285 : : false),
286 : : Function::ExternalLinkage,
287 : : GC_QUEUE_ROOT_NAME);
288 : 334505 : func->addFnAttr(Attribute::InaccessibleMemOrArgMemOnly);
289 : 334505 : return func;
290 : : });
291 : : }
|