Skip to content

Commit 3a1caa1

Browse files
committed
Run global destructors on exit
This commit enables global destructors to run after main. Destructors that are in genericjs are not run. The LowerGlobalDtors pass is run to lower functions with the destructor attribute into calls to __cxa_atexit. If the libc exit function is not reachable from the code, the destructors are removed. Finally, exit is now called automatically after the main function has finished running in WASI mode, or when -pthread is passed.
1 parent cfa5499 commit 3a1caa1

File tree

7 files changed

+45
-13
lines changed

7 files changed

+45
-13
lines changed

clang/lib/CodeGen/CGClass.cpp

Lines changed: 0 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1728,22 +1728,13 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
17281728
// possible to delegate the destructor body to the complete
17291729
// destructor. Do so.
17301730
if (DtorType == Dtor_Deleting) {
1731-
if(getTarget().isByteAddressable() || Dtor->getParent()->hasAttr<AsmJSAttr>()) {
17321731
RunCleanupsScope DtorEpilogue(*this);
17331732
EnterDtorCleanups(Dtor, Dtor_Deleting);
17341733
if (HaveInsertPoint()) {
17351734
QualType ThisTy = Dtor->getThisObjectType();
17361735
EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false,
17371736
/*Delegating=*/false, LoadCXXThisAddress(), ThisTy);
17381737
}
1739-
} else {
1740-
// NOTE: Code is duplicated to avoid RAII cleanup
1741-
if (HaveInsertPoint()) {
1742-
QualType ThisTy = Dtor->getThisObjectType();
1743-
EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false,
1744-
/*Delegating=*/false, LoadCXXThisAddress(), ThisTy);
1745-
}
1746-
}
17471738
return;
17481739
}
17491740

clang/lib/CodeGen/CGDeclCXX.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -143,8 +143,8 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D,
143143
CGF.needsEHCleanup(DtorKind), &D);
144144
Argument = llvm::Constant::getNullValue(CGF.Int8PtrTy);
145145
}
146-
147-
if(CGM.getTarget().isByteAddressable())
146+
// We do not register global destructors if they are genericjs.
147+
if (CGM.getTarget().isByteAddressable() || D.hasAttr<AsmJSAttr>())
148148
CGM.getCXXABI().registerGlobalDtor(CGF, D, Func, Argument);
149149
}
150150

@@ -1154,6 +1154,8 @@ llvm::Function *CodeGenFunction::generateDestroyHelper(
11541154
llvm::FunctionType *FTy = CGM.getTypes().GetFunctionType(FI);
11551155
llvm::Function *fn = CGM.CreateGlobalInitOrCleanUpFunction(
11561156
FTy, "__cxx_global_array_dtor", FI, VD->getLocation());
1157+
if (getContext().getTargetInfo().getTriple().isCheerpWasm())
1158+
fn->setSection("asmjs");
11571159

11581160
CurEHLocation = VD->getBeginLoc();
11591161

clang/lib/Driver/ToolChains/WebAssembly.cpp

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -762,6 +762,7 @@ void cheerp::CheerpOptimizer::ConstructJob(Compilation &C, const JobAction &JA,
762762
CmdArgs.push_back("-cheerp-keep-invokes");
763763
addPass("function(simplifycfg)");
764764

765+
addPass("lower-global-dtors");
765766
addPass("CallConstructors");
766767
addPass("GlobalDepsAnalyzer");
767768
addPass("TypeOptimizer");

llvm/include/llvm/Cheerp/GlobalDepsAnalyzer.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -219,6 +219,8 @@ class GlobalDepsAnalyzer
219219
* \warning Even if this returns 0, it does *not* mean that the module has
220220
* not been modified. This function also *reorders* the variables inside the module.
221221
*/
222+
void removeGlobalDestructors(llvm::Module& M);
223+
222224
int filterModule( const llvm::DenseSet<const llvm::Function*>&, llvm::Module & );
223225

224226
static bool isMathIntrinsic(const llvm::Function* F);

llvm/lib/CheerpUtils/CallConstructors.cpp

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -138,9 +138,11 @@ PreservedAnalyses CallConstructorsPass::run(llvm::Module &M, llvm::ModuleAnalysi
138138
{
139139
ExitCode = Builder.CreateCall(Main->getFunctionType(), Main);
140140
}
141-
if (Wasi)
141+
if (!LowerAtomics || Wasi)
142142
{
143-
Function* Exit = M.getFunction("__syscall_exit");
143+
// In WASI mode, or if -pthread has been passed, we call exit after main, which will run global destructors
144+
Function* Exit = M.getFunction("exit");
145+
assert(Exit != nullptr);
144146
if (ExitCode->getType() != Builder.getInt32Ty())
145147
ExitCode = ConstantInt::get(Builder.getInt32Ty(), 0);
146148
Builder.CreateCall(Exit->getFunctionType(), Exit, ExitCode);

llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -796,6 +796,11 @@ bool GlobalDepsAnalyzer::runOnModule( llvm::Module & module )
796796
markAsReachableIfPresent(module.getFunction("__udivti3"));
797797
}
798798

799+
// If libc exit isn't called, remove global destructors.
800+
Function* exitFunction = module.getFunction("exit");
801+
if (!llcPass && (exitFunction == nullptr || !isReachable(module.getFunction("exit"))))
802+
removeGlobalDestructors(module);
803+
799804
NumRemovedGlobals = filterModule(droppedMathBuiltins, module);
800805

801806
if(hasUndefinedSymbolErrors)
@@ -1566,6 +1571,23 @@ bool GlobalDepsAnalyzer::isMathIntrinsic(const llvm::Function* F)
15661571
(builtinID == BuiltinInstr::MOD_F);
15671572
}
15681573

1574+
void GlobalDepsAnalyzer::removeGlobalDestructors(llvm::Module& M)
1575+
{
1576+
// The goal is to empty the function cxa_atexit, and let optimization do the rest.
1577+
Function* cxaAtexit = M.getFunction("__cxa_atexit");
1578+
if (cxaAtexit == nullptr)
1579+
return;
1580+
1581+
GlobalValue::LinkageTypes linkage = cxaAtexit->getLinkage();
1582+
cxaAtexit->deleteBody();
1583+
BasicBlock* block = BasicBlock::Create(M.getContext(), "entry", cxaAtexit);
1584+
ConstantInt* ret = ConstantInt::get(Type::getInt32Ty(M.getContext()), 0);
1585+
IRBuilder<> Builder(M.getContext());
1586+
Builder.SetInsertPoint(block);
1587+
Builder.CreateRet(ret);
1588+
cxaAtexit->setLinkage(linkage);
1589+
}
1590+
15691591
int GlobalDepsAnalyzer::filterModule( const DenseSet<const Function*>& droppedMathBuiltins, Module & module )
15701592
{
15711593
std::vector< llvm::GlobalValue * > eraseQueue;

llvm/lib/Transforms/Utils/LowerGlobalDtors.cpp

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include "llvm/Transforms/Utils/LowerGlobalDtors.h"
1919

20+
#include "llvm/ADT/Triple.h"
2021
#include "llvm/IR/Constants.h"
2122
#include "llvm/IR/Instructions.h"
2223
#include "llvm/IR/Intrinsics.h"
@@ -111,6 +112,11 @@ static bool runImpl(Module &M) {
111112
if (DtorFunc->isNullValue())
112113
break; // Found a null terminator, skip the rest.
113114

115+
// We do not add global destructors that are genericjs.
116+
Function* F = cast<Function>(DtorFunc);
117+
if (F->getSection() != "asmjs")
118+
continue;
119+
114120
Constant *Associated = CS->getOperand(2);
115121
Associated = cast<Constant>(Associated->stripPointerCastsSafe());
116122

@@ -150,6 +156,8 @@ static bool runImpl(Module &M) {
150156
return GV;
151157
});
152158

159+
bool asmjs = Triple(M.getTargetTriple()).isCheerpWasm();
160+
153161
// For each unique priority level and associated symbol, generate a function
154162
// to call all the destructors at that level, and a function to register the
155163
// first function with __cxa_atexit.
@@ -171,6 +179,8 @@ static bool runImpl(Module &M) {
171179
(!Associated->isNullValue() ? (Twine(".") + Associated->getName())
172180
: Twine()),
173181
&M);
182+
if (asmjs)
183+
CallDtors->setSection("asmjs");
174184
BasicBlock *BB = BasicBlock::Create(C, "body", CallDtors);
175185
FunctionType *VoidVoid = FunctionType::get(Type::getVoidTy(C),
176186
/*isVarArg=*/false);
@@ -189,6 +199,8 @@ static bool runImpl(Module &M) {
189199
(!Associated->isNullValue() ? (Twine(".") + Associated->getName())
190200
: Twine()),
191201
&M);
202+
if (asmjs)
203+
RegisterCallDtors->setSection("asmjs");
192204
BasicBlock *EntryBB = BasicBlock::Create(C, "entry", RegisterCallDtors);
193205
BasicBlock *FailBB = BasicBlock::Create(C, "fail", RegisterCallDtors);
194206
BasicBlock *RetBB = BasicBlock::Create(C, "return", RegisterCallDtors);

0 commit comments

Comments
 (0)