Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 6 additions & 3 deletions lib/Common/ConfigFlagsList.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,11 +31,14 @@ PHASE(All)
PHASE(Speculation)
PHASE(GatherCodeGenData)
PHASE(Wasm)
PHASE(WasmSection)
PHASE(WasmReader)
PHASE(WasmBytecode)
PHASE(WasmLEB128)
PHASE(WasmParser)
PHASE(WasmReader)
PHASE(WasmSection)
PHASE(WasmLEB128)
PHASE(WasmFunctionBody)
PHASE(WasmLazyTrap)
PHASE(WasmDeferred)
PHASE(Asmjs)
PHASE(AsmjsTmpRegisterAllocation)
PHASE(AsmjsEncoder)
Expand Down
9 changes: 8 additions & 1 deletion lib/Runtime/Base/CrossSite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
139 changes: 116 additions & 23 deletions lib/Runtime/Base/ScriptContext.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand All @@ -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
}
}

Expand Down Expand Up @@ -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();
Expand All @@ -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.
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

what does this comment mean?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I got this from another thunk in this file, I don't really know what it means exactly either

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)
Expand All @@ -2020,9 +2110,12 @@ if (!sourceList)
pSrcInfo = this->cache->noContextGlobalSourceInfo;
}

AutoProfilingPhase wasmPhase(this, Js::WasmPhase);
Unused(wasmPhase);

Assert(!this->threadContext->IsScriptActive());
Assert(pse != nullptr);
Wasm::WasmBytecodeGenerator *bytecodeGen = nullptr;
Wasm::WasmModuleGenerator *bytecodeGen = nullptr;
Js::Var exportObj = nullptr;
try
{
Expand All @@ -2037,7 +2130,7 @@ if (!sourceList)
}

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

Var* moduleMemoryPtr = RecyclerNewArrayZ(GetRecycler(), Var, wasmModule->memSize);
Expand Down
38 changes: 38 additions & 0 deletions lib/Runtime/Base/ScriptContext.h
Original file line number Diff line number Diff line change
Expand Up @@ -1605,6 +1605,12 @@ namespace Js
static JavascriptMethod ProfileModeDeferredParse(ScriptFunction **function);
static Var ProfileModeDeferredParsingThunk(RecyclableObject* function, CallInfo callInfo, ...);

#ifdef ENABLE_WASM
static JavascriptMethod WasmDeferredParseEntryPoint(AsmJsScriptFunction** funcPtr, int internalCall);
static Var WasmDeferredParseInternalThunk(RecyclableObject* function, CallInfo callInfo, ...);
static Var WasmDeferredParseExternalThunk(RecyclableObject* function, CallInfo callInfo, ...);
#endif

// Thunks for deferred deserialization of function bodies from the byte code cache
static JavascriptMethod ProfileModeDeferredDeserialize(ScriptFunction* function);
static Var ProfileModeDeferredDeserializeThunk(RecyclableObject* function, CallInfo callInfo, ...);
Expand Down Expand Up @@ -1761,8 +1767,40 @@ namespace Js

return functionBody;
}

class AutoProfilingPhase
{
public:
AutoProfilingPhase(ScriptContext* scriptcontext, Js::Phase phase) : scriptcontext(scriptcontext), phase(phase), isPhaseComplete(false)
{
#ifdef PROFILE_EXEC
scriptcontext->ProfileBegin(phase);
#endif
}

~AutoProfilingPhase()
{
if(!this->isPhaseComplete)
{
EndProfile();
}
}

void EndProfile()
{
this->isPhaseComplete = true;
#ifdef PROFILE_EXEC
scriptcontext->ProfileEnd(phase);
#endif
}
private:
ScriptContext* scriptcontext;
Js::Phase phase;
bool isPhaseComplete;
};
}


#define BEGIN_TEMP_ALLOCATOR(allocator, scriptContext, name) \
Js::TempArenaAllocatorObject *temp##allocator = scriptContext->GetTemporaryAllocator(name); \
ArenaAllocator * allocator = temp##allocator->GetAllocator();
Expand Down
13 changes: 11 additions & 2 deletions lib/Runtime/Language/AsmJsTypes.h
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,11 @@
#pragma once

#ifndef TEMP_DISABLE_ASMJS
namespace Wasm
{
struct WasmReaderInfo;
};

namespace Js
{
typedef uint32 uint32_t;
Expand Down Expand Up @@ -1016,6 +1021,7 @@ namespace Js
int mSimdConstCount, mSimdVarCount, mSimdTmpCount, mSimdByteOffset;

FunctionBody* asmJsModuleFunctionBody;
Wasm::WasmReaderInfo* mWasmReaderInfo;
public:
AsmJsFunctionInfo() : mArgCount(0),
mIntConstCount(0),
Expand All @@ -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;
Expand Down Expand Up @@ -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.
Expand Down
7 changes: 6 additions & 1 deletion lib/Runtime/Language/InterpreterStackFrame.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2965,16 +2965,21 @@ namespace Js
uint homingAreaSize = 0;
#endif

#if DBG_DUMP
#if ENABLE_DEBUG_CONFIG_OPTIONS
const bool tracingFunc = PHASE_TRACE(AsmjsFunctionEntryPhase, functionBody);
if (tracingFunc)
{
#if DBG_DUMP
if (AsmJsCallDepth)
{
Output::Print(_u("%*c"), AsmJsCallDepth, ' ');
}
Output::Print(_u("Executing function %s("), functionBody->GetDisplayName());
++AsmJsCallDepth;
#else
Output::Print(_u("%s()\n"), functionBody->GetDisplayName());
Output::Flush();
#endif
}
#endif
uintptr_t argAddress = (uintptr_t)m_inParams;
Expand Down
Loading