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