diff --git a/clang/lib/Driver/ToolChains/WebAssembly.cpp b/clang/lib/Driver/ToolChains/WebAssembly.cpp index e2ab0b815eef..3f567cade67b 100644 --- a/clang/lib/Driver/ToolChains/WebAssembly.cpp +++ b/clang/lib/Driver/ToolChains/WebAssembly.cpp @@ -837,6 +837,7 @@ static cheerp::CheerpWasmOpt parseWasmOpt(StringRef opt) .Case("simd", cheerp::SIMD) .Case("globalization", cheerp::GLOBALIZATION) .Case("unalignedmem", cheerp::UNALIGNEDMEM) + .Case("mappedmemory", cheerp::MAPPEDMEMORY) .Default(cheerp::INVALID); } @@ -1054,6 +1055,9 @@ void cheerp::CheerpCompiler::ConstructJob(Compilation &C, const JobAction &JA, case UNALIGNEDMEM: noUnalignedMem = false; break; + case MAPPEDMEMORY: + CmdArgs.push_back("-cheerp-wasm-mapped-memory"); + break; default: llvm_unreachable("invalid wasm option"); break; diff --git a/clang/lib/Driver/ToolChains/WebAssembly.h b/clang/lib/Driver/ToolChains/WebAssembly.h index 072690b19090..35241c94ce37 100644 --- a/clang/lib/Driver/ToolChains/WebAssembly.h +++ b/clang/lib/Driver/ToolChains/WebAssembly.h @@ -47,7 +47,8 @@ namespace cheerp { BRANCHHINTS, SIMD, GLOBALIZATION, - UNALIGNEDMEM + UNALIGNEDMEM, + MAPPEDMEMORY }; std::vector getWasmFeatures(const Driver& D, const llvm::Triple& triple, const llvm::opt::ArgList& Args); diff --git a/llvm/include/llvm/Cheerp/BuiltinInstructions.h b/llvm/include/llvm/Cheerp/BuiltinInstructions.h index a7d38aa3ffc4..11af1c57ff57 100644 --- a/llvm/include/llvm/Cheerp/BuiltinInstructions.h +++ b/llvm/include/llvm/Cheerp/BuiltinInstructions.h @@ -23,6 +23,8 @@ namespace BuiltinInstr enum BUILTIN {NONE, ABS_F, ACOS_F, ASIN_F, ATAN_F, ATAN2_F, CEIL_F, COS_F, EXP_F, FLOOR_F, LOG_F, POW_F, SIN_F, SQRT_F, TAN_F, CLZ, MOD_F, GROW_MEM, EXCEPTION_PAD_32, EXCEPTION_PAD_64, EXCEPTION_PAD_F, EXCEPTION_PAD_D, EXCEPTION_PAD_V, + CHECKED_LOAD_8, CHECKED_LOAD_16, CHECKED_LOAD_32, CHECKED_LOAD_64, CHECKED_LOAD_F, CHECKED_LOAD_D, + CHECKED_STORE_8, CHECKED_STORE_16, CHECKED_STORE_32, CHECKED_STORE_64, CHECKED_STORE_F, CHECKED_STORE_D, MAX_BUILTIN }; } //close BuiltinInstr diff --git a/llvm/include/llvm/Cheerp/CheckedLoadStore.h b/llvm/include/llvm/Cheerp/CheckedLoadStore.h new file mode 100644 index 000000000000..d908333e4b42 --- /dev/null +++ b/llvm/include/llvm/Cheerp/CheckedLoadStore.h @@ -0,0 +1,37 @@ +//===-- Cheerp/CheckLoadStore.h - Check addresses before loads/stores -----===// +// +// 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 2026 Leaning Technologies +// +//===----------------------------------------------------------------------===// + +#ifndef _CHEERP_CHECK_LOAD_STORE_H +#define _CHEERP_CHECK_LOAD_STORE_H + +#include "llvm/IR/Module.h" +#include "llvm/IR/PassManager.h" + +namespace cheerp { + +class CheckLoadStore +{ +public: + static char ID; + explicit CheckLoadStore() { } + + bool runOnFunction(llvm::Module& M, llvm::Function &F); +}; + +class CheckLoadStorePass : public llvm::PassInfoMixin { +public: + llvm::PreservedAnalyses run(llvm::Module& M, llvm::ModuleAnalysisManager& MAM); + static bool isRequired() { return true; } +}; + +} + +#endif //_CHEERP_CHECK_LOAD_STORE_H diff --git a/llvm/include/llvm/Cheerp/CommandLine.h b/llvm/include/llvm/Cheerp/CommandLine.h index 04a4ed4fa42d..77ff3798b2f4 100644 --- a/llvm/include/llvm/Cheerp/CommandLine.h +++ b/llvm/include/llvm/Cheerp/CommandLine.h @@ -58,6 +58,7 @@ extern llvm::cl::opt WasmReturnCalls; extern llvm::cl::opt WasmNoSIMD; extern llvm::cl::opt WasmNoGlobalization; extern llvm::cl::opt WasmNoUnalignedMem; +extern llvm::cl::opt WasmMappedMemory; extern llvm::cl::opt WasmSharedModule; extern llvm::cl::opt UseBigInts; extern llvm::cl::opt KeepInvokes; diff --git a/llvm/include/llvm/Cheerp/PassRegistry.h b/llvm/include/llvm/Cheerp/PassRegistry.h index 9cdab5abb606..725e5d3fd12a 100644 --- a/llvm/include/llvm/Cheerp/PassRegistry.h +++ b/llvm/include/llvm/Cheerp/PassRegistry.h @@ -35,6 +35,7 @@ #include "llvm/Cheerp/SourceMaps.h" #include "llvm/Cheerp/StructMemFuncLowering.h" #include "llvm/Cheerp/ConstantExprLowering.h" +#include "llvm/Cheerp/CheckedLoadStore.h" #include "llvm/Cheerp/SpillLocals.h" #include "llvm/Cheerp/InvokeWrapping.h" #include "llvm/Cheerp/FFIWrapping.h" diff --git a/llvm/include/llvm/IR/IntrinsicsCheerp.td b/llvm/include/llvm/IR/IntrinsicsCheerp.td index 27d1a0342c2f..eb79febacd61 100644 --- a/llvm/include/llvm/IR/IntrinsicsCheerp.td +++ b/llvm/include/llvm/IR/IntrinsicsCheerp.td @@ -155,6 +155,12 @@ def int_cheerp_locals_stack : Intrinsic<[llvm_ptr_ty], def int_cheerp_local_store : Intrinsic<[], [llvm_ptr_ty, llvm_any_ty]>; +def int_cheerp_checked_load : Intrinsic<[llvm_any_ty], + [llvm_i32_ty, llvm_i32_ty]>; + +def int_cheerp_checked_store : Intrinsic<[], + [llvm_i32_ty, llvm_i32_ty, llvm_any_ty, llvm_any_ty]>; + def int_cheerp_func_id : Intrinsic<[llvm_i32_ty], []>; diff --git a/llvm/lib/CheerpUtils/CMakeLists.txt b/llvm/lib/CheerpUtils/CMakeLists.txt index 87967eaef145..b4ec554fc102 100644 --- a/llvm/lib/CheerpUtils/CMakeLists.txt +++ b/llvm/lib/CheerpUtils/CMakeLists.txt @@ -37,6 +37,7 @@ add_llvm_component_library(LLVMCheerpUtils JsExport.cpp ThreadLocalLowering.cpp LowerGlobalDestructors.cpp + CheckedLoadStore.cpp SpillLocals.cpp ) diff --git a/llvm/lib/CheerpUtils/CheckedLoadStore.cpp b/llvm/lib/CheerpUtils/CheckedLoadStore.cpp new file mode 100644 index 000000000000..377592b3bc29 --- /dev/null +++ b/llvm/lib/CheerpUtils/CheckedLoadStore.cpp @@ -0,0 +1,86 @@ +//===-- CheckLoadStore.cpp - Check addresses before loads/stores ----------===// +// +// 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 2026 Leaning Technologies +// +//===----------------------------------------------------------------------===// + +#include "llvm/InitializePasses.h" +#include "llvm/Cheerp/CheckedLoadStore.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/IntrinsicInst.h" + +using namespace llvm; + +namespace cheerp +{ + +bool CheckLoadStore::runOnFunction(Module& M, Function& F) +{ + // Inject the cheerp_checked_load and cheerp_checked_store intrinsics + // in place of all plain loads and stores. The backend will insert + // conditional calls to imported helpers to access memory mapped + // in the negative space. + std::vector toDelete; + for ( BasicBlock & BB : F ) + { + // Be mindful of the manual increment of the iterator + for ( BasicBlock::iterator it = BB.begin(); it != BB.end(); ++it) + { + Instruction* I = &*it; + if(LoadInst* LI = dyn_cast(I)) + { + if(LI->isAtomic()) + continue; + // Constant pointers, including globals, can never be mapped + Value* pointerOperand = LI->getPointerOperand(); + if(isa(pointerOperand)) + continue; + Type* loadType = LI->getType(); + Function* checkedLoad = Intrinsic::getDeclaration(&M, Intrinsic::cheerp_checked_load, loadType); + CallInst* CI = CallInst::Create(checkedLoad, { pointerOperand, pointerOperand }, "", LI); + LI->replaceAllUsesWith(CI); + toDelete.push_back(LI); + } + else if(StoreInst* SI = dyn_cast(I)) + { + if(SI->isAtomic()) + continue; + // Constant pointers, including globals, can never be mapped + Value* pointerOperand = SI->getPointerOperand(); + if(isa(pointerOperand)) + continue; + Type* storeType = SI->getValueOperand()->getType(); + Function* checkedStore = Intrinsic::getDeclaration(&M, Intrinsic::cheerp_checked_store, { storeType, storeType }); + CallInst::Create(checkedStore, { pointerOperand, pointerOperand, SI->getValueOperand(), SI->getValueOperand() }, "", SI); + // No need to replace users, stores are void + toDelete.push_back(SI); + } + } + } + for(Instruction* I : toDelete) + { + I->eraseFromParent(); + } + return false; +} + +llvm::PreservedAnalyses CheckLoadStorePass::run(llvm::Module& M, llvm::ModuleAnalysisManager& MAM) +{ + CheckLoadStore inner; + for (Function &F : M) + { + if(F.empty() || F.getSection() != "asmjs") + continue; + inner.runOnFunction(M, F); + } + + return PreservedAnalyses::all(); +} + +} diff --git a/llvm/lib/CheerpUtils/CommandLine.cpp b/llvm/lib/CheerpUtils/CommandLine.cpp index 6e8994017602..debb93d0f811 100644 --- a/llvm/lib/CheerpUtils/CommandLine.cpp +++ b/llvm/lib/CheerpUtils/CommandLine.cpp @@ -88,6 +88,8 @@ llvm::cl::opt WasmNoGlobalization("cheerp-wasm-no-globalization", llvm::cl llvm::cl::opt WasmNoUnalignedMem("cheerp-wasm-no-unaligned-mem", llvm::cl::desc("Disable the use of unaligned load/stores in optimizations")); +llvm::cl::opt WasmMappedMemory("cheerp-wasm-mapped-memory", llvm::cl::desc("Enable checked load/store for mapped memory")); + llvm::cl::opt WasmSharedModule("cheerp-wasm-shared-module", llvm::cl::desc("Enable generation of a dynamically loaded module")); llvm::cl::opt UseBigInts("cheerp-use-bigints", llvm::cl::desc("Use the BigInt type in JS to represent i64 values")); diff --git a/llvm/lib/CheerpUtils/LinearMemoryHelper.cpp b/llvm/lib/CheerpUtils/LinearMemoryHelper.cpp index f6f3a39b0da0..d251d887cf7a 100644 --- a/llvm/lib/CheerpUtils/LinearMemoryHelper.cpp +++ b/llvm/lib/CheerpUtils/LinearMemoryHelper.cpp @@ -618,16 +618,46 @@ if (!functionTypeIndices.count(fTy)) { \ FunctionType* f32_i32_3 = FunctionType::get(f32, i32_3, false); FunctionType* f64_i32_3 = FunctionType::get(f64, i32_3, false); FunctionType* v_i32_3 = FunctionType::get(v, i32_3, false); + FunctionType* i64_i32_1 = FunctionType::get(i64, i32_1, false); + FunctionType* f32_i32_1 = FunctionType::get(f32, i32_1, false); + FunctionType* f64_i32_1 = FunctionType::get(f64, i32_1, false); + Type* i32_i32[] = { i32, i32 }; + Type* i32_i64[] = { i32, i64 }; + Type* i32_f32[] = { i32, f32 }; + Type* i32_f64[] = { i32, f64 }; + FunctionType* v_i32_i32 = FunctionType::get(v, i32_i32, false); + FunctionType* v_i32_i64 = FunctionType::get(v, i32_i64, false); + FunctionType* v_i32_f32 = FunctionType::get(v, i32_f32, false); + FunctionType* v_i32_f64 = FunctionType::get(v, i32_f64, false); ADD_FUNCTION_TYPE(i32_i32_3); ADD_FUNCTION_TYPE(i64_i32_3); ADD_FUNCTION_TYPE(f32_i32_3); ADD_FUNCTION_TYPE(f64_i32_3); ADD_FUNCTION_TYPE(v_i32_3); + ADD_FUNCTION_TYPE(i64_i32_1); + ADD_FUNCTION_TYPE(f32_i32_1); + ADD_FUNCTION_TYPE(f64_i32_1); + ADD_FUNCTION_TYPE(v_i32_i32); + ADD_FUNCTION_TYPE(v_i32_i64); + ADD_FUNCTION_TYPE(v_i32_f32); + ADD_FUNCTION_TYPE(v_i32_f64); builtinIds[BuiltinInstr::EXCEPTION_PAD_32] = maxFunctionId++; builtinIds[BuiltinInstr::EXCEPTION_PAD_64] = maxFunctionId++; builtinIds[BuiltinInstr::EXCEPTION_PAD_F] = maxFunctionId++; builtinIds[BuiltinInstr::EXCEPTION_PAD_D] = maxFunctionId++; builtinIds[BuiltinInstr::EXCEPTION_PAD_V] = maxFunctionId++; + builtinIds[BuiltinInstr::CHECKED_LOAD_8] = maxFunctionId++; + builtinIds[BuiltinInstr::CHECKED_LOAD_16] = maxFunctionId++; + builtinIds[BuiltinInstr::CHECKED_LOAD_32] = maxFunctionId++; + builtinIds[BuiltinInstr::CHECKED_LOAD_64] = maxFunctionId++; + builtinIds[BuiltinInstr::CHECKED_LOAD_F] = maxFunctionId++; + builtinIds[BuiltinInstr::CHECKED_LOAD_D] = maxFunctionId++; + builtinIds[BuiltinInstr::CHECKED_STORE_8] = maxFunctionId++; + builtinIds[BuiltinInstr::CHECKED_STORE_16] = maxFunctionId++; + builtinIds[BuiltinInstr::CHECKED_STORE_32] = maxFunctionId++; + builtinIds[BuiltinInstr::CHECKED_STORE_64] = maxFunctionId++; + builtinIds[BuiltinInstr::CHECKED_STORE_F] = maxFunctionId++; + builtinIds[BuiltinInstr::CHECKED_STORE_D] = maxFunctionId++; // Add a type for the exception tag FunctionType* void_0 = FunctionType::get(Type::getVoidTy(module->getContext()), {}, false); ADD_FUNCTION_TYPE(void_0); diff --git a/llvm/lib/CheerpWriter/CheerpWasmWriter.cpp b/llvm/lib/CheerpWriter/CheerpWasmWriter.cpp index 4c6b234d1a05..f6b761b50307 100644 --- a/llvm/lib/CheerpWriter/CheerpWasmWriter.cpp +++ b/llvm/lib/CheerpWriter/CheerpWasmWriter.cpp @@ -3072,26 +3072,117 @@ bool CheerpWasmWriter::compileInlineInstruction(WasmBuffer& code, const Instruct { // NOTE: We expect only values requiring a local to get here llvm::Value* value = ci.getOperand(1); - // Render the base address - compileOperand(code, ci.getOperand(0)); - // Render the value to store - compileOperand(code, value); - uint32_t localId = 0; - if(Argument* arg = dyn_cast(value)) + llvm::Type* valueType = value->getType(); + auto encodeLocalStore = [&](llvm::Type* ty, uint32_t offset) { + if(ty->isIntegerTy(64)) + encodeInst(WasmU32U32Opcode::I64_STORE, 3, offset, code); + else if(ty->isIntegerTy() || ty->isPointerTy()) + encodeInst(WasmU32U32Opcode::I32_STORE, 2, offset, code); + else if (ty->isDoubleTy()) + encodeInst(WasmU32U32Opcode::F64_STORE, 3, offset, code); + else if (ty->isFloatTy()) + encodeInst(WasmU32U32Opcode::F32_STORE, 2, offset, code); + else + report_fatal_error("Unsupported value type in cheerp_local_store"); + }; + if (StructType* STy = dyn_cast(valueType)) { - localId = arg->getArgNo(); + // Aggregates are expanded to multiple local registers; store each element. + Instruction* inst = cast(value); + for (const auto& ie : getInstElems(inst, PA)) + { + compileOperand(code, ci.getOperand(0)); + compileAggregateElem(code, value, ie.structIdx); + uint32_t regId = registerize.getRegisterId(inst, ie.totalIdx, EdgeContext::emptyContext()); + uint32_t localId = localMap.at(regId); + encodeLocalStore(STy->getElementType(ie.structIdx), localId * 8); + } } else { - Instruction* inst = dyn_cast(value); - assert(inst); - // Encode a store at the offset corresponding to the local - uint32_t regId = registerize.getRegisterId(inst, 0, EdgeContext::emptyContext()); - localId = localMap.at(regId); - // Sanity check - assert(localId < (currentFun->arg_size() + localMap.size())); + compileOperand(code, ci.getOperand(0)); + compileOperand(code, value); + uint32_t localId = 0; + if(Argument* arg = dyn_cast(value)) + localId = arg->getArgNo(); + else + { + Instruction* inst = cast(value); + localId = localMap.at(registerize.getRegisterId(inst, 0, EdgeContext::emptyContext())); + assert(localId < (currentFun->arg_size() + localMap.size())); + } + encodeLocalStore(valueType, localId * 8); } - encodeStore(value->getType(), localId * 8, Align(8), code, false); + return false; + } + case Intrinsic::cheerp_checked_load: + { + // The pointer address is encoded twice to make a local is available + Type* loadType = ci.getType(); + compileOperand(code, ci.getOperand(0)); + encodeInst(WasmS32Opcode::I32_CONST, 0, code); + encodeInst(WasmOpcode::I32_LT_S, code); + encodeInst(WasmU32Opcode::IF, getValType(loadType), code); + compileOperand(code, ci.getOperand(1)); + uint32_t checkedLoadId = 0; + if(loadType->isIntegerTy(8)) + checkedLoadId = linearHelper.getBuiltinId(BuiltinInstr::BUILTIN::CHECKED_LOAD_8); + else if(loadType->isIntegerTy(16)) + checkedLoadId = linearHelper.getBuiltinId(BuiltinInstr::BUILTIN::CHECKED_LOAD_16); + else if(loadType->isIntegerTy(32) || loadType->isPointerTy()) + checkedLoadId = linearHelper.getBuiltinId(BuiltinInstr::BUILTIN::CHECKED_LOAD_32); + else if(loadType->isIntegerTy(64)) + checkedLoadId = linearHelper.getBuiltinId(BuiltinInstr::BUILTIN::CHECKED_LOAD_64); + else if(loadType->isFloatTy()) + checkedLoadId = linearHelper.getBuiltinId(BuiltinInstr::BUILTIN::CHECKED_LOAD_F); + else if(loadType->isDoubleTy()) + checkedLoadId = linearHelper.getBuiltinId(BuiltinInstr::BUILTIN::CHECKED_LOAD_D); + else + report_fatal_error("Unsupported return type for checked load"); + // Leave the returned value on the stack + encodeInst(WasmU32Opcode::CALL, checkedLoadId, code); + encodeInst(WasmOpcode::ELSE, code); + compileOperand(code, ci.getOperand(1)); + encodeLoad(loadType, 0, targetData.getABITypeAlign(loadType), code, false, false); + encodeInst(WasmOpcode::END, code); + return false; + } + case Intrinsic::cheerp_checked_store: + { + // Both pointer and value are encoded twice to make a local is available. + // Render the value immediately and drop the result, it's not efficient + // but the deffered logic interact poorly with the if/else structure + compileOperand(code, ci.getOperand(2)); + encodeInst(WasmOpcode::DROP, code); + Type* storeType = ci.getOperand(2)->getType(); + compileOperand(code, ci.getOperand(0)); + encodeInst(WasmS32Opcode::I32_CONST, 0, code); + encodeInst(WasmOpcode::I32_LT_S, code); + encodeInst(WasmU32Opcode::IF, 0x40, code); + compileOperand(code, ci.getOperand(1)); + uint32_t checkedStoreId = 0; + if(storeType->isIntegerTy(8)) + checkedStoreId = linearHelper.getBuiltinId(BuiltinInstr::BUILTIN::CHECKED_STORE_8); + else if(storeType->isIntegerTy(16)) + checkedStoreId = linearHelper.getBuiltinId(BuiltinInstr::BUILTIN::CHECKED_STORE_16); + else if(storeType->isIntegerTy(32) || storeType->isPointerTy()) + checkedStoreId = linearHelper.getBuiltinId(BuiltinInstr::BUILTIN::CHECKED_STORE_32); + else if(storeType->isIntegerTy(64)) + checkedStoreId = linearHelper.getBuiltinId(BuiltinInstr::BUILTIN::CHECKED_STORE_64); + else if(storeType->isFloatTy()) + checkedStoreId = linearHelper.getBuiltinId(BuiltinInstr::BUILTIN::CHECKED_STORE_F); + else if(storeType->isDoubleTy()) + checkedStoreId = linearHelper.getBuiltinId(BuiltinInstr::BUILTIN::CHECKED_STORE_D); + else + report_fatal_error("Unsupported return type for checked store"); + compileOperand(code, ci.getOperand(3)); + // Leave the returned value on the stack + encodeInst(WasmU32Opcode::CALL, checkedStoreId, code); + encodeInst(WasmOpcode::ELSE, code); + compileOperand(code, ci.getOperand(1)); + compileOperand(code, ci.getOperand(3)); + encodeStore(storeType, 0, targetData.getABITypeAlign(storeType), code, false); + encodeInst(WasmOpcode::END, code); return false; } case Intrinsic::cheerp_func_id: @@ -4940,7 +5031,7 @@ void CheerpWasmWriter::compileImportSection() // In CheerpOS we also import an exception tag and landing pads for the possible wasm return types uint32_t importedTags = 0; - uint32_t importedLandingPads = 0; + uint32_t importedInterpreterHelpers = 0; uint32_t exceptionTagTypeIndex = 0; if (isCheerpOS) { @@ -4949,13 +5040,13 @@ void CheerpWasmWriter::compileImportSection() assert(it != linearHelper.getFunctionTypeIndices().end()); exceptionTagTypeIndex = it->second; importedTags = 1; - importedLandingPads = 5; + importedInterpreterHelpers = /*landing pads*/5 + /*memory ops*/12; } // Total number of function imports (builtins + imports + exception pads) uint32_t importedFunctions = importedBuiltins + globalDeps.asmJSImports().size(); - uint32_t importedTotal = importedFunctions + importedLandingPads; + uint32_t importedTotal = importedFunctions + importedInterpreterHelpers; numberOfImportedFunctions = importedTotal; @@ -5017,7 +5108,7 @@ void CheerpWasmWriter::compileImportSection() if(globalDeps.needsBuiltin(BuiltinInstr::BUILTIN::GROW_MEM)) compileImport(section, namegen.getBuiltinName(NameGenerator::GROW_MEM), i32_i32_1); - if (importedLandingPads) + if (importedInterpreterHelpers) { // Import the five fixed exception pads, one for each possible wasm return type Type* i64 = Type::getInt64Ty(module.getContext()); @@ -5034,6 +5125,29 @@ void CheerpWasmWriter::compileImportSection() compileImport(section, "__exc_pad_f", f32_i32_3); compileImport(section, "__exc_pad_d", f64_i32_3); compileImport(section, "__exc_pad_v", v_i32_3); + FunctionType* i64_i32_1 = FunctionType::get(i64, i32_1, false); + FunctionType* f32_i32_1 = FunctionType::get(f32, i32_1, false); + FunctionType* f64_i32_1 = FunctionType::get(f64, i32_1, false); + compileImport(section, "__exc_load_8", i32_i32_1); + compileImport(section, "__exc_load_16", i32_i32_1); + compileImport(section, "__exc_load_32", i32_i32_1); + compileImport(section, "__exc_load_64", i64_i32_1); + compileImport(section, "__exc_load_f", f32_i32_1); + compileImport(section, "__exc_load_d", f64_i32_1); + Type* i32_i32[] = { i32, i32 }; + Type* i32_i64[] = { i32, i64 }; + Type* i32_f32[] = { i32, f32 }; + Type* i32_f64[] = { i32, f64 }; + FunctionType* v_i32_i32 = FunctionType::get(v, i32_i32, false); + FunctionType* v_i32_i64 = FunctionType::get(v, i32_i64, false); + FunctionType* v_i32_f32 = FunctionType::get(v, i32_f32, false); + FunctionType* v_i32_f64 = FunctionType::get(v, i32_f64, false); + compileImport(section, "__exc_store_8", v_i32_i32); + compileImport(section, "__exc_store_16", v_i32_i32); + compileImport(section, "__exc_store_32", v_i32_i32); + compileImport(section, "__exc_store_64", v_i32_i64); + compileImport(section, "__exc_store_f", v_i32_f32); + compileImport(section, "__exc_store_d", v_i32_f64); assert(importedTags == 1); compileImportTag(section, "__cos_exception", exceptionTagTypeIndex); } diff --git a/llvm/lib/CheerpWriter/CheerpWriter.cpp b/llvm/lib/CheerpWriter/CheerpWriter.cpp index d05d8b4ffad0..b24d426214db 100644 --- a/llvm/lib/CheerpWriter/CheerpWriter.cpp +++ b/llvm/lib/CheerpWriter/CheerpWriter.cpp @@ -6694,6 +6694,18 @@ void CheerpWriter::compileImports() stream << "__exc_pad_f:" << dummy << ',' << NewLine; stream << "__exc_pad_d:" << dummy << ',' << NewLine; stream << "__exc_pad_v:" << dummy << ',' << NewLine; + stream << "__exc_load_8:" << dummy << ',' << NewLine; + stream << "__exc_load_16:" << dummy << ',' << NewLine; + stream << "__exc_load_32:" << dummy << ',' << NewLine; + stream << "__exc_load_64:" << dummy << ',' << NewLine; + stream << "__exc_load_f:" << dummy << ',' << NewLine; + stream << "__exc_load_d:" << dummy << ',' << NewLine; + stream << "__exc_store_8:" << dummy << ',' << NewLine; + stream << "__exc_store_16:" << dummy << ',' << NewLine; + stream << "__exc_store_32:" << dummy << ',' << NewLine; + stream << "__exc_store_64:" << dummy << ',' << NewLine; + stream << "__exc_store_f:" << dummy << ',' << NewLine; + stream << "__exc_store_d:" << dummy << ',' << NewLine; stream << "__cos_exception:" << dummy << ',' << NewLine; } } diff --git a/llvm/lib/Target/WebAssembly/CheerpWritePass.cpp b/llvm/lib/Target/WebAssembly/CheerpWritePass.cpp index abb82df7253b..620d89ba7216 100644 --- a/llvm/lib/Target/WebAssembly/CheerpWritePass.cpp +++ b/llvm/lib/Target/WebAssembly/CheerpWritePass.cpp @@ -266,7 +266,11 @@ bool CheerpWritePass::runOnModule(Module& M) MPM.addPass(createModuleToFunctionPassAdaptor(cheerp::RemoveFwdBlocksPass())); if(triple.isCheerpOS()) + { + if(WasmMappedMemory) + MPM.addPass(cheerp::CheckLoadStorePass()); MPM.addPass(cheerp::SpillLocalsPass()); + } // Keep this pass last, it is going to remove stores to memory from the LLVM visible code, so further optimizing afterwards will break MPM.addPass(cheerp::AllocaStoresExtractorPass());