Skip to content

Commit 99ca8c8

Browse files
committed
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
1 parent b34feed commit 99ca8c8

15 files changed

+731
-635
lines changed

lib/Common/ConfigFlagsList.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ PHASE(All)
3838
PHASE(WasmLEB128)
3939
PHASE(WasmFunctionBody)
4040
PHASE(WasmLazyTrap)
41+
PHASE(WasmDeferred)
4142
PHASE(Asmjs)
4243
PHASE(AsmjsTmpRegisterAllocation)
4344
PHASE(AsmjsEncoder)

lib/Runtime/Base/CrossSite.cpp

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -309,7 +309,14 @@ namespace Js
309309
if (funcInfo->GetFunctionProxy()->IsFunctionBody() &&
310310
funcInfo->GetFunctionBody()->GetIsAsmJsFunction())
311311
{
312-
entryPoint = Js::AsmJsExternalEntryPoint;
312+
if (((AsmJsFunctionInfo*)funcInfo)->IsWasmDeferredParse())
313+
{
314+
entryPoint = ScriptContext::WasmDeferredParseExternalThunk;
315+
}
316+
else
317+
{
318+
entryPoint = Js::AsmJsExternalEntryPoint;
319+
}
313320
}
314321
else
315322
#endif

lib/Runtime/Base/ScriptContext.cpp

Lines changed: 113 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -1835,6 +1835,44 @@ if (!sourceList)
18351835
}
18361836
}
18371837

1838+
void WasmFunctionGenerateBytecode(AsmJsScriptFunction* func, bool propagateError)
1839+
{
1840+
FunctionBody* body = func->GetFunctionBody();
1841+
AsmJsFunctionInfo* info = body->GetAsmJsFunctionInfo();
1842+
ScriptContext* scriptContext = func->GetScriptContext();
1843+
1844+
Js::FunctionEntryPointInfo * entypointInfo = (Js::FunctionEntryPointInfo*)func->GetEntryPointInfo();
1845+
Wasm::WasmReaderInfo* readerInfo = info->GetWasmReaderInfo();
1846+
info->SetWasmReaderInfo(nullptr);
1847+
try
1848+
{
1849+
Wasm::WasmBytecodeGenerator::GenerateFunctionBytecode(scriptContext, body, readerInfo);
1850+
func->GetDynamicType()->SetEntryPoint(Js::AsmJsExternalEntryPoint);
1851+
entypointInfo->jsMethod = AsmJsDefaultEntryThunk;
1852+
// Do MTJRC/MAIC:0 check
1853+
#if ENABLE_DEBUG_CONFIG_OPTIONS
1854+
if (CONFIG_FLAG(ForceNative) || CONFIG_FLAG(MaxAsmJsInterpreterRunCount) == 0)
1855+
{
1856+
GenerateFunction(scriptContext->GetNativeCodeGenerator(), func->GetFunctionBody(), func);
1857+
}
1858+
#endif
1859+
}
1860+
catch (Wasm::WasmCompilationException ex)
1861+
{
1862+
if (propagateError)
1863+
{
1864+
throw;
1865+
}
1866+
Js::JavascriptLibrary *library = scriptContext->GetLibrary();
1867+
Js::JavascriptError *pError = library->CreateError();
1868+
Js::JavascriptError::SetErrorMessage(pError, JSERR_WasmCompileError, ex.ReleaseErrorMessage(), scriptContext);
1869+
1870+
func->GetDynamicType()->SetEntryPoint(WasmLazyTrapCallback);
1871+
entypointInfo->jsMethod = WasmLazyTrapCallback;
1872+
func->SetLazyError(pError);
1873+
}
1874+
}
1875+
18381876
void WasmLoadFunctions(Wasm::WasmModule * wasmModule, ScriptContext* ctx, Var* moduleMemoryPtr, Var* exportObj, Var* localModuleFunctions, bool* hasAnyLazyTraps)
18391877
{
18401878
FrameDisplay * frameDisplay = RecyclerNewPlus(ctx->GetRecycler(), sizeof(void*), FrameDisplay, 1);
@@ -1845,35 +1883,22 @@ if (!sourceList)
18451883
for (uint i = 0; i < wasmModule->funcCount; ++i)
18461884
{
18471885
AsmJsScriptFunction * funcObj = ctx->GetLibrary()->CreateAsmJsScriptFunction(functionArray[i]->body);
1848-
funcObj->GetDynamicType()->SetEntryPoint(AsmJsExternalEntryPoint);
18491886
funcObj->SetModuleMemory(moduleMemoryPtr);
18501887
FunctionEntryPointInfo * entypointInfo = (FunctionEntryPointInfo*)funcObj->GetEntryPointInfo();
18511888
entypointInfo->SetIsAsmJSFunction(true);
1852-
entypointInfo->jsMethod = AsmJsDefaultEntryThunk;
18531889
entypointInfo->SetModuleAddress((uintptr_t)moduleMemoryPtr);
18541890
funcObj->SetEnvironment(frameDisplay);
18551891
localModuleFunctions[i] = funcObj;
1856-
1857-
if (wasmModule->lazyTraps && wasmModule->lazyTraps[i])
1892+
1893+
if (PHASE_ON(WasmDeferredPhase, funcObj->GetFunctionBody()))
18581894
{
1859-
Assert(PHASE_ON1(WasmLazyTrapPhase));
1860-
*hasAnyLazyTraps = true;
1861-
JavascriptLibrary *library = ctx->GetLibrary();
1862-
JavascriptError *pError = library->CreateError();
1863-
JavascriptError::SetErrorMessage(pError, JSERR_WasmCompileError, wasmModule->lazyTraps[i]->ReleaseErrorMessage(), ctx);
1864-
1865-
funcObj->GetDynamicType()->SetEntryPoint(WasmLazyTrapCallback);
1866-
entypointInfo->jsMethod = WasmLazyTrapCallback;
1867-
funcObj->SetLazyError(pError);
1868-
continue;
1895+
funcObj->GetDynamicType()->SetEntryPoint(ScriptContext::WasmDeferredParseExternalThunk);
1896+
entypointInfo->jsMethod = ScriptContext::WasmDeferredParseInternalThunk;
18691897
}
1870-
// Do MTJRC/MAIC:0 check
1871-
#if ENABLE_DEBUG_CONFIG_OPTIONS
1872-
if (CONFIG_FLAG(ForceNative) || CONFIG_FLAG(MaxAsmJsInterpreterRunCount) == 0)
1898+
else
18731899
{
1874-
GenerateFunction(ctx->GetNativeCodeGenerator(), funcObj->GetFunctionBody(), funcObj);
1900+
WasmFunctionGenerateBytecode(funcObj, !PHASE_ON(WasmLazyTrapPhase, funcObj->GetFunctionBody()));
18751901
}
1876-
#endif
18771902
}
18781903
}
18791904

@@ -1996,8 +2021,7 @@ if (!sourceList)
19962021
uint funcIndex = wasmModule->info->GetIndirectFunctionIndex(i);
19972022
if (funcIndex >= wasmModule->info->GetFunctionCount())
19982023
{
1999-
// TODO: michhol give error messages
2000-
Js::Throw::InternalError();
2024+
throw Wasm::WasmCompilationException(_u("Invalid function index %U for indirect function table"), funcIndex);
20012025
}
20022026
Wasm::WasmFunctionInfo * indirFunc = wasmModule->info->GetFunSig(funcIndex);
20032027
uint sigId = indirFunc->GetSignature()->GetSignatureId();
@@ -2011,6 +2035,72 @@ if (!sourceList)
20112035
}
20122036
}
20132037

2038+
#if _M_IX86
2039+
__declspec(naked)
2040+
Var ScriptContext::WasmDeferredParseExternalThunk(RecyclableObject* function, CallInfo callInfo, ...)
2041+
{
2042+
// Register functions
2043+
__asm
2044+
{
2045+
push ebp
2046+
mov ebp, esp
2047+
lea eax, [esp + 8]
2048+
push 0
2049+
push eax
2050+
call ScriptContext::WasmDeferredParseEntryPoint
2051+
#ifdef _CONTROL_FLOW_GUARD
2052+
// verify that the call target is valid
2053+
mov ecx, eax
2054+
call[__guard_check_icall_fptr]
2055+
mov eax, ecx
2056+
#endif
2057+
pop ebp
2058+
// Although we don't restore ESP here on WinCE, this is fine because script profiler is not shipped for WinCE.
2059+
jmp eax
2060+
}
2061+
}
2062+
2063+
__declspec(naked)
2064+
Var ScriptContext::WasmDeferredParseInternalThunk(RecyclableObject* function, CallInfo callInfo, ...)
2065+
{
2066+
// Register functions
2067+
__asm
2068+
{
2069+
push ebp
2070+
mov ebp, esp
2071+
lea eax, [esp + 8]
2072+
push 1
2073+
push eax
2074+
call ScriptContext::WasmDeferredParseEntryPoint
2075+
#ifdef _CONTROL_FLOW_GUARD
2076+
// verify that the call target is valid
2077+
mov ecx, eax
2078+
call[__guard_check_icall_fptr]
2079+
mov eax, ecx
2080+
#endif
2081+
pop ebp
2082+
// Although we don't restore ESP here on WinCE, this is fine because script profiler is not shipped for WinCE.
2083+
jmp eax
2084+
}
2085+
}
2086+
#elif defined(_M_X64)
2087+
// Do nothing: the implementation of ScriptContext::WasmDeferredParseExternalThunk is declared (appropriately decorated) in
2088+
// Language\amd64\amd64_Thunks.asm.
2089+
#endif
2090+
2091+
JavascriptMethod ScriptContext::WasmDeferredParseEntryPoint(AsmJsScriptFunction** funcPtr, int internalCall)
2092+
{
2093+
AsmJsScriptFunction* func = *funcPtr;
2094+
2095+
WasmFunctionGenerateBytecode(func, false);
2096+
Js::FunctionEntryPointInfo * entypointInfo = (Js::FunctionEntryPointInfo*)func->GetEntryPointInfo();
2097+
if (internalCall)
2098+
{
2099+
return entypointInfo->jsMethod;
2100+
}
2101+
return func->GetDynamicType()->GetEntryPoint();
2102+
}
2103+
20142104
char16* lastWasmExceptionMessage = nullptr;
20152105

20162106
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)
20252115

20262116
Assert(!this->threadContext->IsScriptActive());
20272117
Assert(pse != nullptr);
2028-
Wasm::WasmBytecodeGenerator *bytecodeGen = nullptr;
2118+
Wasm::WasmModuleGenerator *bytecodeGen = nullptr;
20292119
Js::Var exportObj = nullptr;
20302120
try
20312121
{
@@ -2040,7 +2130,7 @@ if (!sourceList)
20402130
}
20412131

20422132
*ppSourceInfo = Utf8SourceInfo::New(this, (LPCUTF8)script, lengthBytes / sizeof(char16), lengthBytes, pSrcInfo, false);
2043-
bytecodeGen = HeapNew(Wasm::WasmBytecodeGenerator, this, *ppSourceInfo, (byte*)script, lengthBytes);
2133+
bytecodeGen = HeapNew(Wasm::WasmModuleGenerator, this, *ppSourceInfo, (byte*)script, lengthBytes);
20442134
wasmModule = bytecodeGen->GenerateModule();
20452135

20462136
Var* moduleMemoryPtr = RecyclerNewArrayZ(GetRecycler(), Var, wasmModule->memSize);

lib/Runtime/Base/ScriptContext.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1604,6 +1604,12 @@ namespace Js
16041604
static JavascriptMethod ProfileModeDeferredParse(ScriptFunction **function);
16051605
static Var ProfileModeDeferredParsingThunk(RecyclableObject* function, CallInfo callInfo, ...);
16061606

1607+
#ifdef ENABLE_WASM
1608+
static JavascriptMethod WasmDeferredParseEntryPoint(AsmJsScriptFunction** funcPtr, int internalCall);
1609+
static Var WasmDeferredParseInternalThunk(RecyclableObject* function, CallInfo callInfo, ...);
1610+
static Var WasmDeferredParseExternalThunk(RecyclableObject* function, CallInfo callInfo, ...);
1611+
#endif
1612+
16071613
// Thunks for deferred deserialization of function bodies from the byte code cache
16081614
static JavascriptMethod ProfileModeDeferredDeserialize(ScriptFunction* function);
16091615
static Var ProfileModeDeferredDeserializeThunk(RecyclableObject* function, CallInfo callInfo, ...);

lib/Runtime/Language/AsmJsTypes.h

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,11 @@
2424
#pragma once
2525

2626
#ifndef TEMP_DISABLE_ASMJS
27+
namespace Wasm
28+
{
29+
struct WasmReaderInfo;
30+
};
31+
2732
namespace Js
2833
{
2934
typedef uint32 uint32_t;
@@ -1016,6 +1021,7 @@ namespace Js
10161021
int mSimdConstCount, mSimdVarCount, mSimdTmpCount, mSimdByteOffset;
10171022

10181023
FunctionBody* asmJsModuleFunctionBody;
1024+
Wasm::WasmReaderInfo* mWasmReaderInfo;
10191025
public:
10201026
AsmJsFunctionInfo() : mArgCount(0),
10211027
mIntConstCount(0),
@@ -1042,7 +1048,8 @@ namespace Js
10421048
mUsesHeapBuffer(false),
10431049
mIsHeapBufferConst(false),
10441050
mArgType(nullptr),
1045-
mArgSizes(nullptr) {}
1051+
mArgSizes(nullptr),
1052+
mWasmReaderInfo(nullptr) {}
10461053
// the key is the bytecode address
10471054
typedef JsUtil::BaseDictionary<int, ptrdiff_t, Recycler> ByteCodeToTJMap;
10481055
ByteCodeToTJMap* mbyteCodeTJMap;
@@ -1137,7 +1144,9 @@ namespace Js
11371144
// Normally, heap has min size of 0x10000, but if you use ChangeHeap, min heap size is increased to 0x1000000
11381145
return offset >= 0x1000000 || (IsHeapBufferConst() && offset >= 0x10000);
11391146
}
1140-
1147+
Wasm::WasmReaderInfo* GetWasmReaderInfo() const {return mWasmReaderInfo;}
1148+
void SetWasmReaderInfo(Wasm::WasmReaderInfo* reader) {mWasmReaderInfo = reader;}
1149+
bool IsWasmDeferredParse() const { return mWasmReaderInfo != nullptr; }
11411150
};
11421151

11431152
// The asm.js spec recognizes this set of builtin SIMD functions.

lib/Runtime/Language/amd64/amd64_Thunks.asm

Lines changed: 107 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -447,6 +447,113 @@ endif
447447

448448
?AsmJsExternalEntryPoint@Js@@YAPEAXPEAVRecyclableObject@1@UCallInfo@1@ZZ ENDP
449449

450+
;;============================================================================================================
451+
;; ScriptContext::WasmDeferredParseExternalThunk
452+
;;============================================================================================================
453+
454+
;; JavascriptMethod ScriptContext::WasmDeferredParseEntryPoint(AsmJsScriptFunction** funcPtr, int internalCall);
455+
extrn ?WasmDeferredParseEntryPoint@ScriptContext@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAPEAVAsmJsScriptFunction@2@H@Z : PROC
456+
457+
;; Var ScriptContext::WasmDeferredParseExternalThunk(RecyclableObject* function, CallInfo callInfo, ...)
458+
align 16
459+
?WasmDeferredParseExternalThunk@ScriptContext@Js@@SAPEAXPEAVRecyclableObject@2@UCallInfo@2@ZZ PROC FRAME
460+
;; save volatile registers
461+
mov qword ptr [rsp + 8h], rcx
462+
mov qword ptr [rsp + 10h], rdx
463+
mov qword ptr [rsp + 18h], r8
464+
mov qword ptr [rsp + 20h], r9
465+
466+
push rbp
467+
.pushreg rbp
468+
lea rbp, [rsp]
469+
.setframe rbp, 0
470+
.endprolog
471+
472+
sub rsp, 20h
473+
lea rcx, [rsp + 30h]
474+
mov rdx, 0
475+
call ?WasmDeferredParseEntryPoint@ScriptContext@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAPEAVAsmJsScriptFunction@2@H@Z
476+
477+
ifdef _CONTROL_FLOW_GUARD
478+
mov rcx, rax ; __guard_check_icall_fptr requires the call target in rcx.
479+
call [__guard_check_icall_fptr] ; verify that the call target is valid
480+
mov rax, rcx ;restore call target
481+
endif
482+
add rsp, 20h
483+
484+
lea rsp, [rbp]
485+
pop rbp
486+
487+
;; restore volatile registers
488+
mov rcx, qword ptr [rsp + 8h]
489+
mov rdx, qword ptr [rsp + 10h]
490+
mov r8, qword ptr [rsp + 18h]
491+
mov r9, qword ptr [rsp + 20h]
492+
493+
rex_jmp_reg rax
494+
?WasmDeferredParseExternalThunk@ScriptContext@Js@@SAPEAXPEAVRecyclableObject@2@UCallInfo@2@ZZ ENDP
495+
496+
;;============================================================================================================
497+
498+
;;============================================================================================================
499+
;; ScriptContext::WasmDeferredParseInternalThunk
500+
;;============================================================================================================
501+
502+
;; JavascriptMethod ScriptContext::WasmDeferredParseEntryPoint(AsmJsScriptFunction** funcPtr, int internalCall);
503+
extrn ?WasmDeferredParseEntryPoint@ScriptContext@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAPEAVAsmJsScriptFunction@2@H@Z : PROC
504+
505+
;; Var ScriptContext::WasmDeferredParseInternalThunk(RecyclableObject* function, CallInfo callInfo, ...)
506+
align 16
507+
?WasmDeferredParseInternalThunk@ScriptContext@Js@@SAPEAXPEAVRecyclableObject@2@UCallInfo@2@ZZ PROC FRAME
508+
;; save volatile registers
509+
mov qword ptr [rsp + 8h], rcx
510+
mov qword ptr [rsp + 10h], rdx
511+
mov qword ptr [rsp + 18h], r8
512+
mov qword ptr [rsp + 20h], r9
513+
514+
push rbp
515+
.pushreg rbp
516+
lea rbp, [rsp]
517+
.setframe rbp, 0
518+
.endprolog
519+
520+
sub rsp, 60h
521+
522+
; spill potential floating point arguments to stack
523+
movaps xmmword ptr [rsp + 30h], xmm1
524+
movaps xmmword ptr [rsp + 40h], xmm2
525+
movaps xmmword ptr [rsp + 50h], xmm3
526+
527+
lea rcx, [rsp + 70h]
528+
mov rdx, 1
529+
call ?WasmDeferredParseEntryPoint@ScriptContext@Js@@SAP6APEAXPEAVRecyclableObject@2@UCallInfo@2@ZZPEAPEAVAsmJsScriptFunction@2@H@Z
530+
531+
ifdef _CONTROL_FLOW_GUARD
532+
mov rcx, rax ; __guard_check_icall_fptr requires the call target in rcx.
533+
call [__guard_check_icall_fptr] ; verify that the call target is valid
534+
mov rax, rcx ;restore call target
535+
endif
536+
537+
; restore potential floating point arguments from stack
538+
movaps xmm1, xmmword ptr [rsp + 30h]
539+
movaps xmm2, xmmword ptr [rsp + 40h]
540+
movaps xmm3, xmmword ptr [rsp + 50h]
541+
add rsp, 60h
542+
543+
lea rsp, [rbp]
544+
pop rbp
545+
546+
;; restore volatile registers
547+
mov rcx, qword ptr [rsp + 8h]
548+
mov rdx, qword ptr [rsp + 10h]
549+
mov r8, qword ptr [rsp + 18h]
550+
mov r9, qword ptr [rsp + 20h]
551+
552+
rex_jmp_reg rax
553+
?WasmDeferredParseInternalThunk@ScriptContext@Js@@SAPEAXPEAVRecyclableObject@2@UCallInfo@2@ZZ ENDP
554+
555+
;;============================================================================================================
556+
450557
endif ;; _ENABLE_DYNAMIC_THUNKS
451558

452559
;;============================================================================================================

0 commit comments

Comments
 (0)