Commit 519c82ce authored by Fanchen Kong's avatar Fanchen Kong Committed by V8 LUCI CQ

Collect receiver to feedback for prototype.apply

When a function is invoked by prototype.apply, it may undergo following transformation in the JSCallReducer:
	receiver.apply(this, args) ->
	this.receiver(...args) Since the new target (also the receiver of apply()) is not collected to the feedback slot, further speculative optimization on the new target is not available if the new target
is not a heapconstant.

With this CL, the receiver will be collected to the feedback instead of the target if the target is a prototype.apply. It may improve the performance of the following usecase by ~80%.

function reduceArray(func, arr, r) {
    for (var i = 0, len = arr.length; i < len; i++) {
            r = func.apply(null, r, arr[i]);
    }
    return r;
}

var a = 0; for (var i = 0; i < 10000000; i++) {
    a += reduceArray(Math.imul, [5,6,2,3,7,6,8,3,7,9,2,5,], 1);
}
console.log(a);

This CL also improves the runTime score of JetStream2/richards-wasm by ~45% in default, ~60% with --turbo-inline-js-wasm-calls.

Change-Id: I542eb8d3fcb592f4e0993af93ba1af70e89c3982
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2639813
Commit-Queue: Fanchen Kong <fanchen.kong@intel.com>
Reviewed-by: 's avatarRoss McIlroy <rmcilroy@chromium.org>
Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#74413}
parent b271648e
...@@ -262,6 +262,8 @@ extern enum UpdateFeedbackMode { kOptionalFeedback, kGuaranteedFeedback } ...@@ -262,6 +262,8 @@ extern enum UpdateFeedbackMode { kOptionalFeedback, kGuaranteedFeedback }
extern operator '==' macro UpdateFeedbackModeEqual( extern operator '==' macro UpdateFeedbackModeEqual(
constexpr UpdateFeedbackMode, constexpr UpdateFeedbackMode): constexpr bool; constexpr UpdateFeedbackMode, constexpr UpdateFeedbackMode): constexpr bool;
extern enum CallFeedbackContent extends int32 { kTarget, kReceiver }
extern enum UnicodeEncoding { UTF16, UTF32 } extern enum UnicodeEncoding { UTF16, UTF32 }
// Promise constants // Promise constants
...@@ -961,6 +963,8 @@ extern operator '|' macro ConstexprWord32Or( ...@@ -961,6 +963,8 @@ extern operator '|' macro ConstexprWord32Or(
constexpr int32, constexpr int32): constexpr int32; constexpr int32, constexpr int32): constexpr int32;
extern operator '^' macro Word32Xor(int32, int32): int32; extern operator '^' macro Word32Xor(int32, int32): int32;
extern operator '^' macro Word32Xor(uint32, uint32): uint32; extern operator '^' macro Word32Xor(uint32, uint32): uint32;
extern operator '<<' macro ConstexprWord32Shl(
constexpr uint32, constexpr int32): uint32;
extern operator '==' macro Word64Equal(int64, int64): bool; extern operator '==' macro Word64Equal(int64, int64): bool;
extern operator '==' macro Word64Equal(uint64, uint64): bool; extern operator '==' macro Word64Equal(uint64, uint64): bool;
...@@ -1296,6 +1300,9 @@ macro GetFastAliasedArgumentsMap(implicit context: Context)(): Map { ...@@ -1296,6 +1300,9 @@ macro GetFastAliasedArgumentsMap(implicit context: Context)(): Map {
macro GetWeakCellMap(implicit context: Context)(): Map { macro GetWeakCellMap(implicit context: Context)(): Map {
return %GetClassMapConstant<WeakCell>(); return %GetClassMapConstant<WeakCell>();
} }
macro GetPrototypeApplyFunction(implicit context: Context)(): JSFunction {
return *NativeContextSlot(ContextSlot::FUNCTION_PROTOTYPE_APPLY_INDEX);
}
// Call(Context, Target, Receiver, ...Args) // Call(Context, Target, Receiver, ...Args)
// TODO(joshualitt): Assuming the context parameter is for throwing when Target // TODO(joshualitt): Assuming the context parameter is for throwing when Target
......
...@@ -71,7 +71,8 @@ TF_BUILTIN(Call_ReceiverIsNullOrUndefined_Baseline, ...@@ -71,7 +71,8 @@ TF_BUILTIN(Call_ReceiverIsNullOrUndefined_Baseline,
auto context = LoadContextFromBaseline(); auto context = LoadContextFromBaseline();
auto feedback_vector = LoadFeedbackVectorFromBaseline(); auto feedback_vector = LoadFeedbackVectorFromBaseline();
auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot); auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
CollectCallFeedback(target, context, feedback_vector, slot); auto receiver = UndefinedConstant();
CollectCallFeedback(target, receiver, context, feedback_vector, slot);
TailCallBuiltin(Builtins::kCall_ReceiverIsNullOrUndefined, context, target, TailCallBuiltin(Builtins::kCall_ReceiverIsNullOrUndefined, context, target,
argc); argc);
} }
...@@ -83,7 +84,9 @@ TF_BUILTIN(Call_ReceiverIsNotNullOrUndefined_Baseline, ...@@ -83,7 +84,9 @@ TF_BUILTIN(Call_ReceiverIsNotNullOrUndefined_Baseline,
auto context = LoadContextFromBaseline(); auto context = LoadContextFromBaseline();
auto feedback_vector = LoadFeedbackVectorFromBaseline(); auto feedback_vector = LoadFeedbackVectorFromBaseline();
auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot); auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
CollectCallFeedback(target, context, feedback_vector, slot); CodeStubArguments args(this, argc);
auto receiver = args.GetReceiver();
CollectCallFeedback(target, receiver, context, feedback_vector, slot);
TailCallBuiltin(Builtins::kCall_ReceiverIsNotNullOrUndefined, context, target, TailCallBuiltin(Builtins::kCall_ReceiverIsNotNullOrUndefined, context, target,
argc); argc);
} }
...@@ -94,7 +97,9 @@ TF_BUILTIN(Call_ReceiverIsAny_Baseline, CallOrConstructBuiltinsAssembler) { ...@@ -94,7 +97,9 @@ TF_BUILTIN(Call_ReceiverIsAny_Baseline, CallOrConstructBuiltinsAssembler) {
auto context = LoadContextFromBaseline(); auto context = LoadContextFromBaseline();
auto feedback_vector = LoadFeedbackVectorFromBaseline(); auto feedback_vector = LoadFeedbackVectorFromBaseline();
auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot); auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
CollectCallFeedback(target, context, feedback_vector, slot); CodeStubArguments args(this, argc);
auto receiver = args.GetReceiver();
CollectCallFeedback(target, receiver, context, feedback_vector, slot);
TailCallBuiltin(Builtins::kCall_ReceiverIsAny, context, target, argc); TailCallBuiltin(Builtins::kCall_ReceiverIsAny, context, target, argc);
} }
...@@ -105,7 +110,8 @@ TF_BUILTIN(Call_ReceiverIsNullOrUndefined_WithFeedback, ...@@ -105,7 +110,8 @@ TF_BUILTIN(Call_ReceiverIsNullOrUndefined_WithFeedback,
auto context = Parameter<Context>(Descriptor::kContext); auto context = Parameter<Context>(Descriptor::kContext);
auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector); auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector);
auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot); auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
CollectCallFeedback(target, context, feedback_vector, slot); auto receiver = Parameter<Object>(Descriptor::kReceiver);
CollectCallFeedback(target, receiver, context, feedback_vector, slot);
TailCallBuiltin(Builtins::kCall_ReceiverIsNullOrUndefined, context, target, TailCallBuiltin(Builtins::kCall_ReceiverIsNullOrUndefined, context, target,
argc); argc);
} }
...@@ -117,7 +123,8 @@ TF_BUILTIN(Call_ReceiverIsNotNullOrUndefined_WithFeedback, ...@@ -117,7 +123,8 @@ TF_BUILTIN(Call_ReceiverIsNotNullOrUndefined_WithFeedback,
auto context = Parameter<Context>(Descriptor::kContext); auto context = Parameter<Context>(Descriptor::kContext);
auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector); auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector);
auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot); auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
CollectCallFeedback(target, context, feedback_vector, slot); auto receiver = Parameter<Object>(Descriptor::kReceiver);
CollectCallFeedback(target, receiver, context, feedback_vector, slot);
TailCallBuiltin(Builtins::kCall_ReceiverIsNotNullOrUndefined, context, target, TailCallBuiltin(Builtins::kCall_ReceiverIsNotNullOrUndefined, context, target,
argc); argc);
} }
...@@ -128,7 +135,8 @@ TF_BUILTIN(Call_ReceiverIsAny_WithFeedback, CallOrConstructBuiltinsAssembler) { ...@@ -128,7 +135,8 @@ TF_BUILTIN(Call_ReceiverIsAny_WithFeedback, CallOrConstructBuiltinsAssembler) {
auto context = Parameter<Context>(Descriptor::kContext); auto context = Parameter<Context>(Descriptor::kContext);
auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector); auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector);
auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot); auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
CollectCallFeedback(target, context, feedback_vector, slot); auto receiver = Parameter<Object>(Descriptor::kReceiver);
CollectCallFeedback(target, receiver, context, feedback_vector, slot);
TailCallBuiltin(Builtins::kCall_ReceiverIsAny, context, target, argc); TailCallBuiltin(Builtins::kCall_ReceiverIsAny, context, target, argc);
} }
...@@ -464,7 +472,8 @@ TF_BUILTIN(CallWithArrayLike_WithFeedback, CallOrConstructBuiltinsAssembler) { ...@@ -464,7 +472,8 @@ TF_BUILTIN(CallWithArrayLike_WithFeedback, CallOrConstructBuiltinsAssembler) {
auto context = Parameter<Context>(Descriptor::kContext); auto context = Parameter<Context>(Descriptor::kContext);
auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector); auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector);
auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot); auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
CollectCallFeedback(target, context, feedback_vector, slot); auto receiver = Parameter<Object>(Descriptor::kReceiver);
CollectCallFeedback(target, receiver, context, feedback_vector, slot);
CallOrConstructWithArrayLike(target, new_target, arguments_list, context); CallOrConstructWithArrayLike(target, new_target, arguments_list, context);
} }
...@@ -485,7 +494,9 @@ TF_BUILTIN(CallWithSpread_Baseline, CallOrConstructBuiltinsAssembler) { ...@@ -485,7 +494,9 @@ TF_BUILTIN(CallWithSpread_Baseline, CallOrConstructBuiltinsAssembler) {
auto context = LoadContextFromBaseline(); auto context = LoadContextFromBaseline();
auto feedback_vector = LoadFeedbackVectorFromBaseline(); auto feedback_vector = LoadFeedbackVectorFromBaseline();
auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot); auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
CollectCallFeedback(target, context, feedback_vector, slot); CodeStubArguments args(this, args_count);
auto receiver = args.GetReceiver();
CollectCallFeedback(target, receiver, context, feedback_vector, slot);
CallOrConstructWithSpread(target, new_target, spread, args_count, context); CallOrConstructWithSpread(target, new_target, spread, args_count, context);
} }
...@@ -497,7 +508,8 @@ TF_BUILTIN(CallWithSpread_WithFeedback, CallOrConstructBuiltinsAssembler) { ...@@ -497,7 +508,8 @@ TF_BUILTIN(CallWithSpread_WithFeedback, CallOrConstructBuiltinsAssembler) {
auto context = Parameter<Context>(Descriptor::kContext); auto context = Parameter<Context>(Descriptor::kContext);
auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector); auto feedback_vector = Parameter<FeedbackVector>(Descriptor::kFeedbackVector);
auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot); auto slot = UncheckedParameter<UintPtrT>(Descriptor::kSlot);
CollectCallFeedback(target, context, feedback_vector, slot); auto receiver = Parameter<Object>(Descriptor::kReceiver);
CollectCallFeedback(target, receiver, context, feedback_vector, slot);
CallOrConstructWithSpread(target, new_target, spread, args_count, context); CallOrConstructWithSpread(target, new_target, spread, args_count, context);
} }
......
...@@ -6,6 +6,10 @@ namespace ic { ...@@ -6,6 +6,10 @@ namespace ic {
namespace callable { namespace callable {
extern macro IncrementCallCount(FeedbackVector, uintptr): void; extern macro IncrementCallCount(FeedbackVector, uintptr): void;
const kCallFeedbackContentFieldMask: constexpr int32
generates 'FeedbackNexus::CallFeedbackContentField::kMask';
const kCallFeedbackContentFieldShift: constexpr uint32
generates 'FeedbackNexus::CallFeedbackContentField::kShift';
macro IsMonomorphic(feedback: MaybeObject, target: JSAny): bool { macro IsMonomorphic(feedback: MaybeObject, target: JSAny): bool {
return IsWeakReferenceToObject(feedback, target); return IsWeakReferenceToObject(feedback, target);
...@@ -50,8 +54,42 @@ macro TransitionToMegamorphic(implicit context: Context)( ...@@ -50,8 +54,42 @@ macro TransitionToMegamorphic(implicit context: Context)(
ReportFeedbackUpdate(feedbackVector, slotId, 'Call:TransitionMegamorphic'); ReportFeedbackUpdate(feedbackVector, slotId, 'Call:TransitionMegamorphic');
} }
macro TaggedEqualPrototypeApplyFunction(implicit context: Context)(
target: JSAny): bool {
return TaggedEqual(target, GetPrototypeApplyFunction());
}
macro FeedbackValueIsReceiver(implicit context: Context)(
feedbackVector: FeedbackVector, slotId: uintptr): bool {
const callCount: intptr = SmiUntag(Cast<Smi>(LoadFeedbackVectorSlot(
feedbackVector, slotId, kTaggedSize)) otherwise return false);
return (callCount & IntPtrConstant(kCallFeedbackContentFieldMask)) !=
IntPtrConstant(0);
}
macro SetCallFeedbackContent(implicit context: Context)(
feedbackVector: FeedbackVector, slotId: uintptr,
callFeedbackContent: constexpr CallFeedbackContent): void {
// Load the call count field from the feecback vector.
const callCount: intptr = SmiUntag(Cast<Smi>(LoadFeedbackVectorSlot(
feedbackVector, slotId, kTaggedSize)) otherwise return );
// The second lowest bits of the call count are used to state whether the
// feedback collected is a target or a receiver. Change that bit based on the
// callFeedbackContent input.
const callFeedbackContentFieldMask: intptr =
~IntPtrConstant(kCallFeedbackContentFieldMask);
const newCount: intptr = (callCount & callFeedbackContentFieldMask) |
Convert<intptr>(Signed(
%RawConstexprCast<constexpr uint32>(callFeedbackContent)
<< kCallFeedbackContentFieldShift));
StoreFeedbackVectorSlot(
feedbackVector, slotId, SmiTag(newCount), SKIP_WRITE_BARRIER,
kTaggedSize);
ReportFeedbackUpdate(feedbackVector, slotId, 'Call:SetCallFeedbackContent');
}
macro CollectCallFeedback( macro CollectCallFeedback(
maybeTarget: JSAny, context: Context, maybeTarget: JSAny, maybeReceiver: JSAny, context: Context,
maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void { maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void {
// TODO(v8:9891): Remove this assert once all callers are ported to Torque. // TODO(v8:9891): Remove this assert once all callers are ported to Torque.
// This assert ensures correctness of maybeFeedbackVector's type which can // This assert ensures correctness of maybeFeedbackVector's type which can
...@@ -70,6 +108,22 @@ macro CollectCallFeedback( ...@@ -70,6 +108,22 @@ macro CollectCallFeedback(
if (IsMegamorphic(feedback)) return; if (IsMegamorphic(feedback)) return;
if (IsUninitialized(feedback)) goto TryInitializeAsMonomorphic; if (IsUninitialized(feedback)) goto TryInitializeAsMonomorphic;
if (FeedbackValueIsReceiver(feedbackVector, slotId) &&
TaggedEqualPrototypeApplyFunction(maybeTarget)) {
// If the Receiver is recorded and the target is Function.prototype.apply,
// check whether we can stay monomorphic based on the receiver.
if (IsMonomorphic(feedback, maybeReceiver)) {
return;
} else {
// If not, reinitialize the feedback with target.
SetCallFeedbackContent(
feedbackVector, slotId, CallFeedbackContent::kTarget);
TryInitializeAsMonomorphic(maybeTarget, feedbackVector, slotId)
otherwise TransitionToMegamorphic;
return;
}
}
// If cleared, we have a new chance to become monomorphic. // If cleared, we have a new chance to become monomorphic.
const feedbackValue: HeapObject = const feedbackValue: HeapObject =
MaybeObjectToStrong(feedback) otherwise TryInitializeAsMonomorphic; MaybeObjectToStrong(feedback) otherwise TryInitializeAsMonomorphic;
...@@ -93,7 +147,15 @@ macro CollectCallFeedback( ...@@ -93,7 +147,15 @@ macro CollectCallFeedback(
StoreWeakReferenceInFeedbackVector(feedbackVector, slotId, feedbackCell); StoreWeakReferenceInFeedbackVector(feedbackVector, slotId, feedbackCell);
ReportFeedbackUpdate(feedbackVector, slotId, 'Call:FeedbackVectorCell'); ReportFeedbackUpdate(feedbackVector, slotId, 'Call:FeedbackVectorCell');
} label TryInitializeAsMonomorphic { } label TryInitializeAsMonomorphic {
TryInitializeAsMonomorphic(maybeTarget, feedbackVector, slotId) let recordedFunction = maybeTarget;
if (TaggedEqualPrototypeApplyFunction(maybeTarget)) {
recordedFunction = maybeReceiver;
SetCallFeedbackContent(
feedbackVector, slotId, CallFeedbackContent::kReceiver);
} else {
assert(!FeedbackValueIsReceiver(feedbackVector, slotId));
}
TryInitializeAsMonomorphic(recordedFunction, feedbackVector, slotId)
otherwise TransitionToMegamorphic; otherwise TransitionToMegamorphic;
} label TransitionToMegamorphic { } label TransitionToMegamorphic {
TransitionToMegamorphic(feedbackVector, slotId); TransitionToMegamorphic(feedbackVector, slotId);
......
...@@ -8,10 +8,10 @@ namespace ic { ...@@ -8,10 +8,10 @@ namespace ic {
@export @export
macro CollectCallFeedback( macro CollectCallFeedback(
maybeTarget: JSAny, context: Context, maybeTarget: JSAny, maybeReceiver: JSAny, context: Context,
maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void { maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void {
callable::CollectCallFeedback( callable::CollectCallFeedback(
maybeTarget, context, maybeFeedbackVector, slotId); maybeTarget, maybeReceiver, context, maybeFeedbackVector, slotId);
} }
@export @export
...@@ -51,10 +51,15 @@ macro IsUninitialized(feedback: MaybeObject): bool { ...@@ -51,10 +51,15 @@ macro IsUninitialized(feedback: MaybeObject): bool {
} }
extern macro LoadFeedbackVectorSlot(FeedbackVector, uintptr): MaybeObject; extern macro LoadFeedbackVectorSlot(FeedbackVector, uintptr): MaybeObject;
extern macro LoadFeedbackVectorSlot(
FeedbackVector, uintptr, constexpr int32): MaybeObject;
extern operator '[]' macro LoadFeedbackVectorSlot( extern operator '[]' macro LoadFeedbackVectorSlot(
FeedbackVector, intptr): MaybeObject; FeedbackVector, intptr): MaybeObject;
extern macro StoreFeedbackVectorSlot( extern macro StoreFeedbackVectorSlot(
FeedbackVector, uintptr, MaybeObject): void; FeedbackVector, uintptr, MaybeObject): void;
extern macro StoreFeedbackVectorSlot(
FeedbackVector, uintptr, MaybeObject, constexpr WriteBarrierMode,
constexpr int32): void;
extern macro StoreWeakReferenceInFeedbackVector( extern macro StoreWeakReferenceInFeedbackVector(
FeedbackVector, uintptr, HeapObject): MaybeObject; FeedbackVector, uintptr, HeapObject): MaybeObject;
extern macro ReportFeedbackUpdate(FeedbackVector, uintptr, constexpr string); extern macro ReportFeedbackUpdate(FeedbackVector, uintptr, constexpr string);
......
...@@ -102,7 +102,8 @@ transitioning builtin CallIteratorWithFeedback( ...@@ -102,7 +102,8 @@ transitioning builtin CallIteratorWithFeedback(
feedback: Undefined|FeedbackVector): JSAny { feedback: Undefined|FeedbackVector): JSAny {
// TODO(v8:10047): Use TaggedIndex here once TurboFan supports it. // TODO(v8:10047): Use TaggedIndex here once TurboFan supports it.
const callSlotUnTagged: uintptr = Unsigned(SmiUntag(callSlot)); const callSlotUnTagged: uintptr = Unsigned(SmiUntag(callSlot));
ic::CollectCallFeedback(iteratorMethod, context, feedback, callSlotUnTagged); ic::CollectCallFeedback(
iteratorMethod, receiver, context, feedback, callSlotUnTagged);
const iteratorCallable: Callable = Cast<Callable>(iteratorMethod) const iteratorCallable: Callable = Cast<Callable>(iteratorMethod)
otherwise ThrowCalledNonCallable(iteratorMethod); otherwise ThrowCalledNonCallable(iteratorMethod);
return Call(context, iteratorCallable, receiver); return Call(context, iteratorCallable, receiver);
......
...@@ -3630,6 +3630,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler ...@@ -3630,6 +3630,7 @@ class V8_EXPORT_PRIVATE CodeStubAssembler
} }
int32_t ConstexprWord32Or(int32_t a, int32_t b) { return a | b; } int32_t ConstexprWord32Or(int32_t a, int32_t b) { return a | b; }
uint32_t ConstexprWord32Shl(uint32_t a, int32_t b) { return a << b; }
bool ConstexprUintPtrLessThan(uintptr_t a, uintptr_t b) { return a < b; } bool ConstexprUintPtrLessThan(uintptr_t a, uintptr_t b) { return a < b; }
......
...@@ -1176,12 +1176,13 @@ class CallWithSpread_WithFeedbackDescriptor ...@@ -1176,12 +1176,13 @@ class CallWithSpread_WithFeedbackDescriptor
CallWithSpread_WithFeedbackDescriptor> { CallWithSpread_WithFeedbackDescriptor> {
public: public:
DEFINE_PARAMETERS_VARARGS(kTarget, kArgumentsCount, kSpread, kSlot, DEFINE_PARAMETERS_VARARGS(kTarget, kArgumentsCount, kSpread, kSlot,
kFeedbackVector) kFeedbackVector, kReceiver)
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kTarget DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kTarget
MachineType::Int32(), // kArgumentsCount MachineType::Int32(), // kArgumentsCount
MachineType::AnyTagged(), // kSpread MachineType::AnyTagged(), // kSpread
MachineType::UintPtr(), // kSlot MachineType::UintPtr(), // kSlot
MachineType::AnyTagged()) // kFeedbackVector MachineType::AnyTagged(), // kFeedbackVector
MachineType::AnyTagged()) // kReceiver
DECLARE_DESCRIPTOR(CallWithSpread_WithFeedbackDescriptor) DECLARE_DESCRIPTOR(CallWithSpread_WithFeedbackDescriptor)
}; };
...@@ -1200,11 +1201,12 @@ class CallWithArrayLike_WithFeedbackDescriptor ...@@ -1200,11 +1201,12 @@ class CallWithArrayLike_WithFeedbackDescriptor
: public StaticCallInterfaceDescriptor< : public StaticCallInterfaceDescriptor<
CallWithArrayLike_WithFeedbackDescriptor> { CallWithArrayLike_WithFeedbackDescriptor> {
public: public:
DEFINE_PARAMETERS(kTarget, kArgumentsList, kSlot, kFeedbackVector) DEFINE_PARAMETERS(kTarget, kArgumentsList, kSlot, kFeedbackVector, kReceiver)
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kTarget DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kTarget
MachineType::AnyTagged(), // kArgumentsList MachineType::AnyTagged(), // kArgumentsList
MachineType::UintPtr(), // kSlot MachineType::UintPtr(), // kSlot
MachineType::AnyTagged()) // kFeedbackVector MachineType::AnyTagged(), // kFeedbackVector
MachineType::AnyTagged()) // kReceiver
DECLARE_DESCRIPTOR(CallWithArrayLike_WithFeedbackDescriptor) DECLARE_DESCRIPTOR(CallWithArrayLike_WithFeedbackDescriptor)
}; };
...@@ -1854,11 +1856,12 @@ class CallTrampoline_WithFeedbackDescriptor ...@@ -1854,11 +1856,12 @@ class CallTrampoline_WithFeedbackDescriptor
CallTrampoline_WithFeedbackDescriptor> { CallTrampoline_WithFeedbackDescriptor> {
public: public:
DEFINE_PARAMETERS_VARARGS(kFunction, kActualArgumentsCount, kSlot, DEFINE_PARAMETERS_VARARGS(kFunction, kActualArgumentsCount, kSlot,
kFeedbackVector) kFeedbackVector, kReceiver)
DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kFunction DEFINE_PARAMETER_TYPES(MachineType::AnyTagged(), // kFunction
MachineType::Int32(), // kActualArgumentsCount MachineType::Int32(), // kActualArgumentsCount
MachineType::UintPtr(), // kSlot MachineType::UintPtr(), // kSlot
MachineType::AnyTagged()) // kFeedbackVector MachineType::AnyTagged(), // kFeedbackVector
MachineType::AnyTagged()) // kReceiver
DECLARE_DESCRIPTOR(CallTrampoline_WithFeedbackDescriptor) DECLARE_DESCRIPTOR(CallTrampoline_WithFeedbackDescriptor)
}; };
......
...@@ -1622,6 +1622,7 @@ inline std::ostream& operator<<(std::ostream& os, ...@@ -1622,6 +1622,7 @@ inline std::ostream& operator<<(std::ostream& os,
} }
enum class SpeculationMode { kAllowSpeculation, kDisallowSpeculation }; enum class SpeculationMode { kAllowSpeculation, kDisallowSpeculation };
enum class CallFeedbackContent { kTarget, kReceiver };
inline std::ostream& operator<<(std::ostream& os, inline std::ostream& operator<<(std::ostream& os,
SpeculationMode speculation_mode) { SpeculationMode speculation_mode) {
......
...@@ -347,6 +347,10 @@ class BytecodeGraphBuilder { ...@@ -347,6 +347,10 @@ class BytecodeGraphBuilder {
// feedback. Returns kDisallowSpeculation if feedback is insufficient. // feedback. Returns kDisallowSpeculation if feedback is insufficient.
SpeculationMode GetSpeculationMode(int slot_id) const; SpeculationMode GetSpeculationMode(int slot_id) const;
// Helper function to determine the call feedback relation from the recorded
// type feedback. Returns kUnrelated if feedback is insufficient.
CallFeedbackRelation ComputeCallFeedbackRelation(int slot_id) const;
// Helpers for building the implicit FunctionEntry and IterationBody // Helpers for building the implicit FunctionEntry and IterationBody
// StackChecks. // StackChecks.
void BuildFunctionEntryStackCheck(); void BuildFunctionEntryStackCheck();
...@@ -2529,9 +2533,11 @@ void BytecodeGraphBuilder::BuildCall(ConvertReceiverMode receiver_mode, ...@@ -2529,9 +2533,11 @@ void BytecodeGraphBuilder::BuildCall(ConvertReceiverMode receiver_mode,
FeedbackSource feedback = CreateFeedbackSource(slot_id); FeedbackSource feedback = CreateFeedbackSource(slot_id);
CallFrequency frequency = ComputeCallFrequency(slot_id); CallFrequency frequency = ComputeCallFrequency(slot_id);
SpeculationMode speculation_mode = GetSpeculationMode(slot_id); SpeculationMode speculation_mode = GetSpeculationMode(slot_id);
CallFeedbackRelation call_feedback_relation =
ComputeCallFeedbackRelation(slot_id);
const Operator* op = const Operator* op =
javascript()->Call(arg_count, frequency, feedback, receiver_mode, javascript()->Call(arg_count, frequency, feedback, receiver_mode,
speculation_mode, CallFeedbackRelation::kRelated); speculation_mode, call_feedback_relation);
DCHECK(IrOpcode::IsFeedbackCollectingOpcode(op->opcode())); DCHECK(IrOpcode::IsFeedbackCollectingOpcode(op->opcode()));
JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedCall( JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedCall(
...@@ -3109,6 +3115,19 @@ SpeculationMode BytecodeGraphBuilder::GetSpeculationMode(int slot_id) const { ...@@ -3109,6 +3115,19 @@ SpeculationMode BytecodeGraphBuilder::GetSpeculationMode(int slot_id) const {
: feedback.AsCall().speculation_mode(); : feedback.AsCall().speculation_mode();
} }
CallFeedbackRelation BytecodeGraphBuilder::ComputeCallFeedbackRelation(
int slot_id) const {
FeedbackSlot slot = FeedbackVector::ToSlot(slot_id);
FeedbackSource source(feedback_vector(), slot);
ProcessedFeedback const& feedback = broker()->GetFeedbackForCall(source);
if (feedback.IsInsufficient()) return CallFeedbackRelation::kUnrelated;
CallFeedbackContent call_feedback_content =
feedback.AsCall().call_feedback_content();
return call_feedback_content == CallFeedbackContent::kTarget
? CallFeedbackRelation::kTarget
: CallFeedbackRelation::kReceiver;
}
void BytecodeGraphBuilder::VisitBitwiseNot() { void BytecodeGraphBuilder::VisitBitwiseNot() {
FeedbackSource feedback = CreateFeedbackSource( FeedbackSource feedback = CreateFeedbackSource(
bytecode_iterator().GetSlotOperand(kUnaryOperationHintIndex)); bytecode_iterator().GetSlotOperand(kUnaryOperationHintIndex));
......
...@@ -988,6 +988,11 @@ class V8_EXPORT_PRIVATE CodeAssembler { ...@@ -988,6 +988,11 @@ class V8_EXPORT_PRIVATE CodeAssembler {
static_cast<TNode<Word32T>>(right))); static_cast<TNode<Word32T>>(right)));
} }
TNode<IntPtrT> WordOr(TNode<IntPtrT> left, TNode<IntPtrT> right) {
return Signed(WordOr(static_cast<TNode<WordT>>(left),
static_cast<TNode<WordT>>(right)));
}
TNode<Int32T> Word32Or(TNode<Int32T> left, TNode<Int32T> right) { TNode<Int32T> Word32Or(TNode<Int32T> left, TNode<Int32T> right) {
return Signed(Word32Or(static_cast<TNode<Word32T>>(left), return Signed(Word32Or(static_cast<TNode<Word32T>>(left),
static_cast<TNode<Word32T>>(right))); static_cast<TNode<Word32T>>(right)));
...@@ -1005,6 +1010,9 @@ class V8_EXPORT_PRIVATE CodeAssembler { ...@@ -1005,6 +1010,9 @@ class V8_EXPORT_PRIVATE CodeAssembler {
TNode<BoolT> Word64Equal(TNode<Word64T> left, TNode<Word64T> right); TNode<BoolT> Word64Equal(TNode<Word64T> left, TNode<Word64T> right);
TNode<BoolT> Word64NotEqual(TNode<Word64T> left, TNode<Word64T> right); TNode<BoolT> Word64NotEqual(TNode<Word64T> left, TNode<Word64T> right);
TNode<IntPtrT> WordNot(TNode<IntPtrT> a) {
return Signed(WordNot(static_cast<TNode<WordT>>(a)));
}
TNode<BoolT> Word32Or(TNode<BoolT> left, TNode<BoolT> right) { TNode<BoolT> Word32Or(TNode<BoolT> left, TNode<BoolT> right) {
return UncheckedCast<BoolT>(Word32Or(static_cast<TNode<Word32T>>(left), return UncheckedCast<BoolT>(Word32Or(static_cast<TNode<Word32T>>(left),
static_cast<TNode<Word32T>>(right))); static_cast<TNode<Word32T>>(right)));
......
...@@ -49,17 +49,24 @@ inline size_t hash_value(StackCheckKind kind) { ...@@ -49,17 +49,24 @@ inline size_t hash_value(StackCheckKind kind) {
return static_cast<size_t>(kind); return static_cast<size_t>(kind);
} }
// The CallFeedbackRelation states whether the target feedback stored with a // The CallFeedbackRelation provides the meaning of the call feedback for a
// JSCall is related to the call. If, during lowering, a JSCall (e.g. of a // TurboFan JSCall operator
// higher order function) is replaced by a JSCall with another target, the // - kReceiver: The call target was Function.prototype.apply and its receiver
// feedback has to be kept but is now unrelated. // was recorded as the feedback value.
enum class CallFeedbackRelation { kRelated, kUnrelated }; // - kTarget: The call target was recorded as the feedback value.
// - kUnrelated: The feedback is no longer related to the call. If, during
// lowering, a JSCall (e.g. of a higher order function) is replaced by a
// JSCall with another target, the feedback has to be kept but is now
// unrelated.
enum class CallFeedbackRelation { kReceiver, kTarget, kUnrelated };
inline std::ostream& operator<<(std::ostream& os, inline std::ostream& operator<<(std::ostream& os,
CallFeedbackRelation call_feedback_relation) { CallFeedbackRelation call_feedback_relation) {
switch (call_feedback_relation) { switch (call_feedback_relation) {
case CallFeedbackRelation::kRelated: case CallFeedbackRelation::kReceiver:
return os << "CallFeedbackRelation::kRelated"; return os << "CallFeedbackRelation::kReceiver";
case CallFeedbackRelation::kTarget:
return os << "CallFeedbackRelation::kTarget";
case CallFeedbackRelation::kUnrelated: case CallFeedbackRelation::kUnrelated:
return os << "CallFeedbackRelation::kUnrelated"; return os << "CallFeedbackRelation::kUnrelated";
} }
......
...@@ -467,6 +467,7 @@ class ContextRef : public HeapObjectRef { ...@@ -467,6 +467,7 @@ class ContextRef : public HeapObjectRef {
#define BROKER_COMPULSORY_NATIVE_CONTEXT_FIELDS(V) \ #define BROKER_COMPULSORY_NATIVE_CONTEXT_FIELDS(V) \
V(JSFunction, array_function) \ V(JSFunction, array_function) \
V(JSFunction, function_prototype_apply) \
V(JSFunction, boolean_function) \ V(JSFunction, boolean_function) \
V(JSFunction, bigint_function) \ V(JSFunction, bigint_function) \
V(JSFunction, number_function) \ V(JSFunction, number_function) \
......
...@@ -2448,6 +2448,10 @@ Reduction JSCallReducer::ReduceObjectConstructor(Node* node) { ...@@ -2448,6 +2448,10 @@ Reduction JSCallReducer::ReduceObjectConstructor(Node* node) {
Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
JSCallNode n(node); JSCallNode n(node);
CallParameters const& p = n.Parameters(); CallParameters const& p = n.Parameters();
CallFeedbackRelation new_feedback_relation =
p.feedback_relation() == CallFeedbackRelation::kReceiver
? CallFeedbackRelation::kTarget
: CallFeedbackRelation::kUnrelated;
int arity = p.arity_without_implicit_args(); int arity = p.arity_without_implicit_args();
ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny; ConvertReceiverMode convert_mode = ConvertReceiverMode::kAny;
if (arity == 0) { if (arity == 0) {
...@@ -2480,9 +2484,9 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { ...@@ -2480,9 +2484,9 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
// Morph the {node} to a {JSCallWithArrayLike}. // Morph the {node} to a {JSCallWithArrayLike}.
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
node, javascript()->CallWithArrayLike( node, javascript()->CallWithArrayLike(p.frequency(), p.feedback(),
p.frequency(), p.feedback(), p.speculation_mode(), p.speculation_mode(),
CallFeedbackRelation::kUnrelated)); new_feedback_relation));
return Changed(node).FollowedBy(ReduceJSCallWithArrayLike(node)); return Changed(node).FollowedBy(ReduceJSCallWithArrayLike(node));
} else { } else {
// Check whether {arguments_list} is null. // Check whether {arguments_list} is null.
...@@ -2510,7 +2514,7 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { ...@@ -2510,7 +2514,7 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
Node* value0 = effect0 = control0 = graph()->NewNode( Node* value0 = effect0 = control0 = graph()->NewNode(
javascript()->CallWithArrayLike(p.frequency(), p.feedback(), javascript()->CallWithArrayLike(p.frequency(), p.feedback(),
p.speculation_mode(), p.speculation_mode(),
CallFeedbackRelation::kUnrelated), new_feedback_relation),
target, this_argument, arguments_list, n.feedback_vector(), context, target, this_argument, arguments_list, n.feedback_vector(), context,
frame_state, effect0, control0); frame_state, effect0, control0);
...@@ -2560,7 +2564,7 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) { ...@@ -2560,7 +2564,7 @@ Reduction JSCallReducer::ReduceFunctionPrototypeApply(Node* node) {
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
node, javascript()->Call(JSCallNode::ArityForArgc(arity), p.frequency(), node, javascript()->Call(JSCallNode::ArityForArgc(arity), p.frequency(),
p.feedback(), convert_mode, p.speculation_mode(), p.feedback(), convert_mode, p.speculation_mode(),
CallFeedbackRelation::kUnrelated)); new_feedback_relation));
// Try to further reduce the JSCall {node}. // Try to further reduce the JSCall {node}.
return Changed(node).FollowedBy(ReduceJSCall(node)); return Changed(node).FollowedBy(ReduceJSCall(node));
} }
...@@ -3966,10 +3970,9 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread( ...@@ -3966,10 +3970,9 @@ Reduction JSCallReducer::ReduceCallOrConstructWithArrayLikeOrSpread(
if (IsCallWithArrayLikeOrSpread(node)) { if (IsCallWithArrayLikeOrSpread(node)) {
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
node, node, javascript()->Call(JSCallNode::ArityForArgc(argc), frequency,
javascript()->Call(JSCallNode::ArityForArgc(argc), frequency, feedback, feedback, ConvertReceiverMode::kAny,
ConvertReceiverMode::kAny, speculation_mode, speculation_mode, feedback_relation));
CallFeedbackRelation::kUnrelated));
return Changed(node).FollowedBy(ReduceJSCall(node)); return Changed(node).FollowedBy(ReduceJSCall(node));
} else { } else {
NodeProperties::ChangeOp( NodeProperties::ChangeOp(
...@@ -4201,7 +4204,7 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { ...@@ -4201,7 +4204,7 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
} }
if (!ShouldUseCallICFeedback(target) || if (!ShouldUseCallICFeedback(target) ||
p.feedback_relation() != CallFeedbackRelation::kRelated || p.feedback_relation() == CallFeedbackRelation::kUnrelated ||
!p.feedback().IsValid()) { !p.feedback().IsValid()) {
return NoChange(); return NoChange();
} }
...@@ -4213,7 +4216,14 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) { ...@@ -4213,7 +4216,14 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
node, DeoptimizeReason::kInsufficientTypeFeedbackForCall); node, DeoptimizeReason::kInsufficientTypeFeedbackForCall);
} }
base::Optional<HeapObjectRef> feedback_target = feedback.AsCall().target(); base::Optional<HeapObjectRef> feedback_target;
if (p.feedback_relation() == CallFeedbackRelation::kTarget) {
feedback_target = feedback.AsCall().target();
} else {
DCHECK_EQ(p.feedback_relation(), CallFeedbackRelation::kReceiver);
feedback_target = native_context().function_prototype_apply();
}
if (feedback_target.has_value() && feedback_target->map().is_callable()) { if (feedback_target.has_value() && feedback_target->map().is_callable()) {
Node* target_function = jsgraph()->Constant(*feedback_target); Node* target_function = jsgraph()->Constant(*feedback_target);
...@@ -5074,7 +5084,7 @@ Reduction JSCallReducer::ReduceJSConstructWithArrayLike(Node* node) { ...@@ -5074,7 +5084,7 @@ Reduction JSCallReducer::ReduceJSConstructWithArrayLike(Node* node) {
DCHECK_EQ(n.ArgumentCount(), 1); // The arraylike object. DCHECK_EQ(n.ArgumentCount(), 1); // The arraylike object.
return ReduceCallOrConstructWithArrayLikeOrSpread( return ReduceCallOrConstructWithArrayLikeOrSpread(
node, arraylike_index, p.frequency(), p.feedback(), node, arraylike_index, p.frequency(), p.feedback(),
SpeculationMode::kDisallowSpeculation, CallFeedbackRelation::kRelated); SpeculationMode::kDisallowSpeculation, CallFeedbackRelation::kTarget);
} }
Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) { Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) {
...@@ -5084,7 +5094,7 @@ Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) { ...@@ -5084,7 +5094,7 @@ Reduction JSCallReducer::ReduceJSConstructWithSpread(Node* node) {
DCHECK_GE(n.ArgumentCount(), 1); // At least the spread. DCHECK_GE(n.ArgumentCount(), 1); // At least the spread.
return ReduceCallOrConstructWithArrayLikeOrSpread( return ReduceCallOrConstructWithArrayLikeOrSpread(
node, spread_index, p.frequency(), p.feedback(), node, spread_index, p.frequency(), p.feedback(),
SpeculationMode::kDisallowSpeculation, CallFeedbackRelation::kRelated); SpeculationMode::kDisallowSpeculation, CallFeedbackRelation::kTarget);
} }
Reduction JSCallReducer::ReduceReturnReceiver(Node* node) { Reduction JSCallReducer::ReduceReturnReceiver(Node* node) {
......
...@@ -828,7 +828,9 @@ ProcessedFeedback const& JSHeapBroker::ReadFeedbackForCall( ...@@ -828,7 +828,9 @@ ProcessedFeedback const& JSHeapBroker::ReadFeedbackForCall(
} }
float frequency = nexus.ComputeCallFrequency(); float frequency = nexus.ComputeCallFrequency();
SpeculationMode mode = nexus.GetSpeculationMode(); SpeculationMode mode = nexus.GetSpeculationMode();
return *zone()->New<CallFeedback>(target_ref, frequency, mode, nexus.kind()); CallFeedbackContent content = nexus.GetCallFeedbackContent();
return *zone()->New<CallFeedback>(target_ref, frequency, mode, content,
nexus.kind());
} }
BinaryOperationHint JSHeapBroker::GetFeedbackForBinaryOperation( BinaryOperationHint JSHeapBroker::GetFeedbackForBinaryOperation(
......
...@@ -1585,7 +1585,7 @@ Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) { ...@@ -1585,7 +1585,7 @@ Reduction JSNativeContextSpecialization::ReduceJSGetIterator(Node* node) {
const Operator* call_op = javascript()->Call( const Operator* call_op = javascript()->Call(
JSCallNode::ArityForArgc(0), CallFrequency(), p.callFeedback(), JSCallNode::ArityForArgc(0), CallFrequency(), p.callFeedback(),
ConvertReceiverMode::kNotNullOrUndefined, mode, ConvertReceiverMode::kNotNullOrUndefined, mode,
CallFeedbackRelation::kRelated); CallFeedbackRelation::kTarget);
Node* call_property = Node* call_property =
graph()->NewNode(call_op, load_property, receiver, n.feedback_vector(), graph()->NewNode(call_op, load_property, receiver, n.feedback_vector(),
context, frame_state, effect, control); context, frame_state, effect, control);
......
...@@ -290,9 +290,9 @@ class CallParameters final { ...@@ -290,9 +290,9 @@ class CallParameters final {
} }
using ArityField = base::BitField<size_t, 0, 27>; using ArityField = base::BitField<size_t, 0, 27>;
using CallFeedbackRelationField = base::BitField<CallFeedbackRelation, 27, 1>; using CallFeedbackRelationField = base::BitField<CallFeedbackRelation, 27, 2>;
using SpeculationModeField = base::BitField<SpeculationMode, 28, 1>; using SpeculationModeField = base::BitField<SpeculationMode, 29, 1>;
using ConvertReceiverModeField = base::BitField<ConvertReceiverMode, 29, 2>; using ConvertReceiverModeField = base::BitField<ConvertReceiverMode, 30, 2>;
uint32_t const bit_field_; uint32_t const bit_field_;
CallFrequency const frequency_; CallFrequency const frequency_;
...@@ -951,12 +951,12 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final ...@@ -951,12 +951,12 @@ class V8_EXPORT_PRIVATE JSOperatorBuilder final
CallFrequency const& frequency, CallFrequency const& frequency,
const FeedbackSource& feedback = FeedbackSource{}, const FeedbackSource& feedback = FeedbackSource{},
SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation, SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation,
CallFeedbackRelation feedback_relation = CallFeedbackRelation::kRelated); CallFeedbackRelation feedback_relation = CallFeedbackRelation::kTarget);
const Operator* CallWithSpread( const Operator* CallWithSpread(
uint32_t arity, CallFrequency const& frequency = CallFrequency(), uint32_t arity, CallFrequency const& frequency = CallFrequency(),
FeedbackSource const& feedback = FeedbackSource(), FeedbackSource const& feedback = FeedbackSource(),
SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation, SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation,
CallFeedbackRelation feedback_relation = CallFeedbackRelation::kRelated); CallFeedbackRelation feedback_relation = CallFeedbackRelation::kTarget);
const Operator* CallRuntime(Runtime::FunctionId id); const Operator* CallRuntime(Runtime::FunctionId id);
const Operator* CallRuntime(Runtime::FunctionId id, size_t arity); const Operator* CallRuntime(Runtime::FunctionId id, size_t arity);
const Operator* CallRuntime(const Runtime::Function* function, size_t arity); const Operator* CallRuntime(const Runtime::Function* function, size_t arity);
......
...@@ -197,20 +197,24 @@ class MinimorphicLoadPropertyAccessFeedback : public ProcessedFeedback { ...@@ -197,20 +197,24 @@ class MinimorphicLoadPropertyAccessFeedback : public ProcessedFeedback {
class CallFeedback : public ProcessedFeedback { class CallFeedback : public ProcessedFeedback {
public: public:
CallFeedback(base::Optional<HeapObjectRef> target, float frequency, CallFeedback(base::Optional<HeapObjectRef> target, float frequency,
SpeculationMode mode, FeedbackSlotKind slot_kind) SpeculationMode mode, CallFeedbackContent call_feedback_content,
FeedbackSlotKind slot_kind)
: ProcessedFeedback(kCall, slot_kind), : ProcessedFeedback(kCall, slot_kind),
target_(target), target_(target),
frequency_(frequency), frequency_(frequency),
mode_(mode) {} mode_(mode),
content_(call_feedback_content) {}
base::Optional<HeapObjectRef> target() const { return target_; } base::Optional<HeapObjectRef> target() const { return target_; }
float frequency() const { return frequency_; } float frequency() const { return frequency_; }
SpeculationMode speculation_mode() const { return mode_; } SpeculationMode speculation_mode() const { return mode_; }
CallFeedbackContent call_feedback_content() const { return content_; }
private: private:
base::Optional<HeapObjectRef> const target_; base::Optional<HeapObjectRef> const target_;
float const frequency_; float const frequency_;
SpeculationMode const mode_; SpeculationMode const mode_;
CallFeedbackContent const content_;
}; };
template <class T, ProcessedFeedback::Kind K> template <class T, ProcessedFeedback::Kind K>
......
...@@ -1648,8 +1648,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object, ...@@ -1648,8 +1648,10 @@ void Genesis::InitializeGlobal(Handle<JSGlobalObject> global_object,
// Setup the methods on the %FunctionPrototype%. // Setup the methods on the %FunctionPrototype%.
JSObject::AddProperty(isolate_, prototype, factory->constructor_string(), JSObject::AddProperty(isolate_, prototype, factory->constructor_string(),
function_fun, DONT_ENUM); function_fun, DONT_ENUM);
Handle<JSFunction> function_prototype_apply =
SimpleInstallFunction(isolate_, prototype, "apply", SimpleInstallFunction(isolate_, prototype, "apply",
Builtins::kFunctionPrototypeApply, 2, false); Builtins::kFunctionPrototypeApply, 2, false);
native_context()->set_function_prototype_apply(*function_prototype_apply);
SimpleInstallFunction(isolate_, prototype, "bind", SimpleInstallFunction(isolate_, prototype, "bind",
Builtins::kFastFunctionPrototypeBind, 1, false); Builtins::kFastFunctionPrototypeBind, 1, false);
SimpleInstallFunction(isolate_, prototype, "call", SimpleInstallFunction(isolate_, prototype, "call",
......
...@@ -803,7 +803,9 @@ void InterpreterAssembler::CallJSWithSpreadAndDispatch( ...@@ -803,7 +803,9 @@ void InterpreterAssembler::CallJSWithSpreadAndDispatch(
TNode<UintPtrT> slot_id, TNode<HeapObject> maybe_feedback_vector) { TNode<UintPtrT> slot_id, TNode<HeapObject> maybe_feedback_vector) {
DCHECK(Bytecodes::MakesCallAlongCriticalPath(bytecode_)); DCHECK(Bytecodes::MakesCallAlongCriticalPath(bytecode_));
DCHECK_EQ(Bytecodes::GetReceiverMode(bytecode_), ConvertReceiverMode::kAny); DCHECK_EQ(Bytecodes::GetReceiverMode(bytecode_), ConvertReceiverMode::kAny);
CollectCallFeedback(function, context, maybe_feedback_vector, slot_id); TNode<Object> receiver = LoadRegisterAtOperandIndex(1);
CollectCallFeedback(function, receiver, context, maybe_feedback_vector,
slot_id);
Comment("call using CallWithSpread builtin"); Comment("call using CallWithSpread builtin");
Callable callable = CodeFactory::InterpreterPushArgsThenCall( Callable callable = CodeFactory::InterpreterPushArgsThenCall(
isolate(), ConvertReceiverMode::kAny, isolate(), ConvertReceiverMode::kAny,
......
...@@ -1365,13 +1365,18 @@ class InterpreterJSCallAssembler : public InterpreterAssembler { ...@@ -1365,13 +1365,18 @@ class InterpreterJSCallAssembler : public InterpreterAssembler {
// Generates code to perform a JS call that collects type feedback. // Generates code to perform a JS call that collects type feedback.
void JSCall(ConvertReceiverMode receiver_mode) { void JSCall(ConvertReceiverMode receiver_mode) {
TNode<Object> function = LoadRegisterAtOperandIndex(0); TNode<Object> function = LoadRegisterAtOperandIndex(0);
TNode<Object> receiver =
receiver_mode == ConvertReceiverMode::kNullOrUndefined
? UndefinedConstant()
: LoadRegisterAtOperandIndex(1);
RegListNodePair args = GetRegisterListAtOperandIndex(1); RegListNodePair args = GetRegisterListAtOperandIndex(1);
TNode<UintPtrT> slot_id = BytecodeOperandIdx(3); TNode<UintPtrT> slot_id = BytecodeOperandIdx(3);
TNode<HeapObject> maybe_feedback_vector = LoadFeedbackVector(); TNode<HeapObject> maybe_feedback_vector = LoadFeedbackVector();
TNode<Context> context = GetContext(); TNode<Context> context = GetContext();
// Collect the {function} feedback. // Collect the {function} feedback.
CollectCallFeedback(function, context, maybe_feedback_vector, slot_id); CollectCallFeedback(function, receiver, context, maybe_feedback_vector,
slot_id);
// Call the function and dispatch to the next handler. // Call the function and dispatch to the next handler.
CallJSAndDispatch(function, context, args, receiver_mode); CallJSAndDispatch(function, context, args, receiver_mode);
...@@ -1399,12 +1404,17 @@ class InterpreterJSCallAssembler : public InterpreterAssembler { ...@@ -1399,12 +1404,17 @@ class InterpreterJSCallAssembler : public InterpreterAssembler {
kFirstArgumentOperandIndex + kReceiverAndArgOperandCount; kFirstArgumentOperandIndex + kReceiverAndArgOperandCount;
TNode<Object> function = LoadRegisterAtOperandIndex(0); TNode<Object> function = LoadRegisterAtOperandIndex(0);
TNode<Object> receiver =
receiver_mode == ConvertReceiverMode::kNullOrUndefined
? UndefinedConstant()
: LoadRegisterAtOperandIndex(1);
TNode<UintPtrT> slot_id = BytecodeOperandIdx(kSlotOperandIndex); TNode<UintPtrT> slot_id = BytecodeOperandIdx(kSlotOperandIndex);
TNode<HeapObject> maybe_feedback_vector = LoadFeedbackVector(); TNode<HeapObject> maybe_feedback_vector = LoadFeedbackVector();
TNode<Context> context = GetContext(); TNode<Context> context = GetContext();
// Collect the {function} feedback. // Collect the {function} feedback.
CollectCallFeedback(function, context, maybe_feedback_vector, slot_id); CollectCallFeedback(function, receiver, context, maybe_feedback_vector,
slot_id);
switch (kReceiverAndArgOperandCount) { switch (kReceiverAndArgOperandCount) {
case 0: case 0:
......
...@@ -50,7 +50,8 @@ enum ContextLookupFlags { ...@@ -50,7 +50,8 @@ enum ContextLookupFlags {
V(MATH_POW_INDEX, JSFunction, math_pow) \ V(MATH_POW_INDEX, JSFunction, math_pow) \
V(PROMISE_INTERNAL_CONSTRUCTOR_INDEX, JSFunction, \ V(PROMISE_INTERNAL_CONSTRUCTOR_INDEX, JSFunction, \
promise_internal_constructor) \ promise_internal_constructor) \
V(PROMISE_THEN_INDEX, JSFunction, promise_then) V(PROMISE_THEN_INDEX, JSFunction, promise_then) \
V(FUNCTION_PROTOTYPE_APPLY_INDEX, JSFunction, function_prototype_apply)
#define NATIVE_CONTEXT_FIELDS(V) \ #define NATIVE_CONTEXT_FIELDS(V) \
V(GLOBAL_PROXY_INDEX, JSGlobalProxy, global_proxy_object) \ V(GLOBAL_PROXY_INDEX, JSGlobalProxy, global_proxy_object) \
......
...@@ -118,6 +118,7 @@ extern enum ContextSlot extends intptr constexpr 'Context::Field' { ...@@ -118,6 +118,7 @@ extern enum ContextSlot extends intptr constexpr 'Context::Field' {
SLOPPY_ARGUMENTS_MAP_INDEX: Slot<NativeContext, Map>, SLOPPY_ARGUMENTS_MAP_INDEX: Slot<NativeContext, Map>,
FAST_ALIASED_ARGUMENTS_MAP_INDEX: Slot<NativeContext, Map>, FAST_ALIASED_ARGUMENTS_MAP_INDEX: Slot<NativeContext, Map>,
FUNCTION_CONTEXT_MAP_INDEX: Slot<NativeContext, Map>, FUNCTION_CONTEXT_MAP_INDEX: Slot<NativeContext, Map>,
FUNCTION_PROTOTYPE_APPLY_INDEX: Slot<NativeContext, JSFunction>,
PROMISE_FUNCTION_INDEX: Slot<NativeContext, JSFunction>, PROMISE_FUNCTION_INDEX: Slot<NativeContext, JSFunction>,
PROMISE_THEN_INDEX: Slot<NativeContext, JSFunction>, PROMISE_THEN_INDEX: Slot<NativeContext, JSFunction>,
......
...@@ -1009,6 +1009,15 @@ SpeculationMode FeedbackNexus::GetSpeculationMode() { ...@@ -1009,6 +1009,15 @@ SpeculationMode FeedbackNexus::GetSpeculationMode() {
return SpeculationModeField::decode(value); return SpeculationModeField::decode(value);
} }
CallFeedbackContent FeedbackNexus::GetCallFeedbackContent() {
DCHECK(IsCallICKind(kind()));
Object call_count = GetFeedbackExtra()->cast<Object>();
CHECK(call_count.IsSmi());
uint32_t value = static_cast<uint32_t>(Smi::ToInt(call_count));
return CallFeedbackContentField::decode(value);
}
float FeedbackNexus::ComputeCallFrequency() { float FeedbackNexus::ComputeCallFrequency() {
DCHECK(IsCallICKind(kind())); DCHECK(IsCallICKind(kind()));
......
...@@ -795,13 +795,15 @@ class V8_EXPORT_PRIVATE FeedbackNexus final { ...@@ -795,13 +795,15 @@ class V8_EXPORT_PRIVATE FeedbackNexus final {
int GetCallCount(); int GetCallCount();
void SetSpeculationMode(SpeculationMode mode); void SetSpeculationMode(SpeculationMode mode);
SpeculationMode GetSpeculationMode(); SpeculationMode GetSpeculationMode();
CallFeedbackContent GetCallFeedbackContent();
// Compute the call frequency based on the call count and the invocation // Compute the call frequency based on the call count and the invocation
// count (taken from the type feedback vector). // count (taken from the type feedback vector).
float ComputeCallFrequency(); float ComputeCallFrequency();
using SpeculationModeField = base::BitField<SpeculationMode, 0, 1>; using SpeculationModeField = base::BitField<SpeculationMode, 0, 1>;
using CallCountField = base::BitField<uint32_t, 1, 31>; using CallFeedbackContentField = base::BitField<CallFeedbackContent, 1, 1>;
using CallCountField = base::BitField<uint32_t, 2, 30>;
// For InstanceOf ICs. // For InstanceOf ICs.
MaybeHandle<JSObject> GetConstructorFeedback() const; MaybeHandle<JSObject> GetConstructorFeedback() const;
......
...@@ -184,6 +184,49 @@ TEST(VectorCallICStates) { ...@@ -184,6 +184,49 @@ TEST(VectorCallICStates) {
CHECK_EQ(GENERIC, nexus.ic_state()); CHECK_EQ(GENERIC, nexus.ic_state());
} }
// Test the Call IC states transfer with Function.prototype.apply
TEST(VectorCallICStateApply) {
if (!i::FLAG_use_ic) return;
if (i::FLAG_always_opt) return;
FLAG_allow_natives_syntax = true;
CcTest::InitializeVM();
LocalContext context;
v8::HandleScope scope(context->GetIsolate());
Isolate* isolate = CcTest::i_isolate();
// Make sure function f has a call that uses a type feedback slot.
CompileRun(
"var F;"
"%EnsureFeedbackVectorForFunction(foo);"
"function foo() { return F.apply(null, arguments); }"
"F = Math.min;"
"foo();");
Handle<JSFunction> foo = GetFunction("foo");
Handle<JSFunction> F = GetFunction("F");
Handle<FeedbackVector> feedback_vector =
Handle<FeedbackVector>(foo->feedback_vector(), isolate);
FeedbackSlot slot(4);
FeedbackNexus nexus(feedback_vector, slot);
CHECK_EQ(MONOMORPHIC, nexus.ic_state());
CHECK_EQ(CallFeedbackContent::kReceiver, nexus.GetCallFeedbackContent());
HeapObject heap_object;
CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object));
CHECK_EQ(*F, heap_object);
CompileRun(
"F = Math.max;"
"foo();");
CHECK_EQ(MONOMORPHIC, nexus.ic_state());
CHECK_EQ(CallFeedbackContent::kTarget, nexus.GetCallFeedbackContent());
CHECK(nexus.GetFeedback()->GetHeapObjectIfWeak(&heap_object));
CHECK_EQ(*isolate->function_prototype_apply(), heap_object);
CompileRun(
"F.apply = (function () { return; });"
"foo();");
CHECK_EQ(GENERIC, nexus.ic_state());
}
TEST(VectorCallFeedback) { TEST(VectorCallFeedback) {
if (!i::FLAG_use_ic) return; if (!i::FLAG_use_ic) return;
if (i::FLAG_always_opt) return; if (i::FLAG_always_opt) return;
......
...@@ -28,6 +28,51 @@ assertOptimized(TestFunctionPrototypeApply); ...@@ -28,6 +28,51 @@ assertOptimized(TestFunctionPrototypeApply);
TestFunctionPrototypeApply("abc"); TestFunctionPrototypeApply("abc");
assertUnoptimized(TestFunctionPrototypeApply); assertUnoptimized(TestFunctionPrototypeApply);
// Testing: FunctionPrototypeApply with non-HeapConstant Receiver
var MathMin = (function() { return Math.min.apply(null, arguments); })
function TestFunctionPrototypeApplyReceiver(func, x, y) {
return func(x, y);
}
%PrepareFunctionForOptimization(MathMin);
%PrepareFunctionForOptimization(TestFunctionPrototypeApplyReceiver);
assertEquals(-13, TestFunctionPrototypeApplyReceiver(MathMin, -13, 42));
assertEquals(-4, TestFunctionPrototypeApplyReceiver(MathMin, 3, -4));
%OptimizeFunctionOnNextCall(TestFunctionPrototypeApplyReceiver);
assertEquals(7, TestFunctionPrototypeApplyReceiver(MathMin, 7, 9));
assertOptimized(TestFunctionPrototypeApplyReceiver);
TestFunctionPrototypeApplyReceiver(MathMin, "abc");
assertUnoptimized(TestFunctionPrototypeApplyReceiver);
// Testing: FunctionPrototypeApply with non-HeapConstant Receiver won't cause
// deopt loop
(function() {
var F;
function foo() {
return F.apply(null, arguments);
}
function test(x, y) {
return foo(x, y);
}
F = Math.min;
%PrepareFunctionForOptimization(foo);
%PrepareFunctionForOptimization(test);
assertEquals(-13, test(-13, 42));
%OptimizeFunctionOnNextCall(test);
assertEquals(-13, test(-13, 42));
assertOptimized(test);
%PrepareFunctionForOptimization(test);
F = Math.max;
assertEquals(42, test(-13, 42));
assertUnoptimized(test);
%OptimizeFunctionOnNextCall(test);
assertEquals(42, test(-13, 42));
F = Math.min;
assertEquals(-13, test(-13, 42));
assertOptimized(test);
})();
// Testing: FunctionPrototypeCall // Testing: FunctionPrototypeCall
function TestFunctionPrototypeCall(x) { function TestFunctionPrototypeCall(x) {
......
...@@ -117,7 +117,7 @@ class JSCallReducerTest : public TypedGraphTest { ...@@ -117,7 +117,7 @@ class JSCallReducerTest : public TypedGraphTest {
return javascript()->Call(JSCallNode::ArityForArgc(arity), CallFrequency(), return javascript()->Call(JSCallNode::ArityForArgc(arity), CallFrequency(),
feedback, ConvertReceiverMode::kAny, feedback, ConvertReceiverMode::kAny,
SpeculationMode::kAllowSpeculation, SpeculationMode::kAllowSpeculation,
CallFeedbackRelation::kRelated); CallFeedbackRelation::kTarget);
} }
private: private:
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment