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 : 334499 : LowerPTLS(bool imaging_mode=false)
39 : 334499 : : imaging_mode(imaging_mode)
40 : 334499 : {}
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 : 18294 : void LowerPTLS::set_pgcstack_attrs(CallInst *pgcstack) const
62 : : {
63 : 18294 : addFnAttr(pgcstack, Attribute::ReadNone);
64 : 18294 : addFnAttr(pgcstack, Attribute::NoUnwind);
65 : 18294 : }
66 : :
67 : 329874 : Instruction *LowerPTLS::emit_pgcstack_tp(Value *offset, Instruction *insertBefore) const
68 : : {
69 : : Value *tls;
70 : : #if defined(_CPU_X86_64_) || defined(_CPU_X86_)
71 [ + + ]: 329874 : 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 : 289 : static const std::string const_asm_str = [&] () {
76 : 289 : std::string stm;
77 : : # if defined(_CPU_X86_64_)
78 : 289 : 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 : 289 : return stm;
83 [ + + + - ]: 13612 : }();
84 : : # if defined(_CPU_X86_64_)
85 : 13612 : 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 [ + + ]: 13612 : if (offset) {
92 : 738 : std::vector<Type*> args(0);
93 : 738 : args.push_back(offset->getType());
94 : 738 : auto tp = InlineAsm::get(FunctionType::get(Type::getInt8PtrTy(insertBefore->getContext()), args, false),
95 : : dyn_asm_str, "=&r,r,~{dirflag},~{fpsr},~{flags}", false);
96 : 738 : tls = CallInst::Create(tp, offset, "pgcstack_i8", insertBefore);
97 : : }
98 : : else {
99 : 12874 : auto tp = InlineAsm::get(FunctionType::get(Type::getInt8PtrTy(insertBefore->getContext()), false),
100 : : const_asm_str.c_str(), "=r,~{dirflag},~{fpsr},~{flags}",
101 : : false);
102 : 12874 : 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 : 316262 : 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 [ + + ]: 316262 : if (!offset)
127 : 298706 : offset = ConstantInt::getSigned(getSizeTy(insertBefore->getContext()), jl_tls_offset);
128 : 316262 : auto tp = InlineAsm::get(FunctionType::get(Type::getInt8PtrTy(insertBefore->getContext()), false), asm_str, "=r", false);
129 : 316262 : tls = CallInst::Create(tp, "thread_ptr", insertBefore);
130 : 316262 : tls = GetElementPtrInst::Create(Type::getInt8Ty(insertBefore->getContext()), tls, {offset}, "ppgcstack_i8", insertBefore);
131 : : }
132 : 329874 : tls = new BitCastInst(tls, T_pppjlvalue->getPointerTo(), "ppgcstack", insertBefore);
133 : 329874 : return new LoadInst(T_pppjlvalue, tls, "pgcstack", false, insertBefore);
134 : : }
135 : :
136 : 3 : 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 : 3 : auto GV = new GlobalVariable(*M, T, false, GlobalVariable::InternalLinkage,
143 : 3 : Constant::getNullValue(T), name + ".real");
144 : 3 : add_comdat(GlobalAlias::create(T, 0, GlobalVariable::ExternalLinkage,
145 : 3 : name, GV, M));
146 : 3 : return GV;
147 : : }
148 : :
149 : : template<typename T>
150 : 3 : 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 : 3 : return G;
160 : : }
161 : :
162 : 685617 : void LowerPTLS::fix_pgcstack_use(CallInst *pgcstack, bool *CFGModified)
163 : : {
164 [ + + ]: 685617 : if (pgcstack->use_empty()) {
165 : 355743 : pgcstack->eraseFromParent();
166 : 355743 : return;
167 : : }
168 : :
169 [ + + ]: 329874 : if (imaging_mode) {
170 [ + - ]: 18294 : if (jl_tls_elf_support) {
171 : : // if (offset != 0)
172 : : // pgcstack = tp + offset;
173 : : // else
174 : : // pgcstack = getter();
175 : 18294 : auto offset = new LoadInst(getSizeTy(pgcstack->getContext()), pgcstack_offset, "", false, pgcstack);
176 : 18294 : offset->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
177 : 18294 : offset->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None));
178 : : auto cmp = new ICmpInst(pgcstack, CmpInst::ICMP_NE, offset,
179 : 18294 : Constant::getNullValue(offset->getType()));
180 : 18294 : MDBuilder MDB(pgcstack->getContext());
181 : 18294 : SmallVector<uint32_t, 2> Weights{9, 1};
182 : : TerminatorInst *fastTerm;
183 : : TerminatorInst *slowTerm;
184 : 18294 : SplitBlockAndInsertIfThenElse(cmp, pgcstack, &fastTerm, &slowTerm,
185 : : MDB.createBranchWeights(Weights));
186 [ - + ]: 18294 : if (CFGModified)
187 : 0 : *CFGModified = true;
188 : :
189 : 18294 : auto fastTLS = emit_pgcstack_tp(offset, fastTerm);
190 : 18294 : auto phi = PHINode::Create(T_pppjlvalue, 2, "", pgcstack);
191 : 18294 : pgcstack->replaceAllUsesWith(phi);
192 : 18294 : pgcstack->moveBefore(slowTerm);
193 : 18294 : auto getter = new LoadInst(T_pgcstack_getter, pgcstack_func_slot, "", false, pgcstack);
194 : 18294 : getter->setMetadata(llvm::LLVMContext::MD_tbaa, tbaa_const);
195 : 18294 : getter->setMetadata(llvm::LLVMContext::MD_invariant_load, MDNode::get(pgcstack->getContext(), None));
196 : 18294 : pgcstack->setCalledFunction(pgcstack->getFunctionType(), getter);
197 : 18294 : set_pgcstack_attrs(pgcstack);
198 : :
199 : 18294 : phi->addIncoming(fastTLS, fastTLS->getParent());
200 : 18294 : phi->addIncoming(pgcstack, pgcstack->getParent());
201 : :
202 : 18294 : 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 [ + - ]: 311580 : else if (jl_tls_offset != -1) {
226 : 311580 : pgcstack->replaceAllUsesWith(emit_pgcstack_tp(nullptr, pgcstack));
227 : 311580 : 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 : 334499 : bool LowerPTLS::runOnModule(Module &_M, bool *CFGModified)
252 : : {
253 : 334499 : M = &_M;
254 : 334499 : pgcstack_getter = M->getFunction("julia.get_pgcstack");
255 [ + + ]: 334499 : if (!pgcstack_getter)
256 : 1293 : return false;
257 : :
258 : 333206 : tbaa_const = tbaa_make_child_with_context(_M.getContext(), "jtbaa_const", nullptr, true).first;
259 : :
260 : 333206 : 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 : 333206 : T_pgcstack_getter = FT_pgcstack_getter->getPointerTo();
266 : 333206 : T_pppjlvalue = cast<PointerType>(FT_pgcstack_getter->getReturnType());
267 [ + + ]: 333206 : if (imaging_mode) {
268 : 1 : pgcstack_func_slot = create_aliased_global(T_pgcstack_getter, "jl_pgcstack_func_slot");
269 : 1 : pgcstack_key_slot = create_aliased_global(getSizeTy(_M.getContext()), "jl_pgcstack_key_slot"); // >= sizeof(jl_pgcstack_key_t)
270 : 1 : pgcstack_offset = create_aliased_global(getSizeTy(_M.getContext()), "jl_tls_offset");
271 : : }
272 : :
273 [ + + ]: 1018820 : for (auto it = pgcstack_getter->user_begin(); it != pgcstack_getter->user_end();) {
274 : 685617 : auto call = cast<CallInst>(*it);
275 : 685617 : ++it;
276 [ - + ]: 685617 : assert(call->getCalledOperand() == pgcstack_getter);
277 : 685617 : fix_pgcstack_use(call, CFGModified);
278 : : }
279 [ - + ]: 333206 : assert(pgcstack_getter->use_empty());
280 : 333206 : pgcstack_getter->eraseFromParent();
281 : 333206 : return true;
282 : : }
283 : :
284 : : struct LowerPTLSLegacy: public ModulePass {
285 : : static char ID;
286 : 1052 : LowerPTLSLegacy(bool imaging_mode=false)
287 : 1052 : : ModulePass(ID),
288 : 1052 : imaging_mode(imaging_mode)
289 : 1052 : {}
290 : :
291 : : bool imaging_mode;
292 : 334499 : bool runOnModule(Module &M) override {
293 : 334499 : LowerPTLS lower(imaging_mode);
294 : 334499 : 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 : 1052 : Pass *createLowerPTLSPass(bool imaging_mode)
320 : : {
321 : 1052 : 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 : }
|