Skip to content
Merged
Show file tree
Hide file tree
Changes from all 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
10 changes: 9 additions & 1 deletion lib/Runtime/Base/CrossSite.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -309,7 +309,15 @@ namespace Js
if (funcInfo->GetFunctionProxy()->IsFunctionBody() &&
funcInfo->GetFunctionBody()->GetIsAsmJsFunction())
{
entryPoint = Js::AsmJsExternalEntryPoint;
AsmJsFunctionInfo* asmInfo = funcInfo->GetFunctionBody()->GetAsmJsFunctionInfo();
if (asmInfo && asmInfo->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