Преглед изворни кода

Implement deferred parsing for Wasm functions.
Added new class WasmModuleGenerator which reads only what is necessary to create the wasm module.
Then WasmBytecodeGenerator only generates the bytecode for the function bodies.

Added thunks for x64/x86 for deferred parsing entry points.

Simplified logic for LazyTraps

Michael Ferris пре 9 година
родитељ
комит
5bb8947b15

+ 1 - 0
lib/Common/ConfigFlagsList.h

@@ -38,6 +38,7 @@ PHASE(All)
             PHASE(WasmLEB128)
             PHASE(WasmFunctionBody)
         PHASE(WasmLazyTrap)
+        PHASE(WasmDeferred)
     PHASE(Asmjs)
         PHASE(AsmjsTmpRegisterAllocation)
         PHASE(AsmjsEncoder)

+ 8 - 1
lib/Runtime/Base/CrossSite.cpp

@@ -309,7 +309,14 @@ namespace Js
             if (funcInfo->GetFunctionProxy()->IsFunctionBody() &&
                 funcInfo->GetFunctionBody()->GetIsAsmJsFunction())
             {
-                entryPoint = Js::AsmJsExternalEntryPoint;
+                if (((AsmJsFunctionInfo*)funcInfo)->IsWasmDeferredParse()) 
+                {
+                    entryPoint = ScriptContext::WasmDeferredParseExternalThunk;
+                }
+                else 
+                {
+                    entryPoint = Js::AsmJsExternalEntryPoint;
+                }
             }
             else
 #endif

+ 113 - 23
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)
@@ -2025,7 +2115,7 @@ if (!sourceList)
 
         Assert(!this->threadContext->IsScriptActive());
         Assert(pse != nullptr);
-        Wasm::WasmBytecodeGenerator *bytecodeGen = nullptr;
+        Wasm::WasmModuleGenerator *bytecodeGen = nullptr;
         Js::Var exportObj = nullptr;
         try
         {
@@ -2040,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);

+ 6 - 0
lib/Runtime/Base/ScriptContext.h

@@ -1605,6 +1605,12 @@ private:
         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, ...);

+ 11 - 2
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<int, ptrdiff_t, Recycler> 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.

+ 107 - 0
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
 
 ;;============================================================================================================

+ 20 - 15
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

+ 5 - 9
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<WasmSignature*, ArenaAllocator> 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;

+ 82 - 95
lib/WasmReader/WasmBinaryReader.cpp

@@ -49,22 +49,24 @@ 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(ArenaAllocator* alloc, byte* source, size_t length) :
+    m_alloc(alloc),
+    m_curFuncEnd(nullptr),
+    m_lastOp(WasmBinOp::wbLimit)
 {
-    m_moduleInfo = Anew(&m_alloc, ModuleInfo, &m_alloc);
+    m_moduleInfo = Anew(m_alloc, ModuleInfo, m_alloc);
 
     m_start = m_pc = source;
     m_end = source + length;
     m_currentSection.code = bSectInvalid;
 #if DBG_DUMP
-    m_ops = Anew(&m_alloc, OpSet, &m_alloc);
+    m_ops = Anew(m_alloc, OpSet, m_alloc);
 #endif
 }
 
 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);
 }
 
@@ -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,60 +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;
-        TRACE_WASM_DECODER(_u("Function body header: index = %u, size = %u"), i, m_funcState.size);
+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;
+
+    uint32 len = 0;
+    uint32 entryCount = LEB128(len);
+    m_funcState.count += len;
 
-        // locals
-        for (UINT32 j = 0; j < entryCount; j++)
+    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 (!Wasm::WasmTypes::IsLocalType(type))
-            {
-                ThrowDecodingError(_u("Invalid local type"));
-            }
-            m_funcState.count += len;
-            m_funcInfo->AddLocal(type, count);
-            switch (type) 
-            {
-#define WASM_LOCALTYPE(token, name) case Wasm::WasmTypes::token: TRACE_WASM_DECODER(_u("Local: type = " #name## ", count = %u"), type, count); break;
-#include "WasmKeywords.h"
-            }
+            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
@@ -419,7 +426,7 @@ WasmBinaryReader::ASTNode()
 }
 
 void
-WasmBinaryReader::ModuleHeader()
+WasmBinaryReader::ValidateModuleHeader()
 {
     uint32 magicNumber = ReadConst<UINT32>();
     uint32 version = ReadConst<UINT32>();
@@ -516,7 +523,7 @@ WasmBinaryReader::BrTableNode()
 
     m_currentNode.brTable.numTargets = LEB128(len);
     m_funcState.count += len;
-    m_currentNode.brTable.targetTable = AnewArray(&m_alloc, UINT32, m_currentNode.brTable.numTargets);
+    m_currentNode.brTable.targetTable = AnewArray(m_alloc, UINT32, m_currentNode.brTable.numTargets);
 
     for (UINT32 i = 0; i < m_currentNode.brTable.numTargets; i++)
     {
@@ -622,11 +629,12 @@ WasmBinaryReader::ReadSignatures()
 {
     UINT len = 0;
     const uint32 count = LEB128(len);
+    m_moduleInfo->SetSignatureCount(count);
     // signatures table
     for (UINT32 i = 0; i < count; i++)
     {
         TRACE_WASM_DECODER(_u("Signature #%u"), i);
-        WasmSignature * sig = Anew(&m_alloc, WasmSignature, &m_alloc);
+        WasmSignature * sig = Anew(m_alloc, WasmSignature, m_alloc);
 
         char form = ReadConst<UINT8>();
         if (form != 0x40)
@@ -653,7 +661,7 @@ WasmBinaryReader::ReadSignatures()
             type = ReadWasmType(len);
             sig->SetResultType(type);
         }
-        m_moduleInfo->AddSignature(sig);
+        m_moduleInfo->SetSignature(i, sig);
     }
 }
 
@@ -671,7 +679,8 @@ WasmBinaryReader::ReadFunctionsSignatures()
         {
             ThrowDecodingError(_u("Function signature is out of bound"));
         }
-        WasmFunctionInfo* newFunction = Anew(&m_alloc, WasmFunctionInfo, &m_alloc);
+
+        WasmFunctionInfo* newFunction = Anew(m_alloc, WasmFunctionInfo, m_alloc);
         WasmSignature* sig = m_moduleInfo->GetSignature(sigIndex);
         newFunction->SetSignature(sig);
         m_moduleInfo->SetFunSig(newFunction, iFunc);
@@ -732,7 +741,7 @@ WasmBinaryReader::ReadDataSegments()
         TRACE_WASM_DECODER(L"Data Segment #%u", i);
         UINT32 offset = LEB128(len);
         UINT32 dataByteLen = LEB128(len);
-        WasmDataSegment *dseg = Anew(&m_alloc, WasmDataSegment, &m_alloc, offset, dataByteLen, m_pc);
+        WasmDataSegment *dseg = Anew(m_alloc, WasmDataSegment, m_alloc, offset, dataByteLen, m_pc);
         CheckBytesLeft(dataByteLen);
         m_pc += dataByteLen;
         m_moduleInfo->AddDataSeg(dseg, i);
@@ -772,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
@@ -813,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)
 {
@@ -931,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);
     }
 }
 

+ 14 - 32
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(ArenaAllocator* alloc, 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 <WasmTypes::LocalType type> 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 <WasmTypes::LocalType type> void ConstNode();
+            template <typename T> 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 <typename T> T ReadConst();
-            SectionHeader ReadSectionHeader();
 
             void CheckBytesLeft(UINT bytesNeeded);
             bool EndOfFunc();
@@ -169,9 +151,9 @@ namespace Wasm
             DECLSPEC_NORETURN void ThrowDecodingError(const char16* msg, ...);
             Wasm::WasmTypes::WasmType ReadWasmType(uint32& length);
 
-            ArenaAllocator m_alloc;
+            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

+ 318 - 343
lib/WasmReader/WasmByteCodeGenerator.cpp

@@ -8,59 +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_alloc(scriptContext->GetThreadContext()->GetThreadAlloc())
+{
+    m_reader = Anew(m_alloc, Binary::WasmBinaryReader, m_alloc, 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()
 {
-    Js::AutoProfilingPhase parserProfiler(m_scriptContext, Js::WasmParserPhase);
-    Unused(parserProfiler);
-
     // 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 = Anew(m_alloc, 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<bSectLimit + 1> 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] = {
@@ -71,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 = AnewArrayZ(gen->m_alloc, 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))
             {
@@ -124,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);
     }
@@ -138,24 +179,16 @@ WasmBytecodeGenerator::GenerateModule()
     return m_module;
 }
 
-WasmFunction *
-WasmBytecodeGenerator::GenerateFunction()
+Wasm::WasmFunction * WasmModuleGenerator::GenerateFunctionHeader(uint32 index)
 {
-    Js::AutoProfilingPhase bytecodeProfiler(m_scriptContext, Js::WasmBytecodePhase);
-    Unused(bytecodeProfiler);
-
-    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 = Anew(m_alloc, WasmFunction);
 
     char16* functionName = nullptr;
     int nameLength = 0;
@@ -181,7 +214,7 @@ WasmBytecodeGenerator::GenerateFunction()
         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,
@@ -195,187 +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);
+
+    WasmReaderInfo* readerInfo = Anew(m_alloc, WasmReaderInfo);
+    readerInfo->m_reader = m_reader;
+    readerInfo->m_funcInfo = wasmInfo;
+    readerInfo->m_module = m_module;
+    
+    Js::AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo();
+    info->SetWasmReaderInfo(readerInfo);
 
-    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();
+    if (wasmInfo->GetParamCount() >= Js::Constants::InvalidArgSlot)
+    {
+        Js::Throw::OutOfMemory();
+    }
+    Js::ArgSlot paramCount = (Js::ArgSlot)wasmInfo->GetParamCount();
+    info->SetArgCount(paramCount);
 
-    // TODO: fix these bools
-    m_writer.Begin(m_currentFunc->body, &m_alloc, true, true, false);
-    try
+    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)
     {
-        try
+        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)
         {
-            if (PHASE_OFF(Js::WasmBytecodePhase, m_currentFunc->body) && PHASE_ON1(Js::WasmLazyTrapPhase)) 
-            {
-                throw WasmCompilationException(_u("Compilation skipped"));
-            }
-            m_funcInfo->SetExitLabel(m_writer.DefineLabel());
-            EnregisterLocals();
+        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);
+        }
+        argSizeArray[i] = size;
+        // REVIEW: reduce number of checked adds
+        paramSize = UInt16Math::Add(paramSize, size);
+    }
+    info->SetArgByteSize(paramSize);
+    info->SetReturnType(WasmToAsmJs::GetAsmJsReturnType(wasmInfo->GetResultType()));
 
-            Js::AutoProfilingPhase functionProfiler(m_scriptContext, Js::WasmFunctionBodyPhase);
-            Unused(functionProfiler);
+    return func;
+}
 
-            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();
+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);
+}
 
-            // 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 (...)
-        {
-            m_writer.Reset();
-            throw;
-        }
-        m_writer.MarkAsmJsLabel(m_funcInfo->GetExitLabel());
-        m_writer.EmptyAsm(Js::OpCodeAsmJs::Ret);
+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"));
+    }
+}
 
-        m_writer.End();
 
-#if DBG_DUMP
-        if (PHASE_DUMP(Js::ByteCodePhase, m_currentFunc->body))
-        {
-            Js::AsmJsByteCodeDumper::DumpBasic(m_currentFunc->body);
-        }
-#endif
+void
+WasmBytecodeGenerator::GenerateFunction()
+{
+    if (PHASE_OFF(Js::WasmBytecodePhase, m_body))
+    {
+        throw WasmCompilationException(_u("Compilation skipped"));
+    }
+    Js::AutoProfilingPhase functionProfiler(m_scriptContext, Js::WasmFunctionBodyPhase);
+    Unused(functionProfiler);
 
-        // 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);
+    m_maxArgOutDepth = 0;
 
-        Js::ArgSlot argSizeLength = max(paramCount, 3ui16);
-        info->SetArgSizeArrayLength(argSizeLength);
-        uint* argSizeArray = RecyclerNewArrayLeafZ(m_scriptContext->GetRecycler(), uint, argSizeLength);
-        info->SetArgsSizesArray(argSizeArray);
+    // TODO: fix these bools
+    m_writer.Begin(m_body, &m_alloc, true, true, false);
+    try
+    {
+        m_funcInfo->SetExitLabel(m_writer.DefineLabel());
+        EnregisterLocals();
 
-        if (m_module->memSize > 0)
+        WasmOp op = wnLIMIT;
+        EmitInfo exprInfo;
+        EnterEvalStackScope();
+        while ((op = m_reader->ReadExpr()) != wnFUNC_END)
         {
-            info->SetUsesHeapBuffer(true);
+            exprInfo = EmitExpr(op);
         }
-        if (paramCount > 0)
-        {
-            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));
-        }
-        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"));
+            }
+            uint32 arity = 0;
+            if (returnType != Wasm::WasmTypes::Void)
+            {
+                arity = 1;
             }
-            argSizeArray[i] = size;
-            // REVIEW: reduce number of checked adds
-            paramSize = UInt16Math::Add(paramSize, size);
+            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<int>(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<int>(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
@@ -415,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)
 {
@@ -512,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: \
@@ -532,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);
@@ -552,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);
@@ -582,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);
@@ -603,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);
     }
@@ -665,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"));
@@ -674,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");
@@ -684,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:
@@ -714,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"));
     }
@@ -803,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;
@@ -814,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:
@@ -848,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)
@@ -869,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();
 
@@ -877,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(); 
         // Read END
-        op = m_reader.GetLastOp();
+        op = m_reader->GetLastOp();
     }
 
     if (!WasmTypes::IsLocalType(trueExpr.type) || falseExpr.type != trueExpr.type)
@@ -910,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();
@@ -1004,8 +1034,8 @@ template<WasmOp wasmOp, WasmTypes::WasmType type>
 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();
 
@@ -1015,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));
@@ -1033,8 +1063,8 @@ template<WasmOp wasmOp, WasmTypes::WasmType type>
 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();
@@ -1046,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)
     {
@@ -1070,26 +1100,12 @@ WasmBytecodeGenerator::EmitMemStore()
     return EmitInfo(retLoc, type);
 }
 
-template<typename T>
-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"));
         }
@@ -1098,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"));
         }
@@ -1172,8 +1188,8 @@ template<WasmOp wasmOp>
 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)
@@ -1202,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)
@@ -1418,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:

+ 37 - 16
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<uint, LPCUTF8, ArenaAllocator> 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:
+        ArenaAllocator* m_alloc;
+        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 <typename T>
-        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<BlockInfo> m_blockInfos;
         JsUtil::Stack<EmitInfo> m_evalStack;

+ 1 - 78
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<int32>, m_alloc);
-    m_i64Consts = Anew(m_alloc, ConstMap<int64>, m_alloc);
-    m_f32Consts = Anew(m_alloc, ConstMap<float>, m_alloc);
-    m_f64Consts = Anew(m_alloc, ConstMap<double>, 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>(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>(int64 constVal, Js::RegSlot reg)
-{
-    int result = m_i64Consts->Add(constVal, reg);
-    Assert(result != -1);
-}
-
-template<>
-void
-WasmFunctionInfo::AddConst<float>(float constVal, Js::RegSlot reg)
-{
-    int result = m_f32Consts->Add(constVal, reg);
-    Assert(result != -1);
-}
-
-template<>
-void
-WasmFunctionInfo::AddConst<double>(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>(int32 constVal) const
-{
-    return m_i32Consts->Lookup(constVal, Js::Constants::NoRegister);
-}
-
-template<>
-Js::RegSlot
-WasmFunctionInfo::GetConst<int64>(int64 constVal) const
-{
-    return m_i64Consts->Lookup(constVal, Js::Constants::NoRegister);
-}
-
-template<>
-Js::RegSlot
-WasmFunctionInfo::GetConst<float>(float constVal) const
-{
-    return m_f32Consts->Lookup(constVal, Js::Constants::NoRegister);
-}
-
-template<>
-Js::RegSlot
-WasmFunctionInfo::GetConst<double>(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)
 {

+ 8 - 15
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 <typename T> void AddConst(T constVal, Js::RegSlot reg);
 
         WasmTypes::WasmType GetLocal(uint index) const;
         WasmTypes::WasmType GetParam(uint index) const;
-        template <typename T> 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 <typename T>
-        using ConstMap = JsUtil::BaseDictionary<T, Js::RegSlot, ArenaAllocator>;
-        ConstMap<int32> * m_i32Consts;
-        ConstMap<int64> * m_i64Consts;
-        ConstMap<float> * m_f32Consts;
-        ConstMap<double> * 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

+ 0 - 6
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;