// 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; 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 CollectCallFeedback( 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; 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 TryInitializeAsMonomorphic; // 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 TryInitializeAsMonomorphic { TryInitializeAsMonomorphic(maybeTarget, 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