blob: d418802b75b9d6aa180cc22dab38bab3a7bbfabc [file] [log] [blame]
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft Corporation and contributors. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLanguagePch.h"
#ifdef ASMJS_PLAT
#include "ByteCode/Symbol.h"
#include "ByteCode/FuncInfo.h"
#include "ByteCode/ByteCodeApi.h"
#include "ByteCode/ByteCodeWriter.h"
#include "ByteCode/ByteCodeGenerator.h"
#include "ByteCode/AsmJsByteCodeWriter.h"
#include "Language/AsmJsByteCodeGenerator.h"
#if DBG_DUMP
#include "ByteCode/ByteCodeDumper.h"
#include "ByteCode/AsmJsByteCodeDumper.h"
#endif
namespace Js
{
bool AsmJsModuleCompiler::CompileAllFunctions()
{
const int size = mFunctionArray.Count();
for (int i = 0; i < size; i++)
{
AsmJsFunc* func = mFunctionArray.Item(i);
if (!CompileFunction(func, i))
{
// an error occurred in the function, revert state on all asm.js functions
for (int j = 0; j <= i; j++)
{
RevertFunction(j);
}
return false;
}
func->Finish();
}
return true;
}
void AsmJsModuleCompiler::RevertFunction(int funcIndex)
{
AsmJsFunc* func = mFunctionArray.Item(funcIndex);
FunctionBody * funcBody = func->GetFuncBody();
funcBody->ResetByteCodeGenState();
funcBody->AddDeferParseAttribute();
funcBody->SetFunctionParsed(false);
funcBody->ResetEntryPoint();
funcBody->SetEntryPoint(funcBody->GetDefaultEntryPointInfo(), GetScriptContext()->DeferredParsingThunk);
funcBody->SetIsAsmjsMode(false);
funcBody->SetIsAsmJsFunction(false);
func->GetFncNode()->funcInfo->byteCodeFunction = func->GetFuncBody();
}
void AsmJsModuleCompiler::RevertAllFunctions()
{
for (int i = 0; i < mFunctionArray.Count(); i++)
{
RevertFunction(i);
}
}
bool AsmJsModuleCompiler::CommitFunctions()
{
const int size = mFunctionArray.Count();
// if changeHeap is defined, it must be first function, so we should skip it
for (int i = 0; i < size; i++)
{
AsmJsFunc* func = mFunctionArray.Item(i);
FunctionBody* functionBody = func->GetFuncBody();
AsmJsFunctionInfo* asmInfo = functionBody->AllocateAsmJsFunctionInfo();
if (!asmInfo->Init(func))
{
return false;
}
asmInfo->SetUsesHeapBuffer(mUsesHeapBuffer);
functionBody->CheckAndSetOutParamMaxDepth(func->GetMaxArgOutDepth());
// should be set in EmitOneFunction
Assert(functionBody->GetIsAsmjsMode());
Assert(functionBody->GetIsAsmJsFunction());
((EntryPointInfo*)functionBody->GetDefaultEntryPointInfo())->SetIsAsmJSFunction(true);
#if DBG_DUMP && defined(ASMJS_PLAT)
if (PHASE_DUMP(ByteCodePhase, functionBody))
{
AsmJsByteCodeDumper::Dump(functionBody, nullptr, func);
}
#endif
#if _M_IX86
if (PHASE_ON1(AsmJsJITTemplatePhase) && !Configuration::Global.flags.NoNative)
{
AsmJsCodeGenerator* generator = GetScriptContext()->GetAsmJsCodeGenerator();
AccumulateCompileTime();
if (!generator)
{
generator = GetScriptContext()->InitAsmJsCodeGenerator();
}
Assert(generator);
generator->CodeGen(functionBody);
AccumulateCompileTime(AsmJsCompilation::TemplateJIT);
}
#endif
}
return true;
}
bool AsmJsModuleCompiler::CommitModule()
{
FuncInfo* funcInfo = GetModuleFunctionNode()->funcInfo;
FunctionBody* functionBody = funcInfo->GetParsedFunctionBody();
AsmJsModuleInfo* asmInfo = functionBody->AllocateAsmJsModuleInfo();
if (funcInfo->byteCodeFunction->GetIsNamedFunctionExpression())
{
Assert(GetModuleFunctionNode()->pnodeName);
Assert(GetModuleFunctionNode()->pnodeName->nop == knopVarDecl);
ParseNodeVar * nameNode = GetModuleFunctionNode()->pnodeName;
if (nameNode->sym->IsInSlot(GetByteCodeGenerator(), funcInfo))
{
GetByteCodeGenerator()->AssignPropertyId(nameNode->name());
// if module is a named function expression, we may need to restore this for debugger
AsmJsClosureFunction* closure = Anew(&mAllocator, AsmJsClosureFunction, nameNode->pid, AsmJsSymbol::ClosureFunction, &mAllocator);
DefineIdentifier(nameNode->pid, closure);
}
}
int argCount = 0;
if (mBufferArgName)
{
argCount = 3;
}
else if (mForeignArgName)
{
argCount = 2;
}
else if (mStdLibArgName)
{
argCount = 1;
}
const int functionCount = mFunctionArray.Count();
const int functionTableCount = mFunctionTableArray.Count();
const int importFunctionCount = mImportFunctions.GetTotalVarCount();
asmInfo->SetFunctionCount(functionCount);
asmInfo->SetFunctionTableCount(functionTableCount);
asmInfo->SetFunctionImportCount(importFunctionCount);
asmInfo->SetVarCount(mVarCount);
asmInfo->SetVarImportCount(mVarImportCount);
asmInfo->SetArgInCount(argCount);
asmInfo->SetModuleMemory(mModuleMemory);
asmInfo->SetAsmMathBuiltinUsed(mAsmMathBuiltinUsedBV);
asmInfo->SetAsmArrayBuiltinUsed(mAsmArrayBuiltinUsedBV);
asmInfo->SetMaxHeapAccess(mMaxHeapAccess);
int varCount = 3; // 3 possible arguments
functionBody->SetInParamsCount(4); // Always set 4 inParams so the memory space is the same (globalEnv,stdlib,foreign,buffer)
functionBody->SetReportedInParamsCount(4);
functionBody->CheckAndSetConstantCount(2); // Return register + Root
functionBody->CreateConstantTable();
functionBody->CheckAndSetVarCount(varCount);
functionBody->SetIsAsmjsMode(true);
functionBody->NewObjectLiteral(); // allocate one object literal for the export object
AsmJSByteCodeGenerator::EmitEmptyByteCode(funcInfo, GetByteCodeGenerator(), GetModuleFunctionNode());
// Create export module proxy
asmInfo->SetExportFunctionIndex(mExportFuncIndex);
asmInfo->SetExportsCount(mExports.Count());
auto exportIter = mExports.GetIterator();
for (int exportIndex = 0; exportIter.IsValid(); ++exportIndex)
{
const AsmJsModuleExport& exMod = exportIter.CurrentValue();
auto ex = asmInfo->GetExport(exportIndex);
*ex.id = exMod.id;
*ex.location = exMod.location;
exportIter.MoveNext();
}
int iVar = 0, iVarImp = 0, iFunc = 0, iFuncImp = 0;
const int moduleEnvCount = mModuleEnvironment.Count();
asmInfo->InitializeSlotMap(moduleEnvCount);
auto slotMap = asmInfo->GetAsmJsSlotMap();
for (int i = 0; i < moduleEnvCount; i++)
{
AsmJsSymbol* sym = mModuleEnvironment.GetValueAt(i);
if (sym)
{
AsmJsSlot * slot = RecyclerNewLeaf(GetScriptContext()->GetRecycler(), AsmJsSlot);
slot->symType = sym->GetSymbolType();
slotMap->AddNew(sym->GetName()->GetPropertyId(), slot);
switch (sym->GetSymbolType())
{
case AsmJsSymbol::Variable:
{
AsmJsVar* var = AsmJsVar::FromSymbol(sym);
auto& modVar = asmInfo->GetVar(iVar++);
modVar.location = var->GetLocation();
modVar.type = var->GetVarType().which();
if (var->GetVarType().isInt())
{
modVar.initialiser.intInit = var->GetIntInitialiser();
}
else if (var->GetVarType().isFloat())
{
modVar.initialiser.floatInit = var->GetFloatInitialiser();
}
else if (var->GetVarType().isDouble())
{
modVar.initialiser.doubleInit = var->GetDoubleInitialiser();
}
else
{
Assert(UNREACHED);
}
modVar.isMutable = var->isMutable();
slot->location = modVar.location;
slot->varType = var->GetVarType().which();
slot->isConstVar = !modVar.isMutable;
break;
}
case AsmJsSymbol::ConstantImport:
{
AsmJsConstantImport* var = AsmJsConstantImport::FromSymbol(sym);
auto& modVar = asmInfo->GetVarImport(iVarImp++);
modVar.location = var->GetLocation();
modVar.field = var->GetField()->GetPropertyId();
modVar.type = var->GetVarType().which();
slot->location = modVar.location;
slot->varType = modVar.type;
break;
}
case AsmJsSymbol::ImportFunction:
{
AsmJsImportFunction* func = AsmJsImportFunction::FromSymbol(sym);
auto& modVar = asmInfo->GetFunctionImport(iFuncImp++);
modVar.location = func->GetFunctionIndex();
modVar.field = func->GetField()->GetPropertyId();
slot->location = modVar.location;
break;
}
case AsmJsSymbol::FuncPtrTable:
{
AsmJsFunctionTable* funcTable = AsmJsFunctionTable::FromSymbol(sym);
const uint size = funcTable->GetSize();
const RegSlot index = funcTable->GetFunctionIndex();
asmInfo->SetFunctionTableSize(index, size);
auto& modTable = asmInfo->GetFunctionTable(index);
for (uint j = 0; j < size; j++)
{
modTable.moduleFunctionIndex[j] = funcTable->GetModuleFunctionIndex(j);
}
slot->funcTableSize = size;
slot->location = index;
break;
}
case AsmJsSymbol::ModuleFunction:
{
AsmJsFunc* func = AsmJsFunc::FromSymbol(sym);
auto& modVar = asmInfo->GetFunction(iFunc++);
modVar.location = func->GetFunctionIndex();
slot->location = modVar.location;
break;
}
case AsmJsSymbol::ArrayView:
{
AsmJsArrayView * var = AsmJsArrayView::FromSymbol(sym);
slot->viewType = var->GetViewType();
break;
}
case AsmJsSymbol::ModuleArgument:
{
AsmJsModuleArg * arg = AsmJsModuleArg::FromSymbol(sym);
slot->argType = arg->GetArgType();
break;
}
// used only for module validation
case AsmJsSymbol::MathConstant:
{
AsmJsMathConst * constVar = AsmJsMathConst::FromSymbol(sym);
slot->mathConstVal = *constVar->GetVal();
break;
}
case AsmJsSymbol::MathBuiltinFunction:
{
AsmJsMathFunction * mathFunc = AsmJsMathFunction::FromSymbol(sym);
slot->builtinMathFunc = mathFunc->GetMathBuiltInFunction();
break;
}
case AsmJsSymbol::TypedArrayBuiltinFunction:
{
AsmJsTypedArrayFunction * mathFunc = AsmJsTypedArrayFunction::FromSymbol(sym);
slot->builtinArrayFunc = mathFunc->GetArrayBuiltInFunction();
break;
}
case AsmJsSymbol::ClosureFunction:
// we don't need to store any additional info in this case
break;
default:
Assume(UNREACHED);
}
}
}
return true;
}
void AsmJsModuleCompiler::ASTPrepass(ParseNodePtr pnode, AsmJsFunc * func)
{
ThreadContext::ProbeCurrentStackNoDispose(Js::Constants::MinStackByteCodeVisitor, GetByteCodeGenerator()->GetScriptContext());
if (pnode == NULL)
{
return;
}
switch (pnode->nop) {
// these first cases do the interesting work
case knopBreak:
case knopContinue:
GetByteCodeGenerator()->AddTargetStmt(pnode->AsParseNodeJump()->pnodeTarget);
break;
case knopInt:
func->AddConst<int>(pnode->AsParseNodeInt()->lw);
break;
case knopFlt:
{
const double d = pnode->AsParseNodeFloat()->dbl;
if (ParserWrapper::IsMinInt(pnode))
{
func->AddConst<int>((int)d);
}
else if (ParserWrapper::IsUnsigned(pnode))
{
func->AddConst<int>((int)(uint32)d);
}
else
{
func->AddConst<double>(d);
}
break;
}
case knopName:
{
GetByteCodeGenerator()->AssignPropertyId(pnode->name());
AsmJsSymbol * declSym = LookupIdentifier(pnode->name());
if (declSym)
{
if (AsmJsMathConst::Is(declSym))
{
AsmJsMathConst * definition = AsmJsMathConst::FromSymbol(declSym);
Assert(definition->GetType().isDouble());
func->AddConst<double>(*definition->GetVal());
}
else if (AsmJsVar::Is(declSym) && !declSym->isMutable())
{
AsmJsVar * definition = AsmJsVar::FromSymbol(declSym);
switch (definition->GetVarType().which())
{
case AsmJsVarType::Double:
func->AddConst<double>(definition->GetDoubleInitialiser());
break;
case AsmJsVarType::Float:
func->AddConst<float>(definition->GetFloatInitialiser());
break;
case AsmJsVarType::Int:
func->AddConst<int>(definition->GetIntInitialiser());
break;
default:
Assume(UNREACHED);
}
}
}
break;
}
case knopCall:
{
ASTPrepass(pnode->AsParseNodeCall()->pnodeTarget, func);
bool evalArgs = true;
if (pnode->AsParseNodeCall()->pnodeTarget->nop == knopName)
{
AsmJsFunctionDeclaration* funcDecl = this->LookupFunction(pnode->AsParseNodeCall()->pnodeTarget->name());
if (AsmJsMathFunction::IsFround(funcDecl) && pnode->AsParseNodeCall()->argCount > 0)
{
switch (pnode->AsParseNodeCall()->pnodeArgs->nop)
{
case knopFlt:
func->AddConst<float>((float)pnode->AsParseNodeCall()->pnodeArgs->AsParseNodeFloat()->dbl);
evalArgs = false;
break;
case knopInt:
func->AddConst<float>((float)pnode->AsParseNodeCall()->pnodeArgs->AsParseNodeInt()->lw);
evalArgs = false;
break;
case knopNeg:
if (pnode->AsParseNodeCall()->pnodeArgs->AsParseNodeUni()->pnode1->nop == knopInt && pnode->AsParseNodeCall()->pnodeArgs->AsParseNodeUni()->pnode1->AsParseNodeInt()->lw == 0)
{
func->AddConst<float>(-0.0f);
evalArgs = false;
break;
}
}
}
}
if (evalArgs)
{
ASTPrepass(pnode->AsParseNodeCall()->pnodeArgs, func);
}
break;
}
case knopVarDecl:
GetByteCodeGenerator()->AssignPropertyId(pnode->name());
ASTPrepass(pnode->AsParseNodeVar()->pnodeInit, func);
break;
// all the rest of the cases simply walk the AST
case knopQmark:
ASTPrepass(pnode->AsParseNodeTri()->pnode1, func);
ASTPrepass(pnode->AsParseNodeTri()->pnode2, func);
ASTPrepass(pnode->AsParseNodeTri()->pnode3, func);
break;
case knopList:
do
{
ParseNode * pnode1 = pnode->AsParseNodeBin()->pnode1;
ASTPrepass(pnode1, func);
pnode = pnode->AsParseNodeBin()->pnode2;
} while (pnode->nop == knopList);
ASTPrepass(pnode, func);
break;
case knopFor:
ASTPrepass(pnode->AsParseNodeFor()->pnodeInit, func);
ASTPrepass(pnode->AsParseNodeFor()->pnodeCond, func);
ASTPrepass(pnode->AsParseNodeFor()->pnodeIncr, func);
ASTPrepass(pnode->AsParseNodeFor()->pnodeBody, func);
break;
case knopIf:
ASTPrepass(pnode->AsParseNodeIf()->pnodeCond, func);
ASTPrepass(pnode->AsParseNodeIf()->pnodeTrue, func);
ASTPrepass(pnode->AsParseNodeIf()->pnodeFalse, func);
break;
case knopDoWhile:
case knopWhile:
ASTPrepass(pnode->AsParseNodeWhile()->pnodeCond, func);
ASTPrepass(pnode->AsParseNodeWhile()->pnodeBody, func);
break;
case knopReturn:
ASTPrepass(pnode->AsParseNodeReturn()->pnodeExpr, func);
break;
case knopBlock:
ASTPrepass(pnode->AsParseNodeBlock()->pnodeStmt, func);
break;
case knopSwitch:
ASTPrepass(pnode->AsParseNodeSwitch()->pnodeVal, func);
for (ParseNodeCase *pnodeT = pnode->AsParseNodeSwitch()->pnodeCases; NULL != pnodeT; pnodeT = pnodeT->pnodeNext)
{
ASTPrepass(pnodeT, func);
}
ASTPrepass(pnode->AsParseNodeSwitch()->pnodeBlock, func);
break;
case knopCase:
ASTPrepass(pnode->AsParseNodeCase()->pnodeExpr, func);
ASTPrepass(pnode->AsParseNodeCase()->pnodeBody, func);
break;
case knopComma:
{
ParseNode *pnode1 = pnode->AsParseNodeBin()->pnode1;
if (pnode1->nop == knopComma)
{
// avoid recursion on very large comma expressions.
ArenaAllocator *alloc = GetByteCodeGenerator()->GetAllocator();
SList<ParseNode*> *rhsStack = Anew(alloc, SList<ParseNode*>, alloc);
do {
rhsStack->Push(pnode1->AsParseNodeBin()->pnode2);
pnode1 = pnode1->AsParseNodeBin()->pnode1;
} while (pnode1->nop == knopComma);
ASTPrepass(pnode1, func);
while (!rhsStack->Empty())
{
ParseNode *pnodeRhs = rhsStack->Pop();
ASTPrepass(pnodeRhs, func);
}
Adelete(alloc, rhsStack);
}
else
{
ASTPrepass(pnode1, func);
}
ASTPrepass(pnode->AsParseNodeBin()->pnode2, func);
break;
}
default:
{
uint flags = ParseNode::Grfnop(pnode->nop);
if (flags&fnopUni)
{
ASTPrepass(pnode->AsParseNodeUni()->pnode1, func);
}
else if (flags&fnopBin)
{
ASTPrepass(pnode->AsParseNodeBin()->pnode1, func);
ASTPrepass(pnode->AsParseNodeBin()->pnode2, func);
}
break;
}
}
}
void AsmJsModuleCompiler::BindArguments(ParseNode* argList)
{
for (ParseNode* pnode = argList; pnode; pnode = pnode->AsParseNodeVar()->pnodeNext)
{
GetByteCodeGenerator()->AssignPropertyId(pnode->name());
}
}
bool AsmJsModuleCompiler::CompileFunction(AsmJsFunc * func, int funcIndex)
{
ParseNodeFnc * fncNode = func->GetFncNode();
ParseNodePtr pnodeBody = nullptr;
Assert(fncNode->nop == knopFncDecl && fncNode->funcInfo && fncNode->funcInfo->IsDeferred() && fncNode->pnodeBody == NULL);
Js::ParseableFunctionInfo* deferParseFunction = fncNode->funcInfo->byteCodeFunction;
Utf8SourceInfo * utf8SourceInfo = deferParseFunction->GetUtf8SourceInfo();
ULONG grfscr = utf8SourceInfo->GetParseFlags();
grfscr = grfscr & (~fscrGlobalCode);
func->SetOrigParseFlags(grfscr);
deferParseFunction->SetGrfscr(grfscr | (grfscr & ~fscrDeferredFncExpression));
deferParseFunction->SetSourceInfo(GetByteCodeGenerator()->GetCurrentSourceIndex(),
fncNode,
!!(grfscr & fscrEvalCode),
((grfscr & fscrDynamicCode) && !(grfscr & fscrEvalCode)));
deferParseFunction->SetInParamsCount(fncNode->funcInfo->inArgsCount);
deferParseFunction->SetReportedInParamsCount(fncNode->funcInfo->inArgsCount);
if (fncNode->pnodeBody == NULL)
{
if (!PHASE_OFF1(Js::SkipNestedDeferredPhase) && (grfscr & fscrCreateParserState) == fscrCreateParserState && deferParseFunction->GetCompileCount() == 0)
{
deferParseFunction->BuildDeferredStubs(fncNode);
}
}
deferParseFunction->SetIsAsmjsMode(true);
Parser ps(GetScriptContext(), FALSE, this->GetAllocator()->GetPageAllocator());
FunctionBody * funcBody;
ParseNodeProg * parseTree;
CompileScriptException se;
funcBody = deferParseFunction->ParseAsmJs(&ps, &se, &parseTree);
fncNode->funcInfo->byteCodeFunction = funcBody;
TRACE_BYTECODE(_u("\nDeferred parse %s\n"), funcBody->GetDisplayName());
if (parseTree && parseTree->nop == knopProg)
{
auto body = parseTree->pnodeBody;
if (body && body->nop == knopList)
{
auto fncDecl = body->AsParseNodeBin()->pnode1;
if (fncDecl && fncDecl->nop == knopFncDecl)
{
pnodeBody = fncDecl->AsParseNodeFnc()->pnodeBody;
func->SetFuncBody(funcBody);
}
}
}
GetByteCodeGenerator()->PushFuncInfo(_u("Start asm.js AST prepass"), fncNode->funcInfo);
BindArguments(fncNode->pnodeParams);
ASTPrepass(pnodeBody, func);
GetByteCodeGenerator()->PopFuncInfo(_u("End asm.js AST prepass"));
fncNode->pnodeBody = pnodeBody;
if (!pnodeBody)
{
// body should never be null if parsing succeeded
Assert(UNREACHED);
return Fail(fncNode, _u("Function should always have parse nodes"));
}
// Check if this function requires a bigger Ast
UpdateMaxAstSize(fncNode->astSize);
if (!SetupFunctionArguments(func, pnodeBody))
{
// failure message will be printed by SetupFunctionArguments
fncNode->pnodeBody = NULL;
return false;
}
if (!SetupLocalVariables(func))
{
// failure message will be printed by SetupLocalVariables
fncNode->pnodeBody = NULL;
return false;
}
// now that we have setup the function, we can generate bytecode for it
AsmJSByteCodeGenerator gen(func, this);
bool wasEmit = gen.EmitOneFunction();
fncNode->pnodeBody = NULL;
return wasEmit;
}
bool AsmJsModuleCompiler::SetupFunctionArguments(AsmJsFunc * func, ParseNodePtr pnode)
{
// Check arguments
ArgSlot numArguments = 0;
ParseNode * fncNode = func->GetFncNode();
ParseNode* argNode = ParserWrapper::FunctionArgsList(fncNode, numArguments);
if (!func->EnsureArgCount(numArguments))
{
return Fail(argNode, _u("Cannot have variable number of arguments"));
}
ArgSlot index = 0;
while (argNode)
{
if (pnode->nop != knopList)
{
return Fail(pnode, _u("Missing assignment statement for argument"));
}
if (!ParserWrapper::IsDefinition(argNode))
{
return Fail(argNode, _u("duplicate argument name not allowed"));
}
PropertyName argName = argNode->name();
if (!AsmJSCompiler::CheckIdentifier(*this, argNode, argName))
{
return false;
}
// creates the variable
AsmJsVarBase* var = func->DefineVar(argName, true);
if (!var)
{
return Fail(argNode, _u("Failed to define var"));
}
ParseNode* argDefinition = ParserWrapper::GetBinaryLeft(pnode);
if (argDefinition->nop != knopAsg)
{
return Fail(argDefinition, _u("Expecting an assignment"));
}
ParseNode* lhs = ParserWrapper::GetBinaryLeft(argDefinition);
ParseNode* rhs = ParserWrapper::GetBinaryRight(argDefinition);
#define NodeDefineThisArgument(n,var) (n->nop == knopName && ParserWrapper::VariableName(n)->GetPropertyId() == var->GetName()->GetPropertyId())
if (!NodeDefineThisArgument(lhs, var))
{
return Fail(lhs, _u("Defining wrong argument"));
}
if (rhs->nop == knopPos)
{
// unary + => double
var->SetVarType(AsmJsVarType::Double);
var->SetLocation(func->AcquireRegister<double>());
// validate stmt
ParseNode* argSym = ParserWrapper::GetUnaryNode(rhs);
if (!NodeDefineThisArgument(argSym, var))
{
return Fail(lhs, _u("Defining wrong argument"));
}
}
else if (rhs->nop == knopOr)
{
var->SetVarType(AsmJsVarType::Int);
var->SetLocation(func->AcquireRegister<int>());
ParseNode* argSym = ParserWrapper::GetBinaryLeft(rhs);
ParseNode* intSym = ParserWrapper::GetBinaryRight(rhs);
// validate stmt
if (!NodeDefineThisArgument(argSym, var))
{
return Fail(lhs, _u("Defining wrong argument"));
}
if (intSym->nop != knopInt || intSym->AsParseNodeInt()->lw != 0)
{
return Fail(lhs, _u("Or value must be 0 when defining arguments"));
}
}
else if (rhs->nop == knopCall)
{
ParseNodeCall* callNode = rhs->AsParseNodeCall();
if (callNode->pnodeTarget->nop != knopName)
{
return Fail(rhs, _u("call should be for fround"));
}
AsmJsFunctionDeclaration* funcDecl = this->LookupFunction(callNode->pnodeTarget->name());
if (!funcDecl)
return Fail(rhs, _u("Cannot resolve function for argument definition, or wrong function"));
if (AsmJsMathFunction::Is(funcDecl))
{
if (!AsmJsMathFunction::IsFround(funcDecl))
{
return Fail(rhs, _u("call should be for fround"));
}
var->SetVarType(AsmJsVarType::Float);
var->SetLocation(func->AcquireRegister<float>());
}
else
{
return Fail(rhs, _u("Wrong function used for argument definition"));
}
if (callNode->argCount == 0 || !NodeDefineThisArgument(callNode->pnodeArgs, var))
{
return Fail(lhs, _u("Defining wrong argument"));
}
}
else
{
return Fail(rhs, _u("arguments are not casted as valid Asm.js type"));
}
if (PHASE_TRACE1(ByteCodePhase))
{
Output::Print(_u(" Argument [%s] Valid"), argName->Psz());
}
if (!func->EnsureArgType(var, index++))
{
return Fail(rhs, _u("Unexpected argument type"));
}
argNode = ParserWrapper::NextVar(argNode);
pnode = ParserWrapper::GetBinaryRight(pnode);
}
func->SetBodyNode(pnode);
return true;
}
bool AsmJsModuleCompiler::SetupLocalVariables(AsmJsFunc * func)
{
ParseNodePtr pnode = func->GetBodyNode();
MathBuiltin mathBuiltin;
// define all variables
BVSparse<ArenaAllocator> initializerBV(&mAllocator);
while (pnode->nop == knopList)
{
ParseNode * varNode = ParserWrapper::GetBinaryLeft(pnode);
while (varNode && varNode->nop != knopEndCode)
{
ParseNode * decl;
if (varNode->nop == knopList)
{
decl = ParserWrapper::GetBinaryLeft(varNode);
varNode = ParserWrapper::GetBinaryRight(varNode);
}
else
{
decl = varNode;
varNode = nullptr;
}
// if we have hit a non-declaration, we are done processing the function header
if (decl->nop != knopVarDecl)
{
return true;
}
ParseNode* pnodeInit = decl->AsParseNodeVar()->pnodeInit;
AsmJsSymbol * declSym = nullptr;
bool isFroundInit = false;
if (!pnodeInit)
{
return Fail(decl, _u("The righthand side of a var declaration missing an initialization (empty)"));
}
if (pnodeInit->nop == knopName)
{
declSym = LookupIdentifier(pnodeInit->name(), func);
if ((!AsmJsVar::Is(declSym) && !AsmJsMathConst::Is(declSym)) || declSym->isMutable())
{
return Fail(decl, _u("Var declaration with non-constant"));
}
}
else if (pnodeInit->nop == knopCall)
{
if (pnodeInit->AsParseNodeCall()->pnodeTarget->nop != knopName)
{
return Fail(decl, _u("Var declaration with something else than a literal value|fround call"));
}
AsmJsFunctionDeclaration* funcDecl = this->LookupFunction(pnodeInit->AsParseNodeCall()->pnodeTarget->name());
if (!funcDecl)
return Fail(pnodeInit, _u("Cannot resolve function name"));
if (AsmJsMathFunction::Is(funcDecl))
{
if (!AsmJsMathFunction::IsFround(funcDecl) || !ParserWrapper::IsFroundNumericLiteral(pnodeInit->AsParseNodeCall()->pnodeArgs))
{
return Fail(decl, _u("Var declaration with something else than a literal value|fround call"));
}
isFroundInit = true;
}
else
{
return Fail(varNode, _u("Unknown function call on var declaration"));
}
}
else if (pnodeInit->nop != knopInt && pnodeInit->nop != knopFlt)
{
return Fail(decl, _u("Var declaration with something else than a literal value|fround call"));
}
if (!AsmJSCompiler::CheckIdentifier(*this, decl, decl->name()))
{
// CheckIdentifier will print failure message
return false;
}
AsmJsVar* var = (AsmJsVar*)func->DefineVar(decl->name(), false);
if (!var)
{
return Fail(decl, _u("Failed to define var"));
}
// If we are declaring a var that we previously used in an initializer, that value will be undefined
// so we need to throw an error.
if (initializerBV.Test(var->GetName()->GetPropertyId()))
{
return Fail(decl, _u("Cannot declare a var after using it in an initializer"));
}
RegSlot loc = Constants::NoRegister;
if (pnodeInit->nop == knopInt)
{
var->SetVarType(AsmJsVarType::Int);
var->SetLocation(func->AcquireRegister<int>());
var->SetConstInitialiser(pnodeInit->AsParseNodeInt()->lw);
loc = func->GetConstRegister<int>(pnodeInit->AsParseNodeInt()->lw);
}
else if (ParserWrapper::IsMinInt(pnodeInit))
{
var->SetVarType(AsmJsVarType::Int);
var->SetLocation(func->AcquireRegister<int>());
var->SetConstInitialiser(INT_MIN);
loc = func->GetConstRegister<int>(INT_MIN);
}
else if (ParserWrapper::IsUnsigned(pnodeInit))
{
var->SetVarType(AsmJsVarType::Int);
var->SetLocation(func->AcquireRegister<int>());
var->SetConstInitialiser((int)((uint32)pnodeInit->AsParseNodeFloat()->dbl));
loc = func->GetConstRegister<int>((uint32)pnodeInit->AsParseNodeFloat()->dbl);
}
else if (pnodeInit->nop == knopFlt)
{
if (pnodeInit->AsParseNodeFloat()->maybeInt)
{
return Fail(decl, _u("Var declaration with integer literal outside range [-2^31, 2^32)"));
}
var->SetVarType(AsmJsVarType::Double);
var->SetLocation(func->AcquireRegister<double>());
loc = func->GetConstRegister<double>(pnodeInit->AsParseNodeFloat()->dbl);
var->SetConstInitialiser(pnodeInit->AsParseNodeFloat()->dbl);
}
else if (pnodeInit->nop == knopName)
{
if (AsmJsVar::Is(declSym))
{
AsmJsVar * definition = AsmJsVar::FromSymbol(declSym);
initializerBV.Set(definition->GetName()->GetPropertyId());
switch (definition->GetVarType().which())
{
case AsmJsVarType::Double:
var->SetVarType(AsmJsVarType::Double);
var->SetLocation(func->AcquireRegister<double>());
var->SetConstInitialiser(definition->GetDoubleInitialiser());
break;
case AsmJsVarType::Float:
var->SetVarType(AsmJsVarType::Float);
var->SetLocation(func->AcquireRegister<float>());
var->SetConstInitialiser(definition->GetFloatInitialiser());
break;
case AsmJsVarType::Int:
var->SetVarType(AsmJsVarType::Int);
var->SetLocation(func->AcquireRegister<int>());
var->SetConstInitialiser(definition->GetIntInitialiser());
break;
default:
Assume(UNREACHED);
}
}
else
{
Assert(declSym->GetType() == AsmJsType::Double);
AsmJsMathConst * definition = AsmJsMathConst::FromSymbol(declSym);
var->SetVarType(AsmJsVarType::Double);
var->SetLocation(func->AcquireRegister<double>());
var->SetConstInitialiser(*definition->GetVal());
}
}
else if (pnodeInit->nop == knopCall)
{
if (isFroundInit)
{
var->SetVarType(AsmJsVarType::Float);
var->SetLocation(func->AcquireRegister<float>());
if (pnodeInit->AsParseNodeCall()->pnodeArgs->nop == knopInt)
{
int iVal = pnodeInit->AsParseNodeCall()->pnodeArgs->AsParseNodeInt()->lw;
var->SetConstInitialiser((float)iVal);
loc = func->GetConstRegister<float>((float)iVal);
}
else if (ParserWrapper::IsNegativeZero(pnodeInit->AsParseNodeCall()->pnodeArgs))
{
var->SetConstInitialiser(-0.0f);
loc = func->GetConstRegister<float>(-0.0f);
}
else
{
// note: fround((-)NumericLiteral) is explicitly allowed for any range, so we do not need to check for maybeInt
Assert(pnodeInit->AsParseNodeCall()->pnodeArgs->nop == knopFlt);
float fVal = (float)pnodeInit->AsParseNodeCall()->pnodeArgs->AsParseNodeFloat()->dbl;
var->SetConstInitialiser((float)fVal);
loc = func->GetConstRegister<float>(fVal);
}
}
else
{
Assert(UNREACHED);
}
}
if (loc == Constants::NoRegister && pnodeInit->nop != knopName)
{
return Fail(decl, _u("Cannot find Register constant for var"));
}
}
if (ParserWrapper::GetBinaryRight(pnode)->nop == knopEndCode)
{
break;
}
pnode = ParserWrapper::GetBinaryRight(pnode);
}
return true;
}
AsmJsFunc* AsmJsModuleCompiler::CreateNewFunctionEntry(ParseNodeFnc* pnodeFnc)
{
PropertyName name = ParserWrapper::FunctionName(pnodeFnc);
if (!name)
{
return nullptr;
}
GetByteCodeGenerator()->AssignPropertyId(name);
AsmJsFunc* func = Anew(&mAllocator, AsmJsFunc, name, pnodeFnc, &mAllocator, mCx->scriptContext);
if (func)
{
if (DefineIdentifier(name, func))
{
uint index = (uint)mFunctionArray.Count();
if (pnodeFnc->nestedIndex != index)
{
return nullptr;
}
func->SetFunctionIndex((RegSlot)index);
mFunctionArray.Add(func);
Assert(index + 1 == (uint)mFunctionArray.Count());
return func;
}
// Error adding function
mAllocator.Free(func, sizeof(AsmJsFunc));
}
// Error allocating a new function
return nullptr;
}
bool AsmJsModuleCompiler::CheckByteLengthCall(ParseNode * callNode, ParseNode * bufferDecl)
{
if (callNode->nop != knopCall || callNode->AsParseNodeCall()->pnodeTarget->nop != knopName)
{
return false;
}
AsmJsTypedArrayFunction* arrayFunc = LookupIdentifier<AsmJsTypedArrayFunction>(callNode->AsParseNodeCall()->pnodeTarget->name());
if (!arrayFunc)
{
return false;
}
return callNode->AsParseNodeCall()->argCount == 1 &&
!callNode->AsParseNodeCall()->isApplyCall &&
!callNode->AsParseNodeCall()->isEvalCall &&
callNode->AsParseNodeCall()->spreadArgCount == 0 &&
arrayFunc->GetArrayBuiltInFunction() == AsmJSTypedArrayBuiltin_byteLength &&
callNode->AsParseNodeCall()->pnodeArgs->nop == knopName &&
callNode->AsParseNodeCall()->pnodeArgs->name()->GetPropertyId() == bufferDecl->name()->GetPropertyId();
}
bool AsmJsModuleCompiler::Fail(ParseNode* usepn, const wchar *error)
{
AsmJSCompiler::OutputError(GetScriptContext(), error);
return false;
}
bool AsmJsModuleCompiler::FailName(ParseNode *usepn, const wchar *fmt, PropertyName name)
{
AsmJSCompiler::OutputError(GetScriptContext(), fmt, name->Psz());
return false;
}
bool AsmJsModuleCompiler::LookupStandardLibraryMathName(PropertyName name, MathBuiltin *mathBuiltin) const
{
return mStandardLibraryMathNames.TryGetValue(name->GetPropertyId(), mathBuiltin);
}
bool AsmJsModuleCompiler::LookupStandardLibraryArrayName(PropertyName name, TypedArrayBuiltin *builtin) const
{
return mStandardLibraryArrayNames.TryGetValue(name->GetPropertyId(), builtin);
}
void AsmJsModuleCompiler::InitBufferArgName(PropertyName n)
{
#if DBG
Assert(!mBufferArgNameInit);
mBufferArgNameInit = true;
#endif
mBufferArgName = n;
}
void AsmJsModuleCompiler::InitForeignArgName(PropertyName n)
{
#if DBG
Assert(!mForeignArgNameInit);
mForeignArgNameInit = true;
#endif
mForeignArgName = n;
}
void AsmJsModuleCompiler::InitStdLibArgName(PropertyName n)
{
#if DBG
Assert(!mStdLibArgNameInit);
mStdLibArgNameInit = true;
#endif
mStdLibArgName = n;
}
Js::PropertyName AsmJsModuleCompiler::GetStdLibArgName() const
{
#if DBG
Assert(mBufferArgNameInit);
#endif
return mStdLibArgName;
}
Js::PropertyName AsmJsModuleCompiler::GetForeignArgName() const
{
#if DBG
Assert(mForeignArgNameInit);
#endif
return mForeignArgName;
}
Js::PropertyName AsmJsModuleCompiler::GetBufferArgName() const
{
#if DBG
Assert(mStdLibArgNameInit);
#endif
return mBufferArgName;
}
bool AsmJsModuleCompiler::Init()
{
if (mInitialised)
{
return false;
}
mInitialised = true;
struct MathFunc
{
MathFunc(PropertyId id_ = 0, AsmJsMathFunction* val_ = nullptr) :
id(id_), val(val_)
{
}
PropertyId id;
AsmJsMathFunction* val;
};
MathFunc mathFunctions[AsmJSMathBuiltinFunction_COUNT];
// we could move the mathBuiltinFuncname to MathFunc struct
mathFunctions[AsmJSMathBuiltin_sin] = MathFunc(PropertyIds::sin, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_sin, OpCodeAsmJs::Sin_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_cos] = MathFunc(PropertyIds::cos, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_cos, OpCodeAsmJs::Cos_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_tan] = MathFunc(PropertyIds::tan, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_tan, OpCodeAsmJs::Tan_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_asin] = MathFunc(PropertyIds::asin, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_asin, OpCodeAsmJs::Asin_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_acos] = MathFunc(PropertyIds::acos, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_acos, OpCodeAsmJs::Acos_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_atan] = MathFunc(PropertyIds::atan, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_atan, OpCodeAsmJs::Atan_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_ceil] = MathFunc(PropertyIds::ceil, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_ceil, OpCodeAsmJs::Ceil_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_floor] = MathFunc(PropertyIds::floor, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_floor, OpCodeAsmJs::Floor_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_exp] = MathFunc(PropertyIds::exp, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_exp, OpCodeAsmJs::Exp_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_log] = MathFunc(PropertyIds::log, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_log, OpCodeAsmJs::Log_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_pow] = MathFunc(PropertyIds::pow, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 2, AsmJSMathBuiltin_pow, OpCodeAsmJs::Pow_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_sqrt] = MathFunc(PropertyIds::sqrt, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_sqrt, OpCodeAsmJs::Sqrt_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_abs] = MathFunc(PropertyIds::abs, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_abs, OpCodeAsmJs::Abs_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_atan2] = MathFunc(PropertyIds::atan2, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 2, AsmJSMathBuiltin_atan2, OpCodeAsmJs::Atan2_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_imul] = MathFunc(PropertyIds::imul, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 2, AsmJSMathBuiltin_imul, OpCodeAsmJs::Imul_Int, AsmJsRetType::Signed, AsmJsType::Intish, AsmJsType::Intish));
mathFunctions[AsmJSMathBuiltin_fround] = MathFunc(PropertyIds::fround, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_fround, OpCodeAsmJs::Fround_Flt, AsmJsRetType::Float, AsmJsType::Floatish));
mathFunctions[AsmJSMathBuiltin_min] = MathFunc(PropertyIds::min, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 2, AsmJSMathBuiltin_min, OpCodeAsmJs::Min_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_max] = MathFunc(PropertyIds::max, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 2, AsmJSMathBuiltin_max, OpCodeAsmJs::Max_Db, AsmJsRetType::Double, AsmJsType::MaybeDouble, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_clz32] = MathFunc(PropertyIds::clz32, Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_clz32, OpCodeAsmJs::Clz32_Int, AsmJsRetType::Fixnum, AsmJsType::Intish));
mathFunctions[AsmJSMathBuiltin_abs].val->SetOverload(Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_abs, OpCodeAsmJs::Abs_Int, AsmJsRetType::Unsigned, AsmJsType::Signed));
mathFunctions[AsmJSMathBuiltin_min].val->SetOverload(Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 2, AsmJSMathBuiltin_min, OpCodeAsmJs::Min_Int, AsmJsRetType::Signed, AsmJsType::Signed, AsmJsType::Signed));
mathFunctions[AsmJSMathBuiltin_max].val->SetOverload(Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 2, AsmJSMathBuiltin_max, OpCodeAsmJs::Max_Int, AsmJsRetType::Signed, AsmJsType::Signed, AsmJsType::Signed));
//Float Overloads
mathFunctions[AsmJSMathBuiltin_fround].val->SetOverload(Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_fround, OpCodeAsmJs::Fround_Db, AsmJsRetType::Float, AsmJsType::MaybeDouble));
mathFunctions[AsmJSMathBuiltin_fround].val->SetOverload(Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_fround, OpCodeAsmJs::Fround_Int, AsmJsRetType::Float, AsmJsType::Int));// should we split this into signed and unsigned?
mathFunctions[AsmJSMathBuiltin_abs].val->SetOverload(Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_abs, OpCodeAsmJs::Abs_Flt, AsmJsRetType::Floatish, AsmJsType::MaybeFloat));
mathFunctions[AsmJSMathBuiltin_ceil].val->SetOverload(Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_ceil, OpCodeAsmJs::Ceil_Flt, AsmJsRetType::Floatish, AsmJsType::MaybeFloat));
mathFunctions[AsmJSMathBuiltin_floor].val->SetOverload(Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_floor, OpCodeAsmJs::Floor_Flt, AsmJsRetType::Floatish, AsmJsType::MaybeFloat));
mathFunctions[AsmJSMathBuiltin_sqrt].val->SetOverload(Anew(&mAllocator, AsmJsMathFunction, nullptr, &mAllocator, 1, AsmJSMathBuiltin_sqrt, OpCodeAsmJs::Sqrt_Flt, AsmJsRetType::Floatish, AsmJsType::MaybeFloat));
for (int i = 0; i < AsmJSMathBuiltinFunction_COUNT; i++)
{
if (!AddStandardLibraryMathName((PropertyId)mathFunctions[i].id, mathFunctions[i].val, mathFunctions[i].val->GetMathBuiltInFunction()))
{
return false;
}
}
struct ConstMath
{
ConstMath(PropertyId id_, const double* val_, AsmJSMathBuiltinFunction mathLibConstName_) :
id(id_), val(val_), mathLibConstName(mathLibConstName_) { }
PropertyId id;
AsmJSMathBuiltinFunction mathLibConstName;
const double* val;
};
ConstMath constMath[] = {
ConstMath(PropertyIds::E , &Math::E , AsmJSMathBuiltinFunction::AsmJSMathBuiltin_e),
ConstMath(PropertyIds::LN10 , &Math::LN10 , AsmJSMathBuiltinFunction::AsmJSMathBuiltin_ln10),
ConstMath(PropertyIds::LN2 , &Math::LN2 , AsmJSMathBuiltinFunction::AsmJSMathBuiltin_ln2),
ConstMath(PropertyIds::LOG2E , &Math::LOG2E , AsmJSMathBuiltinFunction::AsmJSMathBuiltin_log2e),
ConstMath(PropertyIds::LOG10E , &Math::LOG10E , AsmJSMathBuiltinFunction::AsmJSMathBuiltin_log10e),
ConstMath(PropertyIds::PI , &Math::PI , AsmJSMathBuiltinFunction::AsmJSMathBuiltin_pi),
ConstMath(PropertyIds::SQRT1_2 , &Math::SQRT1_2 , AsmJSMathBuiltinFunction::AsmJSMathBuiltin_sqrt1_2),
ConstMath(PropertyIds::SQRT2 , &Math::SQRT2 , AsmJSMathBuiltinFunction::AsmJSMathBuiltin_sqrt2),
ConstMath(PropertyIds::Infinity , &NumberConstants::POSITIVE_INFINITY, AsmJSMathBuiltinFunction::AsmJSMathBuiltin_infinity),
ConstMath(PropertyIds::NaN , &NumberConstants::NaN , AsmJSMathBuiltinFunction::AsmJSMathBuiltin_nan),
};
const int size = sizeof(constMath) / sizeof(ConstMath);
for (int i = 0; i < size; i++)
{
if (!AddStandardLibraryMathName(constMath[i].id, constMath[i].val, constMath[i].mathLibConstName))
{
return false;
}
}
struct ArrayFunc
{
ArrayFunc(PropertyId id_ = 0, AsmJsTypedArrayFunction* val_ = nullptr) :
id(id_), val(val_)
{
}
PropertyId id;
AsmJsTypedArrayFunction* val;
};
ArrayFunc arrayFunctions[AsmJSMathBuiltinFunction_COUNT];
arrayFunctions[AsmJSTypedArrayBuiltin_Int8Array] = ArrayFunc(PropertyIds::Int8Array, Anew(&mAllocator, AsmJsTypedArrayFunction, nullptr, &mAllocator, AsmJSTypedArrayBuiltin_Int8Array, ArrayBufferView::TYPE_INT8));
arrayFunctions[AsmJSTypedArrayBuiltin_Uint8Array] = ArrayFunc(PropertyIds::Uint8Array, Anew(&mAllocator, AsmJsTypedArrayFunction, nullptr, &mAllocator, AsmJSTypedArrayBuiltin_Uint8Array, ArrayBufferView::TYPE_UINT8));
arrayFunctions[AsmJSTypedArrayBuiltin_Int16Array] = ArrayFunc(PropertyIds::Int16Array, Anew(&mAllocator, AsmJsTypedArrayFunction, nullptr, &mAllocator, AsmJSTypedArrayBuiltin_Int16Array, ArrayBufferView::TYPE_INT16));
arrayFunctions[AsmJSTypedArrayBuiltin_Uint16Array] = ArrayFunc(PropertyIds::Uint16Array, Anew(&mAllocator, AsmJsTypedArrayFunction, nullptr, &mAllocator, AsmJSTypedArrayBuiltin_Uint16Array, ArrayBufferView::TYPE_UINT16));
arrayFunctions[AsmJSTypedArrayBuiltin_Int32Array] = ArrayFunc(PropertyIds::Int32Array, Anew(&mAllocator, AsmJsTypedArrayFunction, nullptr, &mAllocator, AsmJSTypedArrayBuiltin_Int32Array, ArrayBufferView::TYPE_INT32));
arrayFunctions[AsmJSTypedArrayBuiltin_Uint32Array] = ArrayFunc(PropertyIds::Uint32Array, Anew(&mAllocator, AsmJsTypedArrayFunction, nullptr, &mAllocator, AsmJSTypedArrayBuiltin_Uint32Array, ArrayBufferView::TYPE_UINT32));
arrayFunctions[AsmJSTypedArrayBuiltin_Float32Array] = ArrayFunc(PropertyIds::Float32Array, Anew(&mAllocator, AsmJsTypedArrayFunction, nullptr, &mAllocator, AsmJSTypedArrayBuiltin_Float32Array, ArrayBufferView::TYPE_FLOAT32));
arrayFunctions[AsmJSTypedArrayBuiltin_Float64Array] = ArrayFunc(PropertyIds::Float64Array, Anew(&mAllocator, AsmJsTypedArrayFunction, nullptr, &mAllocator, AsmJSTypedArrayBuiltin_Float64Array, ArrayBufferView::TYPE_FLOAT64));
arrayFunctions[AsmJSTypedArrayBuiltin_byteLength] = ArrayFunc(PropertyIds::byteLength, Anew(&mAllocator, AsmJsTypedArrayFunction, nullptr, &mAllocator, AsmJSTypedArrayBuiltin_byteLength, ArrayBufferView::TYPE_COUNT));
for (int i = 0; i < AsmJSTypedArrayBuiltin_COUNT; i++)
{
if (!AddStandardLibraryArrayName((PropertyId)arrayFunctions[i].id, arrayFunctions[i].val, arrayFunctions[i].val->GetArrayBuiltInFunction()))
{
return false;
}
}
return true;
}
AsmJsModuleCompiler::AsmJsModuleCompiler(ExclusiveContext *cx, AsmJSParser &parser) :
mCx(cx)
, mCurrentParserNode(parser)
, mAllocator(_u("Asmjs"), cx->scriptContext->GetThreadContext()->GetPageAllocator(), Throw::OutOfMemory)
, mModuleFunctionName(nullptr)
, mStandardLibraryMathNames(&mAllocator)
, mStandardLibraryArrayNames(&mAllocator)
, mFunctionArray(&mAllocator)
, mModuleEnvironment(&mAllocator)
, mFunctionTableArray(&mAllocator)
, mInitialised(false)
, mIntVarSpace()
, mDoubleVarSpace()
, mExports(&mAllocator)
, mExportFuncIndex(Js::Constants::NoRegister)
, mVarImportCount(0)
, mVarCount(0)
, mFuncPtrTableCount(0)
, mCompileTime()
, mCompileTimeLastTick(GetTick())
, mMaxAstSize(0)
, mArrayViews(&mAllocator)
, mUsesHeapBuffer(false)
, mMaxHeapAccess(0)
#if DBG
, mStdLibArgNameInit(false)
, mForeignArgNameInit(false)
, mBufferArgNameInit(false)
#endif
{
InitModuleNode(parser->AsParseNodeFnc());
}
bool AsmJsModuleCompiler::AddStandardLibraryMathName(PropertyId id, const double* cstAddr, AsmJSMathBuiltinFunction mathLibFunctionName)
{
// make sure this name is unique
if (mStandardLibraryMathNames.ContainsKey(id))
{
return false;
}
MathBuiltin mathBuiltin(mathLibFunctionName, cstAddr);
int addResult = mStandardLibraryMathNames.AddNew(id, mathBuiltin);
if (addResult == -1)
{
// Error adding the function
return false;
}
return true;
}
bool AsmJsModuleCompiler::AddStandardLibraryMathName(PropertyId id, AsmJsMathFunction* func, AsmJSMathBuiltinFunction mathLibFunctionName)
{
// make sure this name is unique
if (mStandardLibraryMathNames.ContainsKey(id))
{
return false;
}
MathBuiltin mathBuiltin(mathLibFunctionName, func);
int addResult = mStandardLibraryMathNames.AddNew(id, mathBuiltin);
if (addResult == -1)
{
// Error adding the function
return false;
}
return true;
}
bool AsmJsModuleCompiler::AddStandardLibraryArrayName(PropertyId id, AsmJsTypedArrayFunction* func, AsmJSTypedArrayBuiltinFunction arrayLibFunctionName)
{
// make sure this name is unique
if (mStandardLibraryArrayNames.ContainsKey(id))
{
return false;
}
TypedArrayBuiltin arrayBuiltin(arrayLibFunctionName, func);
int addResult = mStandardLibraryArrayNames.AddNew(id, arrayBuiltin);
if (addResult == -1)
{
// Error adding the function
return false;
}
return true;
}
Parser * AsmJsModuleCompiler::GetParser() const
{
return mCx->byteCodeGenerator->GetParser();
}
ByteCodeGenerator* AsmJsModuleCompiler::GetByteCodeGenerator() const
{
return mCx->byteCodeGenerator;
}
ScriptContext * AsmJsModuleCompiler::GetScriptContext() const
{
return mCx->scriptContext;
}
AsmJsSymbol* AsmJsModuleCompiler::LookupIdentifier(PropertyName name, AsmJsFunc* func /*= nullptr */, AsmJsLookupSource::Source* lookupSource /*= nullptr*/)
{
AsmJsSymbol* lookupResult = nullptr;
if (name)
{
if (func)
{
lookupResult = func->LookupIdentifier(name, lookupSource);
if (lookupResult)
{
return lookupResult;
}
}
lookupResult = mModuleEnvironment.LookupWithKey(name->GetPropertyId(), nullptr);
if (lookupSource)
{
*lookupSource = AsmJsLookupSource::AsmJsModule;
}
}
return lookupResult;
}
bool AsmJsModuleCompiler::DefineIdentifier(PropertyName name, AsmJsSymbol* symbol)
{
Assert(symbol);
if (symbol)
{
// make sure this identifier is unique
if (!LookupIdentifier(name))
{
int addResult = mModuleEnvironment.AddNew(name->GetPropertyId(), symbol);
return addResult != -1;
}
}
return false;
}
bool AsmJsModuleCompiler::AddNumericVar(PropertyName name, ParseNode* pnode, bool isFloat, bool isMutable /*= true*/)
{
Assert(ParserWrapper::IsNumericLiteral(pnode) || (isFloat && ParserWrapper::IsFroundNumericLiteral(pnode)));
AsmJsVar* var = Anew(&mAllocator, AsmJsVar, name, isMutable);
if (!var)
{
return false;
}
if (!DefineIdentifier(name, var))
{
return false;
}
++mVarCount;
if (isFloat)
{
var->SetVarType(AsmJsVarType::Float);
var->SetLocation(mFloatVarSpace.AcquireRegister());
if (pnode->nop == knopInt)
{
var->SetConstInitialiser((float)pnode->AsParseNodeInt()->lw);
}
else if (ParserWrapper::IsNegativeZero(pnode))
{
var->SetConstInitialiser(-0.0f);
}
else
{
var->SetConstInitialiser((float)pnode->AsParseNodeFloat()->dbl);
}
}
else if (pnode->nop == knopInt)
{
var->SetVarType(AsmJsVarType::Int);
var->SetLocation(mIntVarSpace.AcquireRegister());
var->SetConstInitialiser(pnode->AsParseNodeInt()->lw);
}
else
{
if (ParserWrapper::IsMinInt(pnode))
{
var->SetVarType(AsmJsVarType::Int);
var->SetLocation(mIntVarSpace.AcquireRegister());
var->SetConstInitialiser(INT_MIN);
}
else if (ParserWrapper::IsUnsigned(pnode))
{
var->SetVarType(AsmJsVarType::Int);
var->SetLocation(mIntVarSpace.AcquireRegister());
var->SetConstInitialiser((int)((uint32)pnode->AsParseNodeFloat()->dbl));
}
else if (pnode->AsParseNodeFloat()->maybeInt)
{
// this means there was an int literal not in range [-2^31,3^32)
return false;
}
else
{
var->SetVarType(AsmJsVarType::Double);
var->SetLocation(mDoubleVarSpace.AcquireRegister());
var->SetConstInitialiser(pnode->AsParseNodeFloat()->dbl);
}
}
return true;
}
bool AsmJsModuleCompiler::AddGlobalVarImport(PropertyName name, PropertyName field, AsmJSCoercion coercion)
{
AsmJsConstantImport* var = Anew(&mAllocator, AsmJsConstantImport, name, field);
if (!var)
{
return false;
}
if (!DefineIdentifier(name, var))
{
return false;
}
++mVarImportCount;
switch (coercion)
{
case Js::AsmJS_ToInt32:
var->SetVarType(AsmJsVarType::Int);
var->SetLocation(mIntVarSpace.AcquireRegister());
break;
case Js::AsmJS_ToNumber:
var->SetVarType(AsmJsVarType::Double);
var->SetLocation(mDoubleVarSpace.AcquireRegister());
break;
case Js::AsmJS_FRound:
var->SetVarType(AsmJsVarType::Float);
var->SetLocation(mFloatVarSpace.AcquireRegister());
break;
default:
break;
}
return true;
}
bool AsmJsModuleCompiler::AddModuleFunctionImport(PropertyName name, PropertyName field)
{
AsmJsImportFunction* var = Anew(&mAllocator, AsmJsImportFunction, name, field, &mAllocator);
if (!var)
{
return false;
}
if (!DefineIdentifier(name, var))
{
return false;
}
var->SetFunctionIndex(mImportFunctions.AcquireRegister());
return true;
}
bool AsmJsModuleCompiler::AddNumericConst(PropertyName name, const double* cst)
{
AsmJsMathConst* var = Anew(&mAllocator, AsmJsMathConst, name, cst);
if (!var)
{
return false;
}
if (!DefineIdentifier(name, var))
{
return false;
}
return true;
}
bool AsmJsModuleCompiler::AddArrayView(PropertyName name, ArrayBufferView::ViewType type)
{
AsmJsArrayView* view = Anew(&mAllocator, AsmJsArrayView, name, type);
if (!view)
{
return false;
}
if (!DefineIdentifier(name, view))
{
return false;
}
mArrayViews.Enqueue(view);
return true;
}
bool AsmJsModuleCompiler::AddFunctionTable(PropertyName name, const int size)
{
GetByteCodeGenerator()->AssignPropertyId(name);
AsmJsFunctionTable* funcTable = Anew(&mAllocator, AsmJsFunctionTable, name, &mAllocator);
if (!funcTable)
{
return false;
}
if (!DefineIdentifier(name, funcTable))
{
return false;
}
funcTable->SetSize(size);
int pos = mFunctionTableArray.Add(funcTable);
funcTable->SetFunctionIndex(pos);
return true;
}
bool AsmJsModuleCompiler::AddExport(PropertyName name, RegSlot location)
{
AsmJsModuleExport * foundExport;
if (mExports.TryGetReference(name->GetPropertyId(), &foundExport))
{
AsmJSCompiler::OutputMessage(GetScriptContext(), DEIT_GENERAL, _u("Warning: redefining export"));
foundExport->location = location;
return true;
}
else
{
AsmJsModuleExport ex;
ex.id = name->GetPropertyId();
ex.location = location;
return mExports.Add(ex.id, ex) >= 0;
// return is < 0 if count overflowed 31bits
}
}
bool AsmJsModuleCompiler::SetExportFunc(AsmJsFunc* func)
{
Assert(mExports.Count() == 0 && func);
mExportFuncIndex = func->GetFunctionIndex();
return mExports.Count() == 0 && (uint32)mExportFuncIndex < (uint32)mFunctionArray.Count();
}
AsmJsFunctionDeclaration* AsmJsModuleCompiler::LookupFunction(PropertyName name)
{
return LookupIdentifier<AsmJsFunctionDeclaration>(name);
}
bool AsmJsModuleCompiler::AreAllFuncTableDefined()
{
const int size = mFunctionTableArray.Count();
for (int i = 0; i < size; i++)
{
AsmJsFunctionTable* funcTable = mFunctionTableArray.Item(i);
if (!funcTable->IsDefined())
{
AsmJSCompiler::OutputError(GetScriptContext(), _u("Function table %s was used in a function but does not appear in the module"), funcTable->GetName()->Psz());
return false;
}
}
return true;
}
void AsmJsModuleCompiler::UpdateMaxHeapAccess(uint index)
{
if (mMaxHeapAccess < index)
{
mMaxHeapAccess = index;
}
}
void AsmJsModuleCompiler::InitMemoryOffsets()
{
mModuleMemory.mArrayBufferOffset = AsmJsModuleMemory::MemoryTableBeginOffset;
mModuleMemory.mStdLibOffset = mModuleMemory.mArrayBufferOffset + 1;
mModuleMemory.mDoubleOffset = mModuleMemory.mStdLibOffset + 1;
mModuleMemory.mFuncOffset = mModuleMemory.mDoubleOffset + (mDoubleVarSpace.GetTotalVarCount() * WAsmJs::DOUBLE_SLOTS_SPACE);
mModuleMemory.mFFIOffset = mModuleMemory.mFuncOffset + mFunctionArray.Count();
mModuleMemory.mFuncPtrOffset = mModuleMemory.mFFIOffset + mImportFunctions.GetTotalVarCount();
mModuleMemory.mFloatOffset = mModuleMemory.mFuncPtrOffset + GetFuncPtrTableCount();
mModuleMemory.mIntOffset = mModuleMemory.mFloatOffset + (int32)(mFloatVarSpace.GetTotalVarCount() * WAsmJs::FLOAT_SLOTS_SPACE + 0.5);
mModuleMemory.mMemorySize = mModuleMemory.mIntOffset + (int32)(mIntVarSpace.GetTotalVarCount() * WAsmJs::INT_SLOTS_SPACE + 0.5);
}
void AsmJsModuleCompiler::AccumulateCompileTime()
{
Js::TickDelta td;
AsmJsCompileTime curTime = GetTick();
td = curTime - mCompileTimeLastTick;
mCompileTime = mCompileTime + td;
mCompileTimeLastTick = curTime;
}
void AsmJsModuleCompiler::AccumulateCompileTime(AsmJsCompilation::Phases phase)
{
Js::TickDelta td;
AsmJsCompileTime curTime = GetTick();
td = curTime - mCompileTimeLastTick;
mCompileTime = mCompileTime + td;
mCompileTimeLastTick = curTime;
mPhaseCompileTime[phase] = mPhaseCompileTime[phase] + td;
}
Js::AsmJsCompileTime AsmJsModuleCompiler::GetTick()
{
return Js::Tick::Now();
}
uint64 AsmJsModuleCompiler::GetCompileTime() const
{
return mCompileTime.ToMicroseconds();
}
static const char16* AsmPhaseNames[AsmJsCompilation::Phases_COUNT] = {
_u("Module"),
_u("ByteCode"),
_u("TemplateJIT"),
};
void AsmJsModuleCompiler::PrintCompileTrace() const
{
// for testtrace, don't print time so that it can be used for baselines
if (PHASE_TESTTRACE1(AsmjsPhase))
{
AsmJSCompiler::OutputMessage(GetScriptContext(), DEIT_ASMJS_SUCCEEDED, _u("Successfully compiled asm.js code"));
}
else
{
uint64 us = GetCompileTime();
uint64 ms = us / 1000;
us = us % 1000;
AsmJSCompiler::OutputMessage(GetScriptContext(), DEIT_ASMJS_SUCCEEDED, _u("Successfully compiled asm.js code (total compilation time %llu.%llums)"), ms, us);
}
if (PHASE_TRACE1(AsmjsPhase))
{
for (int i = 0; i < AsmJsCompilation::Phases_COUNT; i++)
{
uint64 us = mPhaseCompileTime[i].ToMicroseconds();
uint64 ms = us / 1000;
us = us % 1000;
Output::Print(_u("%20s : %llu.%llums\n"), AsmPhaseNames[i], ms, us);
}
Output::Flush();
}
}
BVStatic<ASMMATH_BUILTIN_SIZE> AsmJsModuleCompiler::GetAsmMathBuiltinUsedBV()
{
return mAsmMathBuiltinUsedBV;
}
BVStatic<ASMARRAY_BUILTIN_SIZE> AsmJsModuleCompiler::GetAsmArrayBuiltinUsedBV()
{
return mAsmArrayBuiltinUsedBV;
}
void AsmJsModuleInfo::SetFunctionCount(int val)
{
Assert(mFunctions == nullptr);
mFunctionCount = val;
mFunctions = RecyclerNewArray(mRecycler, ModuleFunction, val);
}
void AsmJsModuleInfo::SetFunctionTableCount(int val)
{
Assert(mFunctionTables == nullptr);
mFunctionTableCount = val;
mFunctionTables = RecyclerNewArray(mRecycler, ModuleFunctionTable, val);
}
void AsmJsModuleInfo::SetFunctionImportCount(int val)
{
Assert(mFunctionImports == nullptr);
mFunctionImportCount = val;
mFunctionImports = RecyclerNewArray(mRecycler, ModuleFunctionImport, val);
}
void AsmJsModuleInfo::SetVarCount(int val)
{
Assert(mVars == nullptr);
mVarCount = val;
mVars = RecyclerNewArray(mRecycler, ModuleVar, val);
}
void AsmJsModuleInfo::SetVarImportCount(int val)
{
Assert(mVarImports == nullptr);
mVarImportCount = val;
mVarImports = RecyclerNewArray(mRecycler, ModuleVarImport, val);
}
void AsmJsModuleInfo::SetExportsCount(int count)
{
if (count)
{
mExports = RecyclerNewPlus(mRecycler, count * sizeof(PropertyId), PropertyIdArray, count, 0);
mExportsFunctionLocation = RecyclerNewArray(mRecycler, RegSlot, count);
}
mExportsCount = count;
}
void AsmJsModuleInfo::InitializeSlotMap(int val)
{
Assert(mSlotMap == nullptr);
mSlotsCount = val;
mSlotMap = RecyclerNew(mRecycler, AsmJsSlotMap, mRecycler);
}
void AsmJsModuleInfo::SetFunctionTableSize(int index, uint size)
{
Assert(mFunctionTables != nullptr);
Assert(index < mFunctionTableCount);
ModuleFunctionTable& table = mFunctionTables[index];
table.size = size;
table.moduleFunctionIndex = RecyclerNewArray(mRecycler, RegSlot, size);
}
void AsmJsModuleInfo::EnsureHeapAttached(ScriptFunction * func)
{
#ifdef ENABLE_WASM
if (VarIs<WasmScriptFunction>(func))
{
WasmScriptFunction* wasmFunc = VarTo<WasmScriptFunction>(func);
WebAssemblyMemory * wasmMem = wasmFunc->GetWebAssemblyMemory();
if (wasmMem && wasmMem->GetBuffer() && wasmMem->GetBuffer()->IsDetached())
{
Throw::OutOfMemory();
}
}
else
#endif
{
AsmJsScriptFunction* asmFunc = VarTo<AsmJsScriptFunction>(func);
ArrayBuffer* moduleArrayBuffer = asmFunc->GetAsmJsArrayBuffer();
if (moduleArrayBuffer && moduleArrayBuffer->IsDetached())
{
Throw::OutOfMemory();
}
}
}
void * AsmJsModuleInfo::ConvertFrameForJavascript(void* env, AsmJsScriptFunction* func)
{
FunctionBody * body = func->GetFunctionBody();
AsmJsFunctionInfo * asmFuncInfo = body->GetAsmJsFunctionInfo();
FunctionBody * moduleBody = asmFuncInfo->GetModuleFunctionBody();
AsmJsModuleInfo * asmModuleInfo = moduleBody->GetAsmJsModuleInfo();
Assert(asmModuleInfo);
ScriptContext * scriptContext = func->GetScriptContext();
// AsmJsModuleEnvironment is all laid out here
Var * asmJsEnvironment = static_cast<Var*>(env);
Var * asmBufferPtr = asmJsEnvironment + asmModuleInfo->GetModuleMemory().mArrayBufferOffset;
ArrayBuffer * asmBuffer = *asmBufferPtr ? VarTo<ArrayBuffer>(*asmBufferPtr) : nullptr;
Var stdLibObj = *(asmJsEnvironment + asmModuleInfo->GetModuleMemory().mStdLibOffset);
Var asmMathObject = stdLibObj ? JavascriptOperators::OP_GetProperty(stdLibObj, PropertyIds::Math, scriptContext) : nullptr;
Var * asmFFIs = asmJsEnvironment + asmModuleInfo->GetModuleMemory().mFFIOffset;
Var * asmFuncs = asmJsEnvironment + asmModuleInfo->GetModuleMemory().mFuncOffset;
Var ** asmFuncPtrs = reinterpret_cast<Var**>(asmJsEnvironment + asmModuleInfo->GetModuleMemory().mFuncPtrOffset);
double * asmDoubleVars = reinterpret_cast<double*>(asmJsEnvironment + asmModuleInfo->GetModuleMemory().mDoubleOffset);
int * asmIntVars = reinterpret_cast<int*>(asmJsEnvironment + asmModuleInfo->GetModuleMemory().mIntOffset);
float * asmFloatVars = reinterpret_cast<float*>(asmJsEnvironment + asmModuleInfo->GetModuleMemory().mFloatOffset);
#if DEBUG
Field(Var) * slotArray = RecyclerNewArrayZ(scriptContext->GetRecycler(), Field(Var), moduleBody->scopeSlotArraySize + ScopeSlots::FirstSlotIndex);
#else
Field(Var) * slotArray = RecyclerNewArray(scriptContext->GetRecycler(), Field(Var), moduleBody->scopeSlotArraySize + ScopeSlots::FirstSlotIndex);
#endif
ScopeSlots scopeSlots(slotArray);
scopeSlots.SetCount(moduleBody->scopeSlotArraySize);
scopeSlots.SetScopeMetadata(moduleBody->GetFunctionInfo());
auto asmSlotMap = asmModuleInfo->GetAsmJsSlotMap();
Assert((uint)asmModuleInfo->GetSlotsCount() >= moduleBody->scopeSlotArraySize);
Js::ActivationObject* activeScopeObject = nullptr;
if (moduleBody->GetObjectRegister() != 0)
{
activeScopeObject = static_cast<ActivationObject*>(scriptContext->GetLibrary()->CreateActivationObject());
}
PropertyId* propertyIdArray = moduleBody->GetPropertyIdsForScopeSlotArray();
uint slotsCount = moduleBody->scopeSlotArraySize;
for (uint i = 0; i < slotsCount; ++i)
{
AsmJsSlot * asmSlot = nullptr;
bool found = asmSlotMap->TryGetValue(propertyIdArray[i], &asmSlot);
// we should have everything we need in the map
Assert(found);
Var value = nullptr;
switch (asmSlot->symType)
{
case AsmJsSymbol::ConstantImport:
case AsmJsSymbol::Variable:
{
switch (asmSlot->varType)
{
case AsmJsVarType::Double:
value = JavascriptNumber::NewWithCheck(asmDoubleVars[asmSlot->location], scriptContext);
break;
case AsmJsVarType::Float:
value = JavascriptNumber::NewWithCheck(asmFloatVars[asmSlot->location], scriptContext);
break;
case AsmJsVarType::Int:
value = JavascriptNumber::ToVar(asmIntVars[asmSlot->location], scriptContext);
break;
default:
Assume(UNREACHED);
}
break;
}
case AsmJsSymbol::ModuleArgument:
{
switch (asmSlot->argType)
{
case AsmJsModuleArg::ArgType::StdLib:
value = stdLibObj;
break;
case AsmJsModuleArg::ArgType::Import:
// we can't reference this inside functions (and don't hold onto it), but must set to something, so set it to be undefined
value = scriptContext->GetLibrary()->GetUndefined();
break;
case AsmJsModuleArg::ArgType::Heap:
value = asmBuffer;
break;
default:
Assume(UNREACHED);
}
break;
}
case AsmJsSymbol::ImportFunction:
value = asmFFIs[asmSlot->location];
break;
case AsmJsSymbol::FuncPtrTable:
value = JavascriptArray::OP_NewScArrayWithElements(asmSlot->funcTableSize, asmFuncPtrs[asmSlot->location], scriptContext);
break;
case AsmJsSymbol::ModuleFunction:
value = asmFuncs[asmSlot->location];
break;
case AsmJsSymbol::MathConstant:
value = JavascriptNumber::NewWithCheck(asmSlot->mathConstVal, scriptContext);
break;
case AsmJsSymbol::ClosureFunction:
// we can't reference this inside functions but must set to something, so set it to be undefined
value = scriptContext->GetLibrary()->GetUndefined();
break;
case AsmJsSymbol::ArrayView:
{
AnalysisAssert(asmBuffer);
#ifdef _M_X64
const bool isOptimizedBuffer = true;
#elif _M_IX86
const bool isOptimizedBuffer = false;
#else
Assert(UNREACHED);
const bool isOptimizedBuffer = false;
#endif
Assert(isOptimizedBuffer == asmBuffer->IsValidVirtualBufferLength(asmBuffer->GetByteLength()));
switch (asmSlot->viewType)
{
case ArrayBufferView::TYPE_FLOAT32:
value = TypedArray<float, false, isOptimizedBuffer>::Create(asmBuffer, 0, asmBuffer->GetByteLength() >> 2, scriptContext->GetLibrary());
break;
case ArrayBufferView::TYPE_FLOAT64:
value = TypedArray<double, false, isOptimizedBuffer>::Create(asmBuffer, 0, asmBuffer->GetByteLength() >> 3, scriptContext->GetLibrary());
break;
case ArrayBufferView::TYPE_INT8:
value = TypedArray<int8, false, isOptimizedBuffer>::Create(asmBuffer, 0, asmBuffer->GetByteLength(), scriptContext->GetLibrary());
break;
case ArrayBufferView::TYPE_INT16:
value = TypedArray<int16, false, isOptimizedBuffer>::Create(asmBuffer, 0, asmBuffer->GetByteLength() >> 1, scriptContext->GetLibrary());
break;
case ArrayBufferView::TYPE_INT32:
value = TypedArray<int32, false, isOptimizedBuffer>::Create(asmBuffer, 0, asmBuffer->GetByteLength() >> 2, scriptContext->GetLibrary());
break;
case ArrayBufferView::TYPE_UINT8:
value = TypedArray<uint8, false, isOptimizedBuffer>::Create(asmBuffer, 0, asmBuffer->GetByteLength(), scriptContext->GetLibrary());
break;
case ArrayBufferView::TYPE_UINT16:
value = TypedArray<uint16, false, isOptimizedBuffer>::Create(asmBuffer, 0, asmBuffer->GetByteLength() >> 1, scriptContext->GetLibrary());
break;
case ArrayBufferView::TYPE_UINT32:
value = TypedArray<uint32, false, isOptimizedBuffer>::Create(asmBuffer, 0, asmBuffer->GetByteLength() >> 2, scriptContext->GetLibrary());
break;
default:
Assume(UNREACHED);
}
break;
}
case AsmJsSymbol::MathBuiltinFunction:
{
switch (asmSlot->builtinMathFunc)
{
#define ASMJS_MATH_FUNC_NAMES(name, propertyName, funcInfo) \
case AsmJSMathBuiltin_##name: \
value = JavascriptOperators::OP_GetProperty(asmMathObject, PropertyIds::##propertyName, scriptContext); \
break;
#include "AsmJsBuiltInNames.h"
default:
Assume(UNREACHED);
}
break;
}
case AsmJsSymbol::TypedArrayBuiltinFunction:
switch (asmSlot->builtinArrayFunc)
{
#define ASMJS_ARRAY_NAMES(name, propertyName) \
case AsmJSTypedArrayBuiltin_##name: \
value = JavascriptOperators::OP_GetProperty(stdLibObj, PropertyIds::##propertyName, scriptContext); \
break;
#include "AsmJsBuiltInNames.h"
default:
Assume(UNREACHED);
}
break;
default:
Assume(UNREACHED);
}
if (activeScopeObject != nullptr)
{
activeScopeObject->SetPropertyWithAttributes(
propertyIdArray[i],
value,
asmSlot->isConstVar ? PropertyConstDefaults : PropertyDynamicTypeDefaults,
nullptr);
}
else
{
// ensure we aren't multiply writing to a slot
Assert(scopeSlots.Get(i) == nullptr);
scopeSlots.Set(i, value);
}
}
if (activeScopeObject != nullptr)
{
return (void*)activeScopeObject;
}
else
{
return (void*)slotArray;
}
}
};
#endif