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
4 changes: 4 additions & 0 deletions clang/lib/Driver/ToolChains/WebAssembly.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion clang/lib/Driver/ToolChains/WebAssembly.h
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,8 @@ namespace cheerp {
BRANCHHINTS,
SIMD,
GLOBALIZATION,
UNALIGNEDMEM
UNALIGNEDMEM,
MAPPEDMEMORY
};
std::vector<CheerpWasmOpt> getWasmFeatures(const Driver& D, const llvm::Triple& triple, const llvm::opt::ArgList& Args);

Expand Down
2 changes: 2 additions & 0 deletions llvm/include/llvm/Cheerp/BuiltinInstructions.h
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
37 changes: 37 additions & 0 deletions llvm/include/llvm/Cheerp/CheckedLoadStore.h
Original file line number Diff line number Diff line change
@@ -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<CheckLoadStorePass> {
public:
llvm::PreservedAnalyses run(llvm::Module& M, llvm::ModuleAnalysisManager& MAM);
static bool isRequired() { return true; }
};

}

#endif //_CHEERP_CHECK_LOAD_STORE_H
1 change: 1 addition & 0 deletions llvm/include/llvm/Cheerp/CommandLine.h
Original file line number Diff line number Diff line change
Expand Up @@ -58,6 +58,7 @@ extern llvm::cl::opt<bool> WasmReturnCalls;
extern llvm::cl::opt<bool> WasmNoSIMD;
extern llvm::cl::opt<bool> WasmNoGlobalization;
extern llvm::cl::opt<bool> WasmNoUnalignedMem;
extern llvm::cl::opt<bool> WasmMappedMemory;
extern llvm::cl::opt<bool> WasmSharedModule;
extern llvm::cl::opt<bool> UseBigInts;
extern llvm::cl::opt<bool> KeepInvokes;
Expand Down
1 change: 1 addition & 0 deletions llvm/include/llvm/Cheerp/PassRegistry.h
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
6 changes: 6 additions & 0 deletions llvm/include/llvm/IR/IntrinsicsCheerp.td
Original file line number Diff line number Diff line change
Expand Up @@ -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],
[]>;

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 @@ -37,6 +37,7 @@ add_llvm_component_library(LLVMCheerpUtils
JsExport.cpp
ThreadLocalLowering.cpp
LowerGlobalDestructors.cpp
CheckedLoadStore.cpp
SpillLocals.cpp
)

Expand Down
86 changes: 86 additions & 0 deletions llvm/lib/CheerpUtils/CheckedLoadStore.cpp
Original file line number Diff line number Diff line change
@@ -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<Instruction*> 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<LoadInst>(I))
{
if(LI->isAtomic())
continue;
// Constant pointers, including globals, can never be mapped
Value* pointerOperand = LI->getPointerOperand();
if(isa<Constant>(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<StoreInst>(I))
{
if(SI->isAtomic())
continue;
// Constant pointers, including globals, can never be mapped
Value* pointerOperand = SI->getPointerOperand();
if(isa<Constant>(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();
}

}
2 changes: 2 additions & 0 deletions llvm/lib/CheerpUtils/CommandLine.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,8 @@ llvm::cl::opt<bool> WasmNoGlobalization("cheerp-wasm-no-globalization", llvm::cl

llvm::cl::opt<bool> WasmNoUnalignedMem("cheerp-wasm-no-unaligned-mem", llvm::cl::desc("Disable the use of unaligned load/stores in optimizations"));

llvm::cl::opt<bool> WasmMappedMemory("cheerp-wasm-mapped-memory", llvm::cl::desc("Enable checked load/store for mapped memory"));

llvm::cl::opt<bool> WasmSharedModule("cheerp-wasm-shared-module", llvm::cl::desc("Enable generation of a dynamically loaded module"));

llvm::cl::opt<bool> UseBigInts("cheerp-use-bigints", llvm::cl::desc("Use the BigInt type in JS to represent i64 values"));
Expand Down
30 changes: 30 additions & 0 deletions llvm/lib/CheerpUtils/LinearMemoryHelper.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Loading
Loading