#include "WAVM/Runtime/Intrinsics.h" #include #include #include #include #include "RuntimePrivate.h" #include "WAVM/IR/FeatureSpec.h" #include "WAVM/IR/Module.h" #include "WAVM/IR/Operators.h" #include "WAVM/IR/Types.h" #include "WAVM/IR/Validate.h" #include "WAVM/Inline/Errors.h" #include "WAVM/Inline/Hash.h" #include "WAVM/Inline/HashMap.h" #include "WAVM/Inline/Serialization.h" #include "WAVM/Inline/Timing.h" #include "WAVM/Platform/RWMutex.h" #include "WAVM/Runtime/Runtime.h" namespace WAVM { namespace Intrinsics { struct ModuleImpl { HashMap functionMap; HashMap tableMap; HashMap memoryMap; HashMap globalMap; }; }} using namespace WAVM; using namespace WAVM::Runtime; using namespace WAVM::IR; Intrinsics::Module::~Module() { if(impl) { delete impl; } } static void initializeModule(Intrinsics::Module* moduleRef) { if(!moduleRef->impl) { moduleRef->impl = new Intrinsics::ModuleImpl; } } Intrinsics::Function::Function(Intrinsics::Module* moduleRef, const char* inName, void* inNativeFunction, FunctionType inType) : name(inName), type(inType), nativeFunction(inNativeFunction) { initializeModule(moduleRef); if(moduleRef->impl->functionMap.contains(name)) { Errors::fatalf("Intrinsic function already registered: %s", name); } moduleRef->impl->functionMap.set(name, this); } Intrinsics::Global::Global(Intrinsics::Module* moduleRef, const char* inName, ValueType inType, Value inValue) : name(inName), type(inType), value(inValue) { initializeModule(moduleRef); if(moduleRef->impl->globalMap.contains(name)) { Errors::fatalf("Intrinsic global already registered: %s", name); } moduleRef->impl->globalMap.set(name, this); } Intrinsics::Table::Table(Intrinsics::Module* moduleRef, const char* inName, const TableType& inType) : name(inName), type(inType) { initializeModule(moduleRef); if(moduleRef->impl->tableMap.contains(name)) { Errors::fatalf("Intrinsic table already registered: %s", name); } moduleRef->impl->tableMap.set(name, this); } Intrinsics::Memory::Memory(Intrinsics::Module* moduleRef, const char* inName, const MemoryType& inType) : name(inName), type(inType) { initializeModule(moduleRef); if(moduleRef->impl->memoryMap.contains(name)) { Errors::fatalf("Intrinsic memory already registered: %s", name); } moduleRef->impl->memoryMap.set(name, this); } Instance* Intrinsics::instantiateModule( Compartment* compartment, const std::initializer_list& moduleRefs, std::string&& debugName) { Timing::Timer timer; IR::Module irModule(FeatureLevel::wavm); irModule.featureSpec.nonWASMFunctionTypes = true; DisassemblyNames names; std::vector functionImportBindings; for(const Intrinsics::Module* moduleRef : moduleRefs) { if(moduleRef->impl) { for(const auto& pair : moduleRef->impl->functionMap) { functionImportBindings.push_back({pair.value->getNativeFunction()}); const Uptr typeIndex = irModule.types.size(); const Uptr functionIndex = irModule.functions.size(); irModule.types.push_back(pair.value->getType()); irModule.functions.imports.push_back({{typeIndex}, "", pair.value->getName()}); irModule.imports.push_back({ExternKind::function, functionIndex}); names.functions.push_back({pair.value->getName(), {}, {}}); } for(const auto& pair : moduleRef->impl->tableMap) { const Uptr tableIndex = irModule.tables.size(); irModule.tables.defs.push_back({pair.value->getType()}); names.tables.push_back(pair.value->getName()); irModule.exports.push_back({pair.value->getName(), ExternKind::table, tableIndex}); } for(const auto& pair : moduleRef->impl->memoryMap) { const Uptr memoryIndex = irModule.memories.size(); irModule.memories.defs.push_back({pair.value->getType()}); names.memories.push_back(pair.value->getName()); irModule.exports.push_back( {pair.value->getName(), ExternKind::memory, memoryIndex}); } for(const auto& pair : moduleRef->impl->globalMap) { InitializerExpression initializerExpression; switch(pair.value->getType()) { case ValueType::i32: initializerExpression.type = InitializerExpression::Type::i32_const; initializerExpression.i32 = pair.value->getValue().i32; break; case ValueType::i64: initializerExpression.type = InitializerExpression::Type::i64_const; initializerExpression.i64 = pair.value->getValue().i64; break; case ValueType::f32: initializerExpression.type = InitializerExpression::Type::f32_const; initializerExpression.f32 = pair.value->getValue().f32; break; case ValueType::f64: initializerExpression.type = InitializerExpression::Type::f64_const; initializerExpression.f64 = pair.value->getValue().f64; break; case ValueType::v128: initializerExpression.type = InitializerExpression::Type::v128_const; initializerExpression.v128 = pair.value->getValue().v128; break; case ValueType::anyref: case ValueType::funcref: Errors::fatal("Intrinsic reference-typed globals are not supported"); case ValueType::none: case ValueType::any: case ValueType::nullref: default: WAVM_UNREACHABLE(); }; const Uptr globalIndex = irModule.globals.size(); irModule.globals.defs.push_back( {GlobalType(pair.value->getType(), false), initializerExpression}); names.globals.push_back(pair.value->getName()); irModule.exports.push_back( {pair.value->getName(), ExternKind::global, globalIndex}); } } } // Generate thunks for the intrinsic functions. for(Uptr functionImportIndex = 0; functionImportIndex < irModule.functions.imports.size(); ++functionImportIndex) { const FunctionImport& functionImport = irModule.functions.imports[functionImportIndex]; const FunctionType intrinsicFunctionType = irModule.types[functionImport.type.index]; const FunctionType wasmFunctionType(intrinsicFunctionType.results(), intrinsicFunctionType.params(), CallingConvention::wasm); const Uptr wasmFunctionTypeIndex = irModule.types.size(); irModule.types.push_back(wasmFunctionType); Serialization::ArrayOutputStream codeStream; OperatorEncoderStream opEncoder(codeStream); for(Uptr paramIndex = 0; paramIndex < intrinsicFunctionType.params().size(); ++paramIndex) { opEncoder.local_get({paramIndex}); } opEncoder.call({functionImportIndex}); opEncoder.end(); const Uptr wasmFunctionIndex = irModule.functions.size(); irModule.functions.defs.push_back({{wasmFunctionTypeIndex}, {}, codeStream.getBytes(), {}}); names.functions.push_back({"thunk:" + functionImport.exportName, {}, {}}); irModule.exports.push_back( {functionImport.exportName, ExternKind::function, wasmFunctionIndex}); } setDisassemblyNames(irModule, names); if(WAVM_ENABLE_ASSERTS) { try { validatePreCodeSections(irModule); validateCodeSection(irModule); validatePostCodeSections(irModule); } catch(ValidationException const& exception) { Errors::fatalf("Validation exception in intrinsic module: %s", exception.message.c_str()); } } ModuleRef module = compileModule(irModule); Instance* instance = instantiateModuleInternal(compartment, module, std::move(functionImportBindings), {}, {}, {}, {}, std::move(debugName)); Timing::logTimer("Instantiated intrinsic module", timer); return instance; } HashMap Intrinsics::getUninstantiatedFunctions( const std::initializer_list& moduleRefs) { HashMap result; for(const Intrinsics::Module* moduleRef : moduleRefs) { if(moduleRef->impl) { for(const auto& pair : moduleRef->impl->functionMap) { result.addOrFail(pair.key, pair.value); } } } return result; }