// Copyright 2020 the V8 project authors. All rights reserved. // Use of this source code is governed by a BSD-style license that can be: // Context found in the LICENSE file. namespace ic { const kSuccess: constexpr int32 generates 'static_cast(DynamicMapChecksStatus::kSuccess)'; const kBailout: constexpr int32 generates 'static_cast(DynamicMapChecksStatus::kBailout)'; const kDeopt: constexpr int32 generates 'static_cast(DynamicMapChecksStatus::kDeopt)'; extern runtime TryMigrateInstance(implicit context: Context)(Object): Object; extern macro LoadFeedbackVectorForStub(): FeedbackVector; macro PerformMapAndHandlerCheck( entry: constexpr int32, polymorphicArray: WeakFixedArray, weakActualMap: WeakHeapObject, actualHandler: Smi|DataHandler): void labels Next, Deopt { const mapIndex = FeedbackIteratorMapIndexForEntry(entry); assert(mapIndex < polymorphicArray.length_intptr); const maybeCachedMap = UnsafeCast(polymorphicArray[mapIndex]); if (maybeCachedMap != weakActualMap) { goto Next; } const handlerIndex = FeedbackIteratorHandlerIndexForEntry(entry); assert(handlerIndex < polymorphicArray.length_intptr); const maybeHandler = Cast(polymorphicArray[handlerIndex]) otherwise unreachable; if (TaggedNotEqual(maybeHandler, actualHandler)) { goto Deopt; } } macro PerformPolymorphicCheck( expectedPolymorphicArray: HeapObject, actualMap: Map, actualHandler: Smi|DataHandler): int32 { if (!Is(expectedPolymorphicArray)) { return kDeopt; } try { const polymorphicArray = UnsafeCast(expectedPolymorphicArray); const weakActualMap = MakeWeak(actualMap); const length = polymorphicArray.length_intptr; assert(length > 0); try { if (length >= FeedbackIteratorSizeFor(4)) goto Len4; if (length == FeedbackIteratorSizeFor(3)) goto Len3; if (length == FeedbackIteratorSizeFor(2)) goto Len2; if (length == FeedbackIteratorSizeFor(1)) goto Len1; unreachable; } label Len4 { PerformMapAndHandlerCheck( 3, polymorphicArray, weakActualMap, actualHandler) otherwise Len3, Deopt; return kSuccess; } label Len3 { PerformMapAndHandlerCheck( 2, polymorphicArray, weakActualMap, actualHandler) otherwise Len2, Deopt; return kSuccess; } label Len2 { PerformMapAndHandlerCheck( 1, polymorphicArray, weakActualMap, actualHandler) otherwise Len1, Deopt; return kSuccess; } label Len1 { PerformMapAndHandlerCheck( 0, polymorphicArray, weakActualMap, actualHandler) otherwise Bailout, Deopt; return kSuccess; } } label Bailout { return kBailout; } label Deopt { return kDeopt; } } macro PerformMonomorphicCheck( feedbackVector: FeedbackVector, slotIndex: intptr, expectedMap: HeapObject, actualMap: Map, actualHandler: Smi|DataHandler): int32 { if (TaggedEqual(expectedMap, actualMap)) { const handlerIndex = slotIndex + 1; assert(handlerIndex < feedbackVector.length_intptr); const maybeHandler = Cast(feedbackVector[handlerIndex]) otherwise unreachable; if (TaggedEqual(actualHandler, maybeHandler)) { return kSuccess; } return kDeopt; } return kBailout; } // This builtin performs map checks by dynamically looking at the // feedback in the feedback vector. // // There are two major cases handled by this builtin: // (a) Monormorphic check // (b) Polymorphic check // // For the monormophic check, the incoming map is migrated and checked // against the map and handler in the feedback vector. Otherwise, we // bailout to the runtime. // // For the polymorphic check, the feedback vector is iterated over and // each of the maps & handers are compared against the incoming map and // handler. // // If any of the map and associated handler checks pass then we return // kSuccess status. // // If any of the map check passes but the associated handler check // fails then we return kFailure status. // // For other cases, we bailout to the runtime. builtin DynamicMapChecks(implicit context: Context)( slotIndex: intptr, actualValue: HeapObject, actualHandler: Smi|DataHandler): int32 { const feedbackVector = LoadFeedbackVectorForStub(); let actualMap = actualValue.map; const feedback = feedbackVector[slotIndex]; try { const maybePolymorphicArray = GetHeapObjectIfStrong(feedback) otherwise MigrateAndDoMonomorphicCheck; return PerformPolymorphicCheck( maybePolymorphicArray, actualMap, actualHandler); } label MigrateAndDoMonomorphicCheck { const expectedMap = GetHeapObjectAssumeWeak(feedback) otherwise Deopt; if (IsDeprecatedMap(actualMap)) { // TODO(gsathya): Should this migration happen before the // polymorphic check? const result = TryMigrateInstance(actualValue); if (TaggedIsSmi(result)) { return kDeopt; } actualMap = actualValue.map; } return PerformMonomorphicCheck( feedbackVector, slotIndex, expectedMap, actualMap, actualHandler); } label Deopt { return kDeopt; } } } // namespace ic