Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Experimental idea - create facility to enable internal commands within JS #6440

Draft
wants to merge 6 commits into
base: master
Choose a base branch
from
Draft
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
4 changes: 4 additions & 0 deletions lib/Backend/BackwardPass.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -6573,6 +6573,8 @@ BackwardPass::TrackBitWiseOrNumberOp(IR::Instr *const instr)
{
// Instructions that can cause src values to escape the local scope have already been excluded

case Js::OpCode::ToInteger:
case Js::OpCode::ToLength:
case Js::OpCode::Conv_Num:
case Js::OpCode::Div_A:
case Js::OpCode::Mul_A:
Expand Down Expand Up @@ -7003,6 +7005,8 @@ BackwardPass::TrackIntUsage(IR::Instr *const instr)
case Js::OpCode::Ld_A:
case Js::OpCode::Conv_Num:
case Js::OpCode::ShrU_A:
case Js::OpCode::ToInteger:
case Js::OpCode::ToLength:
if(!ignoreIntOverflowCandidate)
{
// Int overflow matters for dst, so int overflow also matters for srcs
Expand Down
41 changes: 32 additions & 9 deletions lib/Backend/GlobOpt.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2424,7 +2424,8 @@ void
GlobOpt::TryReplaceLdLen(IR::Instr *& instr)
{
// Change LdLen on objects other than arrays, strings, and 'arguments' to LdFld. Otherwise, convert the SymOpnd to a RegOpnd here.
if (instr->m_opcode == Js::OpCode::LdLen_A && instr->GetSrc1() && instr->GetSrc1()->IsSymOpnd())
// Attempt the same optimisation for GetLength as long as the object is not a typed array
if ((instr->m_opcode == Js::OpCode::LdLen_A || instr->m_opcode == Js::OpCode::GetLength) && instr->GetSrc1() && instr->GetSrc1()->IsSymOpnd())
{
IR::SymOpnd * opnd = instr->GetSrc1()->AsSymOpnd();
Sym *sym = opnd->m_sym;
Expand All @@ -2442,15 +2443,19 @@ GlobOpt::TryReplaceLdLen(IR::Instr *& instr)
(CurrentBlockData()->argObjSyms && CurrentBlockData()->IsArgumentsOpnd(newopnd))
)
{
// We need to properly transfer over the information from the old operand, which is
// a SymOpnd, to the new one, which is a RegOpnd. Unfortunately, the types mean the
// normal copy methods won't work here, so we're going to directly copy data.
newopnd->SetIsJITOptimizedReg(opnd->GetIsJITOptimizedReg());
newopnd->SetValueType(objectValueInfo->Type());
newopnd->SetIsDead(opnd->GetIsDead());
instr->ReplaceSrc1(newopnd);
// GetLength Op can't be optimised for typed arrays
if (instr->m_opcode == Js::OpCode::LdLen_A || !objectValueInfo->IsLikelyTypedArray())
{
// We need to properly transfer over the information from the old operand, which is
// a SymOpnd, to the new one, which is a RegOpnd. Unfortunately, the types mean the
// normal copy methods won't work here, so we're going to directly copy data.
newopnd->SetIsJITOptimizedReg(opnd->GetIsJITOptimizedReg());
newopnd->SetValueType(objectValueInfo->Type());
newopnd->SetIsDead(opnd->GetIsDead());
instr->ReplaceSrc1(newopnd);
}
}
else
else if(instr->m_opcode == Js::OpCode::LdLen_A) // retain the GetLength op when not optimising
{
// otherwise, change the instruction to an LdFld here.
instr->m_opcode = Js::OpCode::LdFld;
Expand Down Expand Up @@ -8114,6 +8119,24 @@ GlobOpt::TypeSpecializeIntUnary(
opcode = Js::OpCode::Ld_I4;
break;

case Js::OpCode::ToInteger:
newMin = min;
newMax = max;
opcode = Js::OpCode::Ld_I4;
isTransfer = true;
break;

case Js::OpCode::ToLength:
if (min >= 0)
{
newMin = min;
newMax = max;
opcode = Js::OpCode::Ld_I4;
isTransfer = true;
break;
}
return false;

case Js::OpCode::Neg_A:
if (min <= 0 && max >= 0)
{
Expand Down
6 changes: 6 additions & 0 deletions lib/Backend/IRBuilder.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -1860,6 +1860,12 @@ IRBuilder::BuildReg2(Js::OpCode newOpcode, uint32 offset, Js::RegSlot R0, Js::Re
return;
}

case Js::OpCode::ToLength:
case Js::OpCode::ToInteger:
case Js::OpCode::GetLength:
dstOpnd->SetValueType(ValueType::Int.ToLikely());
break;

case Js::OpCode::Conv_Str:
dstOpnd->SetValueType(ValueType::String);
break;
Expand Down
3 changes: 3 additions & 0 deletions lib/Backend/JnHelperMethodList.h
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,9 @@ HELPERCALL_MATH(Conv_ToUInt32Core, (uint32(*)(double))Js::JavascriptMath::ToUInt
HELPERCALL_MATH(Op_MaxInAnArray, Js::JavascriptMath::MaxInAnArray, AttrCanThrow)
HELPERCALL_MATH(Op_MinInAnArray, Js::JavascriptMath::MinInAnArray, AttrCanThrow)

HELPERCALLCHK(Op_ToInteger, Js::JavascriptOperators::OP_ToInteger, AttrCanThrow)
HELPERCALLCHK(Op_ToLength, Js::JavascriptOperators::OP_ToLength, AttrCanThrow)
HELPERCALLCHK(Op_GetIterableLength, Js::JavascriptOperators::OP_GetIterableLength, AttrCanThrow)
HELPERCALLCHK(Op_ConvString, Js::JavascriptConversion::ToString, AttrCanThrow)
HELPERCALLCHK(Op_ConvPropertyKey, Js::JavascriptOperators::OP_ToPropertyKey, AttrCanThrow)
HELPERCALLCHK(Op_CoerseString, Js::JavascriptConversion::CoerseString, AttrCanThrow)
Expand Down
12 changes: 12 additions & 0 deletions lib/Backend/Lower.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3143,6 +3143,18 @@ Lowerer::LowerRange(IR::Instr *instrStart, IR::Instr *instrEnd, bool defaultDoFa
instrPrev = this->LowerStPropIdArrFromVar(instr);
break;

case Js::OpCode::ToInteger:
this->LowerUnaryHelperMem(instr, IR::HelperOp_ToInteger);
break;

case Js::OpCode::ToLength:
this->LowerUnaryHelperMem(instr, IR::HelperOp_ToLength);
break;

case Js::OpCode::GetLength:
this->LowerUnaryHelperMem(instr, IR::HelperOp_GetIterableLength);
break;

#ifdef ENABLE_DEBUG_CONFIG_OPTIONS
case Js::OpCode::GeneratorOutputBailInTraceLabel:
#endif
Expand Down
1 change: 1 addition & 0 deletions lib/Common/ConfigFlagsList.h
Original file line number Diff line number Diff line change
Expand Up @@ -1102,6 +1102,7 @@ FLAGR(Boolean, SkipSplitOnNoResult, "If the result of Regex split isn't used, sk
FLAGNR(String, TestEtwDll , "Path of the TestEtwEventSink DLL", nullptr)
#endif
#ifdef ENABLE_TEST_HOOKS
FLAGNR(Boolean, EnableInternalCommands, "Enable certain internal instructions within JS files - intended for library code only," , false)
FLAGNR(Boolean, Force32BitByteCode, "Force CC to generate 32bit bytecode intended only for regenerating bytecode headers.", false)
#endif

Expand Down
15 changes: 15 additions & 0 deletions lib/Parser/InternalCommands.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
//-------------------------------------------------------------------------------------------------------
// Copyright (C) Microsoft. All rights reserved.
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------

// The Internal Command mechanism currently supports OpCodes with one or two parameters
// and a return value. Any such can be enabled as Internal commands by adding them below

// command name, expected parameters
Command(Conv_Obj, 1)
Command(ToInteger, 1)
Command(ToLength, 1)
Command(GetLength, 1)

#undef Command
109 changes: 109 additions & 0 deletions lib/Parser/Parse.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -128,6 +128,13 @@ Parser::Parser(Js::ScriptContext* scriptContext, BOOL strictMode, PageAllocator

// init PID members
InitPids();

#ifdef ENABLE_TEST_HOOKS
if (scriptContext->GetConfig()->IsInternalCommandsEnabled())
{
InitInternalCommandPids();
}
#endif
}

Parser::~Parser(void)
Expand Down Expand Up @@ -2626,6 +2633,88 @@ void Parser::CheckForDuplicateExportEntry(ModuleImportOrExportEntryList* exportE
}
}

#ifdef ENABLE_TEST_HOOKS
template<bool buildAST>
ParseNodePtr Parser::ParseInternalCommand()
{
this->GetScanner()->Scan();
if (m_token.tk != tkID)
{
Error(ERRTokenAfter, GetTokenString(m_token.tk), _u("@@"));
}
charcount_t ichMin = this->GetScanner()->IchMinTok();

// find the command type
InternalCommandType type;
uint32 expectedParams = 0;
IdentPtr id = m_token.GetIdentifier(GetHashTbl());

#define Command(name, params) \
if (id == internalCommandPids.##name) \
{ \
type = InternalCommandType::##name; \
expectedParams = params; \
} \
else

#include "InternalCommands.h"
{
Error(ERRTokenAfter, m_token.GetIdentifier(GetHashTbl())->Psz(), _u("@@"));
}

// parse the parameters - currently only accept identifiers
this->GetScanner()->Scan();
ChkCurTok(tkLParen, ERRnoLparen);
ParseNodePtr params = nullptr;
ParseNodePtr * lastParam = nullptr;
ParseNodePtr currentParam = nullptr;

for (;;)
{
currentParam = ParseExpr<buildAST>(koplCma);
if (expectedParams-- == 0)
{
// throw during parse phase if internal command has too many parameters
// as the excess would be ignored upon execution
Error(ERRsyntax);
}
if (buildAST)
{
AddToNodeListEscapedUse(&params, &lastParam, currentParam);
}

if (m_token.tk == tkComma)
{
this->GetScanner()->Scan();
}
else if (m_token.tk == tkRParen)
{
this->GetScanner()->Scan();
break;
}
else
{
Error(ERRTokenAfter, GetTokenString(m_token.tk), GetTokenString(this->GetScanner()->GetPrevious()));
}
}

if (expectedParams != 0)
{
// throw during parse phase if internal command has too few parameters
// as could produce undefined behaviour if executed
Error(ERRsyntax);
}

ParseNodePtr command = nullptr;
if (buildAST)
{
command = Anew(&m_nodeAllocator, ParseNodeInternalCommand, ichMin, this->GetScanner()->IchLimTok(), type, params);
}

return command;
}
#endif

template<bool buildAST>
void Parser::ParseImportClause(ModuleImportOrExportEntryList* importEntryList, bool parsingAfterComma)
{
Expand Down Expand Up @@ -3744,6 +3833,16 @@ ParseNodePtr Parser::ParseTerm(BOOL fAllowCall,
}
break;

#ifdef ENABLE_TEST_HOOKS
case tkIntCommand:
if (!m_scriptContext->GetConfig()->IsInternalCommandsEnabled())
{
Error(ERRTokenAfter, _u("@@"), GetTokenString(GetScanner()->GetPrevious()));
}
pnode = ParseInternalCommand<buildAST>();
break;
#endif

#if ENABLE_BACKGROUND_PARSING
case tkCASE:
{
Expand Down Expand Up @@ -11775,6 +11874,16 @@ void Parser::InitPids()
wellKnownPropertyPids._importMeta = this->GetHashTbl()->PidHashNameLen(_u("*import.meta*"), sizeof("*import.meta*") - 1);
}

#ifdef ENABLE_TEST_HOOKS
void Parser::InitInternalCommandPids()
{
#define Command(name, params) \
internalCommandPids.##name = this->GetHashTbl()->PidHashNameLen(_u(#name), sizeof(#name) - 1);

#include "InternalCommands.h"
}
#endif

void Parser::RestoreScopeInfo(Js::ScopeInfo * scopeInfo)
{
if (!scopeInfo)
Expand Down
19 changes: 19 additions & 0 deletions lib/Parser/Parse.h
Original file line number Diff line number Diff line change
Expand Up @@ -342,6 +342,10 @@ class Parser

void InitPids();

#ifdef ENABLE_TEST_HOOKS
void InitInternalCommandPids();
#endif

/***********************************************************************
Members needed just for parsing.
***********************************************************************/
Expand Down Expand Up @@ -504,6 +508,17 @@ class Parser

WellKnownPropertyPids wellKnownPropertyPids;

#ifdef ENABLE_TEST_HOOKS
struct InternalCommandPids
{
#define Command(name, params) \
IdentPtr name;

#include "InternalCommands.h"
};
InternalCommandPids internalCommandPids;
#endif

charcount_t m_sourceLim; // The actual number of characters parsed.

Js::ParseableFunctionInfo* m_functionBody; // For a deferred parsed function, the function body is non-null
Expand Down Expand Up @@ -975,6 +990,10 @@ class Parser
charcount_t ichMin,
_Out_opt_ BOOL* pfCanAssign = nullptr);

#ifdef ENABLE_TEST_HOOKS
template<bool buildAST> ParseNodePtr ParseInternalCommand();
#endif

void CheckIfImportOrExportStatementValidHere();
bool IsTopLevelModuleFunc();

Expand Down
10 changes: 10 additions & 0 deletions lib/Parser/Scan.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -2195,6 +2195,16 @@ tokens Scanner<EncodingPolicy>::ScanCore(bool identifyKwds)
token = this->ScanStringConstant((OLECHAR)ch, &pchT);
p = pchT;
break;
#ifdef ENABLE_TEST_HOOKS
case '@':
if (this->PeekFirst(p,last) != '@')
{
goto LDefault;
}
p++;
token = tkIntCommand;
break;
#endif
}

break;
Expand Down
3 changes: 3 additions & 0 deletions lib/Parser/kwd-lsc.h
Original file line number Diff line number Diff line change
Expand Up @@ -173,6 +173,9 @@ TOK_DCL(tkEllipsis , No, knopNone ,Spr, knopEllipsis ) // ...
TOK_DCL(tkLParen , No, knopNone , No, knopNone ) // (
TOK_DCL(tkLBrack , No, knopNone , No, knopNone ) // [
TOK_DCL(tkDot , No, knopNone , No, knopNone ) // .
#ifdef ENABLE_TEST_HOOKS
TOK_DCL(tkIntCommand , No, knopNone , No, knopNone ) //@@
#endif

// String template tokens
TOK_DCL(tkStrTmplBasic , No, knopNone , No, knopNone ) // `...`
Expand Down
4 changes: 4 additions & 0 deletions lib/Parser/pnodewalk.h
Original file line number Diff line number Diff line change
Expand Up @@ -536,6 +536,10 @@ class ParseNodeWalker : public WalkerPolicy
case knopExportDefault:
return Walk(pnode->AsParseNodeExportDefault()->pnodeExpr, context);

#ifdef ENABLE_TEST_HOOKS
case knopIntCommand:
return Walk(pnode->AsParseNodeInternalCommand()->params, context);
#endif
default:
{
uint fnop = ParseNode::Grfnop(pnode->nop);
Expand Down
5 changes: 3 additions & 2 deletions lib/Parser/ptlist.h
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,8 @@ PTNODE(knopObjectPatternMember, "{:} = " , Nop , Bin , fnopBin
PTNODE(knopArrayPattern, "[] = " , Nop , ArrLit , fnopUni , "ArrayAssignmentPattern" )
PTNODE(knopParamPattern, "({[]})" , Nop , ParamPattern, fnopUni , "DestructurePattern" )
PTNODE(knopExportDefault, "export default" , Nop , ExportDefault,fnopNone , "ExportDefault" )


#ifdef ENABLE_TEST_HOOKS
PTNODE(knopIntCommand, "intCommand" , Nop , None , fnopNone , "Internal Command" )
#endif
#undef PTNODE
#undef OP
8 changes: 8 additions & 0 deletions lib/Parser/ptree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -263,6 +263,14 @@ ParseNodeModule * ParseNode::AsParseNodeModule()
return reinterpret_cast<ParseNodeModule*>(this);
}

#ifdef ENABLE_TEST_HOOKS
ParseNodeInternalCommand * ParseNode::AsParseNodeInternalCommand()
{
Assert(this->nop == knopIntCommand);
return reinterpret_cast<ParseNodeInternalCommand *>(this);
}
#endif

IdentPtr ParseNode::name()
{
if (this->nop == knopStr)
Expand Down
Loading