Branch data Line data Source code
1 : : // This file is a part of Julia. License is MIT: https://julialang.org/license
2 : : // LLVM pass to lower TLS access and remove references to julia intrinsics
3 : :
4 : : #include "llvm-version.h"
5 : : #include "support/dtypes.h"
6 : : #include "passes.h"
7 : :
8 : : #include <llvm-c/Core.h>
9 : : #include <llvm-c/Types.h>
10 : :
11 : : #include <llvm/Pass.h>
12 : : #include <llvm/IR/Module.h>
13 : : #include <llvm/IR/LegacyPassManager.h>
14 : : #include <llvm/IR/Function.h>
15 : : #include <llvm/IR/Instructions.h>
16 : : #include <llvm/IR/Constants.h>
17 : : #include <llvm/IR/LLVMContext.h>
18 : : #include <llvm/IR/MDBuilder.h>
19 : :
20 : : #include <llvm/IR/InlineAsm.h>
21 : : #include <llvm/Transforms/Utils/BasicBlockUtils.h>
22 : :
23 : : #include "julia.h"
24 : : #include "julia_internal.h"
25 : : #include "codegen_shared.h"
26 : : #include "julia_assert.h"
27 : :
28 : : #define DEBUG_TYPE "lower_ptls"
29 : : #undef DEBUG
30 : :
31 : : using namespace llvm;
32 : :
33 : : typedef Instruction TerminatorInst;
34 : :
35 : : namespace {
36 : :
37 : : struct LowerPTLS {
38 : 40403 : LowerPTLS(bool imaging_mode=false)
39 : 40403 : : imaging_mode(imaging_mode)
40 : 40403 : {}
41 : :
42 : : bool runOnModule(Module &M, bool *CFGModified);
43 : : private:
44 : : const bool imaging_mode;
45 : : Module *M;
46 : : Function *pgcstack_getter;
47 : : MDNode *tbaa_const;
48 : : FunctionType *FT_pgcstack_getter;
49 : : PointerType *T_pgcstack_getter;
50 : : PointerType *T_pppjlvalue;
51 : : GlobalVariable *pgcstack_func_slot{nullptr};
52 : : GlobalVariable *pgcstack_key_slot{nullptr};
53 : : GlobalVariable *pgcstack_offset{nullptr};
54 : : void set_pgcstack_attrs(CallInst *pgcstack) const;
55 : : Instruction *emit_pgcstack_tp(Value *offset, Instruction *insertBefore) const;
56 : : template<typename T> T *add_comdat(T *G) const;
57 : : GlobalVariable *create_aliased_global(Type *T, StringRef name) const;
58 : : void fix_pgcstack_use(CallInst *pgcstack, bool *CFGModified);
59 : : };
60 : :
61 : 36524 : void LowerPTLS::set_pgcstack_attrs(CallInst *pgcstack) const
62 : : {
63 : 36524 : addFnAttr(pgcstack, Attribute::ReadNone);
64 : 36524 : addFnAttr(pgcstack, Attribute::NoUnwind);
65 : 36524 : }
66 : :
67 : 82447 : Instruction *LowerPTLS::emit_pgcstack_tp(Value *offset, Instruction *insertBefore) const
68 : : {
69 : : Value *tls;
70 : : #if defined(_CPU_X86_64_) || defined(_CPU_X86_)
71 [ + + ]: 82447 : if (insertBefore->getFunction()->callsFunctionThatReturnsTwice()) {
72 : : // Workaround LLVM bug by hiding the offset computation
73 : : // (and therefore the optimization opportunity) from LLVM.
74 : : // Ref https://github.com/JuliaLang/julia/issues/17288
75 : 15 : static const std::string const_asm_str = [&] () {
76 : 15 : std::string stm;
77 : : # if defined(_CPU_X86_64_)
78 : 15 : raw_string_ostream(stm) << "movq %fs:0, $0;\naddq $$" << jl_tls_offset << ", $0";
79 : : # else
80 : : raw_string_ostream(stm) << "movl %gs:0, $0;\naddl $$" << jl_tls_offset << ", $0";
81 : : # endif
82 : 15 : return stm;
83 [ + + + - ]: 3908 : }();
84 : : # if defined(_CPU_X86_64_)
85 : 3908 : const char *dyn_asm_str = "movq %fs:0, $0;\naddq $1, $0";
86 : : # else
87 : : const char *dyn_asm_str = "movl %gs:0, $0;\naddl $1, $0";
88 : : # endif
89 : :
90 : : // The add instruction clobbers flags
91 [ + + ]: 3908 : if (offset) {
92 : 1480 : std::vector<Type*> args(0);
93 : 1480 : args.push_back(offset->getType());
94 : 1480 : auto tp = InlineAsm::get(FunctionType::get(Type::getInt8PtrTy(insertBefore->getContext()), args, false),
95 : : dyn_asm_str, "=&r,r,~{dirflag},~{fpsr},~{flags}", false);
96 : 1480 : tls = CallInst::Create(tp, offset, "pgcstack_i8", insertBefore);
97 : : }
98 : : else {
99 : 2428 : auto tp = InlineAsm::get(FunctionType::get(Type::getInt8PtrTy(insertBefore->getContext()), false),
100 : : const_asm_str.c_str(), "=r,~{dirflag},~{fpsr},~{flags}",
101 : : false);
102 : 2428 : tls = CallInst::Create(tp, "pgcstack_i8", insertBefore);
103 : : }
104 : : }
105 : : else
106 : : #endif
107 : : {
108 : : // AArch64/ARM doesn't seem to have this issue.
109 : : // (Possibly because there are many more registers and the offset is
110 : : // positive and small)
111 : : // It's also harder to emit the offset in a generic way on ARM/AArch64
112 : : // (need to generate one or two `add` with shift) so let llvm emit
113 : : // the add for now.
114 : : #if defined(_CPU_AARCH64_)
115 : : const char *asm_str = "mrs $0, tpidr_el0";
116 : : #elif defined(__ARM_ARCH) && __ARM_ARCH >= 7
117 : : const char *asm_str = "mrc p15, 0, $0, c13, c0, 3";
118 : : #elif defined(_CPU_X86_64_)
119 : 78539 : const char *asm_str = "movq %fs:0, $0";
120 : : #elif defined(_CPU_X86_)
121 : : const char *asm_str = "movl %gs:0, $0";
122 : : #else
123 : : const char *asm_str = nullptr;
124 : : assert(0 && "Cannot emit thread pointer for this architecture.");
125 : : #endif
126 [ + + ]: 78539 : if (!offset)
127 : 43495 : offset = ConstantInt::getSigned(getSizeTy(insertBefore->getContext()), jl_tls_offset);
128 : 78539 : auto tp = InlineAsm::get(FunctionType::get(Type::getInt8PtrTy(insertBefore->getContext()), false), asm_str, "=r", false);
129 : 78539 : tls = CallInst::Create(tp, "thread_ptr", insertBefore);
130 : 78539 : tls = GetElementPtrInst::Create(Type::getInt8Ty(insertBefore->getContext()), tls, {offset}, "ppgcstack_i8", insertBefore);
131 : : }
132 : 82447 : tls = new BitCastInst(tls, T_pppjlvalue->getPointerTo(), "ppgcstack", insertBefore);
133 : 82447 : return new LoadInst(T_pppjlvalue, tls, "pgcstack", false, insertBefore);
134 : : }
135 : :
136 : 6 : GlobalVariable *LowerPTLS::create_aliased_global(Type *T, StringRef name) const
137 : : {
138 : : // Create a static global variable and points a global alias to it so that
139 : : // the address is visible externally but LLVM can still assume that the
140 : : // address of this variable doesn't need dynamic relocation
141 : : // (can be accessed with a single PC-rel load).
142 : 6 : auto GV = new GlobalVariable(*M, T, false, GlobalVariable::InternalLinkage,
143 : 6 : Constant::getNullValue(T), name + ".real");
144 : 6 : add_comdat(GlobalAlias::create(T, 0, GlobalVariable::ExternalLinkage,
145 : 6 : name, GV, M));
146 : 6 : return GV;
147 : : }
148 : :
149 : : template<typename T>
150 : 6 : inline T *LowerPTLS::add_comdat(T *G) const
151 : : {
152 : : #if defined(_OS_WINDOWS_)
153 : : // add __declspec(dllexport) to everything marked for export
154 : : if (G->getLinkage() == GlobalValue::ExternalLinkage)
155 : : G->setDLLStorageClass(GlobalValue::DLLExportStorageClass);
156 : : else
157 : : G->setDLLStorageClass(GlobalValue::DefaultStorageClass);
158 : : #endif
159 : 6 : return G;
160 : : }
161 : :
162 : 131071 : void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, bool *CFGModified)
163 : : {
164 [ + + ]: 131071 : if (pgcstack->use_empty()) {
165 : 48624 : pgcstack->eraseFromParent();
166 : 48624 : return;
167 : : }
168 : :
169 [ + + ]: 82447 : if (imaging_mode) {
170 [ + - ]: 36524 : if (jl_tls_elf_support) {
171 : : // if (offset != 0)
172 : : // pgcstack = tp + offset;
173 : : // else
174 : : // pgcstack = getter();
175 : 36524 : auto offset = new LoadInst(getSizeTy(pgcstack->getContext()), pgcstack_offset, "", false, pgcstack);
176 : 36524 : offset->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
177 : 36524 : offset->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None));
178 : : auto cmp = new ICmpInst(pgcstack, CmpInst::ICMP_NE, offset,
179 : 36524 : Constant::getNullValue(offset->getType()));
180 : 36524 : MDBuilder MDB(pgcstack->getContext());
181 : 36524 : SmallVector<uint32_t, 2> Weights{9, 1};
182 : : TerminatorInst *fastTerm;
183 : : TerminatorInst *slowTerm;
184 : 36524 : SplitBlockAndInsertIfThenElse(cmp, pgcstack, &fastTerm, &slowTerm,
185 : : MDB.createBranchWeights(Weights));
186 [ - + ]: 36524 : if (CFGModified)
187 : 0 : *CFGModified = true;
188 : :
189 : 36524 : auto fastTLS = emit_pgcstack_tp(offset, fastTerm);
190 : 36524 : auto phi = PHINode::Create(T_pppjlvalue, 2, "", pgcstack);
191 : 36524 : pgcstack->replaceAllUsesWith(phi);
192 : 36524 : pgcstack->moveBefore(slowTerm);
193 : 36524 : auto getter = new LoadInst(T_pgcstack_getter, pgcstack_func_slot, "", false, pgcstack);
194 : 36524 : getter->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
195 : 36524 : getter->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None));
196 : 36524 : pgcstack->setCalledFunction(pgcstack->getFunctionType(), getter);
197 : 36524 : set_pgcstack_attrs(pgcstack);
198 : :
199 : 36524 : phi->addIncoming(fastTLS, fastTLS->getParent());
200 : 36524 : phi->addIncoming(pgcstack, pgcstack->getParent());
201 : :
202 : 36524 : return;
203 : : }
204 : : // In imaging mode, we emit the function address as a load of a static
205 : : // variable to be filled (in `staticdata.c`) at initialization time of the sysimg.
206 : : // This way we can bypass the extra indirection in `jl_get_pgcstack`
207 : : // since we may not know which getter function to use ahead of time.
208 : 0 : auto getter = new LoadInst(T_pgcstack_getter, pgcstack_func_slot, "", false, pgcstack);
209 : 0 : getter->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
210 : 0 : getter->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None));
211 : : #if defined(_OS_DARWIN_)
212 : : auto key = new LoadInst(getSizeTy(pgcstack->getContext()), pgcstack_key_slot, "", false, pgcstack);
213 : : key->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
214 : : key->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None));
215 : : auto new_pgcstack = CallInst::Create(FT_pgcstack_getter, getter, {key}, "", pgcstack);
216 : : new_pgcstack->takeName(pgcstack);
217 : : pgcstack->replaceAllUsesWith(new_pgcstack);
218 : : pgcstack->eraseFromParent();
219 : : pgcstack = new_pgcstack;
220 : : #else
221 : 0 : pgcstack->setCalledFunction(pgcstack->getFunctionType(), getter);
222 : : #endif
223 : 0 : set_pgcstack_attrs(pgcstack);
224 : : }
225 [ + - ]: 45923 : else if (jl_tls_offset != -1) {
226 : 45923 : pgcstack->replaceAllUsesWith(emit_pgcstack_tp(nullptr, pgcstack));
227 : 45923 : pgcstack->eraseFromParent();
228 : : }
229 : : else {
230 : : // use the address of the actual getter function directly
231 : : jl_get_pgcstack_func *f;
232 : : jl_pgcstack_key_t k;
233 : 0 : jl_pgcstack_getkey(&f, &k);
234 : 0 : Constant *val = ConstantInt::get(getSizeTy(pgcstack->getContext()), (uintptr_t)f);
235 : 0 : val = ConstantExpr::getIntToPtr(val, T_pgcstack_getter);
236 : : #if defined(_OS_DARWIN_)
237 : : assert(sizeof(k) == sizeof(uintptr_t));
238 : : Constant *key = ConstantInt::get(getSizeTy(pgcstack->getContext()), (uintptr_t)k);
239 : : auto new_pgcstack = CallInst::Create(FT_pgcstack_getter, val, {key}, "", pgcstack);
240 : : new_pgcstack->takeName(pgcstack);
241 : : pgcstack->replaceAllUsesWith(new_pgcstack);
242 : : pgcstack->eraseFromParent();
243 : : pgcstack = new_pgcstack;
244 : : #else
245 : 0 : pgcstack->setCalledFunction(pgcstack->getFunctionType(), val);
246 : : #endif
247 : 0 : set_pgcstack_attrs(pgcstack);
248 : : }
249 : : }
250 : :
251 : 40403 : bool LowerPTLS::runOnModule(Module &_M, bool *CFGModified)
252 : : {
253 : 40403 : M = &_M;
254 : 40403 : pgcstack_getter = M->getFunction("julia.get_pgcstack");
255 [ + + ]: 40403 : if (!pgcstack_getter)
256 : 2081 : return false;
257 : :
258 : 38322 : tbaa_const = tbaa_make_child_with_context(_M.getContext(), "jtbaa_const", nullptr, true).first;
259 : :
260 : 38322 : FT_pgcstack_getter = pgcstack_getter->getFunctionType();
261 : : #if defined(_OS_DARWIN_)
262 : : assert(sizeof(jl_pgcstack_key_t) == sizeof(uintptr_t));
263 : : FT_pgcstack_getter = FunctionType::get(FT_pgcstack_getter->getReturnType(), {getSizeTy(_M.getContext())}, false);
264 : : #endif
265 : 38322 : T_pgcstack_getter = FT_pgcstack_getter->getPointerTo();
266 : 38322 : T_pppjlvalue = cast<PointerType>(FT_pgcstack_getter->getReturnType());
267 [ + + ]: 38322 : if (imaging_mode) {
268 : 2 : pgcstack_func_slot = create_aliased_global(T_pgcstack_getter, "jl_pgcstack_func_slot");
269 : 2 : pgcstack_key_slot = create_aliased_global(getSizeTy(_M.getContext()), "jl_pgcstack_key_slot"); // >= sizeof(jl_pgcstack_key_t)
270 : 2 : pgcstack_offset = create_aliased_global(getSizeTy(_M.getContext()), "jl_tls_offset");
271 : : }
272 : :
273 [ + + ]: 169393 : for (auto it = pgcstack_getter->user_begin(); it != pgcstack_getter->user_end();) {
274 : 131071 : auto call = cast<CallInst>(*it);
275 : 131071 : ++it;
276 [ - + ]: 131071 : assert(call->getCalledOperand() == pgcstack_getter);
277 : 131071 : fix_pgcstack_use(call, CFGModified);
278 : : }
279 [ - + ]: 38322 : assert(pgcstack_getter->use_empty());
280 : 38322 : pgcstack_getter->eraseFromParent();
281 : 38322 : return true;
282 : : }
283 : :
284 : : struct LowerPTLSLegacy: public ModulePass {
285 : : static char ID;
286 : 17 : LowerPTLSLegacy(bool imaging_mode=false)
287 : 17 : : ModulePass(ID),
288 : 17 : imaging_mode(imaging_mode)
289 : 17 : {}
290 : :
291 : : bool imaging_mode;
292 : 40403 : bool runOnModule(Module &M) override {
293 : 40403 : LowerPTLS lower(imaging_mode);
294 : 40403 : return lower.runOnModule(M, nullptr);
295 : : }
296 : : };
297 : :
298 : : char LowerPTLSLegacy::ID = 0;
299 : :
300 : : static RegisterPass<LowerPTLSLegacy> X("LowerPTLS", "LowerPTLS Pass",
301 : : false /* Only looks at CFG */,
302 : : false /* Analysis Pass */);
303 : :
304 : : } // anonymous namespace
305 : :
306 : 0 : PreservedAnalyses LowerPTLSPass::run(Module &M, ModuleAnalysisManager &AM) {
307 : 0 : LowerPTLS lower(imaging_mode);
308 : 0 : bool CFGModified = false;
309 [ # # ]: 0 : if (lower.runOnModule(M, &CFGModified)) {
310 [ # # ]: 0 : if (CFGModified) {
311 : 0 : return PreservedAnalyses::none();
312 : : } else {
313 : 0 : return PreservedAnalyses::allInSet<CFGAnalyses>();
314 : : }
315 : : }
316 : 0 : return PreservedAnalyses::all();
317 : : }
318 : :
319 : 17 : Pass *createLowerPTLSPass(bool imaging_mode)
320 : : {
321 : 17 : return new LowerPTLSLegacy(imaging_mode);
322 : : }
323 : :
324 : 0 : extern "C" JL_DLLEXPORT void LLVMExtraAddLowerPTLSPass_impl(LLVMPassManagerRef PM, LLVMBool imaging_mode)
325 : : {
326 : 0 : unwrap(PM)->add(createLowerPTLSPass(imaging_mode));
327 : 0 : }
|