/Users/buildslave/jenkins/workspace/clang-stage2-coverage-R/llvm/lib/Target/WebAssembly/WebAssemblyLowerGlobalDtors.cpp
Line | Count | Source (jump to first uncovered line) |
1 | | //===-- WebAssemblyLowerGlobalDtors.cpp - Lower @llvm.global_dtors --------===// |
2 | | // |
3 | | // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. |
4 | | // See https://llvm.org/LICENSE.txt for license information. |
5 | | // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception |
6 | | // |
7 | | //===----------------------------------------------------------------------===// |
8 | | /// |
9 | | /// \file |
10 | | /// Lower @llvm.global_dtors. |
11 | | /// |
12 | | /// WebAssembly doesn't have a builtin way to invoke static destructors. |
13 | | /// Implement @llvm.global_dtors by creating wrapper functions that are |
14 | | /// registered in @llvm.global_ctors and which contain a call to |
15 | | /// `__cxa_atexit` to register their destructor functions. |
16 | | /// |
17 | | //===----------------------------------------------------------------------===// |
18 | | |
19 | | #include "WebAssembly.h" |
20 | | #include "llvm/ADT/MapVector.h" |
21 | | #include "llvm/IR/Constants.h" |
22 | | #include "llvm/IR/Instructions.h" |
23 | | #include "llvm/IR/Intrinsics.h" |
24 | | #include "llvm/IR/Module.h" |
25 | | #include "llvm/Pass.h" |
26 | | #include "llvm/Support/Debug.h" |
27 | | #include "llvm/Support/raw_ostream.h" |
28 | | #include "llvm/Transforms/Utils/ModuleUtils.h" |
29 | | using namespace llvm; |
30 | | |
31 | | #define DEBUG_TYPE "wasm-lower-global-dtors" |
32 | | |
33 | | namespace { |
34 | | class LowerGlobalDtors final : public ModulePass { |
35 | 0 | StringRef getPassName() const override { |
36 | 0 | return "WebAssembly Lower @llvm.global_dtors"; |
37 | 0 | } |
38 | | |
39 | 426 | void getAnalysisUsage(AnalysisUsage &AU) const override { |
40 | 426 | AU.setPreservesCFG(); |
41 | 426 | ModulePass::getAnalysisUsage(AU); |
42 | 426 | } |
43 | | |
44 | | bool runOnModule(Module &M) override; |
45 | | |
46 | | public: |
47 | | static char ID; |
48 | 426 | LowerGlobalDtors() : ModulePass(ID) {} |
49 | | }; |
50 | | } // End anonymous namespace |
51 | | |
52 | | char LowerGlobalDtors::ID = 0; |
53 | | INITIALIZE_PASS(LowerGlobalDtors, DEBUG_TYPE, |
54 | | "Lower @llvm.global_dtors for WebAssembly", false, false) |
55 | | |
56 | 426 | ModulePass *llvm::createWebAssemblyLowerGlobalDtors() { |
57 | 426 | return new LowerGlobalDtors(); |
58 | 426 | } |
59 | | |
60 | 427 | bool LowerGlobalDtors::runOnModule(Module &M) { |
61 | 427 | LLVM_DEBUG(dbgs() << "********** Lower Global Destructors **********\n"); |
62 | 427 | |
63 | 427 | GlobalVariable *GV = M.getGlobalVariable("llvm.global_dtors"); |
64 | 427 | if (!GV || !GV->hasInitializer()6 ) |
65 | 422 | return false; |
66 | 5 | |
67 | 5 | const ConstantArray *InitList = dyn_cast<ConstantArray>(GV->getInitializer()); |
68 | 5 | if (!InitList) |
69 | 0 | return false; |
70 | 5 | |
71 | 5 | // Sanity-check @llvm.global_dtor's type. |
72 | 5 | auto *ETy = dyn_cast<StructType>(InitList->getType()->getElementType()); |
73 | 5 | if (!ETy || ETy->getNumElements() != 3 || |
74 | 5 | !ETy->getTypeAtIndex(0U)->isIntegerTy() || |
75 | 5 | !ETy->getTypeAtIndex(1U)->isPointerTy() || |
76 | 5 | !ETy->getTypeAtIndex(2U)->isPointerTy()) |
77 | 0 | return false; // Not (int, ptr, ptr). |
78 | 5 | |
79 | 5 | // Collect the contents of @llvm.global_dtors, collated by priority and |
80 | 5 | // associated symbol. |
81 | 5 | std::map<uint16_t, MapVector<Constant *, std::vector<Constant *>>> DtorFuncs; |
82 | 18 | for (Value *O : InitList->operands()) { |
83 | 18 | auto *CS = dyn_cast<ConstantStruct>(O); |
84 | 18 | if (!CS) |
85 | 0 | continue; // Malformed. |
86 | 18 | |
87 | 18 | auto *Priority = dyn_cast<ConstantInt>(CS->getOperand(0)); |
88 | 18 | if (!Priority) |
89 | 0 | continue; // Malformed. |
90 | 18 | uint16_t PriorityValue = Priority->getLimitedValue(UINT16_MAX); |
91 | 18 | |
92 | 18 | Constant *DtorFunc = CS->getOperand(1); |
93 | 18 | if (DtorFunc->isNullValue()) |
94 | 1 | break; // Found a null terminator, skip the rest. |
95 | 17 | |
96 | 17 | Constant *Associated = CS->getOperand(2); |
97 | 17 | Associated = cast<Constant>(Associated->stripPointerCastsNoFollowAliases()); |
98 | 17 | |
99 | 17 | DtorFuncs[PriorityValue][Associated].push_back(DtorFunc); |
100 | 17 | } |
101 | 5 | if (DtorFuncs.empty()) |
102 | 0 | return false; |
103 | 5 | |
104 | 5 | // extern "C" int __cxa_atexit(void (*f)(void *), void *p, void *d); |
105 | 5 | LLVMContext &C = M.getContext(); |
106 | 5 | PointerType *VoidStar = Type::getInt8PtrTy(C); |
107 | 5 | Type *AtExitFuncArgs[] = {VoidStar}; |
108 | 5 | FunctionType *AtExitFuncTy = |
109 | 5 | FunctionType::get(Type::getVoidTy(C), AtExitFuncArgs, |
110 | 5 | /*isVarArg=*/false); |
111 | 5 | |
112 | 5 | FunctionCallee AtExit = M.getOrInsertFunction( |
113 | 5 | "__cxa_atexit", |
114 | 5 | FunctionType::get(Type::getInt32Ty(C), |
115 | 5 | {PointerType::get(AtExitFuncTy, 0), VoidStar, VoidStar}, |
116 | 5 | /*isVarArg=*/false)); |
117 | 5 | |
118 | 5 | // Declare __dso_local. |
119 | 5 | Constant *DsoHandle = M.getNamedValue("__dso_handle"); |
120 | 5 | if (!DsoHandle) { |
121 | 5 | Type *DsoHandleTy = Type::getInt8Ty(C); |
122 | 5 | GlobalVariable *Handle = new GlobalVariable( |
123 | 5 | M, DsoHandleTy, /*isConstant=*/true, |
124 | 5 | GlobalVariable::ExternalWeakLinkage, nullptr, "__dso_handle"); |
125 | 5 | Handle->setVisibility(GlobalVariable::HiddenVisibility); |
126 | 5 | DsoHandle = Handle; |
127 | 5 | } |
128 | 5 | |
129 | 5 | // For each unique priority level and associated symbol, generate a function |
130 | 5 | // to call all the destructors at that level, and a function to register the |
131 | 5 | // first function with __cxa_atexit. |
132 | 12 | for (auto &PriorityAndMore : DtorFuncs) { |
133 | 12 | uint16_t Priority = PriorityAndMore.first; |
134 | 14 | for (auto &AssociatedAndMore : PriorityAndMore.second) { |
135 | 14 | Constant *Associated = AssociatedAndMore.first; |
136 | 14 | |
137 | 14 | Function *CallDtors = Function::Create( |
138 | 14 | AtExitFuncTy, Function::PrivateLinkage, |
139 | 14 | "call_dtors" + |
140 | 14 | (Priority != UINT16_MAX ? (Twine(".") + Twine(Priority))12 |
141 | 14 | : Twine()2 ) + |
142 | 14 | (!Associated->isNullValue() ? (Twine(".") + Associated->getName())2 |
143 | 14 | : Twine()12 ), |
144 | 14 | &M); |
145 | 14 | BasicBlock *BB = BasicBlock::Create(C, "body", CallDtors); |
146 | 14 | FunctionType *VoidVoid = FunctionType::get(Type::getVoidTy(C), |
147 | 14 | /*isVarArg=*/false); |
148 | 14 | |
149 | 14 | for (auto Dtor : AssociatedAndMore.second) |
150 | 17 | CallInst::Create(VoidVoid, Dtor, "", BB); |
151 | 14 | ReturnInst::Create(C, BB); |
152 | 14 | |
153 | 14 | Function *RegisterCallDtors = Function::Create( |
154 | 14 | VoidVoid, Function::PrivateLinkage, |
155 | 14 | "register_call_dtors" + |
156 | 14 | (Priority != UINT16_MAX ? (Twine(".") + Twine(Priority))12 |
157 | 14 | : Twine()2 ) + |
158 | 14 | (!Associated->isNullValue() ? (Twine(".") + Associated->getName())2 |
159 | 14 | : Twine()12 ), |
160 | 14 | &M); |
161 | 14 | BasicBlock *EntryBB = BasicBlock::Create(C, "entry", RegisterCallDtors); |
162 | 14 | BasicBlock *FailBB = BasicBlock::Create(C, "fail", RegisterCallDtors); |
163 | 14 | BasicBlock *RetBB = BasicBlock::Create(C, "return", RegisterCallDtors); |
164 | 14 | |
165 | 14 | Value *Null = ConstantPointerNull::get(VoidStar); |
166 | 14 | Value *Args[] = {CallDtors, Null, DsoHandle}; |
167 | 14 | Value *Res = CallInst::Create(AtExit, Args, "call", EntryBB); |
168 | 14 | Value *Cmp = new ICmpInst(*EntryBB, ICmpInst::ICMP_NE, Res, |
169 | 14 | Constant::getNullValue(Res->getType())); |
170 | 14 | BranchInst::Create(FailBB, RetBB, Cmp, EntryBB); |
171 | 14 | |
172 | 14 | // If `__cxa_atexit` hits out-of-memory, trap, so that we don't misbehave. |
173 | 14 | // This should be very rare, because if the process is running out of |
174 | 14 | // memory before main has even started, something is wrong. |
175 | 14 | CallInst::Create(Intrinsic::getDeclaration(&M, Intrinsic::trap), "", |
176 | 14 | FailBB); |
177 | 14 | new UnreachableInst(C, FailBB); |
178 | 14 | |
179 | 14 | ReturnInst::Create(C, RetBB); |
180 | 14 | |
181 | 14 | // Now register the registration function with @llvm.global_ctors. |
182 | 14 | appendToGlobalCtors(M, RegisterCallDtors, Priority, Associated); |
183 | 14 | } |
184 | 12 | } |
185 | 5 | |
186 | 5 | // Now that we've lowered everything, remove @llvm.global_dtors. |
187 | 5 | GV->eraseFromParent(); |
188 | 5 | |
189 | 5 | return true; |
190 | 5 | } |