Skip to content

Commit

Permalink
[MERGE #5684 @sigatrev] inline JsBuiltIn helper CallInstanceFunction
Browse files Browse the repository at this point in the history
Merge pull request #5684 from sigatrev:inlineCallInstanceFunction

JsBuiltIns cannot do ```callback.call(thisArg, ...)``` because ```.call``` is loaded off the function prototype which could be changed a user. CallInstanceFunction performs the same operation, taking the form ```CallInstanceFunction(callback, thisArg, ...)```. This commit results in ```callback``` being directly inlined.
  • Loading branch information
sigatrev committed Sep 12, 2018
2 parents 21a310e + c8740cc commit d1524eb
Show file tree
Hide file tree
Showing 22 changed files with 3,222 additions and 3,427 deletions.
125 changes: 90 additions & 35 deletions lib/Backend/Inline.cpp

Large diffs are not rendered by default.

12 changes: 7 additions & 5 deletions lib/Backend/Inline.h
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ class Inline
#endif
IR::Instr * InlineGetterSetterFunction(IR::Instr *accessorInstr, const FunctionJITTimeInfo *const inlineeData, const StackSym *symCallerThis, const uint inlineCacheIndex, bool isGetter, bool *pIsInlined, uint recursiveInlineDepth);
IR::Instr * InlineFunctionCommon(IR::Instr *callInstr, bool originalCallTargetOpndIsJITOpt, StackSym* originalCallTargetStackSym, const FunctionJITTimeInfo *funcInfo, Func *inlinee, IR::Instr *instrNext,
IR::RegOpnd * returnValueOpnd, IR::Instr *inlineBailoutChecksBeforeInstr, const StackSym *symCallerThis, uint recursiveInlineDepth, bool safeThis = false, bool isApplyTarget = false, bool isCallbackCallApplyTarget = false);
IR::RegOpnd * returnValueOpnd, IR::Instr *inlineBailoutChecksBeforeInstr, const StackSym *symCallerThis, uint recursiveInlineDepth, bool safeThis = false, bool isApplyTarget = false);
IR::Instr * SimulateCallForGetterSetter(IR::Instr *accessorInstr, IR::Instr* insertInstr, IR::PropertySymOpnd* methodOpnd, bool isGetter);

IR::Instr * InlineApply(IR::Instr *callInstr, const FunctionJITTimeInfo * applyData, const FunctionJITTimeInfo * inlinerData, const StackSym *symThis, bool* pIsInlined, uint callSiteId, uint recursiveInlineDepth, uint argsCount);
Expand All @@ -73,11 +73,12 @@ class Inline
const StackSym *symThis, IR::Instr ** returnInstr, uint recursiveInlineDepth, bool isArrayOpndArgumentsObject, uint argsCount);
void GetArgInstrsForCallAndApply(IR::Instr* callInstr, IR::Instr** implicitThisArgOut, IR::Instr** explicitThisArgOut, IR::Instr** argumentsOrArrayArgOut, uint &argOutCount);
_Success_(return != false) bool TryGetCallApplyAndTargetLdInstrs(IR::Instr * callInstr, _Outptr_result_nullonfailure_ IR::Instr ** callApplyLdInstr, _Outptr_result_nullonfailure_ IR::Instr ** callApplyTargetLdInstr);
IR::Instr * InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeData, const FunctionJITTimeInfo * inlinerData, const StackSym *symThis, bool* pIsInlined, uint callSiteId, uint recursiveInlineDepth);
IR::Instr * InlineCall(IR::Instr *callInstr, const FunctionJITTimeInfo * inlineeData, const FunctionJITTimeInfo * inlinerData, const StackSym *symThis, bool* pIsInlined, uint callSiteId, uint recursiveInlineDepth, bool isCallInstanceFunction);
bool InlineCallTarget(IR::Instr *callInstr, const FunctionJITTimeInfo* inlinerData, const FunctionJITTimeInfo** pInlineeData, const FunctionJITTimeInfo *callFuncInfo,
const StackSym *symThis, IR::Instr ** returnInstr, uint recursiveInlineDepth);
const StackSym *symThis, IR::Instr ** returnInstr, uint recursiveInlineDepth, bool isCallInstanceFunction);

bool TryGetCallApplyInlineeData(const FunctionJITTimeInfo* inlinerData, IR::Instr * callApplyLdInstr, IR::Instr * callApplyTargetLdInstr, const FunctionJITTimeInfo ** inlineeData, Js::InlineCacheIndex * inlineCacheIndex, IR::Instr ** callbackDefInstr);
bool TryGetCallApplyInlineeData(const FunctionJITTimeInfo* inlinerData, IR::Instr * callInstr, IR::Instr * callApplyLdInstr, IR::Instr * callApplyTargetLdInstr, const FunctionJITTimeInfo ** inlineeData, Js::InlineCacheIndex * inlineCacheIndex,
IR::Instr ** callbackDefInstr, bool isCallInstanceFunction);

bool InlConstFoldArg(IR::Instr *instr, __in_ecount_opt(callerArgOutCount) IR::Instr *callerArgOuts[], Js::ArgSlot callerArgOutCount);
bool InlConstFold(IR::Instr *instr, IntConstType *pValue, __in_ecount_opt(callerArgOutCount) IR::Instr *callerArgOuts[], Js::ArgSlot callerArgOutCount);
Expand All @@ -100,6 +101,7 @@ class Inline
IR::Instr * TryGetCallbackDefInstr(StackSym * callbackSym);
IR::Instr * TryGetCallbackDefInstrForCallInstr(IR::Instr * callInstr);
IR::Instr * TryGetCallbackDefInstrForCallApplyTarget(IR::Instr * callApplyLdInstr);
IR::Instr * TryGetCallbackDefInstrForCallInstanceFunction(IR::Instr * callInstr);

IR::Instr * InlineSpread(IR::Instr *spreadCall);

Expand All @@ -108,7 +110,7 @@ class Inline
void SetupInlineeFrame(Func *inlinee, IR::Instr *inlineeStart, Js::ArgSlot actualCount, IR::Opnd *functionObject);
void FixupExtraActualParams(IR::Instr * instr, IR::Instr *argOuts[], IR::Instr *argOutsExtra[], uint index, uint actualCount, Js::ProfileId callSiteId);
void RemoveExtraFixupArgouts(IR::Instr* instr, uint argoutRemoveCount, Js::ProfileId callSiteId);
IR::Instr* PrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, IR::Instr *insertBeforeInstr, bool isCallbackCallApplyTarget = false);
IR::Instr* PrepareInsertionPoint(IR::Instr *callInstr, const FunctionJITTimeInfo *funcInfo, IR::Instr *insertBeforeInstr);
IR::ByteCodeUsesInstr* EmitFixedMethodOrFunctionObjectChecksForBuiltIns(IR::Instr *callInstr, IR::Instr * funcObjCheckInsertInstr, const FunctionJITTimeInfo * inlineeInfo, bool isPolymorphic, bool isBuiltIn, bool isCtor, bool isInlined);
Js::ArgSlot MapActuals(IR::Instr *callInstr, __out_ecount(maxParamCount) IR::Instr *argOuts[], Js::ArgSlot formalCount, Func *inlinee, Js::ProfileId callSiteId, bool *stackArgsArgOutExpanded, IR::Instr *argOutsExtra[] = nullptr, Js::ArgSlot maxParamCount = Js::InlineeCallInfo::MaxInlineeArgoutCount);
uint32 CountActuals(IR::Instr *callIntr);
Expand Down
3 changes: 3 additions & 0 deletions lib/Backend/InliningDecider.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -553,6 +553,9 @@ bool InliningDecider::GetBuiltInInfoCommon(
case Js::JavascriptBuiltInFunction::JavascriptFunction_Call:
*inlineCandidateOpCode = Js::OpCode::InlineFunctionCall;
break;
case Js::JavascriptBuiltInFunction::EngineInterfaceObject_CallInstanceFunction:
*inlineCandidateOpCode = Js::OpCode::InlineCallInstanceFunction;
break;

// The following are not currently inlined, but are tracked for their return type
// TODO: Add more built-ins that return objects. May consider tracking all built-ins.
Expand Down
2 changes: 2 additions & 0 deletions lib/Common/ConfigFlagsList.h
Original file line number Diff line number Diff line change
Expand Up @@ -586,6 +586,7 @@ PHASE(All)
#endif
#define DEFAULT_CONFIG_JitRepro (false)
#define DEFAULT_CONFIG_LdChakraLib (false)
#define DEFAULT_CONFIG_TestChakraLib (false)
#define DEFAULT_CONFIG_EntryPointInfoRpcData (false)

// ES6 DEFAULT BEHAVIOR
Expand Down Expand Up @@ -1078,6 +1079,7 @@ FLAGNR(Boolean, JitRepro , "Add Function.invokeJit to execute codeg
FLAGNR(Boolean, EntryPointInfoRpcData , "Keep encoded rpc buffer for jitted function on EntryPointInfo until cleanup", DEFAULT_CONFIG_EntryPointInfoRpcData)

FLAGNR(Boolean, LdChakraLib , "Access to the Chakra internal library with the __chakraLibrary keyword", DEFAULT_CONFIG_LdChakraLib)
FLAGNR(Boolean, TestChakraLib , "Access to the Chakra internal library with the __chakraLibrary keyword without global access restriction", DEFAULT_CONFIG_TestChakraLib)
// ES6 (BLUE+1) features/flags

// Master ES6 flag to enable STABLE ES6 features/flags
Expand Down
3 changes: 2 additions & 1 deletion lib/Runtime/ByteCode/ByteCodeEmitter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -5128,7 +5128,8 @@ void ByteCodeGenerator::EmitPropLoad(Js::RegSlot lhsLocation, Symbol *sym, Ident
opcode = Js::OpCode::LdUndef;
break;
case Js::PropertyIds::__chakraLibrary:
if (CONFIG_FLAG(LdChakraLib)) {
if (CONFIG_FLAG(LdChakraLib) || CONFIG_FLAG(TestChakraLib))
{
opcode = Js::OpCode::LdChakraLib;
}
break;
Expand Down
1 change: 1 addition & 0 deletions lib/Runtime/ByteCode/OpCodes.h
Original file line number Diff line number Diff line change
Expand Up @@ -765,6 +765,7 @@ MACRO_BACKEND_ONLY( InlineArrayPop, Empty, OpSideEffect|OpInli
MACRO_BACKEND_ONLY( InlineArrayPush, Empty, OpSideEffect|OpInlinableBuiltIn|OpHasImplicitCall)
MACRO_BACKEND_ONLY( InlineFunctionApply, Empty, OpSideEffect|OpInlinableBuiltIn)
MACRO_BACKEND_ONLY( InlineFunctionCall, Empty, OpSideEffect|OpInlinableBuiltIn)
MACRO_BACKEND_ONLY( InlineCallInstanceFunction, Empty, OpSideEffect|OpInlinableBuiltIn)
MACRO_BACKEND_ONLY( InlineRegExpExec, Empty, OpSideEffect|OpInlinableBuiltIn)

MACRO_BACKEND_ONLY( CallIFixed, Empty, OpSideEffect|OpUseAllFields|OpCallInstr|OpInlineCallInstr)
Expand Down
15 changes: 5 additions & 10 deletions lib/Runtime/Library/EngineInterfaceObject.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,6 @@ namespace Js
NoProfileFunctionInfo EngineInterfaceObject::EntryInfo::SetPrototype(FORCE_NO_WRITE_BARRIER_TAG(EngineInterfaceObject::Entry_SetPrototype));
NoProfileFunctionInfo EngineInterfaceObject::EntryInfo::GetArrayLength(FORCE_NO_WRITE_BARRIER_TAG(EngineInterfaceObject::Entry_GetArrayLength));
NoProfileFunctionInfo EngineInterfaceObject::EntryInfo::RegexMatch(FORCE_NO_WRITE_BARRIER_TAG(EngineInterfaceObject::Entry_RegexMatch));
NoProfileFunctionInfo EngineInterfaceObject::EntryInfo::CallInstanceFunction(FORCE_NO_WRITE_BARRIER_TAG(EngineInterfaceObject::Entry_CallInstanceFunction));

#ifndef GlobalBuiltIn
#define GlobalBuiltIn(global, method) \
Expand Down Expand Up @@ -426,8 +425,7 @@ namespace Js
{
EngineInterfaceObject_CommonFunctionProlog(function, callInfo);

Assert(args.Info.Count <= 5);
if (callInfo.Count < 3 || args.Info.Count > 5 || !JavascriptConversion::IsCallable(args.Values[1]) || !RecyclableObject::Is(args.Values[2]))
if (callInfo.Count < 3 || !JavascriptConversion::IsCallable(args.Values[1]) || !RecyclableObject::Is(args.Values[2]))
{
return scriptContext->GetLibrary()->GetUndefined();
}
Expand All @@ -437,19 +435,16 @@ namespace Js
AssertOrFailFastMsg(func != scriptContext->GetLibrary()->GetUndefined(), "Trying to callInstanceFunction(undefined, ...)");

//Shift the arguments by 2 so argument at index 2 becomes the 'this' argument at index 0
Var newVars[3];
Js::Arguments newArgs(callInfo, newVars);

for (uint i = 0; i<args.Info.Count - 2; ++i)
for (uint i = 0; i < args.Info.Count - 2; ++i)
{
newArgs.Values[i] = args.Values[i + 2];
args.Values[i] = args.Values[i + 2];
}

newArgs.Info.Count = args.Info.Count - 2;
args.Info.Count -= 2;

BEGIN_SAFE_REENTRANT_CALL(scriptContext->GetThreadContext())
{
return JavascriptFunction::CallFunction<true>(func, func->GetEntryPoint(), newArgs);
return JavascriptFunction::CallFunction<true>(func, func->GetEntryPoint(), args);
}
END_SAFE_REENTRANT_CALL
}
Expand Down
2 changes: 1 addition & 1 deletion lib/Runtime/Library/EngineInterfaceObject.h
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ namespace Js
static NoProfileFunctionInfo SetPrototype;
static NoProfileFunctionInfo GetArrayLength;
static NoProfileFunctionInfo RegexMatch;
static NoProfileFunctionInfo CallInstanceFunction;
static FunctionInfo CallInstanceFunction;

#ifndef GlobalBuiltIn
#define GlobalBuiltIn(global, method) \
Expand Down
2 changes: 2 additions & 0 deletions lib/Runtime/Library/JavascriptBuiltInFunctionList.h
Original file line number Diff line number Diff line change
Expand Up @@ -505,4 +505,6 @@ BUILTIN(AtomicsObject, Wait, EntryWait, FunctionInfo::ErrorOnNew)
BUILTIN(AtomicsObject, Notify, EntryNotify, FunctionInfo::ErrorOnNew)
BUILTIN(AtomicsObject, Xor, EntryXor, FunctionInfo::ErrorOnNew)

BUILTIN(EngineInterfaceObject, CallInstanceFunction, Entry_CallInstanceFunction, FunctionInfo::ErrorOnNew | FunctionInfo::DoNotProfile)

#undef BUILTIN_TEMPLATE
1 change: 1 addition & 0 deletions lib/Runtime/Library/JavascriptBuiltInFunctions.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
// Licensed under the MIT license. See LICENSE.txt file in the project root for full license information.
//-------------------------------------------------------------------------------------------------------
#include "RuntimeLibraryPch.h"
#include "EngineInterfaceObject.h"

namespace Js
{
Expand Down
5 changes: 5 additions & 0 deletions lib/Runtime/Library/JavascriptLibrary.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -4741,6 +4741,9 @@ namespace Js
JavascriptLibrary* library = chakraLibraryObject->GetLibrary();
typeHandler->Convert(chakraLibraryObject, mode, 8);

Field(JavascriptFunction*)* builtinFuncs = library->GetBuiltinFunctions();
JavascriptFunction * func = nullptr;

library->AddFunctionToLibraryObject(chakraLibraryObject, PropertyIds::toLength, &JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_ToLengthFunction, 1);
library->AddFunctionToLibraryObject(chakraLibraryObject, PropertyIds::toInteger, &JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_ToIntegerFunction, 1);
library->AddFunctionToLibraryObject(chakraLibraryObject, PropertyIds::GetLength, &JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_GetLength, 1);
Expand All @@ -4749,6 +4752,8 @@ namespace Js
library->AddMember(chakraLibraryObject, PropertyIds::Object, library->objectConstructor);
library->AddFunctionToLibraryObject(chakraLibraryObject, PropertyIds::arraySpeciesCreate, &JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_ArraySpeciesCreate, 2);
library->AddFunctionToLibraryObject(chakraLibraryObject, PropertyIds::arrayCreateDataPropertyOrThrow, &JsBuiltInEngineInterfaceExtensionObject::EntryInfo::JsBuiltIn_Internal_ArrayCreateDataPropertyOrThrow, 3);
func = library->AddFunctionToLibraryObject(chakraLibraryObject, PropertyIds::builtInCallInstanceFunction, &EngineInterfaceObject::EntryInfo::CallInstanceFunction, 1);
builtinFuncs[BuiltinFunction::EngineInterfaceObject_CallInstanceFunction] = func;

return true;
}
Expand Down
64 changes: 15 additions & 49 deletions lib/Runtime/Library/JsBuiltIn/JsBuiltIn.js
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@
__chakraLibrary.raiseNonObjectFromIterable = platform.raiseNonObjectFromIterable;
__chakraLibrary.raiseLengthIsTooBig = platform.raiseLengthIsTooBig;
__chakraLibrary.raiseFunctionArgument_NeedFunction = platform.raiseFunctionArgument_NeedFunction;
__chakraLibrary.callInstanceFunc = platform.builtInCallInstanceFunction;
__chakraLibrary.functionBind = platform.builtInJavascriptFunctionEntryBind;
__chakraLibrary.objectDefineProperty = _objectDefineProperty;

Expand Down Expand Up @@ -220,32 +219,15 @@
let k = 0;
let to = 0;

if (thisArg === undefined) {
// fast path.
while (k < len) {
if (k in o) {
let kValue = o[k];
if (callbackfn(kValue, k, o)) {
__chakraLibrary.arrayCreateDataPropertyOrThrow(a, to, kValue);
to++;
}
}
k++;
}
} else {
// slow path.
// safe equivalent of calling "callbackfn.bind(thisArg)"
let boundCallback = __chakraLibrary.callInstanceFunc(__chakraLibrary.functionBind, callbackfn, thisArg);
while (k < len) {
if (k in o) {
let kValue = o[k];
if (boundCallback(kValue, k, o)) {
__chakraLibrary.arrayCreateDataPropertyOrThrow(a, to, kValue);
to++;
}
while (k < len) {
if (k in o) {
let kValue = o[k];
if (__chakraLibrary.builtInCallInstanceFunction(callbackfn, thisArg, kValue, k, o)) {
__chakraLibrary.arrayCreateDataPropertyOrThrow(a, to, kValue);
to++;
}
k++;
}
k++;
}

return a;
Expand Down Expand Up @@ -297,7 +279,7 @@
return targetIndex;
});

platform.registerChakraLibraryFunction("FlattenIntoArrayMapped", function(target, source, sourceLen, start, mapperFunction) {
platform.registerChakraLibraryFunction("FlattenIntoArrayMapped", function(target, source, sourceLen, start, mapperFunction, thisArg) {
"use strict";
// this is FlattenIntoArray from the flat/flatMap proposal BUT with:
// depth = 1 and the presence of a mapperFunction guaranteed
Expand All @@ -320,7 +302,7 @@
// ii. If mapperFunction is present, then
// 1. Assert: thisArg is present.
// 2. Set element to ? Call(mapperFunction, thisArg , element, sourceIndex, source).
element = mapperFunction(source[sourceIndex], sourceIndex, source);
element = __chakraLibrary.builtInCallInstanceFunction(mapperFunction, thisArg, source[sourceIndex], sourceIndex, source);
// iii. Let shouldFlatten be false.
// iv. If depth > 0, then
// 1. Set shouldFlatten to ? IsArray(element).
Expand Down Expand Up @@ -399,12 +381,7 @@
//5. Let A be ? ArraySpeciesCreate(O, 0).
const A = __chakraLibrary.arraySpeciesCreate(o, 0);
//6. Perform ? FlattenIntoArray(A, O, sourceLen, 0, depthNum).
if (thisArg === undefined) {
__chakraLibrary.FlattenIntoArrayMapped(A, o, sourceLen, 0, mapperFunction);
} else {
const func = __chakraLibrary.callInstanceFunc(__chakraLibrary.functionBind, mapperFunction, thisArg);
__chakraLibrary.FlattenIntoArrayMapped(A, o, sourceLen, 0, func);
}
__chakraLibrary.FlattenIntoArrayMapped(A, o, sourceLen, 0, mapperFunction, thisArg);
//7. Return A.
return A;
});
Expand All @@ -423,23 +400,12 @@

let k = 0;

if (thisArg === undefined) {
while (k < len) {
if (k in o) {
let kValue = o[k];
callbackfn(kValue, k, o);
}
k++;
}
} else {
let boundCallback = __chakraLibrary.callInstanceFunc(__chakraLibrary.functionBind, callbackfn, thisArg);
while (k < len) {
if (k in o) {
let kValue = o[k];
boundCallback(kValue, k, o);
}
k++;
while (k < len) {
if (k in o) {
let kValue = o[k];
__chakraLibrary.builtInCallInstanceFunction(callbackfn, thisArg, kValue, k, o);
}
k++;
}

return undefined;
Expand Down
Loading

0 comments on commit d1524eb

Please sign in to comment.