[新需求]: Function.prototype.bind IR化

对 Function.prototype.bind IR化,以提升性能

Issue: #I9QCI5

Signed-off-by: lichenshuai <lichenshuai@huawei.com>
Change-Id: I2bc7df27a3cdba7baf895dc9064ad0d4c873f4e4
This commit is contained in:
lichenshuai 2024-05-20 16:01:13 +08:00
parent 9cff288511
commit cdb629a9b9
13 changed files with 218 additions and 11 deletions

View File

@ -135,7 +135,8 @@ namespace panda::ecmascript::kungfu {
V(Get, Map, Undefined())
#define BUILTINS_WITH_FUNCTION_STUB_BUILDER(V) \
V(PrototypeApply, Function, Undefined())
V(PrototypeApply, Function, Undefined()) \
V(PrototypeBind, Function, Undefined())
#define BUILTINS_WITH_NUMBER_STUB_BUILDER(V) \
V(ParseFloat, Number, Undefined()) \
@ -287,7 +288,6 @@ namespace panda::ecmascript::kungfu {
V(ReflectHas) \
V(ReflectConstruct) \
V(ReflectApply) \
V(FunctionPrototypeBind) \
V(FunctionPrototypeCall) \
V(FunctionPrototypeHasInstance) \
V(TYPED_BUILTINS_INLINE_FIRST = MathAcos) \

View File

@ -109,6 +109,62 @@ void BuiltinsFunctionStubBuilder::PrototypeApply(GateRef glue, GateRef thisValue
}
}
void BuiltinsFunctionStubBuilder::PrototypeBind(GateRef glue, GateRef thisValue,
GateRef numArgs, Variable* res, Label *exit, Label *slowPath)
{
auto env = GetEnvironment();
Label targetIsHeapObject(env);
Label targetIsCallable(env);
Label targetIsJSFunctionOrBound(env);
Label targetNameAndLengthNotChange(env);
// 1. Let Target be the this value.
GateRef target = thisValue;
// 2. If IsCallable(Target) is false, throw a TypeError exception.
BRANCH(TaggedIsHeapObject(target), &targetIsHeapObject, slowPath);
Bind(&targetIsHeapObject);
BRANCH(IsCallable(target), &targetIsCallable, slowPath);
Bind(&targetIsCallable);
BRANCH(BoolOr(IsJSFunction(target), IsBoundFunction(target)), &targetIsJSFunctionOrBound, slowPath);
Bind(&targetIsJSFunctionOrBound);
{
GateRef hclass = LoadHClass(target);
GateRef nameProperty = GetPropertyInlinedProps(target, hclass,
Int32(JSFunction::NAME_INLINE_PROPERTY_INDEX));
GateRef lengthProperty = GetPropertyInlinedProps(target, hclass,
Int32(JSFunction::LENGTH_INLINE_PROPERTY_INDEX));
GateRef nameAccessor = GetGlobalConstantValue(VariableType::JS_POINTER(), glue,
ConstantIndex::FUNCTION_NAME_ACCESSOR);
GateRef lengthAccessor = GetGlobalConstantValue(VariableType::JS_POINTER(), glue,
ConstantIndex::FUNCTION_LENGTH_ACCESSOR);
BRANCH(BoolAnd(IntPtrEqual(nameProperty, nameAccessor), IntPtrEqual(lengthProperty, lengthAccessor)),
&targetNameAndLengthNotChange, slowPath);
Bind(&targetNameAndLengthNotChange);
{
Label numArgsMoreThan1(env);
Label createTaggedArray(env);
GateRef thisArg = GetCallArg0(numArgs);
DEFVARIABLE(argsLength, VariableType::INT32(), Int32(0));
BRANCH(Int64GreaterThan(numArgs, Int64(1)), &numArgsMoreThan1, &createTaggedArray);
Bind(&numArgsMoreThan1);
{
argsLength = Int32Sub(TruncInt64ToInt32(numArgs), Int32(1));
Jump(&createTaggedArray);
}
Bind(&createTaggedArray);
// 3. Let args be a new (possibly empty) List consisting of all of the argument
// values provided after thisArg in order.
GateRef argsArray = NewTaggedArrayFromArgs(glue, Int32(1), *argsLength, numArgs);
// 4. Let F be BoundFunctionCreate(Target, thisArg, args).
NewObjectStubBuilder newBuilder(this);
GateRef boundFunction = newBuilder.NewJSBoundFunction(glue, target, thisArg, argsArray);
// use default name and length property because they are not changed
res->WriteVariable(boundFunction);
Jump(exit);
}
}
}
// return elements
GateRef BuiltinsFunctionStubBuilder::BuildArgumentsListFastElements(GateRef glue, GateRef arrayObj)
{
@ -286,4 +342,70 @@ GateRef BuiltinsFunctionStubBuilder::MakeArgListWithHole(GateRef glue, GateRef a
env->SubCfgExit();
return ret;
}
GateRef BuiltinsFunctionStubBuilder::NewTaggedArrayFromArgs(GateRef glue, GateRef startIndex, GateRef length,
GateRef numArgs)
{
auto env = GetEnvironment();
Label subentry(env);
env->SubCfgEntry(&subentry);
DEFVARIABLE(res, VariableType::JS_ANY(), Undefined());
DEFVARIABLE(i, VariableType::INT32(), Int32(0));
DEFVARIABLE(value, VariableType::JS_ANY(), Undefined());
NewObjectStubBuilder newBuilder(this);
res = newBuilder.NewTaggedArray(glue, length);
Label loopHead(env);
Label loopEnd(env);
Label afterLoop(env);
BRANCH(Int32LessThan(*i, length), &loopHead, &afterLoop);
LoopBegin(&loopHead);
{
Label valueArg0(env);
Label valueNotArg0(env);
Label valueArg1(env);
Label valueNotArg1(env);
Label valueArg2(env);
Label valueNotArg2(env);
Label valueSet(env);
GateRef index = Int32Add(*i, startIndex);
BRANCH(Int32Equal(index, Int32(0)), &valueArg0, &valueNotArg0); // 0: get arg0
Bind(&valueArg0);
{
value = GetCallArg0(numArgs);
Jump(&valueSet);
}
Bind(&valueNotArg0);
BRANCH(Int32Equal(index, Int32(1)), &valueArg1, &valueNotArg1); // 1: get arg1
Bind(&valueArg1);
{
value = GetCallArg1(numArgs);
Jump(&valueSet);
}
Bind(&valueNotArg1);
BRANCH(Int32Equal(index, Int32(2)), &valueArg2, &valueNotArg2); // 2: get arg2
Bind(&valueArg2);
{
value = GetCallArg2(numArgs);
Jump(&valueSet);
}
Bind(&valueNotArg2);
{
// currently argv will not be used in builtins IR except constructor
value = GetArgFromArgv(ZExtInt32ToPtr(index));
Jump(&valueSet);
}
Bind(&valueSet);
SetValueToTaggedArray(VariableType::JS_ANY(), glue, *res, *i, *value);
i = Int32Add(*i, Int32(1));
BRANCH(Int32LessThan(*i, length), &loopEnd, &afterLoop);
}
Bind(&loopEnd);
LoopEnd(&loopHead);
Bind(&afterLoop);
auto ret = *res;
env->SubCfgExit();
return ret;
}
} // namespace panda::ecmascript::kungfu

View File

@ -34,6 +34,7 @@ BUILTINS_WITH_FUNCTION_STUB_BUILDER(DECLARE_BUILTINS_FUNCTION_STUB_BUILDER)
GateRef BuildArgumentsListFastElements(GateRef glue, GateRef arrayObj);
private:
GateRef MakeArgListWithHole(GateRef glue, GateRef argv, GateRef length);
GateRef NewTaggedArrayFromArgs(GateRef glue, GateRef startIndex, GateRef length, GateRef numArgs);
};
} // namespace panda::ecmascript::kungfu
#endif // ECMASCRIPT_COMPILER_BUILTINS_FUNCTION_STUB_BUILDER_H

View File

@ -868,6 +868,51 @@ void NewObjectStubBuilder::InitializeJSFunction(GateRef glue, GateRef func, Gate
return;
}
GateRef NewObjectStubBuilder::NewJSBoundFunction(GateRef glue, GateRef target, GateRef boundThis, GateRef args)
{
auto env = GetEnvironment();
Label subentry(env);
env->SubCfgEntry(&subentry);
Label exit(env);
DEFVARIABLE(result, VariableType::JS_ANY(), Undefined());
GateRef glueGlobalEnvOffset = IntPtr(JSThread::GlueData::GetGlueGlobalEnvOffset(env->Is32Bit()));
GateRef glueGlobalEnv = Load(VariableType::NATIVE_POINTER(), glue, glueGlobalEnvOffset);
GateRef hclass = GetGlobalEnvValue(VariableType::JS_ANY(), glueGlobalEnv, GlobalEnv::BOUND_FUNCTION_CLASS);
result = NewJSObject(glue, hclass);
GateRef nameAccessor = GetGlobalConstantValue(VariableType::JS_POINTER(), glue,
ConstantIndex::FUNCTION_NAME_ACCESSOR);
SetPropertyInlinedProps(glue, *result, hclass, nameAccessor,
Int32(JSFunction::NAME_INLINE_PROPERTY_INDEX));
GateRef lengthAccessor = GetGlobalConstantValue(VariableType::JS_POINTER(), glue,
ConstantIndex::FUNCTION_LENGTH_ACCESSOR);
SetPropertyInlinedProps(glue, *result, hclass, lengthAccessor,
Int32(JSFunction::LENGTH_INLINE_PROPERTY_INDEX));
SetJSObjectTaggedField(glue, *result, JSBoundFunction::BOUND_TARGET_OFFSET, target);
SetJSObjectTaggedField(glue, *result, JSBoundFunction::BOUND_THIS_OFFSET, boundThis);
SetJSObjectTaggedField(glue, *result, JSBoundFunction::BOUND_ARGUMENTS_OFFSET, args);
GateRef method = GetGlobalConstantValue(VariableType::JS_POINTER(), glue,
ConstantIndex::BOUND_FUNCTION_METHOD_INDEX);
SetMethodToFunction(glue, *result, method);
Label targetIsHeapObject(env);
Label targetIsConstructor(env);
BRANCH(TaggedIsHeapObject(target), &targetIsHeapObject, &exit);
Bind(&targetIsHeapObject);
BRANCH(IsConstructor(target), &targetIsConstructor, &exit);
Bind(&targetIsConstructor);
{
GateRef resultHClass = LoadHClass(*result);
SetHClassBit<JSHClass::ConstructorBit>(glue, resultHClass, Int32(1));
Jump(&exit);
}
Bind(&exit);
auto ret = *result;
env->SubCfgExit();
return ret;
}
GateRef NewObjectStubBuilder::EnumerateObjectProperties(GateRef glue, GateRef obj)
{
auto env = GetEnvironment();

View File

@ -70,6 +70,7 @@ public:
FunctionKind targetKind = FunctionKind::LAST_FUNCTION_KIND);
void InitializeJSFunction(GateRef glue, GateRef func, GateRef kind,
FunctionKind getKind = FunctionKind::LAST_FUNCTION_KIND);
GateRef NewJSBoundFunction(GateRef glue, GateRef target, GateRef boundThis, GateRef args);
GateRef EnumerateObjectProperties(GateRef glue, GateRef obj);
void NewArgumentsList(Variable *result, Label *exit, GateRef sp, GateRef startIdx, GateRef numArgs);
void NewArgumentsObj(Variable *result, Label *exit, GateRef argumentsList, GateRef numArgs);

View File

@ -2637,14 +2637,15 @@ inline GateRef StubBuilder::SetTaggedRepInPropAttr(GateRef attr)
return newVal;
}
inline void StubBuilder::SetHasConstructorToHClass(GateRef glue, GateRef hClass, GateRef value)
template<class T>
void StubBuilder::SetHClassBit(GateRef glue, GateRef hClass, GateRef value)
{
GateRef bitfield = Load(VariableType::INT32(), hClass, IntPtr(JSHClass::BIT_FIELD_OFFSET));
GateRef mask = Int32LSL(
Int32((1LU << JSHClass::HasConstructorBits::SIZE) - 1),
Int32(JSHClass::HasConstructorBits::START_BIT));
Int32((1LU << T::SIZE) - 1),
Int32(T::START_BIT));
GateRef newVal = Int32Or(Int32And(bitfield, Int32Not(mask)),
Int32LSL(value, Int32(JSHClass::HasConstructorBits::START_BIT)));
Int32LSL(value, Int32(T::START_BIT)));
Store(VariableType::INT32(), glue, hClass, IntPtr(JSHClass::BIT_FIELD_OFFSET), newVal);
}
@ -2899,6 +2900,11 @@ inline void StubBuilder::UpdateProfileTypeInfoCellType(GateRef glue, GateRef pro
env->SubCfgExit();
}
inline void StubBuilder::SetJSObjectTaggedField(GateRef glue, GateRef object, size_t offset, GateRef value)
{
Store(VariableType::JS_ANY(), glue, object, IntPtr(offset), value);
}
inline GateRef StubBuilder::GetGlobalObject(GateRef glue)
{
GateRef offset = IntPtr(JSThread::GlueData::GetGlobalObjOffset(env_->Is32Bit()));

View File

@ -1203,7 +1203,7 @@ GateRef StubBuilder::AddPropertyByName(GateRef glue, GateRef receiver, GateRef k
BRANCH(SetHasConstructorCondition(glue, receiver, key), &setHasCtor, &notSetHasCtor);
{
Bind(&setHasCtor);
SetHasConstructorToHClass(glue, hclass, Int32(1));
SetHClassBit<JSHClass::HasConstructorBits>(glue, hclass, Int32(1));
Jump(&afterCtorCon);
Bind(&notSetHasCtor);
Jump(&afterCtorCon);

View File

@ -650,7 +650,8 @@ public:
GateRef IsIntRepInPropAttr(GateRef attr);
GateRef IsDoubleRepInPropAttr(GateRef attr);
GateRef SetTaggedRepInPropAttr(GateRef attr);
void SetHasConstructorToHClass(GateRef glue, GateRef hClass, GateRef value);
template<class T>
void SetHClassBit(GateRef glue, GateRef hClass, GateRef value);
template<typename DictionaryT>
void UpdateValueInDict(GateRef glue, GateRef elements, GateRef index, GateRef value);
GateRef GetBitMask(GateRef bitoffset);
@ -701,6 +702,7 @@ public:
void SetRawProfileTypeInfoToFunction(GateRef glue, GateRef function, GateRef value);
void SetValueToProfileTypeInfoCell(GateRef glue, GateRef profileTypeInfoCell, GateRef value);
void UpdateProfileTypeInfoCellType(GateRef glue, GateRef profileTypeInfoCell);
void SetJSObjectTaggedField(GateRef glue, GateRef object, size_t offset, GateRef value);
GateRef GetGlobalObject(GateRef glue);
GateRef GetMethodFromFunction(GateRef function);
GateRef GetModuleFromFunction(GateRef function);

View File

@ -891,6 +891,17 @@ void EcmaVM::GenerateInternalNativeMethods()
method->SetFunctionKind(FunctionKind::NORMAL_FUNCTION);
internalNativeMethods_.emplace_back(method.GetTaggedValue());
}
// cache to global constants shared because context may change
CacheToGlobalConstants(GetMethodByIndex(MethodIndex::BUILTINS_GLOBAL_CALL_JS_BOUND_FUNCTION),
ConstantIndex::BOUND_FUNCTION_METHOD_INDEX);
}
void EcmaVM::CacheToGlobalConstants(JSTaggedValue value, ConstantIndex idx)
{
auto thread = GetJSThread();
auto context = thread->GetCurrentEcmaContext();
auto constants = const_cast<GlobalEnvConstants *>(context->GlobalConstants());
constants->SetConstant(idx, value);
}
JSTaggedValue EcmaVM::GetMethodByIndex(MethodIndex idx)

View File

@ -795,6 +795,7 @@ private:
// For Internal Native MethodLiteral.
void GenerateInternalNativeMethods();
void CacheToGlobalConstants(JSTaggedValue value, ConstantIndex constant);
NO_MOVE_SEMANTIC(EcmaVM);
NO_COPY_SEMANTIC(EcmaVM);

View File

@ -630,7 +630,8 @@ class ObjectFactory;
V(JSTaggedValue, EmptyMutantArray, EMPTY_MUTANT_ARRAY_OBJECT_INDEX, ecma_roots_special) \
V(JSTaggedValue, Uint64MaxBigInt, UINT64_MAX_BIGINT_INDEX, ecma_roots_special) \
V(JSTaggedValue, Int64MaxBigInt, INT64_MAX_BIGINT_INDEX, ecma_roots_special) \
V(JSTaggedValue, EmptyProfileTypeInfoCell, EMPTY_PROFILE_TYPE_INFO_CELL_INDEX, ecma_roots_special)
V(JSTaggedValue, EmptyProfileTypeInfoCell, EMPTY_PROFILE_TYPE_INFO_CELL_INDEX, ecma_roots_special) \
V(JSTaggedValue, BoundFunctionMethod, BOUND_FUNCTION_METHOD_INDEX, ecma_roots_special)
#define GLOBAL_ENV_CACHES(V) \
V(JSTaggedValue, CachedJSCollatorLocales, CACHED_JSCOLLATOR_LOCALES_INDEX, cachedCollatorLocales)
@ -661,7 +662,7 @@ enum class ConstantIndex : size_t {
CONSTANT_END = CONSTANT_COUNT,
SHARED_BEGIN = HCLASS_CLASS_INDEX,
SHARED_END = EMPTY_PROFILE_TYPE_INFO_CELL_INDEX,
SHARED_END = BOUND_FUNCTION_METHOD_INDEX,
SHARED_HCLASS_BEGIN = HCLASS_CLASS_INDEX,
SHARED_HCLASS_END = VTABLE_CLASS_INDEX,

View File

@ -102,4 +102,16 @@ try {
new t9()
} catch(err) {
print(err);
}
}
function testFunc(...args) {
print(this);
print(args);
print(args.length);
}
let testFuncBound = testFunc.bind(0, 1, 2);
testFuncBound(3, 4, 5);
print(testFuncBound.name);
Object.defineProperty(testFunc, "name", {value: "testFuncChanged"});
testFuncBound = testFunc.bind();
print(testFuncBound.name);

View File

@ -23,3 +23,8 @@ function bound Float32Array() { [native code] }
0,1,2,3,4,5,6,7,8,9,
11
TypeError: Constructor is false
0
1,2,3,4,5
5
bound testFunc
bound testFuncChanged