diff --git a/lib/Common/ConfigFlagsList.h b/lib/Common/ConfigFlagsList.h index 399eaa17af9..ec0470592a5 100644 --- a/lib/Common/ConfigFlagsList.h +++ b/lib/Common/ConfigFlagsList.h @@ -31,11 +31,14 @@ PHASE(All) PHASE(Speculation) PHASE(GatherCodeGenData) PHASE(Wasm) - PHASE(WasmSection) - PHASE(WasmReader) PHASE(WasmBytecode) - PHASE(WasmLEB128) + PHASE(WasmParser) + PHASE(WasmReader) + PHASE(WasmSection) + PHASE(WasmLEB128) + PHASE(WasmFunctionBody) PHASE(WasmLazyTrap) + PHASE(WasmDeferred) PHASE(Asmjs) PHASE(AsmjsTmpRegisterAllocation) PHASE(AsmjsEncoder) diff --git a/lib/Runtime/Base/CrossSite.cpp b/lib/Runtime/Base/CrossSite.cpp index 83b7bc4d951..adf2e814730 100644 --- a/lib/Runtime/Base/CrossSite.cpp +++ b/lib/Runtime/Base/CrossSite.cpp @@ -309,7 +309,15 @@ namespace Js if (funcInfo->GetFunctionProxy()->IsFunctionBody() && funcInfo->GetFunctionBody()->GetIsAsmJsFunction()) { - entryPoint = Js::AsmJsExternalEntryPoint; + AsmJsFunctionInfo* asmInfo = funcInfo->GetFunctionBody()->GetAsmJsFunctionInfo(); + if (asmInfo && asmInfo->IsWasmDeferredParse()) + { + entryPoint = ScriptContext::WasmDeferredParseExternalThunk; + } + else + { + entryPoint = Js::AsmJsExternalEntryPoint; + } } else #endif diff --git a/lib/Runtime/Base/ScriptContext.cpp b/lib/Runtime/Base/ScriptContext.cpp index 127e16b4c15..68d9e66461d 100644 --- a/lib/Runtime/Base/ScriptContext.cpp +++ b/lib/Runtime/Base/ScriptContext.cpp @@ -1835,6 +1835,44 @@ if (!sourceList) } } + void WasmFunctionGenerateBytecode(AsmJsScriptFunction* func, bool propagateError) + { + FunctionBody* body = func->GetFunctionBody(); + AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo(); + ScriptContext* scriptContext = func->GetScriptContext(); + + Js::FunctionEntryPointInfo * entypointInfo = (Js::FunctionEntryPointInfo*)func->GetEntryPointInfo(); + Wasm::WasmReaderInfo* readerInfo = info->GetWasmReaderInfo(); + info->SetWasmReaderInfo(nullptr); + try + { + Wasm::WasmBytecodeGenerator::GenerateFunctionBytecode(scriptContext, body, readerInfo); + func->GetDynamicType()->SetEntryPoint(Js::AsmJsExternalEntryPoint); + entypointInfo->jsMethod = AsmJsDefaultEntryThunk; + // Do MTJRC/MAIC:0 check +#if ENABLE_DEBUG_CONFIG_OPTIONS + if (CONFIG_FLAG(ForceNative) || CONFIG_FLAG(MaxAsmJsInterpreterRunCount) == 0) + { + GenerateFunction(scriptContext->GetNativeCodeGenerator(), func->GetFunctionBody(), func); + } +#endif + } + catch (Wasm::WasmCompilationException ex) + { + if (propagateError) + { + throw; + } + Js::JavascriptLibrary *library = scriptContext->GetLibrary(); + Js::JavascriptError *pError = library->CreateError(); + Js::JavascriptError::SetErrorMessage(pError, JSERR_WasmCompileError, ex.ReleaseErrorMessage(), scriptContext); + + func->GetDynamicType()->SetEntryPoint(WasmLazyTrapCallback); + entypointInfo->jsMethod = WasmLazyTrapCallback; + func->SetLazyError(pError); + } + } + void WasmLoadFunctions(Wasm::WasmModule * wasmModule, ScriptContext* ctx, Var* moduleMemoryPtr, Var* exportObj, Var* localModuleFunctions, bool* hasAnyLazyTraps) { FrameDisplay * frameDisplay = RecyclerNewPlus(ctx->GetRecycler(), sizeof(void*), FrameDisplay, 1); @@ -1845,35 +1883,22 @@ if (!sourceList) for (uint i = 0; i < wasmModule->funcCount; ++i) { AsmJsScriptFunction * funcObj = ctx->GetLibrary()->CreateAsmJsScriptFunction(functionArray[i]->body); - funcObj->GetDynamicType()->SetEntryPoint(AsmJsExternalEntryPoint); funcObj->SetModuleMemory(moduleMemoryPtr); FunctionEntryPointInfo * entypointInfo = (FunctionEntryPointInfo*)funcObj->GetEntryPointInfo(); entypointInfo->SetIsAsmJSFunction(true); - entypointInfo->jsMethod = AsmJsDefaultEntryThunk; entypointInfo->SetModuleAddress((uintptr_t)moduleMemoryPtr); funcObj->SetEnvironment(frameDisplay); localModuleFunctions[i] = funcObj; - - if (wasmModule->lazyTraps && wasmModule->lazyTraps[i]) + + if (PHASE_ON(WasmDeferredPhase, funcObj->GetFunctionBody())) { - Assert(PHASE_ON1(WasmLazyTrapPhase)); - *hasAnyLazyTraps = true; - JavascriptLibrary *library = ctx->GetLibrary(); - JavascriptError *pError = library->CreateError(); - JavascriptError::SetErrorMessage(pError, JSERR_WasmCompileError, wasmModule->lazyTraps[i]->ReleaseErrorMessage(), ctx); - - funcObj->GetDynamicType()->SetEntryPoint(WasmLazyTrapCallback); - entypointInfo->jsMethod = WasmLazyTrapCallback; - funcObj->SetLazyError(pError); - continue; + funcObj->GetDynamicType()->SetEntryPoint(ScriptContext::WasmDeferredParseExternalThunk); + entypointInfo->jsMethod = ScriptContext::WasmDeferredParseInternalThunk; } - // Do MTJRC/MAIC:0 check -#if ENABLE_DEBUG_CONFIG_OPTIONS - if (CONFIG_FLAG(ForceNative) || CONFIG_FLAG(MaxAsmJsInterpreterRunCount) == 0) + else { - GenerateFunction(ctx->GetNativeCodeGenerator(), funcObj->GetFunctionBody(), funcObj); + WasmFunctionGenerateBytecode(funcObj, !PHASE_ON(WasmLazyTrapPhase, funcObj->GetFunctionBody())); } -#endif } } @@ -1996,8 +2021,7 @@ if (!sourceList) uint funcIndex = wasmModule->info->GetIndirectFunctionIndex(i); if (funcIndex >= wasmModule->info->GetFunctionCount()) { - // TODO: michhol give error messages - Js::Throw::InternalError(); + throw Wasm::WasmCompilationException(_u("Invalid function index %U for indirect function table"), funcIndex); } Wasm::WasmFunctionInfo * indirFunc = wasmModule->info->GetFunSig(funcIndex); uint sigId = indirFunc->GetSignature()->GetSignatureId(); @@ -2011,6 +2035,72 @@ if (!sourceList) } } +#if _M_IX86 + __declspec(naked) + Var ScriptContext::WasmDeferredParseExternalThunk(RecyclableObject* function, CallInfo callInfo, ...) + { + // Register functions + __asm + { + push ebp + mov ebp, esp + lea eax, [esp + 8] + push 0 + push eax + call ScriptContext::WasmDeferredParseEntryPoint +#ifdef _CONTROL_FLOW_GUARD + // verify that the call target is valid + mov ecx, eax + call[__guard_check_icall_fptr] + mov eax, ecx +#endif + pop ebp + // Although we don't restore ESP here on WinCE, this is fine because script profiler is not shipped for WinCE. + jmp eax + } + } + + __declspec(naked) + Var ScriptContext::WasmDeferredParseInternalThunk(RecyclableObject* function, CallInfo callInfo, ...) + { + // Register functions + __asm + { + push ebp + mov ebp, esp + lea eax, [esp + 8] + push 1 + push eax + call ScriptContext::WasmDeferredParseEntryPoint +#ifdef _CONTROL_FLOW_GUARD + // verify that the call target is valid + mov ecx, eax + call[__guard_check_icall_fptr] + mov eax, ecx +#endif + pop ebp + // Although we don't restore ESP here on WinCE, this is fine because script profiler is not shipped for WinCE. + jmp eax + } + } +#elif defined(_M_X64) + // Do nothing: the implementation of ScriptContext::WasmDeferredParseExternalThunk is declared (appropriately decorated) in + // Language\amd64\amd64_Thunks.asm. +#endif + + JavascriptMethod ScriptContext::WasmDeferredParseEntryPoint(AsmJsScriptFunction** funcPtr, int internalCall) + { + AsmJsScriptFunction* func = *funcPtr; + + WasmFunctionGenerateBytecode(func, false); + Js::FunctionEntryPointInfo * entypointInfo = (Js::FunctionEntryPointInfo*)func->GetEntryPointInfo(); + if (internalCall) + { + return entypointInfo->jsMethod; + } + return func->GetDynamicType()->GetEntryPoint(); + } + char16* lastWasmExceptionMessage = nullptr; Var ScriptContext::LoadWasmScript(const char16* script, SRCINFO const * pSrcInfo, CompileScriptException * pse, bool isExpression, bool disableDeferredParse, bool isForNativeCode, Utf8SourceInfo** ppSourceInfo, const bool isBinary, const uint lengthBytes, const char16 *rootDisplayName, Js::Var ffi, Js::Var* start) @@ -2020,9 +2110,12 @@ if (!sourceList) pSrcInfo = this->cache->noContextGlobalSourceInfo; } + AutoProfilingPhase wasmPhase(this, Js::WasmPhase); + Unused(wasmPhase); + Assert(!this->threadContext->IsScriptActive()); Assert(pse != nullptr); - Wasm::WasmBytecodeGenerator *bytecodeGen = nullptr; + Wasm::WasmModuleGenerator *bytecodeGen = nullptr; Js::Var exportObj = nullptr; try { @@ -2037,7 +2130,7 @@ if (!sourceList) } *ppSourceInfo = Utf8SourceInfo::New(this, (LPCUTF8)script, lengthBytes / sizeof(char16), lengthBytes, pSrcInfo, false); - bytecodeGen = HeapNew(Wasm::WasmBytecodeGenerator, this, *ppSourceInfo, (byte*)script, lengthBytes); + bytecodeGen = HeapNew(Wasm::WasmModuleGenerator, this, *ppSourceInfo, (byte*)script, lengthBytes); wasmModule = bytecodeGen->GenerateModule(); Var* moduleMemoryPtr = RecyclerNewArrayZ(GetRecycler(), Var, wasmModule->memSize); diff --git a/lib/Runtime/Base/ScriptContext.h b/lib/Runtime/Base/ScriptContext.h index 9a1fa58aa20..96b50884861 100644 --- a/lib/Runtime/Base/ScriptContext.h +++ b/lib/Runtime/Base/ScriptContext.h @@ -1605,6 +1605,12 @@ namespace Js static JavascriptMethod ProfileModeDeferredParse(ScriptFunction **function); static Var ProfileModeDeferredParsingThunk(RecyclableObject* function, CallInfo callInfo, ...); +#ifdef ENABLE_WASM + static JavascriptMethod WasmDeferredParseEntryPoint(AsmJsScriptFunction** funcPtr, int internalCall); + static Var WasmDeferredParseInternalThunk(RecyclableObject* function, CallInfo callInfo, ...); + static Var WasmDeferredParseExternalThunk(RecyclableObject* function, CallInfo callInfo, ...); +#endif + // Thunks for deferred deserialization of function bodies from the byte code cache static JavascriptMethod ProfileModeDeferredDeserialize(ScriptFunction* function); static Var ProfileModeDeferredDeserializeThunk(RecyclableObject* function, CallInfo callInfo, ...); @@ -1761,8 +1767,40 @@ namespace Js return functionBody; } + + class AutoProfilingPhase + { + public: + AutoProfilingPhase(ScriptContext* scriptcontext, Js::Phase phase) : scriptcontext(scriptcontext), phase(phase), isPhaseComplete(false) + { + #ifdef PROFILE_EXEC + scriptcontext->ProfileBegin(phase); + #endif + } + + ~AutoProfilingPhase() + { + if(!this->isPhaseComplete) + { + EndProfile(); + } + } + + void EndProfile() + { + this->isPhaseComplete = true; +#ifdef PROFILE_EXEC + scriptcontext->ProfileEnd(phase); +#endif + } + private: + ScriptContext* scriptcontext; + Js::Phase phase; + bool isPhaseComplete; + }; } + #define BEGIN_TEMP_ALLOCATOR(allocator, scriptContext, name) \ Js::TempArenaAllocatorObject *temp##allocator = scriptContext->GetTemporaryAllocator(name); \ ArenaAllocator * allocator = temp##allocator->GetAllocator(); diff --git a/lib/Runtime/Language/AsmJsTypes.h b/lib/Runtime/Language/AsmJsTypes.h index a1d0cd05f23..dd22f52bba0 100644 --- a/lib/Runtime/Language/AsmJsTypes.h +++ b/lib/Runtime/Language/AsmJsTypes.h @@ -24,6 +24,11 @@ #pragma once #ifndef TEMP_DISABLE_ASMJS +namespace Wasm +{ + struct WasmReaderInfo; +}; + namespace Js { typedef uint32 uint32_t; @@ -1016,6 +1021,7 @@ namespace Js int mSimdConstCount, mSimdVarCount, mSimdTmpCount, mSimdByteOffset; FunctionBody* asmJsModuleFunctionBody; + Wasm::WasmReaderInfo* mWasmReaderInfo; public: AsmJsFunctionInfo() : mArgCount(0), mIntConstCount(0), @@ -1042,7 +1048,8 @@ namespace Js mUsesHeapBuffer(false), mIsHeapBufferConst(false), mArgType(nullptr), - mArgSizes(nullptr) {} + mArgSizes(nullptr), + mWasmReaderInfo(nullptr) {} // the key is the bytecode address typedef JsUtil::BaseDictionary ByteCodeToTJMap; ByteCodeToTJMap* mbyteCodeTJMap; @@ -1137,7 +1144,9 @@ namespace Js // Normally, heap has min size of 0x10000, but if you use ChangeHeap, min heap size is increased to 0x1000000 return offset >= 0x1000000 || (IsHeapBufferConst() && offset >= 0x10000); } - + Wasm::WasmReaderInfo* GetWasmReaderInfo() const {return mWasmReaderInfo;} + void SetWasmReaderInfo(Wasm::WasmReaderInfo* reader) {mWasmReaderInfo = reader;} + bool IsWasmDeferredParse() const { return mWasmReaderInfo != nullptr; } }; // The asm.js spec recognizes this set of builtin SIMD functions. diff --git a/lib/Runtime/Language/InterpreterStackFrame.cpp b/lib/Runtime/Language/InterpreterStackFrame.cpp index 0175c4e75d7..d01b86c269b 100644 --- a/lib/Runtime/Language/InterpreterStackFrame.cpp +++ b/lib/Runtime/Language/InterpreterStackFrame.cpp @@ -2965,16 +2965,21 @@ namespace Js uint homingAreaSize = 0; #endif -#if DBG_DUMP +#if ENABLE_DEBUG_CONFIG_OPTIONS const bool tracingFunc = PHASE_TRACE(AsmjsFunctionEntryPhase, functionBody); if (tracingFunc) { +#if DBG_DUMP if (AsmJsCallDepth) { Output::Print(_u("%*c"), AsmJsCallDepth, ' '); } Output::Print(_u("Executing function %s("), functionBody->GetDisplayName()); ++AsmJsCallDepth; +#else + Output::Print(_u("%s()\n"), functionBody->GetDisplayName()); + Output::Flush(); +#endif } #endif uintptr_t argAddress = (uintptr_t)m_inParams; diff --git a/lib/Runtime/Language/amd64/amd64_Thunks.asm b/lib/Runtime/Language/amd64/amd64_Thunks.asm index ed7e4b54c8d..6d9fde9f925 100644 --- a/lib/Runtime/Language/amd64/amd64_Thunks.asm +++ b/lib/Runtime/Language/amd64/amd64_Thunks.asm @@ -447,6 +447,113 @@ endif ?AsmJsExternalEntryPoint@Js@@YAPEAXPEAVRecyclableObject@1@UCallInfo@1@ZZ ENDP +;;============================================================================================================ +;; ScriptContext::WasmDeferredParseExternalThunk +;;============================================================================================================ + +;; JavascriptMethod ScriptContext::WasmDeferredParseEntryPoint(AsmJsScriptFunction** funcPtr, int internalCall); +extrn ?WasmDeferredParseEntryPoint@ScriptContext@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAPEAVAsmJsScriptFunction@2@H@Z : PROC + +;; Var ScriptContext::WasmDeferredParseExternalThunk(RecyclableObject* function, CallInfo callInfo, ...) +align 16 +?WasmDeferredParseExternalThunk@ScriptContext@Js@@SAPEAXPEAVRecyclableObject@2@UCallInfo@2@ZZ PROC FRAME + ;; save volatile registers + mov qword ptr [rsp + 8h], rcx + mov qword ptr [rsp + 10h], rdx + mov qword ptr [rsp + 18h], r8 + mov qword ptr [rsp + 20h], r9 + + push rbp + .pushreg rbp + lea rbp, [rsp] + .setframe rbp, 0 + .endprolog + + sub rsp, 20h + lea rcx, [rsp + 30h] + mov rdx, 0 + call ?WasmDeferredParseEntryPoint@ScriptContext@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAPEAVAsmJsScriptFunction@2@H@Z + +ifdef _CONTROL_FLOW_GUARD + mov rcx, rax ; __guard_check_icall_fptr requires the call target in rcx. + call [__guard_check_icall_fptr] ; verify that the call target is valid + mov rax, rcx ;restore call target +endif + add rsp, 20h + + lea rsp, [rbp] + pop rbp + + ;; restore volatile registers + mov rcx, qword ptr [rsp + 8h] + mov rdx, qword ptr [rsp + 10h] + mov r8, qword ptr [rsp + 18h] + mov r9, qword ptr [rsp + 20h] + + rex_jmp_reg rax +?WasmDeferredParseExternalThunk@ScriptContext@Js@@SAPEAXPEAVRecyclableObject@2@UCallInfo@2@ZZ ENDP + +;;============================================================================================================ + +;;============================================================================================================ +;; ScriptContext::WasmDeferredParseInternalThunk +;;============================================================================================================ + +;; JavascriptMethod ScriptContext::WasmDeferredParseEntryPoint(AsmJsScriptFunction** funcPtr, int internalCall); +extrn ?WasmDeferredParseEntryPoint@ScriptContext@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAPEAVAsmJsScriptFunction@2@H@Z : PROC + +;; Var ScriptContext::WasmDeferredParseInternalThunk(RecyclableObject* function, CallInfo callInfo, ...) +align 16 +?WasmDeferredParseInternalThunk@ScriptContext@Js@@SAPEAXPEAVRecyclableObject@2@UCallInfo@2@ZZ PROC FRAME + ;; save volatile registers + mov qword ptr [rsp + 8h], rcx + mov qword ptr [rsp + 10h], rdx + mov qword ptr [rsp + 18h], r8 + mov qword ptr [rsp + 20h], r9 + + push rbp + .pushreg rbp + lea rbp, [rsp] + .setframe rbp, 0 + .endprolog + + sub rsp, 60h + + ; spill potential floating point arguments to stack + movaps xmmword ptr [rsp + 30h], xmm1 + movaps xmmword ptr [rsp + 40h], xmm2 + movaps xmmword ptr [rsp + 50h], xmm3 + + lea rcx, [rsp + 70h] + mov rdx, 1 + call ?WasmDeferredParseEntryPoint@ScriptContext@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAPEAVAsmJsScriptFunction@2@H@Z + +ifdef _CONTROL_FLOW_GUARD + mov rcx, rax ; __guard_check_icall_fptr requires the call target in rcx. + call [__guard_check_icall_fptr] ; verify that the call target is valid + mov rax, rcx ;restore call target +endif + + ; restore potential floating point arguments from stack + movaps xmm1, xmmword ptr [rsp + 30h] + movaps xmm2, xmmword ptr [rsp + 40h] + movaps xmm3, xmmword ptr [rsp + 50h] + add rsp, 60h + + lea rsp, [rbp] + pop rbp + + ;; restore volatile registers + mov rcx, qword ptr [rsp + 8h] + mov rdx, qword ptr [rsp + 10h] + mov r8, qword ptr [rsp + 18h] + mov r9, qword ptr [rsp + 20h] + + rex_jmp_reg rax +?WasmDeferredParseInternalThunk@ScriptContext@Js@@SAPEAXPEAVRecyclableObject@2@UCallInfo@2@ZZ ENDP + +;;============================================================================================================ + endif ;; _ENABLE_DYNAMIC_THUNKS ;;============================================================================================================ diff --git a/lib/WasmReader/ModuleInfo.cpp b/lib/WasmReader/ModuleInfo.cpp index db08ab7b316..7772097f500 100644 --- a/lib/WasmReader/ModuleInfo.cpp +++ b/lib/WasmReader/ModuleInfo.cpp @@ -13,17 +13,18 @@ namespace Wasm ModuleInfo::ModuleInfo(ArenaAllocator * alloc) : m_memory(), m_alloc(alloc), + m_funsigs(nullptr), m_funcCount(0), m_importCount(0), + m_indirectfuncs(nullptr), m_indirectFuncCount(0), + m_exports(nullptr), m_exportCount(0), m_datasegCount(0), + m_signatures(nullptr), + m_signaturesCount(0), m_startFunc(Js::Constants::UninitializedValue) { - m_signatures = Anew(m_alloc, WasmSignatureArray, m_alloc, 0); - m_indirectfuncs = nullptr; - m_funsigs = nullptr; - m_exports = nullptr; } bool @@ -53,32 +54,29 @@ ModuleInfo::GetMemory() const return &m_memory; } -uint32 -ModuleInfo::AddSignature(WasmSignature * signature) +void +ModuleInfo::SetSignature(uint32 index, WasmSignature * signature) { - uint32 id = m_signatures->Count(); - - signature->SetSignatureId(id); - m_signatures->Add(signature); - - return id; + Assert(index < GetSignatureCount()); + signature->SetSignatureId(index); + m_signatures[index] = signature; } WasmSignature * ModuleInfo::GetSignature(uint32 index) const { - if (index >= m_signatures->Count()) + if (index >= GetSignatureCount()) { return nullptr; } - return m_signatures->GetBuffer()[index]; + return m_signatures[index]; } uint32 ModuleInfo::GetSignatureCount() const { - return m_signatures->Count(); + return m_signaturesCount; } void @@ -250,6 +248,13 @@ ModuleInfo::GetStartFunction() const return m_startFunc; } +void ModuleInfo::SetSignatureCount(uint32 count) +{ + Assert(m_signaturesCount == 0 && m_signatures == nullptr); + m_signaturesCount = count; + m_signatures = AnewArray(m_alloc, WasmSignature*, count); +} + } // namespace Wasm #endif // ENABLE_WASM diff --git a/lib/WasmReader/ModuleInfo.h b/lib/WasmReader/ModuleInfo.h index b2719773fb2..e4ef15cfaf0 100644 --- a/lib/WasmReader/ModuleInfo.h +++ b/lib/WasmReader/ModuleInfo.h @@ -25,11 +25,11 @@ namespace Wasm bool InitializeMemory(uint32 minSize, uint32 maxSize, bool exported); - const Memory * GetMemory() const; - uint32 AddSignature(WasmSignature * signature); + void SetSignature(uint32 index, WasmSignature * signature); WasmSignature * GetSignature(uint32 index) const; + void SetSignatureCount(uint32 count); uint32 GetSignatureCount() const; void AllocateIndirectFunctions(uint32 entries); @@ -61,19 +61,17 @@ namespace Wasm void SetStartFunction(uint32 i); uint32 GetStartFunction() const; - private: - typedef JsUtil::GrowingArray WasmSignatureArray; - - WasmSignatureArray * m_signatures; + WasmSignature** m_signatures; uint32* m_indirectfuncs; WasmFunctionInfo** m_funsigs; WasmExport* m_exports; WasmImport* m_imports; WasmDataSegment** m_datasegs; - uint m_funcCount; + uint m_signaturesCount; uint m_indirectFuncCount; + uint m_funcCount; uint m_exportCount; uint32 m_importCount; uint32 m_datasegCount; @@ -87,7 +85,6 @@ namespace Wasm { WasmModule() : functions(nullptr), - lazyTraps(nullptr), memSize(0), indirFuncTableOffset(0), heapOffset(0), @@ -97,7 +94,6 @@ namespace Wasm { } WasmFunction** functions; - class WasmCompilationException** lazyTraps; ModuleInfo * info; uint heapOffset; uint funcOffset; diff --git a/lib/WasmReader/WasmBinaryReader.cpp b/lib/WasmReader/WasmBinaryReader.cpp index a6bbee153bc..a1c65c14c55 100644 --- a/lib/WasmReader/WasmBinaryReader.cpp +++ b/lib/WasmReader/WasmBinaryReader.cpp @@ -12,7 +12,7 @@ namespace Wasm { namespace WasmTypes { - bool IsLocalType(WasmTypes::WasmType type) + bool IsLocalType(WasmTypes::WasmType type) { // Check if type in range ]Void,Limit[ return (uint)(type - 1) < (WasmTypes::Limit - 1); @@ -49,8 +49,10 @@ Signature::Signature(ArenaAllocator *alloc, uint count, ...) } } // namespace WasmTypes -WasmBinaryReader::WasmBinaryReader(PageAllocator * alloc, byte* source, size_t length) : - m_alloc(_u("WasmBinaryDecoder"), alloc, Js::Throw::OutOfMemory), m_lastOp(WasmBinOp::wbLimit) +WasmBinaryReader::WasmBinaryReader(Js::ScriptContext* scriptContext, byte* source, size_t length) : + m_curFuncEnd(nullptr), + m_lastOp(WasmBinOp::wbLimit), + m_alloc(_u("WasmBytecodeGen"), scriptContext->GetThreadContext()->GetPageAllocator(), Js::Throw::OutOfMemory) { m_moduleInfo = Anew(&m_alloc, ModuleInfo, &m_alloc); @@ -64,7 +66,7 @@ WasmBinaryReader::WasmBinaryReader(PageAllocator * alloc, byte* source, size_t l void WasmBinaryReader::InitializeReader() { - ModuleHeader(); + ValidateModuleHeader(); #if DBG_DUMP if (DO_WASM_TRACE_SECTION) { @@ -94,7 +96,6 @@ WasmBinaryReader::ThrowDecodingError(const char16* msg, ...) { va_list argptr; va_start(argptr, msg); - // We need to do a format twice (or concat the 2 strings) throw WasmCompilationException(msg, argptr); } @@ -202,7 +203,7 @@ WasmBinaryReader::ReadSectionHeader() } } -#if DBG +#if ENABLE_DEBUG_CONFIG_OPTIONS Assert(idSize < 64); char16 buf[64]; size_t convertedChars = 0; @@ -252,12 +253,12 @@ WasmBinaryReader::PrintOps() } HeapDeleteArray(m_ops->Count(), ops); } + #endif bool -WasmBinaryReader::ReadFunctionBodies(FunctionBodyCallback callback, void* callbackdata) +WasmBinaryReader::ReadFunctionHeaders() { - Assert(callback != nullptr); uint32 len; uint32 entries = LEB128(len); if (entries != m_moduleInfo->GetFunctionCount()) @@ -267,55 +268,66 @@ WasmBinaryReader::ReadFunctionBodies(FunctionBodyCallback callback, void* callba for (uint32 i = 0; i < entries; ++i) { - m_funcInfo = m_moduleInfo->GetFunSig(i); - m_currentNode.func.info = m_funcInfo; + WasmFunctionInfo* funcInfo = m_moduleInfo->GetFunSig(i); - // Reset func state - m_funcState.count = 0; - m_funcState.size = LEB128(len); // function body size in bytes including AST - byte* end = m_pc + m_funcState.size; - CheckBytesLeft(m_funcState.size); + const uint32 funcSize = LEB128(len); + funcInfo->m_readerInfo.index = i; + funcInfo->m_readerInfo.size = funcSize; + funcInfo->m_readerInfo.startOffset = (m_pc - m_start); + CheckBytesLeft(funcSize); + TRACE_WASM_DECODER(_u("Function body header: index = %u, size = %u"), i, funcSize); + byte* end = m_pc + funcSize; + m_pc = end; + } + return m_pc == m_currentSection.end; +} - UINT32 entryCount = LEB128(len); - m_funcState.count += len; +void +WasmBinaryReader::SeekToFunctionBody(FunctionBodyReaderInfo readerInfo) +{ + if (readerInfo.startOffset >= (m_end - m_start)) + { + ThrowDecodingError(_u("Function byte offset out of bounds")); + } + // Seek to the function start and skip function header (count) + m_pc = m_start + readerInfo.startOffset; + m_funcState.size = readerInfo.size; + m_funcState.count = 0; + CheckBytesLeft(readerInfo.size); + m_curFuncEnd = m_pc + m_funcState.size; - // locals - for (UINT32 j = 0; j < entryCount; j++) + uint32 len = 0; + uint32 entryCount = LEB128(len); + m_funcState.count += len; + + WasmFunctionInfo* funcInfo = m_moduleInfo->GetFunSig(readerInfo.index); + if (!funcInfo) + { + ThrowDecodingError(_u("Invalid function index %u"), readerInfo.index); + } + // locals + for (uint32 j = 0; j < entryCount; j++) + { + uint32 count = LEB128(len); + m_funcState.count += len; + Wasm::WasmTypes::WasmType type = ReadWasmType(len); + if (!Wasm::WasmTypes::IsLocalType(type)) { - UINT32 count = LEB128(len); - m_funcState.count += len; - Wasm::WasmTypes::WasmType type = ReadWasmType(len); - if (type == Wasm::WasmTypes::Void) - { - ThrowDecodingError(_u("Invalid local type")); - } - m_funcState.count += len; - m_funcInfo->AddLocal(type, count); - TRACE_WASM_DECODER(_u("Function body header: type = %u, count = %u"), type, count); + ThrowDecodingError(_u("Invalid local type")); } - bool errorOccurred = !callback(i, callbackdata) || m_funcState.count != m_funcState.size; - if (errorOccurred) + m_funcState.count += len; + funcInfo->AddLocal(type, count); + switch (type) { - if (!PHASE_ON1(Js::WasmLazyTrapPhase)) - { - ThrowDecodingError(_u("Error while processing function #%u"), i); - } - m_pc = end; +#define WASM_LOCALTYPE(token, name) case Wasm::WasmTypes::token: TRACE_WASM_DECODER(_u("Local: type = " #name## ", count = %u"), type, count); break; +#include "WasmKeywords.h" } } - return m_pc == m_currentSection.end; } -WasmOp -WasmBinaryReader::ReadFromBlock() +bool WasmBinaryReader::IsCurrentFunctionCompleted() const { - return GetWasmToken(ASTNode()); -} - -WasmOp -WasmBinaryReader::ReadFromCall() -{ - return GetWasmToken(ASTNode()); + return m_pc == m_curFuncEnd; } WasmOp @@ -414,7 +426,7 @@ WasmBinaryReader::ASTNode() } void -WasmBinaryReader::ModuleHeader() +WasmBinaryReader::ValidateModuleHeader() { uint32 magicNumber = ReadConst(); uint32 version = ReadConst(); @@ -617,6 +629,7 @@ WasmBinaryReader::ReadSignatures() { UINT len = 0; const uint32 count = LEB128(len); + m_moduleInfo->SetSignatureCount(count); // signatures table for (UINT32 i = 0; i < count; i++) { @@ -648,7 +661,7 @@ WasmBinaryReader::ReadSignatures() type = ReadWasmType(len); sig->SetResultType(type); } - m_moduleInfo->AddSignature(sig); + m_moduleInfo->SetSignature(i, sig); } } @@ -666,6 +679,7 @@ WasmBinaryReader::ReadFunctionsSignatures() { ThrowDecodingError(_u("Function signature is out of bound")); } + WasmFunctionInfo* newFunction = Anew(&m_alloc, WasmFunctionInfo, &m_alloc); WasmSignature* sig = m_moduleInfo->GetSignature(sigIndex); newFunction->SetSignature(sig); @@ -706,10 +720,7 @@ void WasmBinaryReader::ReadIndirectFunctionTable() { ThrowDecodingError(_u("Indirect function index %u is out of bound (max %u)"), functionIndex, m_moduleInfo->GetFunctionCount()); } - if (PHASE_TRACE1(Js::WasmReaderPhase)) - { - Output::Print(_u("%u, "), functionIndex); - } + TRACE_WASM_DECODER(_u("%u, "), functionIndex); m_moduleInfo->SetIndirectFunction(functionIndex, i); } TRACE_WASM_DECODER(_u("]"), entries); @@ -770,7 +781,20 @@ char16* WasmBinaryReader::ReadInlineName(uint32& length, uint32& nameLength) m_pc += nameLength; length += nameLength; - return CvtUtf8Str(&m_alloc, rawName, nameLength); + return CvtUtf8Str(rawName, nameLength); +} + +char16* WasmBinaryReader::CvtUtf8Str(LPUTF8 name, uint32 nameLen) +{ + utf8::DecodeOptions decodeOptions = utf8::doDefault; + charcount_t utf16Len = utf8::ByteIndexIntoCharacterIndex(name, nameLen, decodeOptions); + char16* contents = AnewArray(&m_alloc, char16, utf16Len + 1); + if (contents == nullptr) + { + Js::Throw::OutOfMemory(); + } + utf8::DecodeIntoAndNullTerminate((char16*)contents, name, utf16Len, decodeOptions); + return contents; } void @@ -811,40 +835,6 @@ WasmBinaryReader::ReadStartFunction() // 2. Function should be void and nullary } -const char * -WasmBinaryReader::Name(UINT32 offset, UINT &length) -{ - BYTE* str = m_start + offset; - length = 0; - if (offset == 0) - { - return ""; - } - // validate string and get length - do - { - if (str >= m_end) - { - ThrowDecodingError(_u("Offset is out of range")); - } - length++; - } while (*str++); - - return (const char*)(m_start + offset); -} - -UINT -WasmBinaryReader::Offset() -{ - UINT len = 0; - UINT32 offset = LEB128(len); - if (offset > (UINT)(m_end - m_start)) - { - ThrowDecodingError(_u("Offset is out of range")); - } - return offset; -} - UINT WasmBinaryReader::LEB128(UINT &length, bool sgn) { @@ -929,10 +919,9 @@ void WasmBinaryReader::CheckBytesLeft(UINT bytesNeeded) { UINT bytesLeft = (UINT)(m_end - m_pc); - if ( bytesNeeded > bytesLeft) + if (bytesNeeded > bytesLeft) { - Output::Print(_u("Out of file: Needed: %d, Left: %d"), bytesNeeded, bytesLeft); - ThrowDecodingError(_u("Out of file.")); + ThrowDecodingError(_u("Out of file: Needed: %d, Left: %d"), bytesNeeded, bytesLeft); } } diff --git a/lib/WasmReader/WasmBinaryReader.h b/lib/WasmReader/WasmBinaryReader.h index 04abcb62e55..9df217a50b7 100644 --- a/lib/WasmReader/WasmBinaryReader.h +++ b/lib/WasmReader/WasmBinaryReader.h @@ -82,47 +82,30 @@ namespace Wasm static const unsigned int experimentalVersion = 0xb; - typedef bool(*FunctionBodyCallback)(uint32 index, void* data); - class WasmBinaryReader { public: - WasmBinaryReader(PageAllocator * alloc, byte* source, size_t length); + WasmBinaryReader(Js::ScriptContext* scriptContext, byte* source, size_t length); static void Init(Js::ScriptContext *scriptContext); void InitializeReader(); bool ReadNextSection(SectionCode nextSection); // Fully read the section in the reader. Return true if the section fully read bool ProcessCurrentSection(); - bool ReadFunctionBodies(FunctionBodyCallback callback, void* callbackdata); - WasmOp ReadFromBlock(); - WasmOp ReadFromCall(); + bool ReadFunctionHeaders(); + void SeekToFunctionBody(FunctionBodyReaderInfo readerInfo); + bool IsCurrentFunctionCompleted() const; WasmOp ReadExpr(); WasmOp GetLastOp(); WasmBinOp GetLastBinOp() const { return m_lastOp; } #if DBG_DUMP void PrintOps(); #endif - // TODO: Move this to somewhere more appropriate and possible make m_alloc part of - // BaseWasmReader state. - char16* CvtUtf8Str(ArenaAllocator* m_alloc, LPUTF8 name, uint32 nameLen) - { - utf8::DecodeOptions decodeOptions = utf8::doDefault; - charcount_t utf16Len = utf8::ByteIndexIntoCharacterIndex(name, nameLen, decodeOptions); - char16* contents = AnewArray(m_alloc, char16, utf16Len + 1); - if (contents == nullptr) - { - Js::Throw::OutOfMemory(); - } - utf8::DecodeIntoAndNullTerminate((char16*)contents, name, utf16Len, decodeOptions); - return contents; - } WasmNode m_currentNode; ModuleInfo * m_moduleInfo; WasmModule * m_module; private: - WasmFunctionInfo * m_funcInfo; struct ReaderState { UINT32 count; // current entry @@ -133,7 +116,6 @@ namespace Wasm WasmOp GetWasmToken(WasmBinOp op); WasmBinOp ASTNode(); - void ModuleHeader(); void CallNode(); void CallIndirectNode(); void CallImportNode(); @@ -141,8 +123,10 @@ namespace Wasm void BrTableNode(); WasmOp MemNode(WasmBinOp op); void VarNode(); - template void ConstNode(); - // readers + + // Module readers + void ValidateModuleHeader(); + SectionHeader ReadSectionHeader(); void ReadMemorySection(); void ReadSignatures(); void ReadFunctionsSignatures(); @@ -151,17 +135,15 @@ namespace Wasm void ReadDataSegments(); void ReadImportEntries(); void ReadStartFunction(); - void ReadNamesSection(); + // Primitive reader + template void ConstNode(); + template T ReadConst(); char16* ReadInlineName(uint32& length, uint32& nameLength); - - const char* Name(UINT32 offset, UINT &length); - UINT32 Offset(); + char16* CvtUtf8Str(LPUTF8 name, uint32 nameLen); UINT LEB128(UINT &length, bool sgn = false); INT SLEB128(UINT &length); - template T ReadConst(); - SectionHeader ReadSectionHeader(); void CheckBytesLeft(UINT bytesNeeded); bool EndOfFunc(); @@ -171,7 +153,7 @@ namespace Wasm ArenaAllocator m_alloc; uint m_funcNumber; - byte *m_start, *m_end, *m_pc; + byte *m_start, *m_end, *m_pc, *m_curFuncEnd; SectionHeader m_currentSection; WasmBinOp m_lastOp; ReaderState m_funcState; // func AST level diff --git a/lib/WasmReader/WasmByteCodeGenerator.cpp b/lib/WasmReader/WasmByteCodeGenerator.cpp index 147d5523ef4..8463c316a4a 100644 --- a/lib/WasmReader/WasmByteCodeGenerator.cpp +++ b/lib/WasmReader/WasmByteCodeGenerator.cpp @@ -8,56 +8,101 @@ #ifdef ENABLE_WASM #if DBG_DUMP -#define DebugPrintOp(op) if (PHASE_TRACE(Js::WasmReaderPhase, m_currentFunc->body)) { PrintOpName(op); } +#define DebugPrintOp(op) if (PHASE_TRACE(Js::WasmReaderPhase, m_body)) { PrintOpName(op); } #else #define DebugPrintOp(op) #endif namespace Wasm { -WasmBytecodeGenerator::WasmBytecodeGenerator(Js::ScriptContext * scriptContext, Js::Utf8SourceInfo * sourceInfo, byte* binaryBuffer, uint binaryBufferLength) : - m_scriptContext(scriptContext), - m_sourceInfo(sourceInfo), - m_alloc(_u("WasmBytecodeGen"), scriptContext->GetThreadContext()->GetPageAllocator(), Js::Throw::OutOfMemory), - m_reader(scriptContext->GetThreadContext()->GetPageAllocator(), binaryBuffer, binaryBufferLength), - m_f32RegSlots(nullptr), - m_f64RegSlots(nullptr), - m_i32RegSlots(nullptr), - m_currentFunc(nullptr), - m_evalStack(&m_alloc), - m_blockInfos(&m_alloc) +#if DBG_DUMP + void + WasmBytecodeGenerator::PrintOpName(WasmOp op) const + { + switch (op) + { +#define WASM_KEYWORD(token, name) \ + case wn##token: \ + Output::Print(_u(#token ## "\r\n")); \ + break; +#include "WasmKeywords.h" + } + } +#endif + +/* static */ +Js::AsmJsRetType + WasmToAsmJs::GetAsmJsReturnType(WasmTypes::WasmType wasmType) { - m_writer.Create(); + switch (wasmType) + { + case WasmTypes::F32: + return Js::AsmJsRetType::Float; + case WasmTypes::F64: + return Js::AsmJsRetType::Double; + case WasmTypes::I32: + return Js::AsmJsRetType::Signed; + case WasmTypes::Void: + return Js::AsmJsRetType::Void; + case WasmTypes::I64: + throw WasmCompilationException(_u("I64 support NYI")); + default: + throw WasmCompilationException(_u("Unknown return type %u"), wasmType); + } +} - // TODO (michhol): try to make this more accurate? - const long astSize = 0; - m_writer.InitData(&m_alloc, astSize); +/* static */ +Js::AsmJsVarType + WasmToAsmJs::GetAsmJsVarType(WasmTypes::WasmType wasmType) +{ + Js::AsmJsVarType asmType = Js::AsmJsVarType::Int; + switch (wasmType) + { + case WasmTypes::F32: + return Js::AsmJsVarType::Float; + case WasmTypes::F64: + return Js::AsmJsVarType::Double; + case WasmTypes::I32: + return Js::AsmJsVarType::Int; + case WasmTypes::I64: + throw WasmCompilationException(_u("I64 support NYI")); + default: + throw WasmCompilationException(_u("Unknown var type %u"), wasmType); + } +} + +typedef bool(*SectionProcessFunc)(WasmModuleGenerator*); +typedef void(*AfterSectionCallback)(WasmModuleGenerator*); + +WasmModuleGenerator::WasmModuleGenerator(Js::ScriptContext * scriptContext, Js::Utf8SourceInfo * sourceInfo, byte* binaryBuffer, uint binaryBufferLength) : + m_sourceInfo(sourceInfo), + m_scriptContext(scriptContext), + m_recycler(scriptContext->GetRecycler()) +{ + m_reader = RecyclerNew(m_recycler, Binary::WasmBinaryReader, scriptContext, binaryBuffer, binaryBufferLength); // Initialize maps needed by binary reader Binary::WasmBinaryReader::Init(scriptContext); } -typedef bool(*SectionProcessFunc)(WasmBytecodeGenerator*); -typedef void(*AfterSectionCallback)(WasmBytecodeGenerator*); -WasmModule * -WasmBytecodeGenerator::GenerateModule() +Wasm::WasmModule* WasmModuleGenerator::GenerateModule() { // TODO: can this be in a better place? m_sourceInfo->EnsureInitialized(0); m_sourceInfo->GetSrcInfo()->sourceContextInfo->EnsureInitialized(); - m_module = Anew(&m_alloc, WasmModule); - m_module->info = m_reader.m_moduleInfo; + m_module = RecyclerNew(m_recycler, WasmModule); + m_module->info = m_reader->m_moduleInfo; m_module->heapOffset = 0; m_module->importFuncOffset = m_module->heapOffset + 1; m_module->funcOffset = m_module->heapOffset + 1; - m_reader.InitializeReader(); - m_reader.m_module = m_module; + m_reader->InitializeReader(); + m_reader->m_module = m_module; - BVFixed* visitedSections = BVFixed::New(bSectLimit + 1, &m_alloc); + BVStatic visitedSections; - const auto readerProcess = [](WasmBytecodeGenerator* gen) { return gen->m_reader.ProcessCurrentSection(); }; + const auto readerProcess = [](WasmModuleGenerator* gen) { return gen->m_reader->ProcessCurrentSection(); }; // By default lest the reader process the section #define WASM_SECTION(name, id, flag, precedent) readerProcess, SectionProcessFunc sectionProcess[bSectLimit + 1] = { @@ -68,43 +113,42 @@ WasmBytecodeGenerator::GenerateModule() // Will callback regardless if the section is present or not AfterSectionCallback afterSectionCallback[bSectLimit + 1] = {}; - afterSectionCallback[bSectFunctionSignatures] = [](WasmBytecodeGenerator* gen) { + afterSectionCallback[bSectFunctionSignatures] = [](WasmModuleGenerator* gen) { gen->m_module->funcOffset = gen->m_module->importFuncOffset + gen->m_module->info->GetImportCount(); }; - afterSectionCallback[bSectIndirectFunctionTable] = [](WasmBytecodeGenerator* gen) { + afterSectionCallback[bSectIndirectFunctionTable] = [](WasmModuleGenerator* gen) { gen->m_module->indirFuncTableOffset = gen->m_module->funcOffset + gen->m_module->info->GetFunctionCount(); }; - sectionProcess[bSectFunctionBodies] = [](WasmBytecodeGenerator* gen) { - gen->m_module->funcCount = gen->m_module->info->GetFunctionCount(); - gen->m_module->functions = AnewArrayZ(&gen->m_alloc, WasmFunction*, gen->m_module->funcCount); - if (PHASE_ON1(Js::WasmLazyTrapPhase)) + sectionProcess[bSectFunctionBodies] = [](WasmModuleGenerator* gen) { + uint32 funcCount = gen->m_module->info->GetFunctionCount(); + gen->m_module->funcCount = funcCount; + gen->m_module->functions = RecyclerNewArrayZ(gen->m_recycler, WasmFunction*, funcCount); + if (!gen->m_reader->ReadFunctionHeaders()) { - gen->m_module->lazyTraps = AnewArrayZ(&gen->m_alloc, WasmCompilationException*, gen->m_module->funcCount); + return false; } - return gen->m_reader.ReadFunctionBodies([](uint32 index, void* g) { - WasmBytecodeGenerator* gen = (WasmBytecodeGenerator*)g; - if (index >= gen->m_module->funcCount) { - return false; - } - WasmFunction* fn = gen->GenerateFunction(); - gen->m_module->functions[index] = fn; - return true; - }, gen); + + for (uint32 i = 0; i < funcCount; ++i) + { + WasmFunction* fn = gen->GenerateFunctionHeader(i); + gen->m_module->functions[i] = fn; + } + return true; }; for (SectionCode sectionCode = (SectionCode)(bSectInvalid + 1); sectionCode < bSectLimit ; sectionCode = (SectionCode)(sectionCode + 1)) { SectionCode precedent = SectionInfo::All[sectionCode].precedent; - if (m_reader.ReadNextSection((SectionCode)sectionCode)) + if (m_reader->ReadNextSection((SectionCode)sectionCode)) { - if (precedent != bSectInvalid && !visitedSections->Test(precedent)) + if (precedent != bSectInvalid && !visitedSections.Test(precedent)) { throw WasmCompilationException(_u("%s section missing before %s"), SectionInfo::All[precedent].name, SectionInfo::All[sectionCode].name); } - visitedSections->Set(sectionCode); + visitedSections.Set(sectionCode); if (!sectionProcess[sectionCode](this)) { @@ -121,11 +165,11 @@ WasmBytecodeGenerator::GenerateModule() #if DBG_DUMP if (PHASE_TRACE1(Js::WasmReaderPhase)) { - m_reader.PrintOps(); + m_reader->PrintOps(); } #endif // If we see a FunctionSignatures section we need to see a FunctionBodies section - if (visitedSections->Test(bSectFunctionSignatures) && !visitedSections->Test(bSectFunctionBodies)) + if (visitedSections.Test(bSectFunctionSignatures) && !visitedSections.Test(bSectFunctionBodies)) { throw WasmCompilationException(_u("Missing required section: %s"), SectionInfo::All[bSectFunctionBodies].name); } @@ -135,21 +179,16 @@ WasmBytecodeGenerator::GenerateModule() return m_module; } -WasmFunction * -WasmBytecodeGenerator::GenerateFunction() +Wasm::WasmFunction * WasmModuleGenerator::GenerateFunctionHeader(uint32 index) { - WasmFunctionInfo* wasmInfo = m_reader.m_currentNode.func.info; - TRACE_WASM_DECODER(_u("GenerateFunction %u \n"), wasmInfo->GetNumber()); - - WasmRegisterSpace f32Space(ReservedRegisterCount); - WasmRegisterSpace f64Space(ReservedRegisterCount); - WasmRegisterSpace i32Space(ReservedRegisterCount); - - m_f32RegSlots = &f32Space; - m_f64RegSlots = &f64Space; - m_i32RegSlots = &i32Space; + TRACE_WASM_DECODER(_u("GenerateFunction %u \n"), index); + WasmFunctionInfo* wasmInfo = m_module->info->GetFunSig(index); + if (!wasmInfo) + { + throw WasmCompilationException(_u("Invalid function index %u"), index); + } - m_currentFunc = Anew(&m_alloc, WasmFunction); + WasmFunction* func = RecyclerNew(m_recycler, WasmFunction); char16* functionName = nullptr; int nameLength = 0; @@ -162,7 +201,7 @@ WasmBytecodeGenerator::GenerateFunction() if (funcIndex == wasmInfo->GetNumber()) { nameLength = funcExport->nameLength + 16; - functionName = RecyclerNewArrayZ(m_scriptContext->GetRecycler(), char16, nameLength); + functionName = RecyclerNewArrayLeafZ(m_scriptContext->GetRecycler(), char16, nameLength); nameLength = swprintf_s(functionName, nameLength, _u("%s[%u]"), funcExport->name, wasmInfo->GetNumber()); break; } @@ -171,11 +210,11 @@ WasmBytecodeGenerator::GenerateFunction() if (!functionName) { - functionName = RecyclerNewArrayZ(m_scriptContext->GetRecycler(), char16, 32); + functionName = RecyclerNewArrayLeafZ(m_scriptContext->GetRecycler(), char16, 32); nameLength = swprintf_s(functionName, 32, _u("wasm-function[%u]"), wasmInfo->GetNumber()); } - m_currentFunc->body = Js::FunctionBody::NewFromRecycler( + Js::FunctionBody* body = func->body = Js::FunctionBody::NewFromRecycler( m_scriptContext, functionName, nameLength, @@ -189,184 +228,210 @@ WasmBytecodeGenerator::GenerateFunction() #ifdef PERF_COUNTERS , false /* is function from deferred deserialized proxy */ #endif - ); + ); // TODO (michhol): numbering - m_currentFunc->body->SetSourceInfo(0); - m_currentFunc->body->AllocateAsmJsFunctionInfo(); - m_currentFunc->body->SetIsAsmJsFunction(true); - m_currentFunc->body->SetIsAsmjsMode(true); - m_currentFunc->body->SetIsWasmFunction(true); - m_currentFunc->body->GetAsmJsFunctionInfo()->SetIsHeapBufferConst(true); - m_funcInfo = wasmInfo; - m_currentFunc->wasmInfo = m_funcInfo; - m_nestedIfLevel = 0; - m_maxArgOutDepth = 0; + body->SetSourceInfo(0); + body->AllocateAsmJsFunctionInfo(); + body->SetIsAsmJsFunction(true); + body->SetIsAsmjsMode(true); + body->SetIsWasmFunction(true); + body->GetAsmJsFunctionInfo()->SetIsHeapBufferConst(true); - Assert(m_evalStack.Empty()); - // The stack should always be empty when starting a new function, make sure by clearing it in case we missed something - m_evalStack.Clear(); + WasmReaderInfo* readerInfo = RecyclerNew(m_recycler, WasmReaderInfo); + readerInfo->m_reader = m_reader; + readerInfo->m_funcInfo = wasmInfo; + readerInfo->m_module = m_module; - // TODO: fix these bools - m_writer.Begin(m_currentFunc->body, &m_alloc, true, true, false); - try + Js::AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo(); + info->SetWasmReaderInfo(readerInfo); + + if (wasmInfo->GetParamCount() >= Js::Constants::InvalidArgSlot) { - try - { - if (PHASE_OFF(Js::WasmBytecodePhase, m_currentFunc->body) && PHASE_ON1(Js::WasmLazyTrapPhase)) - { - throw WasmCompilationException(_u("Compilation skipped")); - } - m_funcInfo->SetExitLabel(m_writer.DefineLabel()); - EnregisterLocals(); + Js::Throw::OutOfMemory(); + } + Js::ArgSlot paramCount = (Js::ArgSlot)wasmInfo->GetParamCount(); + info->SetArgCount(paramCount); - WasmOp op = wnLIMIT; - EmitInfo exprInfo; - EnterEvalStackScope(); - while ((op = m_reader.ReadExpr()) != wnFUNC_END) - { - exprInfo = EmitExpr(op); - } - DebugPrintOp(op); - // Functions are like blocks. Emit implicit return of last stmt/expr, unless it is a return or end of file (sexpr). - Wasm::WasmTypes::WasmType returnType = m_funcInfo->GetSignature()->GetResultType(); + Js::ArgSlot argSizeLength = max(paramCount, 3ui16); + info->SetArgSizeArrayLength(argSizeLength); + uint* argSizeArray = RecyclerNewArrayLeafZ(m_scriptContext->GetRecycler(), uint, argSizeLength); + info->SetArgsSizesArray(argSizeArray); - // If the last expression yielded a value, return it - if (exprInfo.type != WasmTypes::Unreachable) - { - if (exprInfo.type != returnType && returnType != Wasm::WasmTypes::Void) - { - throw WasmCompilationException(_u("Last expression return type mismatch return type")); - } - uint32 arity = 0; - if (returnType != Wasm::WasmTypes::Void) - { - arity = 1; - } - m_reader.m_currentNode.ret.arity = arity; - EmitReturnExpr(); - } - ExitEvalStackScope(); - } - catch (...) + if (m_module->memSize > 0) + { + info->SetUsesHeapBuffer(true); + } + if (paramCount > 0) + { + body->SetHasImplicitArgIns(true); + body->SetInParamsCount(paramCount + 1); + body->SetReportedInParamsCount(paramCount + 1); + info->SetArgTypeArray(RecyclerNewArrayLeaf(m_scriptContext->GetRecycler(), Js::AsmJsVarType::Which, paramCount)); + } + Js::ArgSlot paramSize = 0; + for (Js::ArgSlot i = 0; i < paramCount; ++i) + { + WasmTypes::WasmType type = wasmInfo->GetParam(i); + info->SetArgType(WasmToAsmJs::GetAsmJsVarType(type), i); + uint16 size = 0; + switch (type) { - m_writer.Reset(); - throw; + case WasmTypes::F32: + case WasmTypes::I32: + CompileAssert(sizeof(float) == sizeof(int32)); +#ifdef _M_X64 + // on x64, we always alloc (at least) 8 bytes per arguments + size = sizeof(void*); +#elif _M_IX86 + size = sizeof(int32); +#else + Assert(UNREACHED); +#endif + break; + case WasmTypes::F64: + case WasmTypes::I64: + CompileAssert(sizeof(double) == sizeof(int64)); + size = sizeof(int64); + break; + default: + Assume(UNREACHED); } - m_writer.MarkAsmJsLabel(m_funcInfo->GetExitLabel()); - m_writer.EmptyAsm(Js::OpCodeAsmJs::Ret); + argSizeArray[i] = size; + // REVIEW: reduce number of checked adds + paramSize = UInt16Math::Add(paramSize, size); + } + info->SetArgByteSize(paramSize); + info->SetReturnType(WasmToAsmJs::GetAsmJsReturnType(wasmInfo->GetResultType())); - m_writer.End(); + return func; +} -#if DBG_DUMP - if (PHASE_DUMP(Js::ByteCodePhase, m_currentFunc->body)) - { - Js::AsmJsByteCodeDumper::DumpBasic(m_currentFunc->body); - } -#endif +WasmBytecodeGenerator::WasmBytecodeGenerator(Js::ScriptContext* scriptContext, Js::FunctionBody* body, WasmReaderInfo* readerInfo) : + m_scriptContext(scriptContext), + m_alloc(_u("WasmBytecodeGen"), scriptContext->GetThreadContext()->GetPageAllocator(), Js::Throw::OutOfMemory), + m_evalStack(&m_alloc), + m_i32RegSlots(ReservedRegisterCount), + m_f32RegSlots(ReservedRegisterCount), + m_f64RegSlots(ReservedRegisterCount), + m_blockInfos(&m_alloc), + m_body(body) +{ + m_writer.Create(); + // Init reader to current func offset + m_reader = readerInfo->m_reader; + m_reader->SeekToFunctionBody(readerInfo->m_funcInfo->m_readerInfo); + m_funcInfo = readerInfo->m_funcInfo; + m_module = readerInfo->m_module; + + // Use binary size to estimate bytecode size + const long astSize = readerInfo->m_funcInfo->m_readerInfo.size; + m_writer.InitData(&m_alloc, astSize); +} - // TODO: refactor out to separate procedure - Js::AsmJsFunctionInfo * info = m_currentFunc->body->GetAsmJsFunctionInfo(); - if (m_funcInfo->GetParamCount() >= Js::Constants::InvalidArgSlot) - { - Js::Throw::OutOfMemory(); - } - Js::ArgSlot paramCount = (Js::ArgSlot)m_funcInfo->GetParamCount(); - info->SetArgCount(paramCount); +void +WasmBytecodeGenerator::GenerateFunctionBytecode(Js::ScriptContext* scriptContext, Js::FunctionBody* body, WasmReaderInfo* readerinfo) +{ + WasmBytecodeGenerator generator(scriptContext, body, readerinfo); + generator.GenerateFunction(); + if (!generator.m_reader->IsCurrentFunctionCompleted()) + { + throw WasmCompilationException(_u("Invalid function format")); + } +} - Js::ArgSlot argSizeLength = max(paramCount, 3ui16); - info->SetArgSizeArrayLength(argSizeLength); - uint* argSizeArray = RecyclerNewArrayLeafZ(m_scriptContext->GetRecycler(), uint, argSizeLength); - info->SetArgsSizesArray(argSizeArray); - if (m_module->memSize > 0) - { - info->SetUsesHeapBuffer(true); - } - if (paramCount > 0) +void +WasmBytecodeGenerator::GenerateFunction() +{ + if (PHASE_OFF(Js::WasmBytecodePhase, m_body)) + { + throw WasmCompilationException(_u("Compilation skipped")); + } + Js::AutoProfilingPhase functionProfiler(m_scriptContext, Js::WasmFunctionBodyPhase); + Unused(functionProfiler); + + m_maxArgOutDepth = 0; + + // TODO: fix these bools + m_writer.Begin(m_body, &m_alloc, true, true, false); + try + { + m_funcInfo->SetExitLabel(m_writer.DefineLabel()); + EnregisterLocals(); + + WasmOp op = wnLIMIT; + EmitInfo exprInfo; + EnterEvalStackScope(); + while ((op = m_reader->ReadExpr()) != wnFUNC_END) { - m_currentFunc->body->SetHasImplicitArgIns(true); - m_currentFunc->body->SetInParamsCount(paramCount + 1); - m_currentFunc->body->SetReportedInParamsCount(paramCount + 1); - info->SetArgTypeArray(RecyclerNewArrayLeaf(m_scriptContext->GetRecycler(), Js::AsmJsVarType::Which, paramCount)); + exprInfo = EmitExpr(op); } - Js::ArgSlot paramSize = 0; - for (Js::ArgSlot i = 0; i < paramCount; ++i) + DebugPrintOp(op); + // Functions are like blocks. Emit implicit return of last stmt/expr, unless it is a return or end of file (sexpr). + Wasm::WasmTypes::WasmType returnType = m_funcInfo->GetSignature()->GetResultType(); + + // If the last expression yielded a value, return it + if (exprInfo.type != WasmTypes::Unreachable) { - WasmTypes::WasmType type = m_funcInfo->GetParam(i); - info->SetArgType(GetAsmJsVarType(type), i); - uint16 size = 0; - switch (type) + if (exprInfo.type != returnType && returnType != Wasm::WasmTypes::Void) { - case WasmTypes::F32: - case WasmTypes::I32: - CompileAssert(sizeof(float) == sizeof(int32)); -#ifdef _M_X64 - // on x64, we always alloc (at least) 8 bytes per arguments - size = sizeof(void*); -#elif _M_IX86 - size = sizeof(int32); -#else - Assert(UNREACHED); -#endif - break; - case WasmTypes::F64: - case WasmTypes::I64: - CompileAssert(sizeof(double) == sizeof(int64)); - size = sizeof(int64); - break; - default: - Assume(UNREACHED); + throw WasmCompilationException(_u("Last expression return type mismatch return type")); } - argSizeArray[i] = size; - // REVIEW: reduce number of checked adds - paramSize = UInt16Math::Add(paramSize, size); + uint32 arity = 0; + if (returnType != Wasm::WasmTypes::Void) + { + arity = 1; + } + m_reader->m_currentNode.ret.arity = arity; + EmitReturnExpr(); } - info->SetArgByteSize(paramSize); + ExitEvalStackScope(); + } + catch (...) + { + m_writer.Reset(); + throw; + } + m_writer.MarkAsmJsLabel(m_funcInfo->GetExitLabel()); + m_writer.EmptyAsm(Js::OpCodeAsmJs::Ret); - info->SetIntVarCount(m_i32RegSlots->GetVarCount()); - info->SetFloatVarCount(m_f32RegSlots->GetVarCount()); - info->SetDoubleVarCount(m_f64RegSlots->GetVarCount()); + m_writer.End(); - info->SetIntTmpCount(m_i32RegSlots->GetTmpCount()); - info->SetFloatTmpCount(m_f32RegSlots->GetTmpCount()); - info->SetDoubleTmpCount(m_f64RegSlots->GetTmpCount()); +#if DBG_DUMP + if (PHASE_DUMP(Js::ByteCodePhase, m_body)) + { + Js::AsmJsByteCodeDumper::DumpBasic(m_body); + } +#endif - info->SetIntConstCount(ReservedRegisterCount); - info->SetFloatConstCount(ReservedRegisterCount); - info->SetDoubleConstCount(ReservedRegisterCount); + Js::AsmJsFunctionInfo * info = m_body->GetAsmJsFunctionInfo(); + info->SetIntVarCount(m_i32RegSlots.GetVarCount()); + info->SetFloatVarCount(m_f32RegSlots.GetVarCount()); + info->SetDoubleVarCount(m_f64RegSlots.GetVarCount()); - int nbConst = - ((info->GetDoubleConstCount() + 1) * sizeof(double)) // space required - + (int)((info->GetFloatConstCount() + 1) * sizeof(float) + 0.5 /*ceil*/) - + (int)((info->GetIntConstCount() + 1) * sizeof(int) + 0.5/*ceil*/) // - + Js::AsmJsFunctionMemory::RequiredVarConstants; + info->SetIntTmpCount(m_i32RegSlots.GetTmpCount()); + info->SetFloatTmpCount(m_f32RegSlots.GetTmpCount()); + info->SetDoubleTmpCount(m_f64RegSlots.GetTmpCount()); - m_currentFunc->body->CheckAndSetConstantCount(nbConst); + info->SetIntConstCount(ReservedRegisterCount); + info->SetFloatConstCount(ReservedRegisterCount); + info->SetDoubleConstCount(ReservedRegisterCount); - info->SetReturnType(GetAsmJsReturnType(m_funcInfo->GetResultType())); + int nbConst = + ((info->GetDoubleConstCount() + 1) * sizeof(double)) // space required + + (int)((info->GetFloatConstCount() + 1) * sizeof(float) + 0.5 /*ceil*/) + + (int)((info->GetIntConstCount() + 1) * sizeof(int) + 0.5/*ceil*/) // + + Js::AsmJsFunctionMemory::RequiredVarConstants; - // REVIEW: overflow checks? - info->SetIntByteOffset(ReservedRegisterCount * sizeof(Js::Var)); - info->SetFloatByteOffset(info->GetIntByteOffset() + m_i32RegSlots->GetRegisterCount() * sizeof(int32)); - info->SetDoubleByteOffset(Math::Align(info->GetFloatByteOffset() + m_f32RegSlots->GetRegisterCount() * sizeof(float), sizeof(double))); + m_body->CheckAndSetConstantCount(nbConst); - m_currentFunc->body->SetOutParamMaxDepth(m_maxArgOutDepth); - m_currentFunc->body->SetVarCount(m_f32RegSlots->GetRegisterCount() + m_f64RegSlots->GetRegisterCount() + m_i32RegSlots->GetRegisterCount()); - } - catch (WasmCompilationException& ex) - { - if (!PHASE_ON1(Js::WasmLazyTrapPhase)) - { - throw WasmCompilationException(_u("function %s: %s"), functionName, ex.GetErrorMessage()); - } + // REVIEW: overflow checks? + info->SetIntByteOffset(ReservedRegisterCount * sizeof(Js::Var)); + info->SetFloatByteOffset(info->GetIntByteOffset() + m_i32RegSlots.GetRegisterCount() * sizeof(int32)); + info->SetDoubleByteOffset(Math::Align(info->GetFloatByteOffset() + m_f32RegSlots.GetRegisterCount() * sizeof(float), sizeof(double))); - // Since we will continue compilation for other functions, clear the eval stack - m_evalStack.Clear(); - Assert(m_module->lazyTraps != nullptr); - WasmCompilationException* lazyTrap = Anew(&m_alloc, WasmCompilationException, _u("(delayed) function %s: %s"), functionName, ex.GetErrorMessage()); - m_module->lazyTraps[wasmInfo->GetNumber()] = lazyTrap; - } - return m_currentFunc; + m_body->SetOutParamMaxDepth(m_maxArgOutDepth); + m_body->SetVarCount(m_f32RegSlots.GetRegisterCount() + m_f64RegSlots.GetRegisterCount() + m_i32RegSlots.GetRegisterCount()); } void @@ -378,7 +443,7 @@ WasmBytecodeGenerator::EnregisterLocals() { WasmTypes::WasmType type = m_funcInfo->GetLocal(i); WasmRegisterSpace * regSpace = GetRegisterSpace(type); - if (regSpace == nullptr) + if (regSpace == nullptr) { throw WasmCompilationException(_u("Unable to find local register space")); } @@ -406,21 +471,6 @@ WasmBytecodeGenerator::EnregisterLocals() } } -#if DBG_DUMP -void -WasmBytecodeGenerator::PrintOpName(WasmOp op) const -{ - switch (op) - { -#define WASM_KEYWORD(token, name) \ - case wn##token: \ - Output::Print(_u(#token ## "\r\n")); \ - break; -#include "WasmKeywords.h" - } -} -#endif - EmitInfo WasmBytecodeGenerator::EmitExpr(WasmOp op) { @@ -503,7 +553,7 @@ WasmBytecodeGenerator::EmitExpr(WasmOp op) break; #include "WasmKeywords.h" case wnNYI: - switch (m_reader.GetLastBinOp()) + switch (m_reader->GetLastBinOp()) { #define WASM_OPCODE(opname, opcode, token, sig) \ case opcode: \ @@ -523,12 +573,12 @@ WasmBytecodeGenerator::EmitExpr(WasmOp op) EmitInfo WasmBytecodeGenerator::EmitGetLocal() { - if (m_funcInfo->GetLocalCount() < m_reader.m_currentNode.var.num) + if (m_funcInfo->GetLocalCount() < m_reader->m_currentNode.var.num) { - throw WasmCompilationException(_u("%u is not a valid local"), m_reader.m_currentNode.var.num); + throw WasmCompilationException(_u("%u is not a valid local"), m_reader->m_currentNode.var.num); } - WasmLocal local = m_locals[m_reader.m_currentNode.var.num]; + WasmLocal local = m_locals[m_reader->m_currentNode.var.num]; Js::OpCodeAsmJs op = GetLoadOp(local.type); WasmRegisterSpace * regSpace = GetRegisterSpace(local.type); @@ -543,7 +593,7 @@ WasmBytecodeGenerator::EmitGetLocal() EmitInfo WasmBytecodeGenerator::EmitSetLocal() { - uint localNum = m_reader.m_currentNode.var.num; + uint localNum = m_reader->m_currentNode.var.num; if (localNum >= m_funcInfo->GetLocalCount()) { throw WasmCompilationException(_u("%u is not a valid local"), localNum); @@ -573,13 +623,13 @@ WasmBytecodeGenerator::EmitConst() switch (type) { case WasmTypes::F32: - m_writer.AsmFloat1Const1(Js::OpCodeAsmJs::Ld_FltConst, tmpReg, m_reader.m_currentNode.cnst.f32); + m_writer.AsmFloat1Const1(Js::OpCodeAsmJs::Ld_FltConst, tmpReg, m_reader->m_currentNode.cnst.f32); break; case WasmTypes::F64: - m_writer.AsmDouble1Const1(Js::OpCodeAsmJs::Ld_DbConst, tmpReg, m_reader.m_currentNode.cnst.f64); + m_writer.AsmDouble1Const1(Js::OpCodeAsmJs::Ld_DbConst, tmpReg, m_reader->m_currentNode.cnst.f64); break; case WasmTypes::I32: - m_writer.AsmInt1Const1(Js::OpCodeAsmJs::Ld_IntConst, tmpReg, m_reader.m_currentNode.cnst.i32); + m_writer.AsmInt1Const1(Js::OpCodeAsmJs::Ld_IntConst, tmpReg, m_reader->m_currentNode.cnst.i32); break; default: throw WasmCompilationException(_u("Unknown type %u"), type); @@ -594,7 +644,7 @@ WasmBytecodeGenerator::EmitBlockCommon() WasmOp op; EmitInfo blockInfo; EnterEvalStackScope(); - while ((op = m_reader.ReadFromBlock()) != wnEND && op != wnELSE) + while ((op = m_reader->ReadExpr()) != wnEND && op != wnELSE) { blockInfo = EmitExpr(op); } @@ -656,7 +706,7 @@ WasmBytecodeGenerator::EmitCall() switch (wasmOp) { case wnCALL: - funcNum = m_reader.m_currentNode.call.num; + funcNum = m_reader->m_currentNode.call.num; if (funcNum >= m_module->info->GetFunctionCount()) { throw WasmCompilationException(_u("Call is to unknown function")); @@ -665,7 +715,7 @@ WasmBytecodeGenerator::EmitCall() break; case wnCALL_IMPORT: { - funcNum = m_reader.m_currentNode.call.num; + funcNum = m_reader->m_currentNode.call.num; if (funcNum >= m_module->info->GetImportCount()) { throw WasmCompilationException(L"Call is to unknown function"); @@ -675,7 +725,7 @@ WasmBytecodeGenerator::EmitCall() break; } case wnCALL_INDIRECT: - signatureId = m_reader.m_currentNode.call.num; + signatureId = m_reader->m_currentNode.call.num; calleeSignature = m_module->info->GetSignature(signatureId); break; default: @@ -705,7 +755,7 @@ WasmBytecodeGenerator::EmitCall() m_writer.AsmStartCall(startCallOp, argSize); - if (calleeSignature->GetParamCount() != m_reader.m_currentNode.call.arity) + if (calleeSignature->GetParamCount() != m_reader->m_currentNode.call.arity) { throw WasmCompilationException(_u("Mismatch between call signature and arity")); } @@ -723,7 +773,7 @@ WasmBytecodeGenerator::EmitCall() switch (info.type) { case WasmTypes::F32: - if (wasmOp == wnCALL_IMPORT) + if (wasmOp == wnCALL_IMPORT) { throw WasmCompilationException(_u("External calls with float argument NYI")); } @@ -746,7 +796,7 @@ WasmBytecodeGenerator::EmitCall() throw WasmCompilationException(_u("Unknown argument type %u"), info.type); } argsBytesLeft -= wasmOp == wnCALL_IMPORT ? sizeof(Js::Var) : calleeSignature->GetParamSize(i); - if (argsBytesLeft < 0 || (argsBytesLeft % sizeof(Js::Var)) != 0) + if (argsBytesLeft < 0 || (argsBytesLeft % sizeof(Js::Var)) != 0) { throw WasmCompilationException(_u("Error while emitting call arguments")); } @@ -794,7 +844,7 @@ WasmBytecodeGenerator::EmitCall() callOp = Js::OpCodeAsmJs::I_Call; } - m_writer.AsmCall(callOp, 0, 0, args, GetAsmJsReturnType(calleeSignature->GetResultType())); + m_writer.AsmCall(callOp, 0, 0, args, WasmToAsmJs::GetAsmJsReturnType(calleeSignature->GetResultType())); // emit result coercion EmitInfo retInfo; @@ -805,15 +855,15 @@ WasmBytecodeGenerator::EmitCall() switch (retInfo.type) { case WasmTypes::F32: - retInfo.location = m_f32RegSlots->AcquireTmpRegister(); + retInfo.location = m_f32RegSlots.AcquireTmpRegister(); convertOp = wasmOp == wnCALL_IMPORT ? Js::OpCodeAsmJs::Conv_VTF : Js::OpCodeAsmJs::I_Conv_VTF; break; case WasmTypes::F64: - retInfo.location = m_f64RegSlots->AcquireTmpRegister(); + retInfo.location = m_f64RegSlots.AcquireTmpRegister(); convertOp = wasmOp == wnCALL_IMPORT ? Js::OpCodeAsmJs::Conv_VTD : Js::OpCodeAsmJs::I_Conv_VTD; break; case WasmTypes::I32: - retInfo.location = m_i32RegSlots->AcquireTmpRegister(); + retInfo.location = m_i32RegSlots.AcquireTmpRegister(); convertOp = wasmOp == wnCALL_IMPORT ? Js::OpCodeAsmJs::Conv_VTI : Js::OpCodeAsmJs::I_Conv_VTI; break; case WasmTypes::I64: @@ -839,14 +889,6 @@ WasmBytecodeGenerator::EmitCall() EmitInfo WasmBytecodeGenerator::EmitIfElseExpr() { - ++m_nestedIfLevel; - - if (m_nestedIfLevel == 0) - { - // overflow - Js::Throw::OutOfMemory(); - } - EmitInfo checkExpr = PopEvalStack(); if (checkExpr.type != WasmTypes::I32) @@ -860,7 +902,7 @@ WasmBytecodeGenerator::EmitIfElseExpr() m_writer.AsmBrReg1(Js::OpCodeAsmJs::BrFalse_Int, falseLabel, checkExpr.location); - m_i32RegSlots->ReleaseLocation(&checkExpr); + m_i32RegSlots.ReleaseLocation(&checkExpr); EmitInfo trueExpr = EmitBlock(); @@ -868,14 +910,14 @@ WasmBytecodeGenerator::EmitIfElseExpr() m_writer.MarkAsmJsLabel(falseLabel); - WasmOp op = m_reader.GetLastOp(); // wnEND or wnELSE + WasmOp op = m_reader->GetLastOp(); // wnEND or wnELSE EmitInfo retInfo; EmitInfo falseExpr; if (op == wnELSE) { - falseExpr = EmitBlock(); + falseExpr = EmitBlock(); // Read END - op = m_reader.GetLastOp(); + op = m_reader->GetLastOp(); } if (!WasmTypes::IsLocalType(trueExpr.type) || falseExpr.type != trueExpr.type) @@ -901,19 +943,16 @@ WasmBytecodeGenerator::EmitIfElseExpr() m_writer.MarkAsmJsLabel(endLabel); - Assert(m_nestedIfLevel > 0); - --m_nestedIfLevel; - return retInfo; } EmitInfo WasmBytecodeGenerator::EmitBrTable() { - const uint arity = m_reader.m_currentNode.brTable.arity; - const uint numTargets = m_reader.m_currentNode.brTable.numTargets; - const UINT* targetTable = m_reader.m_currentNode.brTable.targetTable; - const UINT defaultEntry = m_reader.m_currentNode.brTable.defaultTarget; + const uint arity = m_reader->m_currentNode.brTable.arity; + const uint numTargets = m_reader->m_currentNode.brTable.numTargets; + const UINT* targetTable = m_reader->m_currentNode.brTable.targetTable; + const UINT defaultEntry = m_reader->m_currentNode.brTable.defaultTarget; // Compile scrutinee EmitInfo scrutineeInfo = PopEvalStack(); @@ -924,7 +963,7 @@ WasmBytecodeGenerator::EmitBrTable() m_writer.AsmReg2(Js::OpCodeAsmJs::BeginSwitch_Int, scrutineeInfo.location, scrutineeInfo.location); EmitInfo yieldInfo; - if (arity == 1) + if (arity == 1) { yieldInfo = PopEvalStack(); } @@ -995,8 +1034,8 @@ template EmitInfo WasmBytecodeGenerator::EmitMemRead() { - const uint offset = m_reader.m_currentNode.mem.offset; - m_currentFunc->body->GetAsmJsFunctionInfo()->SetUsesHeapBuffer(true); + const uint offset = m_reader->m_currentNode.mem.offset; + m_body->GetAsmJsFunctionInfo()->SetUsesHeapBuffer(true); EmitInfo exprInfo = PopEvalStack(); @@ -1006,13 +1045,13 @@ WasmBytecodeGenerator::EmitMemRead() } if (offset != 0) { - Js::RegSlot tempReg = m_i32RegSlots->AcquireTmpRegister(); + Js::RegSlot tempReg = m_i32RegSlots.AcquireTmpRegister(); m_writer.AsmInt1Const1(Js::OpCodeAsmJs::Ld_IntConst, tempReg, offset); m_writer.AsmReg3(Js::OpCodeAsmJs::Add_Int, exprInfo.location, exprInfo.location, tempReg); - m_i32RegSlots->ReleaseTmpRegister(tempReg); + m_i32RegSlots.ReleaseTmpRegister(tempReg); } - m_i32RegSlots->ReleaseLocation(&exprInfo); + m_i32RegSlots.ReleaseLocation(&exprInfo); Js::RegSlot resultReg = GetRegisterSpace(type)->AcquireTmpRegister(); m_writer.AsmTypedArr(Js::OpCodeAsmJs::LdArr, resultReg, exprInfo.location, GetViewType(wasmOp)); @@ -1024,8 +1063,8 @@ template EmitInfo WasmBytecodeGenerator::EmitMemStore() { - const uint offset = m_reader.m_currentNode.mem.offset; - m_currentFunc->body->GetAsmJsFunctionInfo()->SetUsesHeapBuffer(true); + const uint offset = m_reader->m_currentNode.mem.offset; + m_body->GetAsmJsFunctionInfo()->SetUsesHeapBuffer(true); // TODO (michhol): combine with MemRead EmitInfo rhsInfo = PopEvalStack(); @@ -1037,11 +1076,11 @@ WasmBytecodeGenerator::EmitMemStore() } if (offset != 0) { - Js::RegSlot indexReg = m_i32RegSlots->AcquireTmpRegister(); + Js::RegSlot indexReg = m_i32RegSlots.AcquireTmpRegister(); m_writer.AsmInt1Const1(Js::OpCodeAsmJs::Ld_IntConst, indexReg, offset); m_writer.AsmReg3(Js::OpCodeAsmJs::Add_Int, exprInfo.location, exprInfo.location, indexReg); - m_i32RegSlots->ReleaseTmpRegister(indexReg); + m_i32RegSlots.ReleaseTmpRegister(indexReg); } if (rhsInfo.type != type) { @@ -1061,26 +1100,12 @@ WasmBytecodeGenerator::EmitMemStore() return EmitInfo(retLoc, type); } -template -Js::RegSlot -WasmBytecodeGenerator::GetConstReg(T constVal) -{ - Js::RegSlot location = m_funcInfo->GetConst(constVal); - if (location == Js::Constants::NoRegister) - { - WasmRegisterSpace * regSpace = GetRegisterSpace(m_reader.m_currentNode.type); - location = regSpace->AcquireConstRegister(); - m_funcInfo->AddConst(constVal, location); - } - return location; -} - EmitInfo WasmBytecodeGenerator::EmitReturnExpr() { if (m_funcInfo->GetResultType() == WasmTypes::Void) { - if (m_reader.m_currentNode.ret.arity != 0) + if (m_reader->m_currentNode.ret.arity != 0) { throw WasmCompilationException(_u("Nonzero arity for return op in void function")); } @@ -1089,7 +1114,7 @@ WasmBytecodeGenerator::EmitReturnExpr() } else { - if (m_reader.m_currentNode.ret.arity != 1) + if (m_reader->m_currentNode.ret.arity != 1) { throw WasmCompilationException(_u("Unexpected arity for return op")); } @@ -1163,8 +1188,8 @@ template EmitInfo WasmBytecodeGenerator::EmitBr() { - UINT depth = m_reader.m_currentNode.br.depth; - bool hasSubExpr = m_reader.m_currentNode.br.hasSubExpr; + UINT depth = m_reader->m_currentNode.br.depth; + bool hasSubExpr = m_reader->m_currentNode.br.hasSubExpr; EmitInfo conditionInfo; if (wasmOp == WasmOp::wnBR_IF) @@ -1193,54 +1218,13 @@ WasmBytecodeGenerator::EmitBr() { Assert(wasmOp == WasmOp::wnBR_IF); m_writer.AsmBrReg1(Js::OpCodeAsmJs::BrTrue_Int, target, conditionInfo.location); - m_i32RegSlots->ReleaseLocation(&conditionInfo); + m_i32RegSlots.ReleaseLocation(&conditionInfo); } ReleaseLocation(&info); return EmitInfo(WasmTypes::Unreachable); } -/* static */ -Js::AsmJsRetType -WasmBytecodeGenerator::GetAsmJsReturnType(WasmTypes::WasmType wasmType) -{ - switch (wasmType) - { - case WasmTypes::F32: - return Js::AsmJsRetType::Float; - case WasmTypes::F64: - return Js::AsmJsRetType::Double; - case WasmTypes::I32: - return Js::AsmJsRetType::Signed; - case WasmTypes::Void: - return Js::AsmJsRetType::Void; - case WasmTypes::I64: - throw WasmCompilationException(_u("I64 support NYI")); - default: - throw WasmCompilationException(_u("Unknown return type %u"), wasmType); - } -} - -/* static */ -Js::AsmJsVarType -WasmBytecodeGenerator::GetAsmJsVarType(WasmTypes::WasmType wasmType) -{ - Js::AsmJsVarType asmType = Js::AsmJsVarType::Int; - switch (wasmType) - { - case WasmTypes::F32: - return Js::AsmJsVarType::Float; - case WasmTypes::F64: - return Js::AsmJsVarType::Double; - case WasmTypes::I32: - return Js::AsmJsVarType::Int; - case WasmTypes::I64: - throw WasmCompilationException(_u("I64 support NYI")); - default: - throw WasmCompilationException(_u("Unknown var type %u"), wasmType); - } -} - /* static */ Js::OpCodeAsmJs WasmBytecodeGenerator::GetLoadOp(WasmTypes::WasmType wasmType) @@ -1335,7 +1319,7 @@ WasmBytecodeGenerator::PopLabel(Js::ByteCodeLabel labelValidation) yieldEmitInfo.location = yieldInfo->yieldLocs[yieldInfo->type]; yieldEmitInfo.type = yieldInfo->type; } - else + else { yieldEmitInfo.type = yieldInfo->type == WasmTypes::Limit ? WasmTypes::Unreachable : WasmTypes::Void; yieldEmitInfo.location = Js::Constants::NoRegister; @@ -1352,18 +1336,13 @@ WasmBytecodeGenerator::PushLabel(Js::ByteCodeLabel label, bool addBlockYieldInfo if (addBlockYieldInfo) { info.yieldInfo = Anew(&m_alloc, BlockYieldInfo); - for (int type = WasmTypes::Void + 1; type < WasmTypes::Limit; ++type) - { - try - { - info.yieldInfo->yieldLocs[type] = GetRegisterSpace((WasmTypes::WasmType)type)->AcquireTmpRegister(); - } - catch (WasmCompilationException) - { - // This might be a NYI type - info.yieldInfo->yieldLocs[type] = Js::Constants::NoRegister; - } - } + info.yieldInfo->yieldLocs[WasmTypes::I32] = GetRegisterSpace(WasmTypes::I32)->AcquireTmpRegister(); + info.yieldInfo->yieldLocs[WasmTypes::I64] = Js::Constants::NoRegister; // NYI + info.yieldInfo->yieldLocs[WasmTypes::F32] = GetRegisterSpace(WasmTypes::F32)->AcquireTmpRegister(); + info.yieldInfo->yieldLocs[WasmTypes::F64] = GetRegisterSpace(WasmTypes::F64)->AcquireTmpRegister(); + + // Make sure we don't forget to add new types here + CompileAssert(WasmTypes::Limit == 5); } m_blockInfos.Push(info); } @@ -1380,7 +1359,7 @@ WasmBytecodeGenerator::YieldToBlock(uint relativeDepth, EmitInfo expr) { return; } - + // If the type differs on any path, do not yield. // This will introduce some dead code on the first paths that tried to Yield a valid type if ( @@ -1414,16 +1393,16 @@ WasmBytecodeGenerator::GetLabel(uint relativeDepth) } WasmRegisterSpace * -WasmBytecodeGenerator::GetRegisterSpace(WasmTypes::WasmType type) const +WasmBytecodeGenerator::GetRegisterSpace(WasmTypes::WasmType type) { switch (type) { case WasmTypes::F32: - return m_f32RegSlots; + return &m_f32RegSlots; case WasmTypes::F64: - return m_f64RegSlots; + return &m_f64RegSlots; case WasmTypes::I32: - return m_i32RegSlots; + return &m_i32RegSlots; case WasmTypes::I64: throw WasmCompilationException(_u("I64 support NYI")); default: diff --git a/lib/WasmReader/WasmByteCodeGenerator.h b/lib/WasmReader/WasmByteCodeGenerator.h index 190b1dbdf4f..659aad0fc46 100644 --- a/lib/WasmReader/WasmByteCodeGenerator.h +++ b/lib/WasmReader/WasmByteCodeGenerator.h @@ -39,6 +39,13 @@ namespace Wasm WasmTypes::WasmType type; }; + class WasmToAsmJs + { + public: + static Js::AsmJsRetType GetAsmJsReturnType(WasmTypes::WasmType wasmType); + static Js::AsmJsVarType GetAsmJsVarType(WasmTypes::WasmType wasmType); + }; + class WasmCompilationException { void FormatError(const char16* _msg, va_list arglist); @@ -81,6 +88,27 @@ namespace Wasm typedef JsUtil::BaseDictionary WasmExportDictionary; + struct WasmReaderInfo + { + Binary::WasmBinaryReader* m_reader; + WasmFunctionInfo* m_funcInfo; + WasmModule* m_module; + }; + + class WasmModuleGenerator + { + public: + WasmModuleGenerator(Js::ScriptContext * scriptContext, Js::Utf8SourceInfo * sourceInfo, byte* binaryBuffer, uint binaryBufferLength); + WasmModule * GenerateModule(); + WasmFunction * GenerateFunctionHeader(uint32 index); + private: + Memory::Recycler* m_recycler; + Js::Utf8SourceInfo * m_sourceInfo; + Js::ScriptContext * m_scriptContext; + Binary::WasmBinaryReader* m_reader; + WasmModule * m_module; + }; + class WasmBytecodeGenerator { public: @@ -95,11 +123,11 @@ namespace Wasm static const Js::RegSlot ScriptContextBufferRegister = 4; static const Js::RegSlot ReservedRegisterCount = 5; - WasmBytecodeGenerator(Js::ScriptContext * scriptContext, Js::Utf8SourceInfo * sourceInfo, byte* binaryBuffer, uint binaryBufferLength); - WasmModule * GenerateModule(); - WasmFunction * GenerateFunction(); + WasmBytecodeGenerator(Js::ScriptContext* scriptContext, Js::FunctionBody* body, WasmReaderInfo* readerinfo); + static void GenerateFunctionBytecode(Js::ScriptContext* scriptContext, Js::FunctionBody* body, WasmReaderInfo* readerinfo); private: + void GenerateFunction(); EmitInfo EmitExpr(WasmOp op); EmitInfo EmitBlock(); @@ -144,14 +172,9 @@ namespace Wasm BlockInfo GetBlockInfo(uint relativeDepth); Js::ByteCodeLabel GetLabel(uint relativeDepth); - template - Js::RegSlot GetConstReg(T constVal); - - static Js::AsmJsRetType GetAsmJsReturnType(WasmTypes::WasmType wasmType); - static Js::AsmJsVarType GetAsmJsVarType(WasmTypes::WasmType wasmType); static Js::ArrayBufferView::ViewType GetViewType(WasmOp op); static Js::OpCodeAsmJs GetLoadOp(WasmTypes::WasmType type); - WasmRegisterSpace * GetRegisterSpace(WasmTypes::WasmType type) const; + WasmRegisterSpace * GetRegisterSpace(WasmTypes::WasmType type); EmitInfo PopEvalStack(); void PushEvalStack(EmitInfo); @@ -163,21 +186,19 @@ namespace Wasm WasmLocal * m_locals; WasmFunctionInfo * m_funcInfo; - WasmFunction * m_currentFunc; + Js::FunctionBody * m_body; WasmModule * m_module; - uint m_nestedIfLevel; uint m_maxArgOutDepth; - Binary::WasmBinaryReader m_reader; + Binary::WasmBinaryReader* m_reader; Js::AsmJsByteCodeWriter m_writer; Js::ScriptContext * m_scriptContext; - Js::Utf8SourceInfo * m_sourceInfo; - WasmRegisterSpace * m_i32RegSlots; - WasmRegisterSpace * m_f32RegSlots; - WasmRegisterSpace * m_f64RegSlots; + WasmRegisterSpace m_i32RegSlots; + WasmRegisterSpace m_f32RegSlots; + WasmRegisterSpace m_f64RegSlots; JsUtil::Stack m_blockInfos; JsUtil::Stack m_evalStack; diff --git a/lib/WasmReader/WasmFunctionInfo.cpp b/lib/WasmReader/WasmFunctionInfo.cpp index ec52713f461..fc4536731ca 100644 --- a/lib/WasmReader/WasmFunctionInfo.cpp +++ b/lib/WasmReader/WasmFunctionInfo.cpp @@ -12,13 +12,8 @@ namespace Wasm WasmFunctionInfo::WasmFunctionInfo(ArenaAllocator * alloc) : m_alloc(alloc), - m_name(nullptr), - m_mod(nullptr) + m_name(nullptr) { - m_i32Consts = Anew(m_alloc, ConstMap, m_alloc); - m_i64Consts = Anew(m_alloc, ConstMap, m_alloc); - m_f32Consts = Anew(m_alloc, ConstMap, m_alloc); - m_f64Consts = Anew(m_alloc, ConstMap, m_alloc); m_locals = Anew(m_alloc, WasmTypeArray, m_alloc, 0); } @@ -31,38 +26,6 @@ WasmFunctionInfo::AddLocal(WasmTypes::WasmType type, uint count) } } -template<> -void -WasmFunctionInfo::AddConst(int32 constVal, Js::RegSlot reg) -{ - int result = m_i32Consts->Add(constVal, reg); - Assert(result != -1); // REVIEW: should always succeed (or at least throw OOM)? -} - -template<> -void -WasmFunctionInfo::AddConst(int64 constVal, Js::RegSlot reg) -{ - int result = m_i64Consts->Add(constVal, reg); - Assert(result != -1); -} - -template<> -void -WasmFunctionInfo::AddConst(float constVal, Js::RegSlot reg) -{ - int result = m_f32Consts->Add(constVal, reg); - Assert(result != -1); -} - -template<> -void -WasmFunctionInfo::AddConst(double constVal, Js::RegSlot reg) -{ - int result = m_f64Consts->Add(constVal, reg); - Assert(result != -1); -} - WasmTypes::WasmType WasmFunctionInfo::GetLocal(uint index) const { @@ -79,34 +42,6 @@ WasmFunctionInfo::GetParam(uint index) const return m_signature->GetParam(index); } -template<> -Js::RegSlot -WasmFunctionInfo::GetConst(int32 constVal) const -{ - return m_i32Consts->Lookup(constVal, Js::Constants::NoRegister); -} - -template<> -Js::RegSlot -WasmFunctionInfo::GetConst(int64 constVal) const -{ - return m_i64Consts->Lookup(constVal, Js::Constants::NoRegister); -} - -template<> -Js::RegSlot -WasmFunctionInfo::GetConst(float constVal) const -{ - return m_f32Consts->Lookup(constVal, Js::Constants::NoRegister); -} - -template<> -Js::RegSlot -WasmFunctionInfo::GetConst(double constVal) const -{ - return m_f64Consts->Lookup(constVal, Js::Constants::NoRegister); -} - WasmTypes::WasmType WasmFunctionInfo::GetResultType() const { @@ -137,18 +72,6 @@ WasmFunctionInfo::GetName() const return m_name; } -void -WasmFunctionInfo::SetModuleName(char16* name) -{ - m_mod = name; -} - -char16* -WasmFunctionInfo::GetModuleName() const -{ - return m_mod; -} - void WasmFunctionInfo::SetNumber(UINT32 number) { diff --git a/lib/WasmReader/WasmFunctionInfo.h b/lib/WasmReader/WasmFunctionInfo.h index 41666b82bf5..7b4aa653a90 100644 --- a/lib/WasmReader/WasmFunctionInfo.h +++ b/lib/WasmReader/WasmFunctionInfo.h @@ -7,17 +7,22 @@ namespace Wasm { + struct FunctionBodyReaderInfo + { + uint32 index; + uint32 size; + intptr_t startOffset; + }; + class WasmFunctionInfo { public: WasmFunctionInfo(ArenaAllocator * alloc); void AddLocal(WasmTypes::WasmType type, uint count = 1); - template void AddConst(T constVal, Js::RegSlot reg); WasmTypes::WasmType GetLocal(uint index) const; WasmTypes::WasmType GetParam(uint index) const; - template Js::RegSlot GetConst(T constVal) const; WasmTypes::WasmType GetResultType() const; uint32 GetLocalCount() const; @@ -25,8 +30,6 @@ namespace Wasm void SetName(char16* name); char16* GetName() const; - void SetModuleName(char16* name); - char16* GetModuleName() const; void SetNumber(UINT32 number); UINT32 GetNumber() const; @@ -39,23 +42,14 @@ namespace Wasm void SetLocalName(uint i, char16* n); char16* GetLocalName(uint i); + FunctionBodyReaderInfo m_readerInfo; private: - - // TODO: need custom comparator so -0 != 0 - template - using ConstMap = JsUtil::BaseDictionary; - ConstMap * m_i32Consts; - ConstMap * m_i64Consts; - ConstMap * m_f32Consts; - ConstMap * m_f64Consts; - WasmTypeArray * m_locals; ArenaAllocator * m_alloc; WasmSignature * m_signature; Js::ByteCodeLabel m_ExitLabel; char16* m_name; - char16* m_mod; // imported module UINT32 m_number; }; @@ -66,6 +60,5 @@ namespace Wasm { } Js::FunctionBody * body; - WasmFunctionInfo * wasmInfo; }; } // namespace Wasm diff --git a/lib/WasmReader/WasmParseTree.h b/lib/WasmReader/WasmParseTree.h index 72d82966475..8c443f3f840 100644 --- a/lib/WasmReader/WasmParseTree.h +++ b/lib/WasmReader/WasmParseTree.h @@ -30,11 +30,6 @@ namespace Wasm wnNYI }; - struct WasmFuncNode - { - WasmFunctionInfo * info; - }; - struct WasmConstLitNode { union @@ -94,7 +89,6 @@ namespace Wasm { WasmVarNode var; WasmConstLitNode cnst; - WasmFuncNode func; WasmBrNode br; WasmBrTableNode brTable; WasmMemOpNode mem; diff --git a/lib/WasmReader/WasmReader.h b/lib/WasmReader/WasmReader.h index e6d2e494897..c5ab9dea54b 100644 --- a/lib/WasmReader/WasmReader.h +++ b/lib/WasmReader/WasmReader.h @@ -9,7 +9,7 @@ #include "Runtime.h" #ifdef ENABLE_WASM -#if DBG +#if ENABLE_DEBUG_CONFIG_OPTIONS #define TRACE_WASM(condition, ...) \ if (condition)\ {\ diff --git a/test/wasm/basic.js b/test/wasm/basic.js index 3f41d94e007..47540762c6e 100644 --- a/test/wasm/basic.js +++ b/test/wasm/basic.js @@ -3,11 +3,11 @@ // Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. //------------------------------------------------------------------------------------------------------- -var a = WScript.LoadWasmFile('basic.wast', {foo: function(a){print(a); return 2;}}); +var a = Wasm.instantiateModule(readbuffer('basic.wasm'), {test: {foo: function(a){print(a); return 2;}}}); print(a.exports.a(11)); print(a.exports.a(11)); var b = 0; -var c = new Int32Array(a.memory); +var c = new Int32Array(a.exports.memory); for(var i=0; i<10000; i++) { b+= c[i]; diff --git a/test/wasm/basic.wasm b/test/wasm/basic.wasm index daad5c30c84..d1bb887fd5f 100644 Binary files a/test/wasm/basic.wasm and b/test/wasm/basic.wasm differ diff --git a/test/wasm/basic.wast b/test/wasm/basic.wast index 4fc2195e5be..6dd43343804 100644 --- a/test/wasm/basic.wast +++ b/test/wasm/basic.wast @@ -2,39 +2,41 @@ ;; Copyright (C) Microsoft. All rights reserved. ;; Licensed under the MIT license. See LICENSE.txt file in the project root for full license information. ;;------------------------------------------------------------------------------------------------------- +(module + (type $t1 (func (result i32))) + (type $t2 (func (param i32) (result i32))) -(type (result i32)) -(type (param i32) (result i32)) - -(memory 16777216 16777216 1) -(func (type 0) - (return (i32.const 2)) -) -(import "foo" (type 1)) -(func (type 1) (local f32) - (if (i32.ges (i32.const 26) (i32.const 25)) - (setlocal 0 (i32.add (getlocal 0) (i32.const 4))) - ) - (setlocal 0 (i32.add (getlocal 0) (call_indirect (type 0) (i32.const 0)))) - (if_else (i32.ges (i32.const 22) (i32.const 25)) - (setlocal 0 (i32.add (getlocal 0) (i32.const 4))) - (setlocal 0 (i32.sub (getlocal 0) (i32.const 5))) + (memory 5000 5000) + (func (type $t1) + (return (i32.const 2)) ) - (block - (setlocal 0 (i32.add (getlocal 0) (i32.const 4))) - (setlocal 0 (i32.add (getlocal 0) (i32.clz (getlocal 0)))) - (setlocal 0 (i32.add (getlocal 0) (call 0))) - (br_if 0 (select (f32.neq (getlocal 1) (getlocal 1)) (i32.const 0) (i32.const 1))) - (setlocal 0 (i32.add (getlocal 0) (i32.const 4))) + (import $foo "test" "foo" (type $t2)) + (func (type $t2) (local f32) + (if (i32.ge_s (i32.const 26) (i32.const 25)) + (set_local 0 (i32.add (get_local 0) (i32.const 4))) + ) + (set_local 0 (i32.add (get_local 0) (call_indirect $t1 (i32.const 0)))) + (if_else (i32.ge_s (i32.const 22) (i32.const 25)) + (set_local 0 (i32.add (get_local 0) (i32.const 4))) + (set_local 0 (i32.sub (get_local 0) (i32.const 5))) + ) + (block + (set_local 0 (i32.add (get_local 0) (i32.const 4))) + (set_local 0 (i32.add (get_local 0) (i32.clz (get_local 0)))) + (set_local 0 (i32.add (get_local 0) (call 0))) + (br_if 0 (select (f32.ne (get_local 1) (get_local 1)) (i32.const 0) (i32.const 1))) + (set_local 0 (i32.add (get_local 0) (i32.const 4))) + ) + (call_import $foo (get_local 0)) + (i32.store (get_local 0) (i32.add (get_local 0) (i32.const 7))) + (set_local 0 (i32.load (get_local 0))) + (set_local 1 (f32.convert_s/i32 (get_local 0))) + (set_local 1 (f32.add (get_local 1) (get_local 1))) + (set_local 0 (i32.reinterpret/f32 (get_local 1))) + (set_local 0 (i32.add (get_local 0) (call_import $foo (get_local 0)))) + (return (i32.add (get_local 0) (i32.const 42))) ) - (call 1 (getlocal 0)) - (i32.store 0 10000 (getlocal 0) (i32.add (getlocal 0) (i32.const 7))) - (setlocal 0 (i32.load 0 10000 (getlocal 0))) - (setlocal 1 (f32.convert_s/i32 (getlocal 0))) - (setlocal 1 (f32.add (getlocal 1) (getlocal 1))) - (setlocal 0 (i32.reinterpret/f32 (getlocal 1))) - (setlocal 0 (i32.add (getlocal 0) (call 1 (getlocal 0)))) - (return (i32.add (getlocal 0) (i32.const 42))) + (table 0 1) + (export "a" 1) + (export "memory" memory) ) -(table 0 2) -(export "a" 2)