Ver Fonte

Always use AsmJsDefault entrypoint instead of a wasm deferred parse entry point

Michael Ferris há 7 anos atrás
pai
commit
26833effbb

+ 5 - 4
lib/Backend/NativeCodeGenerator.cpp

@@ -1567,8 +1567,7 @@ NativeCodeGenerator::GetCheckCodeGenFunction(Js::JavascriptMethod codeAddress)
     return nullptr;
 }
 
-
-Js::Var
+Js::JavascriptMethod
 NativeCodeGenerator::CheckAsmJsCodeGen(Js::ScriptFunction * function)
 {
     Assert(function);
@@ -1579,6 +1578,7 @@ NativeCodeGenerator::CheckAsmJsCodeGen(Js::ScriptFunction * function)
     Assert(scriptContext->GetThreadContext()->IsScriptActive());
     Assert(scriptContext->GetThreadContext()->IsInScript());
 
+    AssertOrFailFastMsg(!functionBody->IsWasmFunction() || functionBody->GetByteCodeCount() > 0, "Wasm function should be parsed by now");
     // Load the entry point here to validate it got changed afterwards
 
     Js::FunctionEntryPointInfo* entryPoint = function->GetFunctionEntryPointInfo();
@@ -1595,16 +1595,17 @@ NativeCodeGenerator::CheckAsmJsCodeGen(Js::ScriptFunction * function)
         {
             Output::Print(_u("Codegen not done yet for function: %s, Entrypoint is CheckAsmJsCodeGenThunk\n"), function->GetFunctionBody()->GetDisplayName());
         }
-        return reinterpret_cast<Js::Var>(functionBody->GetOriginalEntryPoint());
+        return functionBody->GetOriginalEntryPoint();
     }
     if (PHASE_TRACE1(Js::AsmjsEntryPointInfoPhase))
     {
         Output::Print(_u("CodeGen Done for function: %s, Changing Entrypoint to Full JIT\n"), function->GetFunctionBody()->GetDisplayName());
     }
     // we will need to set the functionbody external and asmjs entrypoint to the fulljit entrypoint
-    return reinterpret_cast<Js::Var>(CheckCodeGenDone(functionBody, entryPoint, function));
+    return CheckCodeGenDone(functionBody, entryPoint, function);
 }
 
+
 Js::JavascriptMethod
 NativeCodeGenerator::CheckCodeGen(Js::ScriptFunction * function)
 {

+ 1 - 1
lib/Backend/NativeCodeGenerator.h

@@ -59,7 +59,7 @@ public:
     static bool IsAsmJsCodeGenThunk(Js::JavascriptMethod codeAddress);
     static CheckCodeGenFunction GetCheckCodeGenFunction(Js::JavascriptMethod codeAddress);
     static Js::JavascriptMethod CheckCodeGen(Js::ScriptFunction * function);
-    static Js::Var CheckAsmJsCodeGen(Js::ScriptFunction * function);
+    static Js::JavascriptMethod CheckAsmJsCodeGen(Js::ScriptFunction * function);
 
 public:
     static void Jit_TransitionFromSimpleJit(void *const framePointer);

+ 2 - 5
lib/Backend/amd64/Thunks.asm

@@ -61,7 +61,7 @@ endif
 ;; NativeCodeGenerator::CheckAsmJsCodeGenThunk
 ;;============================================================================================================
 
-extrn ?CheckAsmJsCodeGen@NativeCodeGenerator@@SAPEAXPEAVScriptFunction@Js@@@Z : PROC
+extrn ?CheckAsmJsCodeGen@NativeCodeGenerator@@SAP6APEAXPEAVRecyclableObject@Js@@UCallInfo@3@ZZPEAVScriptFunction@3@@Z : PROC
 align 16
 ?CheckAsmJsCodeGenThunk@NativeCodeGenerator@@SAPEAXPEAVRecyclableObject@Js@@UCallInfo@3@ZZ PROC FRAME
         ;; save volatile registers
@@ -83,14 +83,11 @@ align 16
         movups xmmword ptr [rsp + 40h], xmm2
         movups xmmword ptr [rsp + 50h], xmm3
 
+        call ?CheckAsmJsCodeGen@NativeCodeGenerator@@SAP6APEAXPEAVRecyclableObject@Js@@UCallInfo@3@ZZPEAVScriptFunction@3@@Z
 ifdef _CONTROL_FLOW_GUARD
-        call ?CheckAsmJsCodeGen@NativeCodeGenerator@@SAPEAXPEAVScriptFunction@Js@@@Z
-
         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 ; CFG is guaranteed not to mess up rcx
-else
-        call ?CheckAsmJsCodeGen@NativeCodeGenerator@@SAPEAXPEAVScriptFunction@Js@@@Z
 endif
 
         ;EPILOGUE starts here

+ 0 - 4
lib/Common/BackendApi.h

@@ -7,11 +7,7 @@
 
 #if DYNAMIC_INTERPRETER_THUNK
 #define DefaultEntryThunk Js::InterpreterStackFrame::DelayDynamicInterpreterThunk
-#if _M_X64
 #define AsmJsDefaultEntryThunk Js::InterpreterStackFrame::AsmJsDelayDynamicInterpreterThunk
-#elif _M_IX86
-#define AsmJsDefaultEntryThunk Js::InterpreterStackFrame::DelayDynamicInterpreterThunk
-#endif
 #else
 #define DefaultEntryThunk Js::InterpreterStackFrame::InterpreterThunk
 #endif

+ 2 - 21
lib/Runtime/Base/CrossSite.cpp

@@ -357,16 +357,7 @@ namespace Js
 #ifdef ENABLE_WASM
         if (WasmScriptFunction::Is(function))
         {
-            AsmJsFunctionInfo* asmInfo = funcInfo->GetFunctionBody()->GetAsmJsFunctionInfo();
-            Assert(asmInfo);
-            if (asmInfo->IsWasmDeferredParse())
-            {
-                entryPoint = WasmLibrary::WasmDeferredParseExternalThunk;
-            }
-            else
-            {
-                entryPoint = Js::AsmJsExternalEntryPoint;
-            }
+            entryPoint = Js::AsmJsExternalEntryPoint;
         } else
 #endif
         if (funcInfo->HasBody())
@@ -414,17 +405,7 @@ namespace Js
             if (funcInfo->GetFunctionProxy()->IsFunctionBody() &&
                 funcInfo->GetFunctionBody()->GetIsAsmJsFunction())
             {
-#ifdef ENABLE_WASM
-                AsmJsFunctionInfo* asmInfo = funcInfo->GetFunctionBody()->GetAsmJsFunctionInfo();
-                if (asmInfo && asmInfo->IsWasmDeferredParse())
-                {
-                    entryPoint = WasmLibrary::WasmDeferredParseExternalThunk;
-                }
-                else
-#endif
-                {
-                    entryPoint = Js::AsmJsExternalEntryPoint;
-                }
+                entryPoint = Js::AsmJsExternalEntryPoint;
             }
             else
 #endif

+ 24 - 11
lib/Runtime/Base/FunctionBody.cpp

@@ -3564,23 +3564,33 @@ namespace Js
         JavascriptMethod directEntryPoint = this->GetDefaultEntryPointInfo()->jsMethod;
         JavascriptMethod originalEntryPoint = this->GetOriginalEntryPoint_Unchecked();
 
+        FunctionBody* body = this->GetFunctionBody();
+        Unused(body); // in some configuration
+#ifdef ASMJS_PLAT
+        if (body->GetIsAsmJsFunction())
+        {
+#ifdef ENABLE_WASM
+            if (body->IsWasmFunction() && body->GetByteCodeCount() == 0)
+            {
+                // The only valid 2 entrypoints if the function hasn't been parsed
+                return directEntryPoint == AsmJsDefaultEntryThunk || directEntryPoint == WasmLibrary::WasmLazyTrapCallback;
+            }
+#endif
+            // Entrypoints valid only for asm.js/wasm
+            if (directEntryPoint == AsmJsDefaultEntryThunk || IsAsmJsCodeGenThunk(directEntryPoint))
+            {
+                return true;
+            }
+        }
+#endif
         // Check the direct entry point to see if it is codegen thunk
         // if it is not, the background codegen thread has updated both original entry point and direct entry point
         // and they should still match, same as cases other then code gen
         return IsIntermediateCodeGenThunk(directEntryPoint) || originalEntryPoint == directEntryPoint
 #if ENABLE_PROFILE_INFO
-            || (directEntryPoint == DynamicProfileInfo::EnsureDynamicProfileInfoThunk &&
-            this->IsFunctionBody() && this->GetFunctionBody()->IsNativeOriginalEntryPoint())
-#ifdef ENABLE_WASM
-            || (GetFunctionBody()->IsWasmFunction() &&
-                (directEntryPoint == WasmLibrary::WasmDeferredParseInternalThunk || directEntryPoint == WasmLibrary::WasmLazyTrapCallback))
+            || (directEntryPoint == DynamicProfileInfo::EnsureDynamicProfileInfoThunk && body->IsNativeOriginalEntryPoint())
 #endif
-#ifdef ASMJS_PLAT
-            || (GetFunctionBody()->GetIsAsmJsFunction() && directEntryPoint == AsmJsDefaultEntryThunk)
-            || IsAsmJsCodeGenThunk(directEntryPoint)
-#endif
-#endif
-        ;
+            ;
     }
 #if defined(ENABLE_SCRIPT_PROFILING) || defined(ENABLE_SCRIPT_DEBUGGING)
     bool FunctionProxy::HasValidProfileEntryPoint() const
@@ -3695,6 +3705,8 @@ namespace Js
 #if DYNAMIC_INTERPRETER_THUNK
     void FunctionBody::GenerateDynamicInterpreterThunk()
     {
+        AssertOrFailFastMsg(!m_isWasmFunction || GetByteCodeCount() > 0, "The wasm function should have been parsed before generating the dynamic interpreter thunk");
+
         if (this->m_dynamicInterpreterThunk == nullptr)
         {
             // NOTE: Etw rundown thread may be reading this->dynamicInterpreterThunk concurrently. We don't need to synchronize
@@ -3746,6 +3758,7 @@ namespace Js
         this->EnsureDynamicProfileInfo();
 
         Assert(HasValidEntryPoint());
+        AssertMsg(!m_isWasmFunction || GetByteCodeCount() > 0, "Wasm function should be parsed by this point");
         if (InterpreterStackFrame::IsDelayDynamicInterpreterThunk(this->GetEntryPoint(entryPointInfo)))
         {
             // We are not doing code gen on this function, just change the entry point directly

+ 3 - 6
lib/Runtime/Base/ScriptContext.cpp

@@ -3970,13 +3970,10 @@ namespace Js
             functionBody->ResetEntryPoint();
             CurrentThunk = realThunk;
 
-            bool isDeferred = functionBody->GetAsmJsFunctionInfo()->IsWasmDeferredParse();
+            Js::WasmLibrary::ResetFunctionBodyDefaultEntryPoint(functionBody);
             // Make sure the function and the function body are using the same entry point
-            scriptFunction->ChangeEntryPoint(functionBody->GetDefaultEntryPointInfo(), AsmJsDefaultEntryThunk);
-            WasmLibrary::SetWasmEntryPointToInterpreter(scriptFunction, isDeferred);
-            // Reset jit status for this function
-            functionBody->SetIsAsmJsFullJitScheduled(false);
-            Assert(functionBody->HasValidEntryPoint());
+            scriptFunction->ChangeEntryPoint(functionBody->GetDefaultEntryPointInfo(), functionBody->GetDefaultEntryPointInfo()->jsMethod);
+            Assert(scriptFunction->GetFunctionEntryPointInfo()->GetIsAsmJSFunction());
         }
         else
 #endif

+ 2 - 3
lib/Runtime/Language/AsmJsTypes.h

@@ -993,10 +993,9 @@ namespace Js
         }
 
         Wasm::WasmReaderInfo* GetWasmReaderInfo() const {return mWasmReaderInfo;}
-        void SetWasmReaderInfo(Wasm::WasmReaderInfo* reader) {mWasmReaderInfo = reader;}
+        void SetWasmReaderInfo(Wasm::WasmReaderInfo* reader) { Assert(reader);  mWasmReaderInfo = reader; }
         WebAssemblyModule* GetWebAssemblyModule() const { return mWasmModule; }
-        void SetWebAssemblyModule(WebAssemblyModule * module) { mWasmModule= module; }
-        bool IsWasmDeferredParse() const { return mWasmReaderInfo != nullptr; }
+        void SetWebAssemblyModule(WebAssemblyModule * module) { mWasmModule = module; }
 #endif
     };
 };

+ 61 - 22
lib/Runtime/Language/InterpreterStackFrame.cpp

@@ -1592,31 +1592,68 @@ namespace Js
 #endif
 #endif
 
+
 #if DYNAMIC_INTERPRETER_THUNK
 #ifdef _M_IX86
+    __declspec(naked)
+        Var InterpreterStackFrame::AsmJsDelayDynamicInterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...)
+    {
+        __asm
+        {
+            push ebp;
+            mov ebp, esp;
+            push[esp + 8];    // push function object
+            call WasmLibrary::EnsureWasmEntrypoint;
+            test eax, eax;
+            jne skipThunk;
+
+            push[esp + 8];    // push function object
+            call InterpreterStackFrame::EnsureDynamicInterpreterThunk;
+skipThunk:
+#ifdef _CONTROL_FLOW_GUARD
+            // verify that the call target is valid
+            push eax;
+            mov  ecx, eax;
+            call[__guard_check_icall_fptr];
+            pop eax;
+#endif
+
+            pop ebp;
+
+            jmp eax;
+        }
+    }
+
     __declspec(naked)
         Var InterpreterStackFrame::DelayDynamicInterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...)
     {
         __asm
         {
-            push ebp
-            mov ebp, esp
-            push[esp + 8]     // push function object
+            push ebp;
+            mov ebp, esp;
+            push[esp + 8];    // push function object
             call InterpreterStackFrame::EnsureDynamicInterpreterThunk;
 
 #ifdef _CONTROL_FLOW_GUARD
             // verify that the call target is valid
-            push eax
-                mov  ecx, eax
-                call[__guard_check_icall_fptr]
-                pop eax
+            push eax;
+            mov  ecx, eax;
+            call[__guard_check_icall_fptr];
+            pop eax;
 #endif
 
-                pop ebp
+            pop ebp;
 
-                jmp eax
+            jmp eax;
         }
     }
+#elif !defined(_M_AMD64)
+    Var InterpreterStackFrame::AsmJsDelayDynamicInterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...)
+    {
+        // Asm.js only supported on x64 and x86
+        AssertOrFailFast(UNREACHED);
+        return nullptr;
+    }
 #endif
 #endif
 
@@ -1645,15 +1682,14 @@ namespace Js
 
     bool InterpreterStackFrame::IsDelayDynamicInterpreterThunk(JavascriptMethod entryPoint)
     {
-        return
+        return false
 #if DYNAMIC_INTERPRETER_THUNK
-#if _M_X64
-            entryPoint == InterpreterStackFrame::AsmJsDelayDynamicInterpreterThunk ||
+            || entryPoint == InterpreterStackFrame::DelayDynamicInterpreterThunk
+#ifdef ASMJS_PLAT
+            || entryPoint == InterpreterStackFrame::AsmJsDelayDynamicInterpreterThunk
 #endif
-            entryPoint == InterpreterStackFrame::DelayDynamicInterpreterThunk;
-#else
-            false;
 #endif
+            ;
     }
 
 #ifdef ENABLE_DEBUG_CONFIG_OPTIONS
@@ -3648,24 +3684,27 @@ namespace Js
         ScriptContext * scriptContext = function->GetScriptContext();
         Js::FunctionEntryPointInfo* entrypointInfo = (Js::FunctionEntryPointInfo*)function->GetEntryPointInfo();
         PROBE_STACK_CALL(scriptContext, function, alignedArgsSize + Js::Constants::MinStackDefault);
+        // Calling the jsMethod might change the entrypoint, adding the variable here 
+        // will save the method on the stack helping debug what really got called
+        JavascriptMethod jsMethod = entrypointInfo->jsMethod;
 
         switch (asmInfo->GetReturnType().which())
         {
         case AsmJsRetType::Void:
-            JavascriptFunction::CallAsmJsFunction<int>(function, entrypointInfo->jsMethod, m_outParams, alignedArgsSize, reg);
+            JavascriptFunction::CallAsmJsFunction<int>(function, jsMethod, m_outParams, alignedArgsSize, reg);
             break;
         case AsmJsRetType::Signed:
 
-            m_localIntSlots[returnReg] = JavascriptFunction::CallAsmJsFunction<int>(function, entrypointInfo->jsMethod, m_outParams, alignedArgsSize, reg);
+            m_localIntSlots[returnReg] = JavascriptFunction::CallAsmJsFunction<int>(function, jsMethod, m_outParams, alignedArgsSize, reg);
             break;
         case AsmJsRetType::Int64:
-            m_localInt64Slots[returnReg] = JavascriptFunction::CallAsmJsFunction<int64>(function, entrypointInfo->jsMethod, m_outParams, alignedArgsSize, reg);
+            m_localInt64Slots[returnReg] = JavascriptFunction::CallAsmJsFunction<int64>(function, jsMethod, m_outParams, alignedArgsSize, reg);
             break;
         case AsmJsRetType::Double:
-            m_localDoubleSlots[returnReg] = JavascriptFunction::CallAsmJsFunction<double>(function, entrypointInfo->jsMethod, m_outParams, alignedArgsSize, reg);
+            m_localDoubleSlots[returnReg] = JavascriptFunction::CallAsmJsFunction<double>(function, jsMethod, m_outParams, alignedArgsSize, reg);
             break;
         case AsmJsRetType::Float:
-            m_localFloatSlots[returnReg] = JavascriptFunction::CallAsmJsFunction<float>(function, entrypointInfo->jsMethod, m_outParams, alignedArgsSize, reg);
+            m_localFloatSlots[returnReg] = JavascriptFunction::CallAsmJsFunction<float>(function, jsMethod, m_outParams, alignedArgsSize, reg);
             break;
 #ifdef ENABLE_WASM_SIMD
         case AsmJsRetType::Float32x4:
@@ -3682,10 +3721,10 @@ namespace Js
 #if _WIN32 //WASM.SIMD ToDo: Enable thunk for Xplat
 #if _M_X64
             X86SIMDValue simdVal;
-            simdVal.m128_value = JavascriptFunction::CallAsmJsFunction<__m128>(function, entrypointInfo->jsMethod, m_outParams, alignedArgsSize, reg);
+            simdVal.m128_value = JavascriptFunction::CallAsmJsFunction<__m128>(function, jsMethod, m_outParams, alignedArgsSize, reg);
             m_localSimdSlots[returnReg] = X86SIMDValue::ToSIMDValue(simdVal);
 #else
-            m_localSimdSlots[returnReg] = JavascriptFunction::CallAsmJsFunction<AsmJsSIMDValue>(function, entrypointInfo->jsMethod, m_outParams, alignedArgsSize, reg);
+            m_localSimdSlots[returnReg] = JavascriptFunction::CallAsmJsFunction<AsmJsSIMDValue>(function, jsMethod, m_outParams, alignedArgsSize, reg);
 #endif
 #endif
             break;

+ 1 - 4
lib/Runtime/Language/InterpreterStackFrame.h

@@ -344,11 +344,7 @@ namespace Js
         template <typename T>
         static T AsmJsInterpreter(AsmJsCallStackLayout* layout);
         static void * GetAsmJsInterpreterEntryPoint(AsmJsCallStackLayout* stack);
-
-        static Var AsmJsDelayDynamicInterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...);
-
         static __m128 AsmJsInterpreterSimdJs(AsmJsCallStackLayout* func);
-
 #endif
 
 #ifdef ASMJS_PLAT
@@ -357,6 +353,7 @@ namespace Js
 #endif
 
 #if DYNAMIC_INTERPRETER_THUNK
+        static Var AsmJsDelayDynamicInterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...);
         static Var DelayDynamicInterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...);
         _NOINLINE static Var InterpreterThunk(JavascriptCallStackLayout* layout);
         _NOINLINE static Var StaticInterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...);

+ 1 - 0
lib/Runtime/Language/WAsmjsUtils.cpp

@@ -6,6 +6,7 @@
 #include "RuntimeLanguagePch.h"
 
 #if defined(ASMJS_PLAT) || defined(ENABLE_WASM)
+#include "InterpreterStackFrame.h"
 
 namespace WAsmJs
 {

+ 5 - 69
lib/Runtime/Language/amd64/amd64_Thunks.S

@@ -62,7 +62,12 @@ NESTED_ENTRY _ZN2Js21InterpreterStackFrame33AsmJsDelayDynamicInterpreterThunkEPN
         push r8
         push r9
 
+        call C_FUNC(_ZN2Js11WasmLibrary20EnsureWasmEntrypointEPNS_14ScriptFunctionE)
+        test rax, rax
+        jne skipThunk
+        mov rdi, [rbp-0x8]
         call C_FUNC(_ZN2Js21InterpreterStackFrame29EnsureDynamicInterpreterThunkEPNS_14ScriptFunctionE)
+skipThunk:
 
         pop r9
         pop r8
@@ -270,73 +275,4 @@ NESTED_ENTRY _ZN2Js23AsmJsExternalEntryPointEPNS_16RecyclableObjectENS_8CallInfo
         ret
 NESTED_END _ZN2Js23AsmJsExternalEntryPointEPNS_16RecyclableObjectENS_8CallInfoEz, _TEXT
 
-
-//============================================================================================================
-// WasmLibrary::WasmDeferredParseExternalThunk
-//============================================================================================================
-
-// Var WasmLibrary::WasmDeferredParseExternalThunk(RecyclableObject* function, CallInfo callInfo, ...)
-.balign 16
-NESTED_ENTRY _ZN2Js11WasmLibrary30WasmDeferredParseExternalThunkEPNS_16RecyclableObjectENS_8CallInfoEz, _TEXT, NoHandler
-        push_nonvol_reg rbp             // push rbp and adjust CFA offset
-        lea  rbp, [rsp]
-
-        set_cfa_register rbp, (2*8)     // Set to compute CFA as: rbp + 16 (sizeof: [rbp] [ReturnAddress])
-
-        // save argument registers used by custom calling convention
-        push rdi
-        push rsi
-        push rdx
-        push rcx
-        push r8
-        push r9
-
-        mov rsi, 0
-        call C_FUNC(_ZN2Js11WasmLibrary27WasmDeferredParseEntryPointEPNS_19AsmJsScriptFunctionEi)
-
-        pop r9
-        pop r8
-        pop rcx
-        pop rdx
-        pop rsi
-        pop rdi
-
-        pop_nonvol_reg rbp
-        jmp rax
-NESTED_END _ZN2Js11WasmLibrary30WasmDeferredParseExternalThunkEPNS_16RecyclableObjectENS_8CallInfoEz, _TEXT
-
-//============================================================================================================
-// WasmLibrary::WasmDeferredParseInternalThunk
-//============================================================================================================
-
-// Var WasmLibrary::WasmDeferredParseInternalThunk(RecyclableObject* function, CallInfo callInfo, ...)
-.balign 16
-NESTED_ENTRY _ZN2Js11WasmLibrary30WasmDeferredParseInternalThunkEPNS_16RecyclableObjectENS_8CallInfoEz, _TEXT, NoHandler
-        push_nonvol_reg rbp             // push rbp and adjust CFA offset
-        lea  rbp, [rsp]
-
-        set_cfa_register rbp, (2*8)     // Set to compute CFA as: rbp + 16 (sizeof: [rbp] [ReturnAddress])
-
-        // save argument registers used by custom calling convention
-        push rdi
-        push rsi
-        push rdx
-        push rcx
-        push r8
-        push r9
-
-        mov rsi, 1
-        call C_FUNC(_ZN2Js11WasmLibrary27WasmDeferredParseEntryPointEPNS_19AsmJsScriptFunctionEi)
-
-        pop r9
-        pop r8
-        pop rcx
-        pop rdx
-        pop rsi
-        pop rdi
-
-        pop_nonvol_reg rbp
-        jmp rax
-NESTED_END _ZN2Js11WasmLibrary30WasmDeferredParseInternalThunkEPNS_16RecyclableObjectENS_8CallInfoEz, _TEXT
-
 #endif // _ENABLE_DYNAMIC_THUNKS

+ 10 - 102
lib/Runtime/Language/amd64/amd64_Thunks.asm

@@ -66,6 +66,7 @@ endif
 
 ;; JavascriptMethod InterpreterStackFrame::EnsureDynamicInterpreterThunk(ScriptFunction * function)
 extrn ?EnsureDynamicInterpreterThunk@InterpreterStackFrame@Js@@CAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAVScriptFunction@2@@Z : PROC
+extrn ?EnsureWasmEntrypoint@WasmLibrary@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAVScriptFunction@2@@Z : PROC
 
 ;; Var InterpreterStackFrame::AsmJsDelayDynamicInterpreterThunk(RecyclableObject* function, CallInfo callInfo, ...)
 align 16
@@ -88,14 +89,20 @@ align 16
         movaps xmmword ptr [rsp + 30h], xmm1
         movaps xmmword ptr [rsp + 40h], xmm2
         movaps xmmword ptr [rsp + 50h], xmm3
-ifdef _CONTROL_FLOW_GUARD
+
+        ;; Make sure the wasm function has the right entrypoint
+        call ?EnsureWasmEntrypoint@WasmLibrary@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAVScriptFunction@2@@Z
+        test rax, rax
+        jne skipThunk
+        mov rcx, qword ptr [rsp + 70h]
+
         call ?EnsureDynamicInterpreterThunk@InterpreterStackFrame@Js@@CAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAVScriptFunction@2@@Z
+skipThunk:
 
+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
-else
-        call ?EnsureDynamicInterpreterThunk@InterpreterStackFrame@Js@@CAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAVScriptFunction@2@@Z
 endif
         ; restore potential floating point arguments from stack
         movaps xmm1, xmmword ptr [rsp + 30h]
@@ -452,105 +459,6 @@ endif
 
 ?AsmJsExternalEntryPoint@Js@@YAPEAXPEAVRecyclableObject@1@UCallInfo@1@ZZ ENDP
 
-;;============================================================================================================
-;; WasmLibrary::WasmDeferredParseExternalThunk
-;;============================================================================================================
-
-;;  JavascriptMethod WasmLibrary::WasmDeferredParseEntryPoint(AsmJsScriptFunction** funcPtr, int internalCall);
-extrn ?WasmDeferredParseEntryPoint@WasmLibrary@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAVAsmJsScriptFunction@2@H@Z : PROC
-
-;; Var WasmLibrary::WasmDeferredParseExternalThunk(RecyclableObject* function, CallInfo callInfo, ...)
-align 16
-?WasmDeferredParseExternalThunk@WasmLibrary@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
-        mov rdx, 0
-        call ?WasmDeferredParseEntryPoint@WasmLibrary@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAVAsmJsScriptFunction@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
-
-        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@WasmLibrary@Js@@SAPEAXPEAVRecyclableObject@2@UCallInfo@2@ZZ ENDP
-
-;;============================================================================================================
-
-;;============================================================================================================
-;; WasmLibrary::WasmDeferredParseInternalThunk
-;;============================================================================================================
-
-;;  JavascriptMethod WasmLibrary::WasmDeferredParseEntryPoint(AsmJsScriptFunction** funcPtr, int internalCall);
-extrn ?WasmDeferredParseEntryPoint@WasmLibrary@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAVAsmJsScriptFunction@2@H@Z : PROC
-
-;; Var WasmLibrary::WasmDeferredParseInternalThunk(RecyclableObject* function, CallInfo callInfo, ...)
-align 16
-?WasmDeferredParseInternalThunk@WasmLibrary@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
-        mov rdx, 1
-        call ?WasmDeferredParseEntryPoint@WasmLibrary@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAVAsmJsScriptFunction@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]
-
-        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@WasmLibrary@Js@@SAPEAXPEAVRecyclableObject@2@UCallInfo@2@ZZ ENDP
-
 ;;============================================================================================================
 
 endif ;; _ENABLE_DYNAMIC_THUNKS

+ 1 - 5
lib/Runtime/Library/ScriptFunction.cpp

@@ -222,11 +222,7 @@ using namespace Js;
             return;
         }
 
-        bool isAsmJS = false;
-        if (HasFunctionBody())
-        {
-            isAsmJS = this->GetFunctionBody()->GetIsAsmjsMode();
-        }
+        bool isAsmJS = HasFunctionBody() && this->GetFunctionBody()->GetIsAsmjsMode();
 
         // ASMJS:- for asmjs we don't need to update the entry point here as it updates the types entry point
         if (!isAsmJS)

+ 46 - 102
lib/Runtime/Library/WasmLibrary.cpp

@@ -11,7 +11,6 @@
 
 namespace Js
 {
-
     Var WasmLibrary::WasmLazyTrapCallback(RecyclableObject *callee, CallInfo, ...)
     {
         WasmScriptFunction* asmFunction = static_cast<WasmScriptFunction*>(callee);
@@ -22,128 +21,73 @@ namespace Js
         JavascriptExceptionOperators::Throw(error, scriptContext);
     }
 
-    void WasmLibrary::SetWasmEntryPointToInterpreter(Js::ScriptFunction* func, bool deferParse)
-    {
-        Assert(WasmScriptFunction::Is(func));
-        FunctionEntryPointInfo* entrypointInfo = (FunctionEntryPointInfo*)func->GetEntryPointInfo();
-        entrypointInfo->SetIsAsmJSFunction(true);
-
-        if (deferParse)
-        {
-            func->SetEntryPoint(WasmLibrary::WasmDeferredParseExternalThunk);
-            entrypointInfo->jsMethod = WasmLibrary::WasmDeferredParseInternalThunk;
-        }
-        else
-        {
-            func->SetEntryPoint(Js::AsmJsExternalEntryPoint);
-            entrypointInfo->jsMethod = AsmJsDefaultEntryThunk;
-        }
-    }
-
-#if _M_IX86
-    __declspec(naked)
-    Var WasmLibrary::WasmDeferredParseExternalThunk(RecyclableObject* function, CallInfo callInfo, ...)
-    {
-        __asm
-        {
-            push 0;
-            push [esp + 8];
-            call WasmLibrary::WasmDeferredParseEntryPoint
-#ifdef _CONTROL_FLOW_GUARD
-            // verify that the call target is valid
-            mov  ecx, eax
-            call[__guard_check_icall_fptr]
-            mov eax, ecx
-#endif
-            // 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 WasmLibrary::WasmDeferredParseInternalThunk(RecyclableObject* function, CallInfo callInfo, ...)
+    void WasmLibrary::ResetFunctionBodyDefaultEntryPoint(FunctionBody* body)
     {
-        __asm
-        {
-            push 1;
-            push [esp + 8];
-            call WasmLibrary::WasmDeferredParseEntryPoint
-#ifdef _CONTROL_FLOW_GUARD
-            // verify that the call target is valid
-            mov  ecx, eax
-            call[__guard_check_icall_fptr]
-            mov eax, ecx
-#endif
-            // Although we don't restore ESP here on WinCE, this is fine because script profiler is not shipped for WinCE.
-            jmp eax
-        }
+        body->GetDefaultFunctionEntryPointInfo()->SetIsAsmJSFunction(true);
+        body->GetDefaultFunctionEntryPointInfo()->jsMethod = AsmJsDefaultEntryThunk;
+        body->SetOriginalEntryPoint(AsmJsDefaultEntryThunk);
+        // Reset jit status for this function
+        body->SetIsAsmJsFullJitScheduled(false);
+        Assert(body->HasValidEntryPoint());
     }
-#elif defined(_M_X64)
-    // Do nothing: the implementation of WasmLibrary::WasmDeferredParseExternalThunk is declared (appropriately decorated) in
-    // Language\amd64\amd64_Thunks.asm.
-#endif // _M_IX86
-
 }
 
 #endif // ENABLE_WASM
 
-Js::JavascriptMethod Js::WasmLibrary::WasmDeferredParseEntryPoint(Js::AsmJsScriptFunction* func, int internalCall)
+Js::JavascriptMethod Js::WasmLibrary::EnsureWasmEntrypoint(Js::ScriptFunction* func)
 {
 #ifdef ENABLE_WASM
-    FunctionBody* body = func->GetFunctionBody();
-    AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo();
-    ScriptContext* scriptContext = func->GetScriptContext();
-
-    Js::FunctionEntryPointInfo * entrypointInfo = (Js::FunctionEntryPointInfo*)func->GetEntryPointInfo();
-    Wasm::WasmReaderInfo* readerInfo = info->GetWasmReaderInfo();
-    if (readerInfo)
+    if (func->GetFunctionBody()->IsWasmFunction())
     {
-        try
+        FunctionBody* body = func->GetFunctionBody();
+        AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo();
+        ScriptContext* scriptContext = func->GetScriptContext();
+
+        Js::FunctionEntryPointInfo * entrypointInfo = (Js::FunctionEntryPointInfo*)func->GetEntryPointInfo();
+        if (info->GetLazyError())
         {
-            Wasm::WasmBytecodeGenerator::GenerateFunctionBytecode(scriptContext, readerInfo);
-            func->GetDynamicType()->SetEntryPoint(Js::AsmJsExternalEntryPoint);
-            entrypointInfo->jsMethod = AsmJsDefaultEntryThunk;
-            WAsmJs::JitFunctionIfReady(func);
+            // We might have parsed this in the past and there was an error
+            entrypointInfo->jsMethod = WasmLibrary::WasmLazyTrapCallback;
         }
-        catch (Wasm::WasmCompilationException& ex)
+        else if (body->GetByteCodeCount() == 0)
         {
-            AutoFreeExceptionMessage autoCleanExceptionMessage;
-            char16* exceptionMessage = WebAssemblyModule::FormatExceptionMessage(&ex, &autoCleanExceptionMessage, readerInfo->m_module, body);
+            Wasm::WasmReaderInfo* readerInfo = info->GetWasmReaderInfo();
+            AssertOrFailFast(readerInfo);
+            try
+            {
+                Wasm::WasmBytecodeGenerator::GenerateFunctionBytecode(scriptContext, readerInfo);
+                entrypointInfo->jsMethod = AsmJsDefaultEntryThunk;
+                WAsmJs::JitFunctionIfReady(func);
+            }
+            catch (Wasm::WasmCompilationException& ex)
+            {
+                AutoFreeExceptionMessage autoCleanExceptionMessage;
+                char16* exceptionMessage = WebAssemblyModule::FormatExceptionMessage(&ex, &autoCleanExceptionMessage, readerInfo->m_module, body);
 
-            JavascriptLibrary *library = scriptContext->GetLibrary();
-            JavascriptError *pError = library->CreateWebAssemblyCompileError();
-            JavascriptError::SetErrorMessage(pError, WASMERR_WasmCompileError, exceptionMessage, scriptContext);
+                JavascriptLibrary *library = scriptContext->GetLibrary();
+                JavascriptError *pError = library->CreateWebAssemblyCompileError();
+                JavascriptError::SetErrorMessage(pError, WASMERR_WasmCompileError, exceptionMessage, scriptContext);
 
-            func->GetDynamicType()->SetEntryPoint(WasmLazyTrapCallback);
-            entrypointInfo->jsMethod = WasmLazyTrapCallback;
-            info->SetLazyError(pError);
+                entrypointInfo->jsMethod = WasmLibrary::WasmLazyTrapCallback;
+                info->SetLazyError(pError);
+            }
         }
-        info->SetWasmReaderInfo(nullptr);
-    }
-    else
-    {
-        // This can happen if another function had its type changed and then was parsed
-        // They still share the function body, so just change the entry point
-        Assert(body->GetByteCodeCount() > 0);
-        Js::JavascriptMethod externalEntryPoint = info->GetLazyError() ? WasmLazyTrapCallback : Js::AsmJsExternalEntryPoint;
-        func->GetDynamicType()->SetEntryPoint(externalEntryPoint);
-        if (body->GetIsAsmJsFullJitScheduled())
+        // The function has already been parsed, just fix up the entry point
+        else if (body->GetIsAsmJsFullJitScheduled())
         {
             Js::FunctionEntryPointInfo* defaultEntryPoint = (Js::FunctionEntryPointInfo*)body->GetDefaultEntryPointInfo();
             func->ChangeEntryPoint(defaultEntryPoint, defaultEntryPoint->jsMethod);
         }
-        else if (entrypointInfo->jsMethod == WasmLibrary::WasmDeferredParseInternalThunk)
+        else
         {
-            // The entrypointInfo is still shared even if the type has been changed
-            // However, no sibling functions changed this entry point yet, so fix it
-            entrypointInfo->jsMethod = info->GetLazyError() ? WasmLazyTrapCallback : AsmJsDefaultEntryThunk;
+            entrypointInfo->jsMethod = AsmJsDefaultEntryThunk;
         }
-    }
 
-    Assert(body->HasValidEntryPoint());
-    Js::JavascriptMethod entryPoint = internalCall ? entrypointInfo->jsMethod : func->GetDynamicType()->GetEntryPoint();
-    return entryPoint;
-#else
-    Js::Throw::InternalError();
+        Assert(body->HasValidEntryPoint());
+        Js::JavascriptMethod jsMethod = func->GetEntryPointInfo()->jsMethod;
+        // We are already in AsmJsDefaultEntryThunk so return null so it just keeps going
+        return jsMethod == AsmJsDefaultEntryThunk ? nullptr : jsMethod;
+    }
 #endif
+    return nullptr;
 }

+ 2 - 4
lib/Runtime/Library/WasmLibrary.h

@@ -9,12 +9,10 @@ namespace Js
     class WasmLibrary
     {
     public:
-        static JavascriptMethod WasmDeferredParseEntryPoint(AsmJsScriptFunction* funcPtr, int internalCall);
-        static void SetWasmEntryPointToInterpreter(Js::ScriptFunction* func, bool deferParse);
+        static JavascriptMethod EnsureWasmEntrypoint(ScriptFunction* funcPtr);
+        static void ResetFunctionBodyDefaultEntryPoint(FunctionBody* body);
 #ifdef ENABLE_WASM
         static Var WasmLazyTrapCallback(RecyclableObject *callee, CallInfo, ...);
-        static Var WasmDeferredParseInternalThunk(RecyclableObject* function, CallInfo callInfo, ...);
-        static Var WasmDeferredParseExternalThunk(RecyclableObject* function, CallInfo callInfo, ...);
 #endif
     };
 }

+ 10 - 21
lib/Runtime/Library/WebAssemblyInstance.cpp

@@ -191,31 +191,20 @@ void WebAssemblyInstance::CreateWasmFunctions(WebAssemblyModule * wasmModule, Sc
         funcObj->SetSignature(body->GetAsmJsFunctionInfo()->GetWasmSignature());
         funcObj->SetEnvironment(frameDisplay);
 
-        // Todo:: need to fix issue #2452 before we can do this,
-        // otherwise we'll change the type of the functions and cause multiple instance to not share jitted code
-        //Wasm::WasmSignature* sig = wasmFuncInfo->GetSignature();
-        //funcObj->SetPropertyWithAttributes(PropertyIds::length, JavascriptNumber::ToVar(sig->GetParamCount(), ctx), PropertyNone, nullptr);
-        //funcObj->SetPropertyWithAttributes(PropertyIds::name, JavascriptConversion::ToString(JavascriptNumber::ToVar(i, ctx), ctx), PropertyNone, nullptr);
+        Wasm::WasmSignature* sig = wasmFuncInfo->GetSignature();
+        funcObj->SetPropertyWithAttributes(PropertyIds::length, JavascriptNumber::ToVar(sig->GetParamCount(), ctx), PropertyNone, nullptr);
+        funcObj->SetPropertyWithAttributes(PropertyIds::name, JavascriptConversion::ToString(JavascriptNumber::ToVar(i, ctx), ctx), PropertyNone, nullptr);
 
         env->SetWasmFunction(i, funcObj);
 
-        if (PHASE_ENABLED(WasmDeferredPhase, body))
+        FunctionEntryPointInfo* entrypointInfo = (FunctionEntryPointInfo*)funcObj->GetEntryPointInfo();
+        AssertOrFailFast(entrypointInfo->GetIsAsmJSFunction());
+        AssertOrFailFast(!funcObj->IsCrossSiteObject());
+        funcObj->SetEntryPoint(Js::AsmJsExternalEntryPoint);
+        entrypointInfo->jsMethod = funcObj->GetFunctionInfo()->GetOriginalEntryPoint();
+        if (!PHASE_ENABLED(WasmDeferredPhase, body))
         {
-            // if we still have WasmReaderInfo we haven't yet parsed
-            if (body->GetAsmJsFunctionInfo()->GetWasmReaderInfo())
-            {
-                WasmLibrary::SetWasmEntryPointToInterpreter(funcObj, true);
-            }
-        }
-        else
-        {
-            AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo();
-            if (info->GetWasmReaderInfo())
-            {
-                WasmLibrary::SetWasmEntryPointToInterpreter(funcObj, false);
-                WAsmJs::JitFunctionIfReady(funcObj);
-                info->SetWasmReaderInfo(nullptr);
-            }
+            WAsmJs::JitFunctionIfReady(funcObj);
         }
     }
 }

+ 3 - 0
lib/WasmReader/WasmByteCodeGenerator.cpp

@@ -11,6 +11,7 @@
 #include "EmptyWasmByteCodeWriter.h"
 #include "ByteCode/ByteCodeDumper.h"
 #include "AsmJsByteCodeDumper.h"
+#include "Language/InterpreterStackFrame.h"
 
 #if DBG_DUMP
 #define DebugPrintOp(op) if (DO_WASM_TRACE_BYTECODE) { PrintOpBegin(op); }
@@ -429,6 +430,8 @@ void WasmModuleGenerator::GenerateFunctionHeader(uint32 index)
     readerInfo->m_funcInfo = wasmInfo;
     readerInfo->m_module = m_module;
 
+    Js::WasmLibrary::ResetFunctionBodyDefaultEntryPoint(body);
+
     Js::AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo();
     info->SetWasmReaderInfo(readerInfo);
     info->SetWebAssemblyModule(m_module);

+ 0 - 1
lib/WasmReader/WasmReaderInfo.h

@@ -17,6 +17,5 @@ struct WasmReaderInfo
 {
     Field(WasmFunctionInfo*) m_funcInfo;
     Field(Js::WebAssemblyModule*) m_module;
-    Field(Js::Var) m_bufferSrc;
 };
 }

+ 4 - 4
test/WasmSpec/baselines/jsapi.baseline

@@ -1,5 +1,5 @@
 Harness Status: OK
-Found 104 tests: Pass = 87 Fail = 17
+Found 104 tests: Pass = 90 Fail = 14
 Pass 'WebAssembly' data property on global object  
 Pass 'WebAssembly' object  
 Pass 'WebAssembly.(Compile|Link|Runtime)Error' data property  
@@ -23,7 +23,7 @@ Pass 'WebAssembly.Instance.prototype' object
 Pass 'WebAssembly.Instance' instance objects  
 Pass 'WebAssembly.Instance.prototype.exports' accessor property  
 Pass exports object  
-Fail Exported WebAssembly functions  assert_equals: expected 0 but got -1
+Pass Exported WebAssembly functions  
 Pass 'WebAssembly.Memory' data property  
 Pass 'WebAssembly.Memory' constructor function  
 Pass 'WebAssembly.Memory.prototype' data property  
@@ -101,6 +101,6 @@ Pass unexpected failure in assertInstantiateSuccess
 Pass unexpected failure in assertInstantiateSuccess  
 Pass unexpected failure in assertInstantiateSuccess  
 Pass unexpected failure in assertInstantiateSuccess  
-Fail Exported values have cached JS objects  assert_equals: expected "0" but got ""
-Fail Tables export cached  assert_equals: expected "0" but got ""
+Pass Exported values have cached JS objects  
+Pass Tables export cached  
 Pass WebAssembly integers are converted to JavaScript as if by ToInt32