| // 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<int>(DynamicMapChecksStatus::kSuccess)'; |
| const kBailout: constexpr int32 |
| generates 'static_cast<int>(DynamicMapChecksStatus::kBailout)'; |
| const kDeopt: constexpr int32 |
| generates 'static_cast<int>(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<WeakHeapObject>(polymorphicArray[mapIndex]); |
| if (maybeCachedMap != weakActualMap) { |
| goto Next; |
| } |
| |
| const handlerIndex = FeedbackIteratorHandlerIndexForEntry(entry); |
| assert(handlerIndex < polymorphicArray.length_intptr); |
| const maybeHandler = |
| Cast<Object>(polymorphicArray[handlerIndex]) otherwise unreachable; |
| if (TaggedNotEqual(maybeHandler, actualHandler)) { |
| goto Deopt; |
| } |
| } |
| |
| macro PerformPolymorphicCheck( |
| expectedPolymorphicArray: HeapObject, actualMap: Map, |
| actualHandler: Smi|DataHandler): int32 { |
| if (!Is<WeakFixedArray>(expectedPolymorphicArray)) { |
| return kDeopt; |
| } |
| try { |
| const polymorphicArray = |
| UnsafeCast<WeakFixedArray>(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<Object>(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 |