Przeglądaj źródła

Fixes entrypoints when 2 functions not sharing the same type are being deferred parsed

Michael Ferris 9 lat temu
rodzic
commit
3293ada557

+ 57 - 34
lib/Runtime/Library/WasmLibrary.cpp

@@ -106,49 +106,72 @@ Js::JavascriptMethod Js::WasmLibrary::WasmDeferredParseEntryPoint(Js::AsmJsScrip
     AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo();
     ScriptContext* scriptContext = func->GetScriptContext();
 
-    Js::FunctionEntryPointInfo * entypointInfo = (Js::FunctionEntryPointInfo*)func->GetEntryPointInfo();
+    Js::FunctionEntryPointInfo * entrypointInfo = (Js::FunctionEntryPointInfo*)func->GetEntryPointInfo();
     Wasm::WasmReaderInfo* readerInfo = info->GetWasmReaderInfo();
-    info->SetWasmReaderInfo(nullptr);
-    try
+    if (readerInfo)
     {
-        Wasm::WasmBytecodeGenerator::GenerateFunctionBytecode(scriptContext, readerInfo);
-        func->GetDynamicType()->SetEntryPoint(Js::AsmJsExternalEntryPoint);
-        entypointInfo->jsMethod = AsmJsDefaultEntryThunk;
-        // Do MTJRC/MAIC:0 check
+        info->SetWasmReaderInfo(nullptr);
+        try
+        {
+            Wasm::WasmBytecodeGenerator::GenerateFunctionBytecode(scriptContext, readerInfo);
+            func->GetDynamicType()->SetEntryPoint(Js::AsmJsExternalEntryPoint);
+            entrypointInfo->jsMethod = AsmJsDefaultEntryThunk;
+            // Do MTJRC/MAIC:0 check
 #if ENABLE_DEBUG_CONFIG_OPTIONS
-        if (CONFIG_FLAG(ForceNative) || CONFIG_FLAG(MaxAsmJsInterpreterRunCount) == 0)
+            if (CONFIG_FLAG(ForceNative) || CONFIG_FLAG(MaxAsmJsInterpreterRunCount) == 0)
+            {
+                GenerateFunction(scriptContext->GetNativeCodeGenerator(), body, func);
+                body->SetIsAsmJsFullJitScheduled(true);
+            }
+#endif
+        }
+        catch (Wasm::WasmCompilationException& ex)
         {
-            GenerateFunction(scriptContext->GetNativeCodeGenerator(), body, func);
-            body->SetIsAsmJsFullJitScheduled(true);
+            char16* originalMessage = ex.ReleaseErrorMessage();
+            intptr_t offset = readerInfo->m_module->GetReader()->GetCurrentOffset();
+            intptr_t start = readerInfo->m_funcInfo->m_readerInfo.startOffset;
+            uint32 size = readerInfo->m_funcInfo->m_readerInfo.size;
+
+            Wasm::WasmCompilationException newEx = Wasm::WasmCompilationException(
+                _u("function %s at offset %d/%d: %s"),
+                body->GetDisplayName(),
+                offset - start,
+                size,
+                originalMessage
+            );
+            SysFreeString(originalMessage);
+            char16* msg = newEx.ReleaseErrorMessage();
+            JavascriptLibrary *library = scriptContext->GetLibrary();
+            JavascriptError *pError = library->CreateWebAssemblyCompileError();
+            JavascriptError::SetErrorMessage(pError, WASMERR_WasmCompileError, msg, scriptContext);
+
+            func->GetDynamicType()->SetEntryPoint(WasmLazyTrapCallback);
+            entrypointInfo->jsMethod = WasmLazyTrapCallback;
+            info->SetLazyError(pError);
         }
-#endif
     }
-    catch (Wasm::WasmCompilationException& ex)
+    else
     {
-        char16* originalMessage = ex.ReleaseErrorMessage();
-        intptr_t offset = readerInfo->m_module->GetReader()->GetCurrentOffset();
-        intptr_t start = readerInfo->m_funcInfo->m_readerInfo.startOffset;
-        uint32 size = readerInfo->m_funcInfo->m_readerInfo.size;
-
-        Wasm::WasmCompilationException newEx = Wasm::WasmCompilationException(
-            _u("function %s at offset %d/%d: %s"),
-            body->GetDisplayName(),
-            offset - start,
-            size,
-            originalMessage
-        );
-        SysFreeString(originalMessage);
-        char16* msg = newEx.ReleaseErrorMessage();
-        JavascriptLibrary *library = scriptContext->GetLibrary();
-        JavascriptError *pError = library->CreateWebAssemblyCompileError();
-        JavascriptError::SetErrorMessage(pError, WASMERR_WasmCompileError, msg, scriptContext);
-
-        func->GetDynamicType()->SetEntryPoint(WasmLazyTrapCallback);
-        entypointInfo->jsMethod = WasmLazyTrapCallback;
-        info->SetLazyError(pError);
+        // 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())
+        {
+            Js::FunctionEntryPointInfo* defaultEntryPoint = (Js::FunctionEntryPointInfo*)body->GetDefaultEntryPointInfo();
+            func->ChangeEntryPoint(defaultEntryPoint, defaultEntryPoint->jsMethod);
+        }
+        else if (entrypointInfo->jsMethod == WasmLibrary::WasmDeferredParseInternalThunk)
+        {
+            // 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;
+        }
     }
+
     Assert(body->HasValidEntryPoint());
-    Js::JavascriptMethod entryPoint = internalCall ? entypointInfo->jsMethod : func->GetDynamicType()->GetEntryPoint();
+    Js::JavascriptMethod entryPoint = internalCall ? entrypointInfo->jsMethod : func->GetDynamicType()->GetEntryPoint();
     return entryPoint;
 #else
     Js::Throw::InternalError();

BIN
test/wasm/binaries/bugDeferred.wasm


+ 12 - 0
test/wasm/bugs.js

@@ -31,6 +31,18 @@ async function main() {
       throw e;
     }
   }
+
+  {
+    const mod = new WebAssembly.Module(readbuffer("binaries/bugDeferred.wasm"));
+    const instance1 = new WebAssembly.Instance(mod);
+    const instance2 = new WebAssembly.Instance(mod);
+
+    // Change the type of the function on the first instance
+    instance1.exports.foo.asdf = 5;
+    instance1.exports.foo();
+    // Make sure the entrypoint has been correctly updated on the second instance
+    instance2.exports.foo();
+  }
 }
 
 main().then(() => console.log("PASSED"), console.log);

+ 7 - 0
test/wasm/wasts/bugDeferred.wast

@@ -0,0 +1,7 @@
+;;-------------------------------------------------------------------------------------------------------
+;; Copyright (C) Microsoft. All rights reserved.
+;; Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
+;;-------------------------------------------------------------------------------------------------------
+(module
+  (func (export "foo") (result i32) (i32.const 0))
+)