| // Copyright 2020 the V8 project authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| namespace ic { |
| namespace callable { |
| |
| 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 { |
| return IsWeakReferenceToObject(feedback, target); |
| } |
| |
| macro InSameNativeContext(lhs: Context, rhs: Context): bool { |
| return LoadNativeContext(lhs) == LoadNativeContext(rhs); |
| } |
| |
| macro MaybeObjectToStrong(maybeObject: MaybeObject): |
| HeapObject labels IfCleared { |
| assert(IsWeakOrCleared(maybeObject)); |
| const weakObject = %RawDownCast<Weak<HeapObject>>(maybeObject); |
| return WeakToStrong(weakObject) otherwise IfCleared; |
| } |
| |
| macro TryInitializeAsMonomorphic(implicit context: Context)( |
| maybeTarget: JSAny, feedbackVector: FeedbackVector, |
| slotId: uintptr): void labels TransitionToMegamorphic { |
| const targetHeapObject = |
| Cast<HeapObject>(maybeTarget) otherwise TransitionToMegamorphic; |
| |
| let unwrappedTarget = targetHeapObject; |
| while (Is<JSBoundFunction>(unwrappedTarget)) { |
| unwrappedTarget = |
| UnsafeCast<JSBoundFunction>(unwrappedTarget).bound_target_function; |
| } |
| |
| const unwrappedTargetJSFunction = |
| Cast<JSFunction>(unwrappedTarget) otherwise TransitionToMegamorphic; |
| if (!InSameNativeContext(unwrappedTargetJSFunction.context, context)) { |
| goto TransitionToMegamorphic; |
| } |
| |
| StoreWeakReferenceInFeedbackVector(feedbackVector, slotId, targetHeapObject); |
| ReportFeedbackUpdate(feedbackVector, slotId, 'Call:Initialize'); |
| } |
| |
| macro TransitionToMegamorphic(implicit context: Context)( |
| feedbackVector: FeedbackVector, slotId: uintptr): void { |
| StoreFeedbackVectorSlot(feedbackVector, slotId, kMegamorphicSymbol); |
| 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( |
| maybeTarget: JSAny, maybeReceiver: Lazy<JSAny>, context: Context, |
| maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void { |
| // TODO(v8:9891): Remove this assert once all callers are ported to Torque. |
| // This assert ensures correctness of maybeFeedbackVector's type which can |
| // be easily broken for calls from CSA. |
| assert( |
| IsUndefined(maybeFeedbackVector) || |
| Is<FeedbackVector>(maybeFeedbackVector)); |
| const feedbackVector = |
| Cast<FeedbackVector>(maybeFeedbackVector) otherwise return; |
| IncrementCallCount(feedbackVector, slotId); |
| |
| try { |
| const feedback: MaybeObject = |
| LoadFeedbackVectorSlot(feedbackVector, slotId); |
| if (IsMonomorphic(feedback, maybeTarget)) return; |
| if (IsMegamorphic(feedback)) return; |
| if (IsUninitialized(feedback)) goto TryInitializeAsMonomorphic; |
| |
| // If cleared, we have a new chance to become monomorphic. |
| const feedbackValue: HeapObject = |
| MaybeObjectToStrong(feedback) otherwise TryReinitializeAsMonomorphic; |
| |
| 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, RunLazy(maybeReceiver))) { |
| return; |
| } else { |
| // If not, reinitialize the feedback with target. |
| SetCallFeedbackContent( |
| feedbackVector, slotId, CallFeedbackContent::kTarget); |
| TryInitializeAsMonomorphic(maybeTarget, feedbackVector, slotId) |
| otherwise TransitionToMegamorphic; |
| return; |
| } |
| } |
| |
| // Try transitioning to a feedback cell. |
| // Check if {target}s feedback cell matches the {feedbackValue}. |
| const target = |
| Cast<JSFunction>(maybeTarget) otherwise TransitionToMegamorphic; |
| const targetFeedbackCell: FeedbackCell = target.feedback_cell; |
| if (TaggedEqual(feedbackValue, targetFeedbackCell)) return; |
| |
| // Check if {target} and {feedbackValue} are both JSFunctions with |
| // the same feedback vector cell, and that those functions were |
| // actually compiled already. |
| const feedbackValueJSFunction = |
| Cast<JSFunction>(feedbackValue) otherwise TransitionToMegamorphic; |
| const feedbackCell: FeedbackCell = feedbackValueJSFunction.feedback_cell; |
| if (!TaggedEqual(feedbackCell, targetFeedbackCell)) |
| goto TransitionToMegamorphic; |
| |
| StoreWeakReferenceInFeedbackVector(feedbackVector, slotId, feedbackCell); |
| ReportFeedbackUpdate(feedbackVector, slotId, 'Call:FeedbackVectorCell'); |
| } label TryReinitializeAsMonomorphic { |
| SetCallFeedbackContent( |
| feedbackVector, slotId, CallFeedbackContent::kTarget); |
| goto TryInitializeAsMonomorphic; |
| } label TryInitializeAsMonomorphic { |
| let recordedFunction = maybeTarget; |
| if (TaggedEqualPrototypeApplyFunction(maybeTarget)) { |
| recordedFunction = RunLazy(maybeReceiver); |
| SetCallFeedbackContent( |
| feedbackVector, slotId, CallFeedbackContent::kReceiver); |
| } else { |
| assert(!FeedbackValueIsReceiver(feedbackVector, slotId)); |
| } |
| TryInitializeAsMonomorphic(recordedFunction, feedbackVector, slotId) |
| otherwise TransitionToMegamorphic; |
| } label TransitionToMegamorphic { |
| TransitionToMegamorphic(feedbackVector, slotId); |
| } |
| } |
| |
| macro CollectInstanceOfFeedback( |
| maybeTarget: JSAny, context: Context, |
| maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr): void { |
| // TODO(v8:9891): Remove this assert once all callers are ported to Torque. |
| // This assert ensures correctness of maybeFeedbackVector's type which can |
| // be easily broken for calls from CSA. |
| assert( |
| IsUndefined(maybeFeedbackVector) || |
| Is<FeedbackVector>(maybeFeedbackVector)); |
| const feedbackVector = |
| Cast<FeedbackVector>(maybeFeedbackVector) otherwise return; |
| // Note: The call count is not incremented. |
| |
| try { |
| const feedback: MaybeObject = |
| LoadFeedbackVectorSlot(feedbackVector, slotId); |
| if (IsMonomorphic(feedback, maybeTarget)) return; |
| if (IsMegamorphic(feedback)) return; |
| if (IsUninitialized(feedback)) goto TryInitializeAsMonomorphic; |
| |
| // If cleared, we have a new chance to become monomorphic. |
| const _feedbackValue: HeapObject = |
| MaybeObjectToStrong(feedback) otherwise TryInitializeAsMonomorphic; |
| |
| goto TransitionToMegamorphic; |
| } label TryInitializeAsMonomorphic { |
| TryInitializeAsMonomorphic(maybeTarget, feedbackVector, slotId) |
| otherwise TransitionToMegamorphic; |
| } label TransitionToMegamorphic { |
| TransitionToMegamorphic(feedbackVector, slotId); |
| } |
| } |
| |
| macro BothTaggedEqualArrayFunction(implicit context: Context)( |
| first: JSAny, second: JSAny): bool { |
| return TaggedEqual(first, second) && TaggedEqual(second, GetArrayFunction()); |
| } |
| |
| extern macro CreateAllocationSiteInFeedbackVector( |
| FeedbackVector, uintptr): AllocationSite; |
| |
| macro CastFeedbackVector( |
| maybeFeedbackVector: Undefined|FeedbackVector, |
| updateFeedbackMode: constexpr UpdateFeedbackMode): |
| FeedbackVector labels Fallback { |
| if constexpr (updateFeedbackMode == UpdateFeedbackMode::kGuaranteedFeedback) { |
| return UnsafeCast<FeedbackVector>(maybeFeedbackVector); |
| } else if constexpr ( |
| updateFeedbackMode == UpdateFeedbackMode::kOptionalFeedback) { |
| return Cast<FeedbackVector>(maybeFeedbackVector) otherwise goto Fallback; |
| } else { |
| unreachable; |
| } |
| } |
| |
| macro CollectConstructFeedback(implicit context: Context)( |
| target: JSAny, newTarget: JSAny, |
| maybeFeedbackVector: Undefined|FeedbackVector, slotId: uintptr, |
| updateFeedbackMode: constexpr UpdateFeedbackMode): |
| never labels ConstructGeneric, |
| ConstructArray(AllocationSite) { |
| // TODO(v8:9891): Remove this assert once all callers are ported to Torque. |
| // This assert ensures correctness of maybeFeedbackVector's type which can |
| // be easily broken for calls from CSA. |
| assert( |
| IsUndefined(maybeFeedbackVector) || |
| Is<FeedbackVector>(maybeFeedbackVector)); |
| |
| const feedbackVector = CastFeedbackVector( |
| maybeFeedbackVector, updateFeedbackMode) otherwise goto ConstructGeneric; |
| |
| IncrementCallCount(feedbackVector, slotId); |
| |
| try { |
| const feedback: MaybeObject = |
| LoadFeedbackVectorSlot(feedbackVector, slotId); |
| if (IsMonomorphic(feedback, newTarget)) goto ConstructGeneric; |
| if (IsMegamorphic(feedback)) goto ConstructGeneric; |
| if (IsUninitialized(feedback)) goto TryInitializeAsMonomorphic; |
| |
| if (!IsWeakOrCleared(feedback)) { |
| const feedbackAsStrong = %RawDownCast<Object>(feedback); |
| if (Is<AllocationSite>(feedbackAsStrong)) { |
| if (BothTaggedEqualArrayFunction(target, newTarget)) { |
| goto ConstructArray(UnsafeCast<AllocationSite>(feedbackAsStrong)); |
| } |
| goto TransitionToMegamorphic; |
| } |
| } |
| |
| // If cleared, we have a new chance to become monomorphic. |
| const _feedbackValue: HeapObject = |
| MaybeObjectToStrong(feedback) otherwise TryInitializeAsMonomorphic; |
| |
| goto TransitionToMegamorphic; |
| } label TryInitializeAsMonomorphic { |
| if (BothTaggedEqualArrayFunction(target, newTarget)) { |
| // In this case we can skip unwrapping and context validation since we |
| // know the target is the current context's array function. |
| const allocationSite = |
| CreateAllocationSiteInFeedbackVector(feedbackVector, slotId); |
| ReportFeedbackUpdate( |
| feedbackVector, slotId, 'Construct:CreateAllocationSite'); |
| goto ConstructArray(allocationSite); |
| } |
| |
| TryInitializeAsMonomorphic(newTarget, feedbackVector, slotId) |
| otherwise TransitionToMegamorphic; |
| } label TransitionToMegamorphic { |
| TransitionToMegamorphic(feedbackVector, slotId); |
| } |
| goto ConstructGeneric; |
| } |
| |
| } // namespace callable |
| } // namespace ic |