Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 0 additions & 9 deletions clang/lib/CodeGen/CGClass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1728,22 +1728,13 @@ void CodeGenFunction::EmitDestructorBody(FunctionArgList &Args) {
// possible to delegate the destructor body to the complete
// destructor. Do so.
if (DtorType == Dtor_Deleting) {
if(getTarget().isByteAddressable() || Dtor->getParent()->hasAttr<AsmJSAttr>()) {
RunCleanupsScope DtorEpilogue(*this);
EnterDtorCleanups(Dtor, Dtor_Deleting);
if (HaveInsertPoint()) {
QualType ThisTy = Dtor->getThisObjectType();
EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false,
/*Delegating=*/false, LoadCXXThisAddress(), ThisTy);
}
} else {
// NOTE: Code is duplicated to avoid RAII cleanup
if (HaveInsertPoint()) {
QualType ThisTy = Dtor->getThisObjectType();
EmitCXXDestructorCall(Dtor, Dtor_Complete, /*ForVirtualBase=*/false,
/*Delegating=*/false, LoadCXXThisAddress(), ThisTy);
}
}
return;
}

Expand Down
6 changes: 4 additions & 2 deletions clang/lib/CodeGen/CGDeclCXX.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -143,8 +143,8 @@ static void EmitDeclDestroy(CodeGenFunction &CGF, const VarDecl &D,
CGF.needsEHCleanup(DtorKind), &D);
Argument = llvm::Constant::getNullValue(CGF.Int8PtrTy);
}

if(CGM.getTarget().isByteAddressable())
// We do not register global destructors if they are genericjs.
if (CGM.getTarget().isByteAddressable() || D.hasAttr<AsmJSAttr>())
CGM.getCXXABI().registerGlobalDtor(CGF, D, Func, Argument);
}

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

CurEHLocation = VD->getBeginLoc();

Expand Down
1 change: 1 addition & 0 deletions clang/lib/Driver/ToolChains/WebAssembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -762,6 +762,7 @@ void cheerp::CheerpOptimizer::ConstructJob(Compilation &C, const JobAction &JA,
CmdArgs.push_back("-cheerp-keep-invokes");
addPass("function(simplifycfg)");

addPass("LowerGlobalDestructors");
addPass("CallConstructors");
addPass("GlobalDepsAnalyzer");
addPass("TypeOptimizer");
Expand Down
5 changes: 4 additions & 1 deletion compiler-rt/lib/asan/asan_cheerpwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,11 @@ void FlushUnneededASanShadowMemory(uptr p, uptr size) {}

static void (*tsd_destructor)(void *tsd) = nullptr;

// Cheerp: for now we've removed the initializer for tsd_key's constructor.
// It caused a crash in ASAN because the constructor for the global is run after the asan module constructor has already used and set it.
// Originally the global is meant to be a thread_local, but thread_local constructors and destructors are not yet supported.
struct tsd_key {
tsd_key() : key(nullptr) {}
tsd_key() {}
~tsd_key() {
CHECK(tsd_destructor);
if (key)
Expand Down
2 changes: 1 addition & 1 deletion compiler-rt/lib/sanitizer_common/sanitizer_cheerpwasm.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,6 @@ bool WriteToFile(fd_t, const void *, uptr, uptr *, error_t *) {
bool FileExists(const char *) { UNIMPLEMENTED(); }
bool IsAbsolutePath(const char *) { UNIMPLEMENTED(); }
bool ReadFromFile(fd_t, void *, uptr, uptr *, error_t *) { UNIMPLEMENTED(); }
void UnsetAlternateSignalStack() { UNIMPLEMENTED(); }
bool DontDumpShadowMemory(uptr addr, uptr length) { UNIMPLEMENTED(); }
bool IsPathSeparator(const char) { UNIMPLEMENTED(); }
bool DirExists(const char *) { UNIMPLEMENTED(); }
Expand All @@ -213,6 +212,7 @@ void GetThreadStackAndTls(bool main, uptr *stk_addr, uptr *stk_size,
}

void SetAlternateSignalStack() {}
void UnsetAlternateSignalStack() {}

void internal__exit(int exitcode) { exit(exitcode); }

Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/Cheerp/GlobalDepsAnalyzer.h
Original file line number Diff line number Diff line change
Expand Up @@ -219,6 +219,8 @@ class GlobalDepsAnalyzer
* \warning Even if this returns 0, it does *not* mean that the module has
* not been modified. This function also *reorders* the variables inside the module.
*/
void removeGlobalDestructors(llvm::Module& M);

int filterModule( const llvm::DenseSet<const llvm::Function*>&, llvm::Module & );

static bool isMathIntrinsic(const llvm::Function* F);
Expand Down
31 changes: 31 additions & 0 deletions llvm/include/llvm/Cheerp/LowerGlobalDestructors.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
//===-- Cheerp/LowerGlobalDestructors.h - Cheerp helper -------------------===//
//
// Cheerp: The C++ compiler for the Web
//
// This file is distributed under the Apache License v2.0 with LLVM Exceptions.
// See LICENSE.TXT for details.
//
// Copyright 2025 Leaning Technologies
//
//===----------------------------------------------------------------------===//

#ifndef CHEERP_LOWER_GLOBAL_DESTRUCTORS_H
#define CHEERP_LOWER_GLOBAL_DESTRUCTORS_H

#include "llvm/IR/PassManager.h"

namespace cheerp{

using namespace llvm;

class LowerGlobalDestructorsPass: public PassInfoMixin<LowerGlobalDestructorsPass> {
private:
void filterGenericJSDestructors(Module& M);
public:
PreservedAnalyses run(Module& M, ModuleAnalysisManager& MAM);
static bool isRequired() { return true;}
};

}

#endif
1 change: 1 addition & 0 deletions llvm/include/llvm/Cheerp/PassRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,7 @@
#include "llvm/Cheerp/CommandLine.h"
#include "llvm/Cheerp/CheerpLowerAtomic.h"
#include "llvm/Cheerp/ThreadLocalLowering.h"
#include "llvm/Cheerp/LowerGlobalDestructors.h"

namespace cheerp {

Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Cheerp/Utility.h
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ struct GlobalConstructor
};

std::vector<llvm::Constant*> getGlobalConstructors(llvm::Module& M);
void removeGlobalConstructorsGlobal(llvm::Module& M);

uint32_t getIntFromValue(const llvm::Value* v);

Expand Down
1 change: 1 addition & 0 deletions llvm/lib/CheerpUtils/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ add_llvm_component_library(LLVMCheerpUtils
CheerpLowerAtomic.cpp
JsExport.cpp
ThreadLocalLowering.cpp
LowerGlobalDestructors.cpp
)

add_dependencies(LLVMCheerpUtils intrinsics_gen)
9 changes: 5 additions & 4 deletions llvm/lib/CheerpUtils/CallConstructors.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -63,9 +63,8 @@ PreservedAnalyses CallConstructorsPass::run(llvm::Module &M, llvm::ModuleAnalysi
Builder.CreateCall(GetEnviron->getFunctionType(), GetEnviron);

for (Constant* C: cheerp::getGlobalConstructors(M))
{
Builder.CreateCall(Ty, cast<Function>(C->getAggregateElement(1)->stripPointerCastsSafe()));
}
removeGlobalConstructorsGlobal(M);

if (useUtilityThread)
{
Expand Down Expand Up @@ -139,9 +138,11 @@ PreservedAnalyses CallConstructorsPass::run(llvm::Module &M, llvm::ModuleAnalysi
{
ExitCode = Builder.CreateCall(Main->getFunctionType(), Main);
}
if (Wasi)
if (!LowerAtomics || Wasi)
{
Function* Exit = M.getFunction("__syscall_exit");
// In WASI mode, or if -pthread has been passed, we call exit after main, which will run global destructors
Function* Exit = M.getFunction("exit");
assert(Exit != nullptr);
if (ExitCode->getType() != Builder.getInt32Ty())
ExitCode = ConstantInt::get(Builder.getInt32Ty(), 0);
Builder.CreateCall(Exit->getFunctionType(), Exit, ExitCode);
Expand Down
24 changes: 23 additions & 1 deletion llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -796,6 +796,11 @@ bool GlobalDepsAnalyzer::runOnModule( llvm::Module & module )
markAsReachableIfPresent(module.getFunction("__udivti3"));
}

// If libc exit isn't called, remove global destructors.
Function* exitFunction = module.getFunction("exit");
if (!llcPass && (exitFunction == nullptr || !isReachable(module.getFunction("exit"))))
removeGlobalDestructors(module);

NumRemovedGlobals = filterModule(droppedMathBuiltins, module);

if(hasUndefinedSymbolErrors)
Expand Down Expand Up @@ -1566,6 +1571,23 @@ bool GlobalDepsAnalyzer::isMathIntrinsic(const llvm::Function* F)
(builtinID == BuiltinInstr::MOD_F);
}

void GlobalDepsAnalyzer::removeGlobalDestructors(llvm::Module& M)
{
// The goal is to empty the function cxa_atexit, and let optimization do the rest.
Function* cxaAtexit = M.getFunction("__cxa_atexit");
if (cxaAtexit == nullptr)
return;

GlobalValue::LinkageTypes linkage = cxaAtexit->getLinkage();
cxaAtexit->deleteBody();
BasicBlock* block = BasicBlock::Create(M.getContext(), "entry", cxaAtexit);
ConstantInt* ret = ConstantInt::get(Type::getInt32Ty(M.getContext()), 0);
IRBuilder<> Builder(M.getContext());
Builder.SetInsertPoint(block);
Builder.CreateRet(ret);
cxaAtexit->setLinkage(linkage);
}

int GlobalDepsAnalyzer::filterModule( const DenseSet<const Function*>& droppedMathBuiltins, Module & module )
{
std::vector< llvm::GlobalValue * > eraseQueue;
Expand All @@ -1582,7 +1604,7 @@ int GlobalDepsAnalyzer::filterModule( const DenseSet<const Function*>& droppedMa
bool isClient = TypeSupport::isClientGlobal(var);
if( var->hasInitializer() )
{
if( !WasmSharedModule && !isClient && var->getName()!="llvm.global_ctors" )
if( !WasmSharedModule && !isClient)
var->setLinkage(GlobalValue::InternalLinkage);
}
else if( !isClient && var->getName() != "__cxa_cheerp_clause_table")
Expand Down
92 changes: 92 additions & 0 deletions llvm/lib/CheerpUtils/LowerGlobalDestructors.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
//===-- ThreadLocalLowering.h - Cheerp helper -------------------------===//
//
// Cheerp: The C++ compiler for the Web
//
// This file is distributed under the Apache License v2.0 with LLVM Exceptions.
// See LICENSE.TXT for details.
//
// Copyright 2025 Leaning Technologies
//
//===----------------------------------------------------------------------===//

#include <unordered_set>
#include "llvm/Cheerp/LowerGlobalDestructors.h"
#include "llvm/IR/Constants.h"
#include "llvm/IR/Module.h"
#include "llvm/Transforms/Utils/LowerGlobalDtors.h"

using namespace llvm;

namespace cheerp
{

void LowerGlobalDestructorsPass::filterGenericJSDestructors(Module& M)
{
GlobalVariable* var = M.getGlobalVariable("llvm.global_dtors");
if (!var || !var->hasInitializer())
return;
ConstantArray* initList = dyn_cast<ConstantArray>(var->getInitializer());
if (!initList)
return;

GlobalValue::LinkageTypes linkageTypes = var->getLinkage();
SmallVector<Constant*, 8> validDestructors;
Type* elementType = nullptr;

// Loop over the global destructors, put all the ones tagged asmjs into a new list.
for (Value* O: initList->operands())
{
ConstantStruct* CS = dyn_cast<ConstantStruct>(O);
if (!CS)
continue;

Constant* destructor = CS->getOperand(1);
if (destructor->isNullValue())
break;

elementType = O->getType();
Function* destructorFunc = dyn_cast<Function>(destructor);
assert(destructorFunc);
if (destructorFunc->getSection() == "asmjs")
validDestructors.push_back(cast<Constant>(O));
}

var->eraseFromParent();
uint32_t numElements = validDestructors.size();
// If there are no valid destructors, don't create a new global.
if (numElements == 0)
return;

ArrayType* arrayType = ArrayType::get(elementType, numElements);
Constant* newInitList = ConstantArray::get(arrayType, validDestructors);
new GlobalVariable(M, arrayType, true, linkageTypes, newInitList, "llvm.global_dtors");
}

PreservedAnalyses LowerGlobalDestructorsPass::run(Module& M, ModuleAnalysisManager& MAM)
{
ModulePassManager MPM;
MPM.addPass(LowerGlobalDtorsPass());

// Collect all currently existing functions in a set.
std::unordered_set<Function*> functionsBeforePass;
for (Function& F: M.getFunctionList())
functionsBeforePass.insert(&F);

// Remove the destructors that aren't tagged asmjs.
filterGenericJSDestructors(M);

// Run the LowerGlobalDtorsPass.
PreservedAnalyses PA = MPM.run(M, MAM);

// The functions that weren't in the list before are the new functions
// created by the LowerGlobalDtorsPass. Tag them asmjs.
for (Function& F: M.getFunctionList())
{
if (!functionsBeforePass.count(&F))
F.setSection("asmjs");
}

return PA;
}

}
8 changes: 8 additions & 0 deletions llvm/lib/CheerpUtils/Utility.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1038,6 +1038,14 @@ std::vector<Constant*> getGlobalConstructors(Module& module)
return ret;
}

void removeGlobalConstructorsGlobal(Module& M)
{
GlobalVariable* var = M.getGlobalVariable("llvm.global_ctors");
if (!var)
return;
var->eraseFromParent();
}

const llvm::Loop* findCommonLoop(const llvm::LoopInfo* LI, const llvm::BasicBlock* first, const llvm::BasicBlock* second)
{
//Find the innermost common loop between two BB.
Expand Down
3 changes: 0 additions & 3 deletions llvm/lib/CheerpWriter/CheerpWriter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6719,9 +6719,6 @@ void CheerpWriter::compileGenericJS()
}
for ( const GlobalVariable & GV : module.getGlobalList() )
{
// Skip global ctors array
if (GV.getName() == "llvm.global_ctors")
continue;
if (GV.getSection() != StringRef("asmjs"))
compileGlobal(GV);
}
Expand Down
1 change: 1 addition & 0 deletions llvm/lib/Passes/PassRegistry.def
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,7 @@ MODULE_PASS("PreExecute", cheerp::PreExecutePass())
MODULE_PASS("FreeAndDeleteRemoval", cheerp::FreeAndDeleteRemovalPass())
MODULE_PASS("CallConstructors", cheerp::CallConstructorsPass())
MODULE_PASS("CheerpLowerAtomic", cheerp::CheerpLowerAtomicPass())
MODULE_PASS("LowerGlobalDestructors", cheerp::LowerGlobalDestructorsPass())
#undef MODULE_PASS

#ifndef MODULE_PASS_WITH_PARAMS
Expand Down
Loading