diff --git a/clang/include/clang/Basic/BuiltinsCheerp.def b/clang/include/clang/Basic/BuiltinsCheerp.def index 6d50ba73fe9b..ac49a5e0fb68 100644 --- a/clang/include/clang/Basic/BuiltinsCheerp.def +++ b/clang/include/clang/Basic/BuiltinsCheerp.def @@ -43,6 +43,7 @@ BUILTIN(__builtin_cheerp_set_thread_pointer, "vUi", "") BUILTIN(__builtin_cheerp_get_thread_pointer, "Ui", "") BUILTIN(__builtin_cheerp_atomic_wait, "iv*iLLi", "") BUILTIN(__builtin_cheerp_atomic_notify, "iv*i", "") +BUILTIN(__builtin_cheerp_thread_setup_resolve, "v", "") // SIMD builtins TARGET_BUILTIN(__builtin_wasm_swizzle_i8x16, "V16ScV16ScV16Sc", "nc", "simd128") diff --git a/llvm/include/llvm/Cheerp/NameGenerator.h b/llvm/include/llvm/Cheerp/NameGenerator.h index 57fc7ccfb925..5d1252940afd 100644 --- a/llvm/include/llvm/Cheerp/NameGenerator.h +++ b/llvm/include/llvm/Cheerp/NameGenerator.h @@ -80,6 +80,10 @@ class NameGenerator ATOMICXCHG, ATOMICCMPXCHG, THREADINGOBJECT, + THREADINGOBJECTWORKER, + BLOBNAME, + THREADSETUPPROMISE, + THREADSETUPRESOLVE, MEMORY, HEAP8, HEAP16, diff --git a/llvm/include/llvm/Cheerp/PassRegistry.h b/llvm/include/llvm/Cheerp/PassRegistry.h index 770c22e9fe7e..50a110c18e7c 100644 --- a/llvm/include/llvm/Cheerp/PassRegistry.h +++ b/llvm/include/llvm/Cheerp/PassRegistry.h @@ -41,6 +41,7 @@ #include "llvm/Cheerp/CallConstructors.h" #include "llvm/Cheerp/CommandLine.h" #include "llvm/Cheerp/CheerpLowerAtomic.h" +#include "llvm/Cheerp/ThreadLocalLowering.h" namespace cheerp { diff --git a/llvm/include/llvm/Cheerp/ThreadLocalLowering.h b/llvm/include/llvm/Cheerp/ThreadLocalLowering.h new file mode 100644 index 000000000000..731e389aa541 --- /dev/null +++ b/llvm/include/llvm/Cheerp/ThreadLocalLowering.h @@ -0,0 +1,40 @@ +//===-- 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 2024-2025 Leaning Technologies +// +//===----------------------------------------------------------------------===// + +#ifndef CHEERP_THREAD_LOCAL_LOWERING_H +#define CHEERP_THREAD_LOCAL_LOWERING_H + +#include "llvm/IR/PassManager.h" +#include "llvm/Cheerp/GlobalDepsAnalyzer.h" + +namespace cheerp{ + +using namespace llvm; + +class ThreadLocalLoweringInnerPass: public PassInfoMixin { + GlobalDepsAnalyzer& GDA; +public: + ThreadLocalLoweringInnerPass(GlobalDepsAnalyzer& GDA) : GDA(GDA) + { + } + PreservedAnalyses run(Function& F, FunctionAnalysisManager& FAM); + static bool isRequired() { return true;} +}; + +class ThreadLocalLoweringPass: public PassInfoMixin { +public: + PreservedAnalyses run(Module& M, ModuleAnalysisManager& MAM); + static bool isRequired() { return true;} +}; + +} + +#endif diff --git a/llvm/include/llvm/IR/IntrinsicsCheerp.td b/llvm/include/llvm/IR/IntrinsicsCheerp.td index d2735f787504..6203f7ad03ab 100644 --- a/llvm/include/llvm/IR/IntrinsicsCheerp.td +++ b/llvm/include/llvm/IR/IntrinsicsCheerp.td @@ -105,6 +105,9 @@ def int_cheerp_get_thread_pointer : Intrinsic<[llvm_i32_ty]>; def int_cheerp_set_thread_pointer : Intrinsic<[], [llvm_i32_ty]>; +def int_cheerp_get_threadlocal_offset : Intrinsic<[llvm_i32_ty], + [llvm_anyptr_ty]>; + def int_cheerp_atomic_wait : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty, llvm_i32_ty, llvm_i64_ty]>, ClangBuiltin<"__builtin_cheerp_atomic_wait">; @@ -113,6 +116,10 @@ def int_cheerp_atomic_notify : Intrinsic<[llvm_i32_ty], [llvm_ptr_ty, llvm_i32_ty]>, ClangBuiltin<"__builtin_cheerp_atomic_notify">; +def int_cheerp_thread_setup_resolve : Intrinsic<[], + []>, + ClangBuiltin<"__builtin_cheerp_thread_setup_resolve">; + def int_cheerp_environ : Intrinsic<[llvm_anyptr_ty]>; def int_cheerp_argv : Intrinsic<[llvm_anyptr_ty]>; diff --git a/llvm/lib/CheerpUtils/CMakeLists.txt b/llvm/lib/CheerpUtils/CMakeLists.txt index c98f81d57803..47506d52ecd0 100644 --- a/llvm/lib/CheerpUtils/CMakeLists.txt +++ b/llvm/lib/CheerpUtils/CMakeLists.txt @@ -35,6 +35,7 @@ add_llvm_component_library(LLVMCheerpUtils FinalizeMemoryInfo.cpp CheerpLowerAtomic.cpp JsExport.cpp + ThreadLocalLowering.cpp ) add_dependencies(LLVMCheerpUtils intrinsics_gen) diff --git a/llvm/lib/CheerpUtils/CallConstructors.cpp b/llvm/lib/CheerpUtils/CallConstructors.cpp index cb8a63577d87..d1af991e88b5 100644 --- a/llvm/lib/CheerpUtils/CallConstructors.cpp +++ b/llvm/lib/CheerpUtils/CallConstructors.cpp @@ -30,12 +30,17 @@ namespace cheerp PreservedAnalyses CallConstructorsPass::run(llvm::Module &M, llvm::ModuleAnalysisManager &MPA) { FunctionType* Ty = FunctionType::get(Type::getVoidTy(M.getContext()), false); - Function* Ctors = cast(M.getOrInsertFunction("_start", Ty).getCallee()); - if (!Ctors->empty()) + Function* StartFunction = cast(M.getOrInsertFunction("_start", Ty).getCallee()); + if (!StartFunction->empty()) return PreservedAnalyses::all(); - BasicBlock* Entry = BasicBlock::Create(M.getContext(),"entry", Ctors); - IRBuilder<> Builder(Entry); + BasicBlock* StartEntry = BasicBlock::Create(M.getContext(),"entry", StartFunction); + IRBuilder<> Builder(StartEntry); + + Function* StartPreThread = cast(M.getOrInsertFunction("_startPreThread", Ty).getCallee()); + BasicBlock* StartPreThreadEntry = BasicBlock::Create(M.getContext(),"entry", StartPreThread); + if (!LowerAtomics) + Builder.SetInsertPoint(StartPreThreadEntry); if (LinearOutput == LinearOutputTy::Wasm) { @@ -56,10 +61,24 @@ PreservedAnalyses CallConstructorsPass::run(llvm::Module &M, llvm::ModuleAnalysi { Builder.CreateCall(Ty, cast(C->getAggregateElement(1)->stripPointerCastsSafe())); } + + if (!LowerAtomics) + { + // If -pthread is passed, add a call to spawnUtility to setup the utility thread. + Function* spawnUtility = cast(M.getOrInsertFunction("spawnUtility", Ty).getCallee()); + Builder.CreateCall(Ty, spawnUtility); + Builder.CreateRetVoid(); + Builder.SetInsertPoint(StartEntry); + } + Function* Main = getMainFunction(M); bool Wasi = Triple(M.getTargetTriple()).getOS() == Triple::WASI; if (Wasi || (Main && Main->getSection() == "asmjs")) - Ctors->setSection("asmjs"); + { + StartFunction->setSection("asmjs"); + if (!LowerAtomics) + StartPreThread->setSection("asmjs"); + } if (Main) { Value* ExitCode = nullptr; @@ -124,7 +143,6 @@ PreservedAnalyses CallConstructorsPass::run(llvm::Module &M, llvm::ModuleAnalysi Builder.CreateCall(Exit->getFunctionType(), Exit, ExitCode); } } - Builder.CreateRetVoid(); PreservedAnalyses PA = PreservedAnalyses::none(); diff --git a/llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp b/llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp index ad7ac02f2a5b..012a59362ac6 100644 --- a/llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp +++ b/llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp @@ -700,7 +700,7 @@ bool GlobalDepsAnalyzer::runOnModule( llvm::Module & module ) extendLifetime(startFunc); if (startFunc->getSection() == "asmjs") asmJSExportedFunctions.insert(startFunc); - else if (LinearOutput == LinearOutputTy::Wasm) + else if (LinearOutput == LinearOutputTy::Wasm && LowerAtomics) { // Because memory_init is empty, it will not be automatically tagged as asmjs // when the _start function is not, but it should. So we do it manually. @@ -714,7 +714,27 @@ bool GlobalDepsAnalyzer::runOnModule( llvm::Module & module ) llvm::errs() << "warning: _start function point not found\n"; } entryPoint = startFunc; - + + // If -pthread is linked in, keep the _startPreThread function alive. + if (!LowerAtomics) + { + llvm::Function* startPreThread = module.getFunction("_startPreThread"); + if (startPreThread) + { + extendLifetime(startPreThread); + if (startPreThread->getSection() == "asmjs") + asmJSExportedFunctions.insert(startPreThread); + else if (LinearOutput == LinearOutputTy::Wasm) + { + llvm::Function* initFunc = module.getFunction("__memory_init"); + assert(initFunc); + asmJSExportedFunctions.insert(initFunc); + } + } + else + llvm::errs() << "warning: _startPtrThread function point not found, and -pthread is linked\n"; + } + processEnqueuedFunctions(); // Flush out all functions diff --git a/llvm/lib/CheerpUtils/LinearMemoryHelper.cpp b/llvm/lib/CheerpUtils/LinearMemoryHelper.cpp index 42565c869376..1fb39b937085 100644 --- a/llvm/lib/CheerpUtils/LinearMemoryHelper.cpp +++ b/llvm/lib/CheerpUtils/LinearMemoryHelper.cpp @@ -438,7 +438,6 @@ void LinearMemoryHelper::addGlobals() // Compute the global variable addresses. // Also, for thread locals, calculate offsets to the image start, and the total size of the image. - threadLocalImageSize = 0; threadLocalStart = 0; globalsStart = 0; for (const auto G: asmjsGlobals) { @@ -460,10 +459,12 @@ void LinearMemoryHelper::addGlobals() asmjsThreadLocals.push_back(G); if (threadLocalStart == 0) threadLocalStart = heapStart; - threadLocalImageSize += size; } heapStart += size; } + heapStart = (heapStart + 7) & ~7; + threadLocalImageSize = heapStart - threadLocalStart; + // Align the thread local storage to 8 bytes. } void LinearMemoryHelper::generateGlobalizedGlobalsUsage() diff --git a/llvm/lib/CheerpUtils/PointerAnalyzer.cpp b/llvm/lib/CheerpUtils/PointerAnalyzer.cpp index 329c3d380960..9b2af83e36f5 100644 --- a/llvm/lib/CheerpUtils/PointerAnalyzer.cpp +++ b/llvm/lib/CheerpUtils/PointerAnalyzer.cpp @@ -672,6 +672,7 @@ PointerKindWrapper& PointerUsageVisitor::visitValue(PointerKindWrapper& ret, con case Intrinsic::invariant_start: return CacheAndReturn(visitValue(ret, intrinsic->getArgOperand(1), /*first*/ false)); case Intrinsic::stacksave: + case Intrinsic::threadlocal_address: return CacheAndReturn(ret = PointerKindWrapper(RAW)); case Intrinsic::invariant_end: case Intrinsic::vastart: diff --git a/llvm/lib/CheerpUtils/ThreadLocalLowering.cpp b/llvm/lib/CheerpUtils/ThreadLocalLowering.cpp new file mode 100644 index 000000000000..d75078ce0474 --- /dev/null +++ b/llvm/lib/CheerpUtils/ThreadLocalLowering.cpp @@ -0,0 +1,127 @@ +//===-- ThreadLocalLowering.cpp - 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 2024-2025 Leaning Technologies +// +//===----------------------------------------------------------------------===// + +#include "llvm/Cheerp/ThreadLocalLowering.h" +#include "llvm/Cheerp/GlobalDepsAnalyzer.h" +#include "llvm/Cheerp/LinearMemoryHelper.h" +#include "llvm/Cheerp/InvokeWrapping.h" +#include "llvm/Cheerp/Registerize.h" +#include "llvm/IR/Instruction.h" +#include "llvm/IR/IntrinsicInst.h" +#include "llvm/IR/IRBuilder.h" + +using namespace llvm; + +namespace cheerp +{ + +static Function* getOrCreateThreadLocalWrapper(Module* M, GlobalDepsAnalyzer& GDA) +{ + Type* i8Ty = IntegerType::getInt8Ty(M->getContext()); + Type* i8PtrTy = PointerType::get(i8Ty, 0); + Type* i32Ty = IntegerType::getInt32Ty(M->getContext()); + Type* argTy[] = {i32Ty}; + FunctionType* fTy = FunctionType::get(i8PtrTy,ArrayRef(argTy, 1), false); + Function* wrapper = cast(M->getOrInsertFunction("__getThreadLocalAddress", fTy).getCallee()); + if (!wrapper->empty()) + return wrapper; + + BasicBlock* entry = BasicBlock::Create(M->getContext(),"entry", wrapper); + IRBuilder<> Builder(entry); + // Get the thread local address. + Function* threadPointerIntrinsic = Intrinsic::getDeclaration(M, Intrinsic::cheerp_get_thread_pointer); + Value* threadPointer = Builder.CreateCall(threadPointerIntrinsic); + // Add the offset argument + Value* offset = wrapper->getArg(0); + Value* address = Builder.CreateAdd(threadPointer, offset); + // Bitcast to a pointer + address = Builder.CreateIntToPtr(address, i8PtrTy); + Builder.CreateRet(address); + + wrapper->setSection("asmjs"); + GDA.insertAsmJSExport(wrapper); + return wrapper; +} + +bool replaceThreadLocalIntrinsicWithFunction(Function& F, GlobalDepsAnalyzer& GDA) +{ + Module* M = F.getParent(); + bool changed = false; + SmallVector deleteList; + + for (BasicBlock& BB: F) + { + for (Instruction& I: BB) + { + if (isa(I)) + { + IntrinsicInst& II = cast(I); + Intrinsic::ID intrId = II.getIntrinsicID(); + if (intrId == Intrinsic::threadlocal_address) + { + IRBuilder<> Builder(&II); + // Replace call to intrinsic with function + // 1. Use an intrinsic that will be the offset for the threadlocal. + Type* argTy[] = {II.getOperand(0)->getType()}; + Function* offsetIntrinsic = Intrinsic::getDeclaration(M, Intrinsic::cheerp_get_threadlocal_offset, argTy); + Value* offset = Builder.CreateCall(offsetIntrinsic, II.getOperand(0)); + // 2. Pass this offset to the wasm function that will calculate the address from the thread pointer. + Function* newFunc = getOrCreateThreadLocalWrapper(M, GDA); + Value* newCall = Builder.CreateCall(newFunc, offset); + // 3. Bitcast return code from this function to required type. + Type* origType = II.getType(); + if (origType != newCall->getType()) + newCall = Builder.CreateBitCast(newCall, origType); + I.replaceAllUsesWith(newCall); + deleteList.push_back(&I); + changed = true; + } + } + } + } + for (Instruction* I: deleteList) + I->eraseFromParent(); + return changed; +} + +PreservedAnalyses ThreadLocalLoweringInnerPass::run(Function& F, FunctionAnalysisManager& FAM) +{ + if (F.getSection() == "asmjs") + return PreservedAnalyses::all(); + + // Find calls to threadlocal.address intrinsic, replace with calls to function. + bool changed = replaceThreadLocalIntrinsicWithFunction(F, GDA); + if (!changed) + return PreservedAnalyses::all(); + PreservedAnalyses PA; + PA.preserve(); + PA.preserve(); + PA.preserve(); + PA.preserve(); + PA.preserve(); + return PA; +} + +PreservedAnalyses ThreadLocalLoweringPass::run(Module& M, ModuleAnalysisManager& MAM) +{ + FunctionPassManager FPM; + + GlobalDepsAnalyzer& GDA = MAM.getResult(M); + FPM.addPass(ThreadLocalLoweringInnerPass(GDA)); + + ModulePassManager MPM; + + MPM.addPass(createModuleToFunctionPassAdaptor(std::move(FPM))); + PreservedAnalyses PA = MPM.run(M, MAM); + return PA; +} + +} diff --git a/llvm/lib/CheerpWriter/CheerpWriter.cpp b/llvm/lib/CheerpWriter/CheerpWriter.cpp index 5c26666d84f3..ca151277aa7a 100644 --- a/llvm/lib/CheerpWriter/CheerpWriter.cpp +++ b/llvm/lib/CheerpWriter/CheerpWriter.cpp @@ -973,15 +973,14 @@ CheerpWriter::COMPILE_INSTRUCTION_FEEDBACK CheerpWriter::handleBuiltinCall(const else if(intrinsicId==Intrinsic::cheerp_get_threading_blob) { StringRef threadingObject = namegen.getBuiltinName(NameGenerator::Builtin::THREADINGOBJECT); - stream << "new Blob([" << '"'; - stream << "onmessage=(e)=>{"; - stream << threadingObject << "=e.data;"; - stream << threadingObject << ".inWorker=true;"; - if (makeModule == MODULE_TYPE::ES6) - stream << "import(" << threadingObject << ".script).then(m=>{m.default();});"; - else - stream << "importScripts(" << threadingObject << ".script);"; - stream << "}" << '"' << "])"; + StringRef blobText = namegen.getBuiltinName(NameGenerator::Builtin::BLOBNAME); + stream << "new Blob([" << threadingObject << "." << blobText << "])"; + return COMPILE_OK; + } + else if(intrinsicId==Intrinsic::cheerp_thread_setup_resolve) + { + StringRef threadSetupResolve = namegen.getBuiltinName(NameGenerator::Builtin::THREADSETUPRESOLVE); + stream << threadSetupResolve << "();" << NewLine; return COMPILE_OK; } else if(intrinsicId==Intrinsic::abs) @@ -1095,6 +1094,17 @@ CheerpWriter::COMPILE_INSTRUCTION_FEEDBACK CheerpWriter::handleBuiltinCall(const compileOperand(*it); return COMPILE_OK; } + else if(intrinsicId==Intrinsic::threadlocal_address) + { + llvm::report_fatal_error("Encountered threadlocal_address in writer"); + } + else if(intrinsicId==Intrinsic::cheerp_get_threadlocal_offset) + { + const GlobalVariable* GV = dyn_cast(callV.getOperand(0)); + int32_t offset = linearHelper.getThreadLocalOffset(GV); + stream << offset; + return COMPILE_OK; + } else if(ident=="fmod") { // Handle this internally, C++ does not have float mod operation @@ -6844,7 +6854,7 @@ void CheerpWriter::compileWasmLoader() stream << "module.exports=" << NewLine; else if (makeModule == MODULE_TYPE::ES6) stream << "return "; - stream << dummyName << ".promise.then(" << shortestName << "=>{" << NewLine; + stream << dummyName << ".promise=" << dummyName << ".promise.then(" << shortestName << "=>{" << NewLine; stream << threadObject << ".module=" << shortestName << ".module;" << NewLine; stream << "__asm=" << shortestName << ".instance.exports;" << NewLine; } @@ -7114,7 +7124,9 @@ void CheerpWriter::compileCommonJSExports() void CheerpWriter::compileEntryPoint() { - const Function * entryPoint = module.getFunction("_start"); + // Compile call to _startPreThread instead if -pthread is linked. + StringRef entryName = LowerAtomics ? "_start" : "_startPreThread"; + const Function * entryPoint = module.getFunction(entryName); if (entryPoint) { if (entryPoint->getSection() == "asmjs") @@ -7125,11 +7137,20 @@ void CheerpWriter::compileEntryPoint() void CheerpWriter::compileThreadingObject() { + // First create the promise and resolve variables for threading setup. + StringRef threadPromise = namegen.getBuiltinName(NameGenerator::Builtin::THREADSETUPPROMISE); + StringRef threadResolve = namegen.getBuiltinName(NameGenerator::Builtin::THREADSETUPRESOLVE); + stream << "var " << threadResolve << "=null;" << NewLine; + stream << "var " << threadPromise << "=new Promise((r)=>{" << threadResolve << "=r;});" << NewLine; + + // Then create the rest of the threading variables. StringRef threadObject = namegen.getBuiltinName(NameGenerator::Builtin::THREADINGOBJECT); + StringRef workerThreadObject = namegen.getBuiltinName(NameGenerator::Builtin::THREADINGOBJECTWORKER); + StringRef blobName = namegen.getBuiltinName(NameGenerator::Builtin::BLOBNAME); stream << "if(typeof "; if (makeModule == MODULE_TYPE::ES6 || makeModule == MODULE_TYPE::COMMONJS) stream << "globalThis."; - stream << threadObject << "==='undefined'){" << NewLine; + stream << workerThreadObject << "==='undefined'){" << NewLine; stream << "var script="; if (makeModule == MODULE_TYPE::ES6) stream << "import.meta.url;"; @@ -7144,11 +7165,26 @@ void CheerpWriter::compileThreadingObject() stream << "else script = __filename;"; } stream << NewLine; - stream << "var " << threadObject << "={inWorker:false,module:null,script:script,memory:null,func:null,args:null,tls:null,tid:null,stack:null};" << NewLine; + stream << "var " << blobName << "=" << '"'; + stream << "onmessage=(e)=>{"; + stream << workerThreadObject << "=e.data;"; + stream << workerThreadObject << ".inWorker=true;"; + if (makeModule == MODULE_TYPE::ES6) + stream << "import(" << workerThreadObject << ".script).then(m=>m.default()).catch(e=>{if(e!=='LeakUtilityThread')throw e;});"; + else + stream << "importScripts(" << workerThreadObject << ".script);"; + stream << "postMessage(null);"; + stream << "}" << '"' << ";" << NewLine; + stream << "var " << threadObject << "={inWorker:false,module:null,script:script,memory:null,func:null,args:null,tls:null,tid:null,stack:null," << blobName << ":" << blobName << "};" << NewLine; if (makeModule == MODULE_TYPE::ES6 || makeModule == MODULE_TYPE::COMMONJS) { stream << "}else{" << NewLine; - stream << "var " << threadObject << "=globalThis." << threadObject << ";" << NewLine; + stream << "var " << threadObject << "=globalThis." << workerThreadObject << ";" << NewLine; + } + else + { + stream << "}else{" << NewLine; + stream << "var " << threadObject << "=" << workerThreadObject << ";" << NewLine; } stream << "}" << NewLine; } @@ -7158,6 +7194,10 @@ void CheerpWriter::compileWorkerMainScript() std::string shortestName = namegen.getShortestLocalName(); StringRef threadObject = namegen.getBuiltinName(NameGenerator::Builtin::THREADINGOBJECT); stream << "}else{" << NewLine; + if (makeModule == MODULE_TYPE::COMMONJS) + stream << "module.exports=" << NewLine; + else if (makeModule == MODULE_TYPE::ES6) + stream << "return "; stream << namegen.getBuiltinName(NameGenerator::Builtin::DUMMY) << ".promise.then(" << shortestName << "=>{" << NewLine; stream << "__asm=" << shortestName << ".exports;" << NewLine; compileDefineExports(); @@ -7239,6 +7279,16 @@ void CheerpWriter::makeJS() compileDefineExports(); compileEntryPoint(); + + // If -pthread is linked in, we want to return the thread setup promise here. + if (!LowerAtomics) + { + stream << "return " << namegen.getBuiltinName(NameGenerator::Builtin::THREADSETUPPROMISE) << ";" << NewLine; + // If we are in ES6 or CommonJS module mode, chain the thread setup promise here. + if (makeModule == MODULE_TYPE::ES6 || makeModule == MODULE_TYPE::COMMONJS) + stream << "}).then(()=>{" << NewLine; + } + if (makeModule == MODULE_TYPE::COMMONJS || makeModule == MODULE_TYPE::ES6) compileCommonJSExports(); diff --git a/llvm/lib/CheerpWriter/NameGenerator.cpp b/llvm/lib/CheerpWriter/NameGenerator.cpp index 78552228da99..47f0120c04b4 100644 --- a/llvm/lib/CheerpWriter/NameGenerator.cpp +++ b/llvm/lib/CheerpWriter/NameGenerator.cpp @@ -693,6 +693,10 @@ void NameGenerator::generateReadableNames(const Module& M, const GlobalDepsAnaly builtins[ATOMICXCHG] = "__atomicexchange"; builtins[ATOMICCMPXCHG] = "__atomiccompareExchange"; builtins[THREADINGOBJECT] = "threadingObject"; + builtins[THREADINGOBJECTWORKER] = "threadingObjectWorker"; + builtins[BLOBNAME] = "blobText"; + builtins[THREADSETUPPROMISE] = "__threadSetupPromise"; + builtins[THREADSETUPRESOLVE] = "__threadSetupResolve"; builtins[HEAP8] = "HEAP8"; builtins[HEAP16] = "HEAP16"; builtins[HEAP32] = "HEAP32"; diff --git a/llvm/lib/Target/WebAssembly/CheerpWritePass.cpp b/llvm/lib/Target/WebAssembly/CheerpWritePass.cpp index ccf65ff4d8c6..f5120a1869a5 100644 --- a/llvm/lib/Target/WebAssembly/CheerpWritePass.cpp +++ b/llvm/lib/Target/WebAssembly/CheerpWritePass.cpp @@ -241,6 +241,7 @@ bool CheerpWritePass::runOnModule(Module& M) MPM.addPass(cheerp::InvokeWrappingPass()); if (isWasmTarget) MPM.addPass(cheerp::AllocaLoweringPass()); + MPM.addPass(cheerp::ThreadLocalLoweringPass()); MPM.addPass(cheerp::FFIWrappingPass()); MPM.addPass(createModuleToFunctionPassAdaptor(cheerp::FixIrreducibleControlFlowPass())); MPM.addPass(createModuleToFunctionPassAdaptor(cheerp::PointerArithmeticToArrayIndexingPass()));