From bfca724dbf8d7307c52f0339777e14aed0bead07 Mon Sep 17 00:00:00 2001 From: Yuri Iozzelli Date: Tue, 28 May 2024 15:24:31 +0200 Subject: [PATCH 1/4] Remove support for linear memory in the cheerp-genericjs target This was always just theoretical, and just a pain to support. The result is mostly a simplification of GDA and the CheerpWriter, that don't have to try and figure out if there is any linear memory used in the module: if the target is cheerp-wasm, there is; otherwise, there isn't. The other main change is in the exceptions ABI, where __cheerp_landingpad is not always in linear memory, but it's genericjs for the cheerp-genericjs target. This required changing the fist field from void* to int32, and casting appropriately. TODO: frontend check to forbid use of the wasm attribute on the cheerp-genericjs target --- clang/lib/CodeGen/CGException.cpp | 24 +++ clang/lib/CodeGen/CodeGenFunction.h | 5 +- libcxxabi/src/cxa_cheerp.cpp | 14 +- llvm/include/llvm/ADT/Triple.h | 2 +- llvm/include/llvm/Cheerp/GlobalDepsAnalyzer.h | 24 +-- llvm/include/llvm/Cheerp/Writer.h | 3 + llvm/include/llvm/IR/IntrinsicsCheerp.td | 3 + llvm/lib/CheerpUtils/AllocaLowering.cpp | 2 +- llvm/lib/CheerpUtils/CallConstructors.cpp | 3 +- llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp | 155 ++++++------------ llvm/lib/CheerpUtils/InvokeWrapping.cpp | 8 + llvm/lib/CheerpUtils/PointerAnalyzer.cpp | 2 + llvm/lib/CheerpUtils/PointerPasses.cpp | 45 +++-- llvm/lib/CheerpWriter/CheerpWriter.cpp | 51 +++--- llvm/lib/CheerpWriter/NameGenerator.cpp | 4 +- .../Target/CheerpBackend/CheerpBackend.cpp | 7 +- 16 files changed, 155 insertions(+), 197 deletions(-) diff --git a/clang/lib/CodeGen/CGException.cpp b/clang/lib/CodeGen/CGException.cpp index 410c06e95882..8698d30b989d 100644 --- a/clang/lib/CodeGen/CGException.cpp +++ b/clang/lib/CodeGen/CGException.cpp @@ -877,6 +877,14 @@ llvm::BasicBlock *CodeGenFunction::EmitLandingPad() { LPadInst = Builder.CreateLandingPad(LPadTy, 0); llvm::Value *LPadExn = Builder.CreateExtractValue(LPadInst, 0); + if(!CGM.getTarget().isByteAddressable()) { + if (CGM.getTarget().getTriple().isCheerpWasm()) { + LPadExn = Builder.CreateIntToPtr(LPadExn, Int8PtrTy); + } else { + llvm::Function *MakeReg = CGM.getIntrinsic(llvm::Intrinsic::cheerp_make_regular, {Int8PtrTy, Int8PtrTy}); + LPadExn = Builder.CreateCall(MakeReg, {llvm::ConstantPointerNull::get(Int8PtrTy), LPadExn}); + } + } Builder.CreateStore(LPadExn, getExceptionSlot()); llvm::Value *LPadSel = Builder.CreateExtractValue(LPadInst, 1); Builder.CreateStore(LPadSel, getEHSelectorSlot()); @@ -1575,6 +1583,14 @@ llvm::BasicBlock *CodeGenFunction::getTerminateLandingPad() { llvm::Value *Exn = nullptr; if (getLangOpts().CPlusPlus) { Exn = Builder.CreateExtractValue(LPadInst, 0); + if(!CGM.getTarget().isByteAddressable()) { + if (CGM.getTarget().getTriple().isCheerpWasm()) { + Exn = Builder.CreateIntToPtr(Exn, Int8PtrTy); + } else { + llvm::Function *MakeReg = CGM.getIntrinsic(llvm::Intrinsic::cheerp_make_regular, {Int8PtrTy, Int8PtrTy}); + Exn = Builder.CreateCall(MakeReg, {llvm::ConstantPointerNull::get(Int8PtrTy), Exn}); + } + } } llvm::CallInst *terminateCall = CGM.getCXXABI().emitTerminateForUnexpectedException(*this, Exn); @@ -1675,6 +1691,14 @@ llvm::BasicBlock *CodeGenFunction::getEHResumeBlock(bool isCleanup) { llvm::Type *LPadType = GetLandingPadTy(); llvm::Value *LPadVal = llvm::UndefValue::get(LPadType); + if (!CGM.getTarget().isByteAddressable()) { + if (CGM.getTarget().getTriple().isCheerpWasm()) { + Exn = Builder.CreatePtrToInt(Exn, Int32Ty); + } else { + llvm::Function *PtrOffset = CGM.getIntrinsic(llvm::Intrinsic::cheerp_pointer_offset, {Int8PtrTy}); + Exn = Builder.CreateCall(PtrOffset, Exn); + } + } LPadVal = Builder.CreateInsertValue(LPadVal, Exn, 0, "lpad.val"); LPadVal = Builder.CreateInsertValue(LPadVal, Sel, 1, "lpad.val"); Builder.CreateResume(LPadVal); diff --git a/clang/lib/CodeGen/CodeGenFunction.h b/clang/lib/CodeGen/CodeGenFunction.h index 897ab91fa818..ea6d1e1731a4 100644 --- a/clang/lib/CodeGen/CodeGenFunction.h +++ b/clang/lib/CodeGen/CodeGenFunction.h @@ -2016,9 +2016,10 @@ class CodeGenFunction : public CodeGenTypeCache { auto* Ret = llvm::StructType::getTypeByName(CGM.getLLVMContext(), "struct._ZN10__cxxabiv119__cheerp_landingpadE"); if (Ret) return Ret; - llvm::Type* Tys[] { CGM.Int8PtrTy, CGM.Int32Ty}; + llvm::Type* Tys[] { CGM.Int32Ty, CGM.Int32Ty}; - return llvm::StructType::create(Tys, "struct._ZN10__cxxabiv119__cheerp_landingpadE", false, nullptr, false, true /*asmjs*/); + bool asmjs = getTarget().getTriple().isCheerpWasm(); + return llvm::StructType::create(Tys, "struct._ZN10__cxxabiv119__cheerp_landingpadE", false, nullptr, false, asmjs); } /// Returns the contents of the function's exception object and selector diff --git a/libcxxabi/src/cxa_cheerp.cpp b/libcxxabi/src/cxa_cheerp.cpp index f2a93da6a49d..54a85e1586fb 100644 --- a/libcxxabi/src/cxa_cheerp.cpp +++ b/libcxxabi/src/cxa_cheerp.cpp @@ -53,18 +53,18 @@ terminate() noexcept namespace [[cheerp::genericjs]] __cxxabiv1 { struct +#ifdef __ASMJS__ [[cheerp::wasm]] +#endif __cheerp_landingpad { - void* val; + uintptr_t val; int sel; [[cheerp::genericjs]] - static void set_val(__cheerp_landingpad* lp, void* v) noexcept + void set_val(void* v) noexcept { - int intval = __builtin_cheerp_pointer_offset(v); - __cheerp_landingpad** hack = reinterpret_cast<__cheerp_landingpad**>(lp); - *hack = reinterpret_cast<__cheerp_landingpad*>(intval); + val = __builtin_cheerp_pointer_offset(v); } }; @@ -537,7 +537,7 @@ __gxx_personality_v0 } } } - __cheerp_landingpad lp{nullptr, 0}; + __cheerp_landingpad lp{0, 0}; if(!native) { curNonNativeException = obj; @@ -545,7 +545,7 @@ __gxx_personality_v0 } Exception* ex = current_exception; - __cheerp_landingpad::set_val(&lp, ex); + lp.set_val(ex); for(int i = start; i < start+n; i++) { diff --git a/llvm/include/llvm/ADT/Triple.h b/llvm/include/llvm/ADT/Triple.h index c0afc5a87306..397d4fb2f623 100644 --- a/llvm/include/llvm/ADT/Triple.h +++ b/llvm/include/llvm/ADT/Triple.h @@ -786,7 +786,7 @@ class Triple { /// Tests wheter the target is cheerp-wasm bool isCheerpWasm() const { - return getArch() == Triple::cheerp && getObjectFormat() == Triple::Wasm; + return getArch() == Triple::cheerp && getEnvironment() == Triple::WebAssembly; } /// Tests whether the target supports the EHABI exception diff --git a/llvm/include/llvm/Cheerp/GlobalDepsAnalyzer.h b/llvm/include/llvm/Cheerp/GlobalDepsAnalyzer.h index 484b0a75e5bf..e4c10a29c369 100644 --- a/llvm/include/llvm/Cheerp/GlobalDepsAnalyzer.h +++ b/llvm/include/llvm/Cheerp/GlobalDepsAnalyzer.h @@ -119,23 +119,10 @@ class GlobalDepsAnalyzer */ bool needCreatePointerArray() const { return hasPointerArrays; } - /** - * Determine if we need to compile the asm.js module - */ - bool needAsmJSCode() const { return hasAsmJSCode; } - /** - * Determine if we need to compile the asm.js module - */ - bool needAsmJSMemory() const { return hasAsmJSMemory; } /** * Determine if we need to compile the definition of CheerpException */ bool needCheerpException() const { return hasCheerpException; } - - /** - * Determine if linear memory malloc is ever used - */ - bool usesAsmJSMalloc() const { return hasAsmJSMalloc; } bool usesAtomics() const { return hasAtomics; } @@ -241,8 +228,10 @@ class GlobalDepsAnalyzer static void replaceFunctionAliasWithAliasee(llvm::Module &module, llvm::StringRef name); - //Extend lifetime of function, visiting them and declaring external - void extendLifetime(llvm::Function* F); + //Extend lifetime of global, visiting them and declaring external + void extendLifetime(llvm::GlobalValue* G); + // Same but F might be null + void extendLifetimeIfPresent(llvm::GlobalValue* G); //Determine whether an instruction is atomic. bool isAtomicInstruction(const llvm::Instruction& I); @@ -274,16 +263,11 @@ class GlobalDepsAnalyzer bool hasCreateClosureUsers; bool hasVAArgs; bool hasPointerArrays; - bool hasAsmJSCode; bool hasAtomics; - bool hasAsmJSMemory; - bool hasAsmJSMalloc; bool hasCheerpException; - bool mayNeedAsmJSFree; bool llcPass; bool hasUndefinedSymbolErrors; - bool preserveFree; public: bool forceTypedArrays; bool needsBuiltin(BuiltinInstr::BUILTIN b) diff --git a/llvm/include/llvm/Cheerp/Writer.h b/llvm/include/llvm/Cheerp/Writer.h index 709b16bd7f46..47407bb5bef8 100644 --- a/llvm/include/llvm/Cheerp/Writer.h +++ b/llvm/include/llvm/Cheerp/Writer.h @@ -12,6 +12,7 @@ #ifndef _CHEERP_WRITER_H #define _CHEERP_WRITER_H +#include "llvm/ADT/Triple.h" #include "llvm/Analysis/AliasAnalysis.h" #include "llvm/Cheerp/AllocaMerging.h" #include "llvm/Cheerp/BaseWriter.h" @@ -182,6 +183,7 @@ class CheerpWriter final : public CheerpBaseWriter llvm::ModuleAnalysisManager& MAM; llvm::FunctionAnalysisManager& FAM; llvm::DataLayout targetData; + bool isWasmTarget; const llvm::Function* currentFun; // function-local map of type_info* to typeid, for exception handling llvm::DenseMap typeIdMap; @@ -617,6 +619,7 @@ class CheerpWriter final : public CheerpBaseWriter MAM(MAM), FAM(MAM.getResult(m).getManager()), targetData(&m), + isWasmTarget(llvm::Triple(m.getTargetTriple()).isCheerpWasm()), currentFun(NULL), PA(PA), registerize(registerize), diff --git a/llvm/include/llvm/IR/IntrinsicsCheerp.td b/llvm/include/llvm/IR/IntrinsicsCheerp.td index b7687d9843cb..20a26f99f90b 100644 --- a/llvm/include/llvm/IR/IntrinsicsCheerp.td +++ b/llvm/include/llvm/IR/IntrinsicsCheerp.td @@ -61,6 +61,9 @@ def int_cheerp_pointer_offset : Intrinsic<[llvm_i32_ty], def int_cheerp_is_linear_heap : Intrinsic<[llvm_i1_ty], [llvm_anyptr_ty], [IntrNoMem]>; +def int_cheerp_pointer_elem_size : Intrinsic<[llvm_i32_ty], + [llvm_anyptr_ty], + [IntrNoMem]>; // Closure creation for callbacks def int_cheerp_create_closure : Intrinsic<[llvm_anyptr_ty], diff --git a/llvm/lib/CheerpUtils/AllocaLowering.cpp b/llvm/lib/CheerpUtils/AllocaLowering.cpp index 970ca32548b4..345f72f6f552 100644 --- a/llvm/lib/CheerpUtils/AllocaLowering.cpp +++ b/llvm/lib/CheerpUtils/AllocaLowering.cpp @@ -172,7 +172,7 @@ bool AllocaLowering::runOnFunction(Function& F, DominatorTree& DT, cheerp::Globa nbytes = (nbytes + 7) & -8; Function *getStack, *setStack; - if (asmjs || !GDA.needAsmJSCode()) + if (asmjs) { getStack = Intrinsic::getDeclaration(M, Intrinsic::stacksave); setStack = Intrinsic::getDeclaration(M, Intrinsic::stackrestore); diff --git a/llvm/lib/CheerpUtils/CallConstructors.cpp b/llvm/lib/CheerpUtils/CallConstructors.cpp index cb91a39491ff..4c7917f10e70 100644 --- a/llvm/lib/CheerpUtils/CallConstructors.cpp +++ b/llvm/lib/CheerpUtils/CallConstructors.cpp @@ -98,8 +98,7 @@ PreservedAnalyses CallConstructorsPass::run(llvm::Module &M, llvm::ModuleAnalysi } else { - Value* EnvA = Builder.CreateAlloca(EnvTy); - Env = Builder.CreateLoad(EnvTy, EnvA); + Env = Builder.CreateAlloca(Builder.getInt8Ty()->getPointerTo(0)); Builder.CreateStore(ConstantPointerNull::get(cast(EnvTy->getPointerElementType())), Env); } diff --git a/llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp b/llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp index 35e9d43109c1..c2033bc52eae 100644 --- a/llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp +++ b/llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp @@ -44,9 +44,8 @@ const char* wasmNullptrName = "__wasm_nullptr"; GlobalDepsAnalyzer::GlobalDepsAnalyzer(MATH_MODE mathMode_, bool llcPass) : hasBuiltin{{false}}, mathMode(mathMode_), DL(NULL), entryPoint(NULL), hasCreateClosureUsers(false), hasVAArgs(false), - hasPointerArrays(false), hasAsmJSCode(false), hasAsmJSMemory(false), hasAsmJSMalloc(false), - hasCheerpException(false), mayNeedAsmJSFree(false), llcPass(llcPass), - hasUndefinedSymbolErrors(false), forceTypedArrays(false), preserveFree(PreserveFree) + hasPointerArrays(false), hasCheerpException(false), llcPass(llcPass), + hasUndefinedSymbolErrors(false), forceTypedArrays(false) { } static void createNullptrFunction(llvm::Module& module) @@ -118,17 +117,24 @@ void GlobalDepsAnalyzer::simplifyCalls(llvm::Module & module) const } } -void GlobalDepsAnalyzer::extendLifetime(Function* F) +void GlobalDepsAnalyzer::extendLifetime(GlobalValue* G) { - assert(F); + assert(G); - externals.push_back(F); + externals.push_back(G); VisitedSet visited; SubExprVec vec; - visitGlobal( F, visited, vec ); + visitGlobal( G, visited, vec ); assert( visited.empty() ); } +void GlobalDepsAnalyzer::extendLifetimeIfPresent(GlobalValue* G) +{ + if (!G) + return; + extendLifetime(G); +} + void GlobalDepsAnalyzer::replaceFunctionAliasWithAliasee(llvm::Module &module, StringRef name) { GlobalAlias *funcAlias = module.getNamedAlias(name); @@ -692,63 +698,16 @@ bool GlobalDepsAnalyzer::runOnModule( llvm::Module & module ) // Flush out all functions processEnqueuedFunctions(); - if(mayNeedAsmJSFree) - { - Function* ffree = module.getFunction("free"); - if (ffree) - { - if(!hasAsmJSMalloc && !preserveFree) - { - // The symbol is still used around, so keep it but make it empty - ffree->deleteBody(); - asmJSExportedFuncions.erase(ffree); - Function* jsfree = module.getFunction("__genericjs__free"); - // For jsfree, keep an empty body (could still be called if we don't run lto) - if (jsfree) - { - jsfree->deleteBody(); - BasicBlock* Entry = BasicBlock::Create(module.getContext(),"entry", jsfree); - IRBuilder<> Builder(Entry); - Builder.CreateRetVoid(); - } - } - else - { - hasAsmJSCode = true; - asmJSExportedFuncions.insert(ffree); - externals.push_back(ffree); - // Visit free and friends - enqueueFunction(ffree); - processEnqueuedFunctions(); - reachableGlobals.insert(ffree); - } - } - } - // If we are in opt, there is a chance that a following - // pass will convert malloc into a calloc, so keep that if we keep malloc - if(!llcPass && hasAsmJSMalloc) - { - Function* fcalloc = module.getFunction("calloc"); - if (fcalloc) - { - asmJSExportedFuncions.insert(fcalloc); - externals.push_back(fcalloc); - // Visit calloc and friends - enqueueFunction(fcalloc); - processEnqueuedFunctions(); - reachableGlobals.insert(fcalloc); - } - } - + bool isWasmTarget = Triple(module.getTargetTriple()).isCheerpWasm(); // Create a dummy function that prevents nullptr conflicts. - if(hasAsmJSCode) + if(isWasmTarget) createNullptrFunction(module); // Set the sret slot in the asmjs section if there is asmjs code GlobalVariable* Sret = module.getGlobalVariable("cheerpSretSlot"); if (Sret) { - if (hasAsmJSCode) + if (isWasmTarget) Sret->setSection(StringRef("asmjs")); if (llcPass && !Sret->hasInitializer()) Sret->setInitializer(ConstantInt::get(Type::getInt32Ty(module.getContext()), 0)); @@ -764,6 +723,7 @@ bool GlobalDepsAnalyzer::runOnModule( llvm::Module & module ) BitCastSlot->setSection("asmjs"); BitCastSlot->setInitializer(ConstantAggregateZero::get(BitCastSlot->getValueType())); BitCastSlot->setAlignment(Align(8)); + extendLifetime(BitCastSlot); SubExprVec vec; visitGlobal(BitCastSlot, visited, vec ); // Ensure external linkage to prevent GlobalOpts to remove it @@ -816,11 +776,11 @@ bool GlobalDepsAnalyzer::runOnModule( llvm::Module & module ) hasBuiltin[BuiltinInstr::SIN_F] = false; } } - bool WasmOnly = Triple(module.getTargetTriple()).getOS() == Triple::WASI; + bool isWasi = Triple(module.getTargetTriple()).getOS() == Triple::WASI; // Detect all used non-math builtins for(const Function& F: module) { - if(F.getIntrinsicID() == Intrinsic::cheerp_grow_memory && !WasmOnly) + if(F.getIntrinsicID() == Intrinsic::cheerp_grow_memory && !isWasi) { hasBuiltin[BuiltinInstr::GROW_MEM] = true; } @@ -1168,12 +1128,6 @@ bool GlobalDepsAnalyzer::runOnModule( llvm::Module & module ) replaceSomeUsesWith(indirectUses, placeholderFunc); } - // If the linear output is wasm, pretend that there is always some code. - // This simplify the writer for the logic that doesn't output a module if we only have - // asmjs data but not code - if (LinearOutput == Wasm) - hasAsmJSCode = true; - return true; } @@ -1202,30 +1156,10 @@ void GlobalDepsAnalyzer::visitGlobal( const GlobalValue * C, VisitedSet & visite } else if (const Function * F = dyn_cast(C) ) { - if (isFreeFunctionName(C->getName())) - { - // Don't visit free right now. We do it only if - // actyally needed at the end - mayNeedAsmJSFree = true; - } - else - { - if (C->getName() == StringRef("malloc")) - hasAsmJSMalloc = true; - - if (C->getSection() == StringRef("asmjs")) - { - hasAsmJSCode = true; - } - enqueueFunction(F); - } + enqueueFunction(F); } else if (const GlobalVariable * GV = dyn_cast(C) ) { - if (C->getSection() == StringRef("asmjs")) - { - hasAsmJSMemory = true; - } if (GV->hasInitializer() ) { // Add the "GlobalVariable - initializer" use to the subexpr, @@ -1337,8 +1271,6 @@ void GlobalDepsAnalyzer::visitFunction(const Function* F, VisitedSet& visited) Type* allocaType = AI->getAllocatedType(); if(!isAsmJS) visitType(allocaType, forceTypedArrays); - if (isAsmJS || (isa(allocaType) && cast(allocaType)->hasAsmJS())) - hasAsmJSMemory = true; } else if ( const CallBase* CB = dyn_cast(&I) ) { @@ -1361,13 +1293,7 @@ void GlobalDepsAnalyzer::visitFunction(const Function* F, VisitedSet& visited) } else if (isa(&I)) { - Function* cxa_resume = module->getFunction("__cxa_resume"); - if (cxa_resume) - { - SubExprVec vec; - visitGlobal(cxa_resume, visited, vec ); - externals.push_back(cxa_resume); - } + extendLifetimeIfPresent(module->getFunction("__cxa_resume")); } else if (!isAsmJS && I.getOpcode() == Instruction::VAArg) hasVAArgs = true; @@ -1384,9 +1310,6 @@ void GlobalDepsAnalyzer::visitFunction(const Function* F, VisitedSet& visited) const llvm::User* bc = cast(ci.getCalledOperand()); calledFunc = dyn_cast(bc->getOperand(0)); } - // To call asmjs functions with variable arguments, we need the linear memory - if (ci.getFunctionType()->isVarArg() && (isAsmJS || (calledFunc && calledFunc->getSection() == "asmjs"))) - hasAsmJSMemory = true; // TODO: Handle import/export of indirect calls if possible if (!calledFunc) continue; @@ -1400,6 +1323,17 @@ void GlobalDepsAnalyzer::visitFunction(const Function* F, VisitedSet& visited) // normal function called from asm.js else if (!calleeIsAsmJS && isAsmJS) asmJSImportedFuncions.insert(calledFunc); + + if (!llcPass && calledFunc->getName() == "malloc") + { + // If we are in opt, there is a chance that a following + // pass will convert malloc into a calloc, so keep that if we keep malloc + Function* fcalloc = module->getFunction("calloc"); + if (fcalloc) + { + extendLifetime(fcalloc); + } + } } // if this is an allocation intrinsic and we are in asmjs, // visit the corresponding libc function. The same applies if the allocated type is asmjs. @@ -1411,12 +1345,16 @@ void GlobalDepsAnalyzer::visitFunction(const Function* F, VisitedSet& visited) Function* fmalloc = module->getFunction("malloc"); if (fmalloc) { - SubExprVec vec; - visitGlobal(fmalloc, visited, vec ); if(!isAsmJS) asmJSExportedFuncions.insert(fmalloc); - externals.push_back(fmalloc); - hasAsmJSMalloc = true; + extendLifetime(fmalloc); + } + // If we are in opt, there is a chance that a following + // pass will convert malloc into a calloc, so keep that if we keep malloc + Function* fcalloc = module->getFunction("calloc"); + if (fcalloc && !llcPass) + { + extendLifetime(fcalloc); } } } @@ -1427,12 +1365,9 @@ void GlobalDepsAnalyzer::visitFunction(const Function* F, VisitedSet& visited) Function* frealloc = module->getFunction("realloc"); if (frealloc) { - SubExprVec vec; - visitGlobal(frealloc, visited, vec ); if(!isAsmJS) asmJSExportedFuncions.insert(frealloc); - externals.push_back(frealloc); - hasAsmJSMalloc = true; + extendLifetime(frealloc); } } } @@ -1441,10 +1376,12 @@ void GlobalDepsAnalyzer::visitFunction(const Function* F, VisitedSet& visited) Type* ty = ci.getOperand(0)->getType(); bool basicType = !ty->isAggregateType(); bool asmjsPtr = TypeSupport::isAsmJSPointer(ty); - if (isAsmJS || basicType || asmjsPtr) + Function* ffree = module->getFunction("free"); + if (ffree) { - // Delay adding free, it will be done only if asm.js malloc is actually there - mayNeedAsmJSFree = true; + if(!isAsmJS && (basicType || asmjsPtr)) + asmJSExportedFuncions.insert(ffree); + extendLifetime(ffree); } } else if (calledFunc->getIntrinsicID() == Intrinsic::memset) diff --git a/llvm/lib/CheerpUtils/InvokeWrapping.cpp b/llvm/lib/CheerpUtils/InvokeWrapping.cpp index 992359877156..c856327aa55c 100644 --- a/llvm/lib/CheerpUtils/InvokeWrapping.cpp +++ b/llvm/lib/CheerpUtils/InvokeWrapping.cpp @@ -9,6 +9,7 @@ // //===----------------------------------------------------------------------===// +#include "llvm/ADT/Triple.h" #include "llvm/Cheerp/InvokeWrapping.h" #include "llvm/Cheerp/Utility.h" #include "llvm/IR/Instructions.h" @@ -362,11 +363,18 @@ static Function* wrapInvoke(Module& M, InvokeInst& IV, DenseSet& T static Function* wrapResume(Module& M, ResumeInst* RS) { + PointerType* Int8PtrTy = Type::getInt8Ty(M.getContext())->getPointerTo(0); Function* CxaResume = M.getFunction("__cxa_resume"); assert(CxaResume); IRBuilder<> Builder(RS); Value* LP = RS->getOperand(0); Value* Val = Builder.CreateExtractValue(LP, {0}); + if (Triple(M.getTargetTriple()).isCheerpWasm()) { + Val = Builder.CreateIntToPtr(Val, Int8PtrTy); + } else { + llvm::Function *MakeReg = Intrinsic::getDeclaration(&M, Intrinsic::cheerp_make_regular, {Int8PtrTy, Int8PtrTy}); + Val = Builder.CreateCall(MakeReg, {ConstantPointerNull::get(Int8PtrTy), Val}); + } Value* Call = Builder.CreateCall(CxaResume->getFunctionType(), CxaResume, Val); RS->replaceAllUsesWith(Call); Builder.CreateUnreachable(); diff --git a/llvm/lib/CheerpUtils/PointerAnalyzer.cpp b/llvm/lib/CheerpUtils/PointerAnalyzer.cpp index d468cfc23c4b..4ee1e22741c2 100644 --- a/llvm/lib/CheerpUtils/PointerAnalyzer.cpp +++ b/llvm/lib/CheerpUtils/PointerAnalyzer.cpp @@ -659,6 +659,7 @@ PointerKindWrapper& PointerUsageVisitor::visitValue(PointerKindWrapper& ret, con return CacheAndReturn(visitValue(ret, intrinsic->getArgOperand(0), /*first*/ false)); case Intrinsic::cheerp_pointer_offset: case Intrinsic::cheerp_is_linear_heap: + case Intrinsic::cheerp_pointer_elem_size: case Intrinsic::invariant_start: return CacheAndReturn(visitValue(ret, intrinsic->getArgOperand(1), /*first*/ false)); case Intrinsic::stacksave: @@ -893,6 +894,7 @@ PointerKindWrapper& PointerUsageVisitor::visitUse(PointerKindWrapper& ret, const case Intrinsic::cheerp_pointer_base: case Intrinsic::cheerp_pointer_offset: case Intrinsic::cheerp_is_linear_heap: + case Intrinsic::cheerp_pointer_elem_size: case Intrinsic::vastart: case Intrinsic::vacopy: case Intrinsic::vaend: diff --git a/llvm/lib/CheerpUtils/PointerPasses.cpp b/llvm/lib/CheerpUtils/PointerPasses.cpp index 1ef1a6b276ff..82dbf4eb9a26 100644 --- a/llvm/lib/CheerpUtils/PointerPasses.cpp +++ b/llvm/lib/CheerpUtils/PointerPasses.cpp @@ -14,6 +14,7 @@ #include "llvm/Analysis/LoopInfo.h" #include "llvm/Analysis/PostDominators.h" #include "llvm/ADT/Statistic.h" +#include "llvm/ADT/Triple.h" #include "llvm/Cheerp/DeterministicUnorderedSet.h" #include "llvm/Cheerp/InvokeWrapping.h" #include "llvm/Cheerp/PointerPasses.h" @@ -58,9 +59,9 @@ class FreeAndDeleteRemoval { private: void deleteInstructionAndUnusedOperands(llvm::Instruction* I); - bool isAllGenericJS; + bool isWasmTarget; public: - explicit FreeAndDeleteRemoval() : isAllGenericJS(false) { } + explicit FreeAndDeleteRemoval(): isWasmTarget(false) { } bool runOnModule(llvm::Module &M); }; @@ -578,6 +579,7 @@ static Function* getOrCreateGenericJSFree(Module& M, bool isAllGenericJS) Type* VoidPtr = IntegerType::get(M.getContext(), 8)->getPointerTo(); Type* Tys[] = { VoidPtr }; Function *GetBase = Intrinsic::getDeclaration(&M, Intrinsic::cheerp_is_linear_heap, Tys); + Function *ElemSize = Intrinsic::getDeclaration(&M, Intrinsic::cheerp_pointer_elem_size, Tys); BasicBlock* ExitBlock = BasicBlock::Create(M.getContext(), "exitblk", New); BasicBlock* ForwardBlock = BasicBlock::Create(M.getContext(), "fwdblk", New); @@ -592,7 +594,9 @@ static Function* getOrCreateGenericJSFree(Module& M, bool isAllGenericJS) Builder.SetInsertPoint(ForwardBlock); Function *PtrOffset = Intrinsic::getDeclaration(&M, Intrinsic::cheerp_pointer_offset, Tys); CallInst* Offset = Builder.CreateCall(PtrOffset, Params); - Value* OffsetP = Builder.CreateIntToPtr(Offset, VoidPtr); + CallInst* Size = Builder.CreateCall(ElemSize, Params); + Value* OffsetShifted = Builder.CreateMul(Offset, Size); + Value* OffsetP = Builder.CreateIntToPtr(OffsetShifted, VoidPtr); Value* Params2[] = { OffsetP }; Builder.CreateCall(Orig, Params2); } @@ -604,20 +608,7 @@ bool FreeAndDeleteRemoval::runOnModule(Module& M) { bool Changed = false; - isAllGenericJS = true; - bool hasFree = M.getFunction("free") != nullptr; - if(hasFree) - { - for (const Function& f: M) - { - if (f.getSection() == StringRef("asmjs") && !cheerp::isFreeFunctionName(f.getName())) - { - isAllGenericJS = false; - break; - } - } - } - + isWasmTarget = Triple(M.getTargetTriple()).isCheerpWasm(); std::vector usesToBeReplaced; for (Function& f: M) { @@ -631,7 +622,7 @@ bool FreeAndDeleteRemoval::runOnModule(Module& M) User* Usr = U.getUser(); if (CallBase* call = dyn_cast(Usr)) { - if (isAllGenericJS) + if (!isWasmTarget) { deleteInstructionAndUnusedOperands(call); Changed = true; @@ -646,7 +637,7 @@ bool FreeAndDeleteRemoval::runOnModule(Module& M) { continue; } - U.set(getOrCreateGenericJSFree(M, isAllGenericJS)); + U.set(getOrCreateGenericJSFree(M, !isWasmTarget)); Changed = true; } else if (GlobalValue* gv = dyn_cast(Usr)) @@ -655,7 +646,7 @@ bool FreeAndDeleteRemoval::runOnModule(Module& M) { continue; } - U.set(getOrCreateGenericJSFree(M, isAllGenericJS)); + U.set(getOrCreateGenericJSFree(M, !isWasmTarget)); Changed = true; } else if (Constant* c = dyn_cast(Usr)) @@ -668,7 +659,7 @@ bool FreeAndDeleteRemoval::runOnModule(Module& M) } else { - U.set(getOrCreateGenericJSFree(M, isAllGenericJS)); + U.set(getOrCreateGenericJSFree(M, !isWasmTarget)); Changed = true; } @@ -689,11 +680,19 @@ bool FreeAndDeleteRemoval::runOnModule(Module& M) Type* ty = call->getOperand(0)->getType(); assert(isa(ty)); Type* elemTy = cast(ty)->getPointerElementType(); - if (isAllGenericJS || (!cheerp::TypeSupport::isAsmJSPointed(elemTy) && elemTy->isAggregateType())) + if (!isWasmTarget || (!cheerp::TypeSupport::isAsmJSPointed(elemTy) && elemTy->isAggregateType())) { deleteInstructionAndUnusedOperands(call); Changed = true; } + else if (cheerp::TypeSupport::isAsmJSPointed(elemTy)) + { + U.set(M.getFunction("free")); + } + else + { + U.set(getOrCreateGenericJSFree(M, false)); + } } } } @@ -721,7 +720,7 @@ bool FreeAndDeleteRemoval::runOnModule(Module& M) if (!usesToBeReplaced.empty()) { - cheerp::replaceSomeUsesWith(usesToBeReplaced, getOrCreateGenericJSFree(M, isAllGenericJS)); + cheerp::replaceSomeUsesWith(usesToBeReplaced, getOrCreateGenericJSFree(M, !isWasmTarget)); } return Changed; diff --git a/llvm/lib/CheerpWriter/CheerpWriter.cpp b/llvm/lib/CheerpWriter/CheerpWriter.cpp index 94c612838561..4a2689bcd5fb 100644 --- a/llvm/lib/CheerpWriter/CheerpWriter.cpp +++ b/llvm/lib/CheerpWriter/CheerpWriter.cpp @@ -712,7 +712,7 @@ void CheerpWriter::compileAllocation(const DynamicAllocInfo & info) CheerpWriter::COMPILE_INSTRUCTION_FEEDBACK CheerpWriter::compileFree(const Value* obj) { // Only arrays of primitives can be backed by the linear heap - bool needsLinearCheck = TypeSupport::isTypedArrayType(obj->getType()->getPointerElementType(), /*forceTypedArray*/ true) && globalDeps.usesAsmJSMalloc(); + bool needsLinearCheck = TypeSupport::isTypedArrayType(obj->getType()->getPointerElementType(), /*forceTypedArray*/ true) && isWasmTarget; if(const ConstantInt* CI = PA.getConstantOffsetForPointer(obj)) { // 0 is clearly not a good address in the linear address space @@ -944,6 +944,13 @@ CheerpWriter::COMPILE_INSTRUCTION_FEEDBACK CheerpWriter::handleBuiltinCall(const stream<<".buffer===__heap)"; return COMPILE_OK; } + else if(intrinsicId==Intrinsic::cheerp_pointer_elem_size) + { + stream << '('; + compilePointerBase(*it); + stream<<".BYTES_PER_ELEMENT)"; + return COMPILE_OK; + } else if(intrinsicId==Intrinsic::cheerp_pointer_kind) { stream << (int)PA.getPointerKindAssert(*it); @@ -1974,7 +1981,7 @@ void CheerpWriter::compilePointerBaseTyped(const Value* p, Type* elementType, bo { assert(isa(p->getType())); Type* ty = llvm::cast(p->getType())->getPointerElementType(); - if (globalDeps.needAsmJSMemory()||globalDeps.needAsmJSCode()) + if (isWasmTarget) compileHeapForType(ty); else stream << "nullArray"; @@ -1999,7 +2006,7 @@ void CheerpWriter::compilePointerBaseTyped(const Value* p, Type* elementType, bo Type* ty = llvm::cast(p->getType())->getPointerElementType(); if (isa(p)) stream << "nullArray"; - else if ((globalDeps.needAsmJSMemory() || globalDeps.needAsmJSCode()) && !ty->isStructTy()) + else if (isWasmTarget && !ty->isStructTy()) { compileHeapForType(ty); } @@ -3090,7 +3097,7 @@ CheerpWriter::COMPILE_INSTRUCTION_FEEDBACK CheerpWriter::compileTerminatorInstru { if(auto* STy = dyn_cast(retVal->getType())) { - assert(STy->getName() == "struct._ZN10__cxxabiv119__cheerp_landingpadE"); + assert(STy->getNumElements() == 2 && STy->getElementType(0) == STy->getElementType(1) && STy->getElementType(0)->isIntegerTy(32)); stream << "oSlot="; compileAggregateElem(retVal, 1, 1, LOWEST); stream << ";" << NewLine; @@ -6489,12 +6496,12 @@ void CheerpWriter::compileHelpers() if(!wasmFile.empty() || asmJSMem) compileFetchBuffer(); - if ((globalDeps.needAsmJSMemory() || globalDeps.needAsmJSCode() ) && checkBounds) + if (isWasmTarget && checkBounds) { compileCheckBoundsAsmJSHelper(); } - if (globalDeps.needAsmJSMemory() && !globalDeps.needAsmJSCode()) + if (isWasmTarget) { stream << "var " << namegen.getBuiltinName(NameGenerator::Builtin::STACKPTR) << '=' << linearHelper.getStackStart() << "|0;" << NewLine; @@ -6657,7 +6664,7 @@ void CheerpWriter::compileAsmJSTopLevel() stream << ";" << NewLine; } stream << namegen.getBuiltinName(NameGenerator::Builtin::ASSIGN_HEAPS) << "(__heap);" << NewLine; - if (globalDeps.needAsmJSCode()) + if (isWasmTarget) { stream << "var stdlib = {"<{" << NewLine; stream << "__asm=" << shortestName << ".instance.exports;" << NewLine; stream << "__heap=" << namegen.getBuiltinName(NameGenerator::MEMORY) << ".buffer;" << NewLine; - if (globalDeps.needAsmJSMemory()) - { - stream << namegen.getBuiltinName(NameGenerator::Builtin::ASSIGN_HEAPS) << "(__heap);" << NewLine; - } + stream << namegen.getBuiltinName(NameGenerator::Builtin::ASSIGN_HEAPS) << "(__heap);" << NewLine; } void CheerpWriter::compileDeclareExports() @@ -7095,9 +7096,6 @@ void CheerpWriter::compileFileEnd(const OptionsSet& options) void CheerpWriter::makeJS() { const bool needWasmLoader = !wasmFile.empty(); - const bool needAssignHeaps = globalDeps.needsBuiltin(BuiltinInstr::BUILTIN::GROW_MEM) - || globalDeps.needAsmJSMemory() - || globalDeps.needAsmJSCode(); auto initializeOptions = [&]() -> OptionsSet { @@ -7124,23 +7122,20 @@ void CheerpWriter::makeJS() compileWasmLoader(); else { - if (globalDeps.needAsmJSCode()) + if (isWasmTarget) { compileAsmJSClosure(); - } - if (globalDeps.needAsmJSCode() || globalDeps.needAsmJSMemory()) - { compileAsmJSTopLevel(); } - if (globalDeps.needAsmJSMemory() && asmJSMem) + if (isWasmTarget && asmJSMem) compileAsmJSLoader(); else if (makeModule == MODULE_TYPE::COMMONJS || makeModule == MODULE_TYPE::ES6) compileCommonJSModule(); else areExtraParenthesisOpen = false; - if (globalDeps.needAsmJSCode()) + if (isWasmTarget) { stream << "var __asm=asmJS(stdlib, "; compileAsmJSffiObject(); @@ -7159,7 +7154,7 @@ void CheerpWriter::makeJS() if (makeModule == MODULE_TYPE::ES6) stream << "}" << NewLine; - if (needAssignHeaps) + if (isWasmTarget) compileAssignHeaps(needWasmLoader); compileFileEnd(options); diff --git a/llvm/lib/CheerpWriter/NameGenerator.cpp b/llvm/lib/CheerpWriter/NameGenerator.cpp index 5642d64e38ec..b1db72ddbee1 100644 --- a/llvm/lib/CheerpWriter/NameGenerator.cpp +++ b/llvm/lib/CheerpWriter/NameGenerator.cpp @@ -19,6 +19,7 @@ #include "llvm/Cheerp/Writer.h" #include "llvm/IR/Function.h" #include "llvm/IR/InlineAsm.h" +#include "llvm/ADT/Triple.h" #include #include @@ -354,8 +355,9 @@ void NameGenerator::generateCompressedNames(const Module& M, const GlobalDepsAna NameHelper nameHelper(GlobalPrefix, reservedNames); + bool isWasmTarget = Triple(M.getTargetTriple()).isCheerpWasm(); // Generate HEAP names first to keep them short - if(gda.needAsmJSMemory() || gda.needAsmJSCode()) + if(isWasmTarget) { if (exportedMemory) { diff --git a/llvm/lib/Target/CheerpBackend/CheerpBackend.cpp b/llvm/lib/Target/CheerpBackend/CheerpBackend.cpp index 0f85287764a5..c76ae5c1694e 100644 --- a/llvm/lib/Target/CheerpBackend/CheerpBackend.cpp +++ b/llvm/lib/Target/CheerpBackend/CheerpBackend.cpp @@ -104,11 +104,11 @@ bool CheerpWritePass::runOnModule(Module& M) PB.crossRegisterProxies(LAM, FAM, CGAM, MAM); ModulePassManager MPM; - + bool isWasmTarget = Triple(M.getTargetTriple()).isCheerpWasm(); cheerp::GlobalDepsAnalyzer::MATH_MODE mathMode; if (NoNativeJavaScriptMath) mathMode = cheerp::GlobalDepsAnalyzer::NO_BUILTINS; - else if(Triple(M.getTargetTriple()).getEnvironment() == llvm::Triple::WebAssembly && LinearOutput != AsmJs) + else if(isWasmTarget && LinearOutput != AsmJs) mathMode = cheerp::GlobalDepsAnalyzer::WASM_BUILTINS; else @@ -149,7 +149,8 @@ bool CheerpWritePass::runOnModule(Module& M) MPM.addPass(cheerp::FreeAndDeleteRemovalPass()); MPM.addPass(cheerp::GlobalDepsAnalyzerPass(mathMode, /*resolveAliases*/true)); - MPM.addPass(cheerp::AllocaLoweringPass()); + if (isWasmTarget) + MPM.addPass(cheerp::AllocaLoweringPass()); MPM.addPass(cheerp::InvokeWrappingPass()); MPM.addPass(cheerp::FFIWrappingPass()); MPM.addPass(createModuleToFunctionPassAdaptor(cheerp::FixIrreducibleControlFlowPass())); From 651dc25cc9613e315efb8bee61fb2084aaad13f6 Mon Sep 17 00:00:00 2001 From: Yuri Iozzelli Date: Thu, 30 May 2024 16:03:17 +0200 Subject: [PATCH 2/4] TypeOptimizer: preserve function metadata when rewriting signatures --- llvm/lib/CheerpUtils/TypeOptimizer.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/llvm/lib/CheerpUtils/TypeOptimizer.cpp b/llvm/lib/CheerpUtils/TypeOptimizer.cpp index 562a91211d0d..9be06fa932ff 100644 --- a/llvm/lib/CheerpUtils/TypeOptimizer.cpp +++ b/llvm/lib/CheerpUtils/TypeOptimizer.cpp @@ -1447,6 +1447,7 @@ Function* TypeOptimizer::rewriteFunctionSignature(Function* F) // Create the new function body and insert it into the module. Function *NF = Function::Create(newFuncType, F->getLinkage(), F->getName()); NF->copyAttributesFrom(F); + NF->copyMetadata(F, 0); // Patch the pointer to LLVM function in debug info descriptor. NF->setSubprogram(F->getSubprogram()); From f77f23d8fd3230710528768e3b8a66d22685e08a Mon Sep 17 00:00:00 2001 From: Yuri Iozzelli Date: Fri, 31 May 2024 13:54:54 +0200 Subject: [PATCH 3/4] GDA: don't create the __wasm_nullptr function is no wasm function address is taken --- llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp b/llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp index c2033bc52eae..0fd30e082aa8 100644 --- a/llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp +++ b/llvm/lib/CheerpUtils/GlobalDepsAnalyzer.cpp @@ -209,9 +209,13 @@ bool GlobalDepsAnalyzer::runOnModule( llvm::Module & module ) } } + bool anyWasmFuncAddrTaken = false; // Replace calls like 'printf("Hello!")' with 'puts("Hello!")'. for (Function& F : module.getFunctionList()) { bool asmjs = F.getSection() == StringRef("asmjs"); + if (asmjs) { + anyWasmFuncAddrTaken |= F.hasAddressTaken(); + } for (BasicBlock& bb : F) { bool advance = false; //Do not advance at the start @@ -700,7 +704,7 @@ bool GlobalDepsAnalyzer::runOnModule( llvm::Module & module ) bool isWasmTarget = Triple(module.getTargetTriple()).isCheerpWasm(); // Create a dummy function that prevents nullptr conflicts. - if(isWasmTarget) + if(isWasmTarget && anyWasmFuncAddrTaken) createNullptrFunction(module); // Set the sret slot in the asmjs section if there is asmjs code From 5bf9bf56efc7b88bfd0256336f81d720ee511339 Mon Sep 17 00:00:00 2001 From: Yuri Iozzelli Date: Fri, 31 May 2024 14:57:19 +0200 Subject: [PATCH 4/4] unittests: add missing link components to cheerp tests --- llvm/unittests/Cheerp/CMakeLists.txt | 2 ++ 1 file changed, 2 insertions(+) diff --git a/llvm/unittests/Cheerp/CMakeLists.txt b/llvm/unittests/Cheerp/CMakeLists.txt index 1d84085fa2ed..51fd4fa00d5e 100644 --- a/llvm/unittests/Cheerp/CMakeLists.txt +++ b/llvm/unittests/Cheerp/CMakeLists.txt @@ -2,6 +2,8 @@ set(LLVM_LINK_COMPONENTS CheerpUtils Core IRReader + TransformUtils + CodeGen ) add_llvm_unittest(CheerpTests