diff --git a/src/compiler/effect-control-linearizer.cc b/src/compiler/effect-control-linearizer.cc index 965831c0bb..b88906cfc1 100644 --- a/src/compiler/effect-control-linearizer.cc +++ b/src/compiler/effect-control-linearizer.cc @@ -1058,25 +1058,79 @@ Node* EffectControlLinearizer::LowerCheckMaps(Node* node, Node* frame_state) { Node* value = node->InputAt(0); ZoneHandleSet const& maps = p.maps(); - int const map_count = static_cast(maps.size()); + size_t const map_count = maps.size(); - auto done = __ MakeLabelFor(GraphAssemblerLabelType::kNonDeferred, - static_cast(map_count)); + if (p.flags() & CheckMapsFlag::kTryMigrateInstance) { + auto done = + __ MakeLabelFor(GraphAssemblerLabelType::kNonDeferred, map_count * 2); + auto migrate = __ MakeDeferredLabel<1>(); - // Load the current map of the {value}. - Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); + // Load the current map of the {value}. + Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); - for (int i = 0; i < map_count; ++i) { - Node* map = __ HeapConstant(maps[i]); - Node* check = __ WordEqual(value_map, map); - if (i == map_count - 1) { - __ DeoptimizeUnless(DeoptimizeReason::kWrongMap, check, frame_state); - } else { - __ GotoIf(check, &done); + // Perform the map checks. + for (size_t i = 0; i < map_count; ++i) { + Node* map = __ HeapConstant(maps[i]); + Node* check = __ WordEqual(value_map, map); + if (i == map_count - 1) { + __ GotoUnless(check, &migrate); + __ Goto(&done); + } else { + __ GotoIf(check, &done); + } } + + // Perform the (deferred) instance migration. + __ Bind(&migrate); + { + Operator::Properties properties = Operator::kNoDeopt | Operator::kNoThrow; + Runtime::FunctionId id = Runtime::kTryMigrateInstance; + CallDescriptor const* desc = Linkage::GetRuntimeCallDescriptor( + graph()->zone(), id, 1, properties, CallDescriptor::kNoFlags); + Node* result = + __ Call(desc, __ CEntryStubConstant(1), value, + __ ExternalConstant(ExternalReference(id, isolate())), + __ Int32Constant(1), __ NoContextConstant()); + Node* check = ObjectIsSmi(result); + __ DeoptimizeIf(DeoptimizeReason::kInstanceMigrationFailed, check, + frame_state); + } + + // Reload the current map of the {value}. + value_map = __ LoadField(AccessBuilder::ForMap(), value); + + // Perform the map checks again. + for (size_t i = 0; i < map_count; ++i) { + Node* map = __ HeapConstant(maps[i]); + Node* check = __ WordEqual(value_map, map); + if (i == map_count - 1) { + __ DeoptimizeUnless(DeoptimizeReason::kWrongMap, check, frame_state); + } else { + __ GotoIf(check, &done); + } + } + + __ Goto(&done); + __ Bind(&done); + } else { + auto done = + __ MakeLabelFor(GraphAssemblerLabelType::kNonDeferred, map_count); + + // Load the current map of the {value}. + Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); + + for (size_t i = 0; i < map_count; ++i) { + Node* map = __ HeapConstant(maps[i]); + Node* check = __ WordEqual(value_map, map); + if (i == map_count - 1) { + __ DeoptimizeUnless(DeoptimizeReason::kWrongMap, check, frame_state); + } else { + __ GotoIf(check, &done); + } + } + __ Goto(&done); + __ Bind(&done); } - __ Goto(&done); - __ Bind(&done); return value; } diff --git a/src/compiler/js-global-object-specialization.cc b/src/compiler/js-global-object-specialization.cc index cbb7ce8275..2fe5cabc22 100644 --- a/src/compiler/js-global-object-specialization.cc +++ b/src/compiler/js-global-object-specialization.cc @@ -214,9 +214,11 @@ Reduction JSGlobalObjectSpecialization::ReduceJSStoreGlobal(Node* node) { value, effect, control); // Check {value} map agains the {property_cell} map. - effect = graph()->NewNode(simplified()->CheckMaps(ZoneHandleSet( - property_cell_value_map)), - value, effect, control); + effect = + graph()->NewNode(simplified()->CheckMaps( + CheckMapsFlag::kNone, + ZoneHandleSet(property_cell_value_map)), + value, effect, control); property_cell_value_type = Type::OtherInternal(); representation = MachineRepresentation::kTaggedPointer; } else { diff --git a/src/compiler/js-native-context-specialization.cc b/src/compiler/js-native-context-specialization.cc index ccae14d118..aa5db2a398 100644 --- a/src/compiler/js-native-context-specialization.cc +++ b/src/compiler/js-native-context-specialization.cc @@ -1197,8 +1197,9 @@ JSNativeContextSpecialization::BuildPropertyAccess( if (access_info.field_map().ToHandle(&field_map)) { // Emit a map check for the value. effect = graph()->NewNode( - simplified()->CheckMaps(ZoneHandleSet(field_map)), value, - effect, control); + simplified()->CheckMaps(CheckMapsFlag::kNone, + ZoneHandleSet(field_map)), + value, effect, control); } field_access.write_barrier_kind = kPointerWriteBarrier; break; @@ -1519,9 +1520,11 @@ JSNativeContextSpecialization::BuildElementAccess( if (access_mode == AccessMode::kStore && IsFastSmiOrObjectElementsKind(elements_kind) && store_mode != STORE_NO_TRANSITION_HANDLE_COW) { - effect = graph()->NewNode(simplified()->CheckMaps(ZoneHandleSet( - factory()->fixed_array_map())), - elements, effect, control); + effect = graph()->NewNode( + simplified()->CheckMaps( + CheckMapsFlag::kNone, + ZoneHandleSet(factory()->fixed_array_map())), + elements, effect, control); } // Check if the {receiver} is a JSArray. @@ -1748,11 +1751,15 @@ Node* JSNativeContextSpecialization::BuildCheckMaps( } } ZoneHandleSet maps; + CheckMapsFlags flags = CheckMapsFlag::kNone; for (Handle map : receiver_maps) { maps.insert(map, graph()->zone()); + if (map->is_migration_target()) { + flags |= CheckMapsFlag::kTryMigrateInstance; + } } - return graph()->NewNode(simplified()->CheckMaps(maps), receiver, effect, - control); + return graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver, + effect, control); } void JSNativeContextSpecialization::AssumePrototypesStable( diff --git a/src/compiler/simplified-operator.cc b/src/compiler/simplified-operator.cc index 98e136682c..31dac61d7e 100644 --- a/src/compiler/simplified-operator.cc +++ b/src/compiler/simplified-operator.cc @@ -234,9 +234,19 @@ std::ostream& operator<<(std::ostream& os, CheckForMinusZeroMode mode) { return os; } +std::ostream& operator<<(std::ostream& os, CheckMapsFlags flags) { + bool empty = true; + if (flags & CheckMapsFlag::kTryMigrateInstance) { + os << "TryMigrateInstance"; + empty = false; + } + if (empty) os << "None"; + return os; +} + bool operator==(CheckMapsParameters const& lhs, CheckMapsParameters const& rhs) { - return lhs.maps() == rhs.maps(); + return lhs.flags() == rhs.flags() && lhs.maps() == rhs.maps(); } bool operator!=(CheckMapsParameters const& lhs, @@ -244,13 +254,15 @@ bool operator!=(CheckMapsParameters const& lhs, return !(lhs == rhs); } -size_t hash_value(CheckMapsParameters const& p) { return hash_value(p.maps()); } +size_t hash_value(CheckMapsParameters const& p) { + return base::hash_combine(p.flags(), p.maps()); +} std::ostream& operator<<(std::ostream& os, CheckMapsParameters const& p) { ZoneHandleSet const& maps = p.maps(); + os << p.flags(); for (size_t i = 0; i < maps.size(); ++i) { - if (i != 0) os << ", "; - os << Brief(*maps[i]); + os << ", " << Brief(*maps[i]); } return os; } @@ -742,8 +754,9 @@ const Operator* SimplifiedOperatorBuilder::CheckedTaggedToFloat64( return nullptr; } -const Operator* SimplifiedOperatorBuilder::CheckMaps(ZoneHandleSet maps) { - CheckMapsParameters const parameters(maps); +const Operator* SimplifiedOperatorBuilder::CheckMaps(CheckMapsFlags flags, + ZoneHandleSet maps) { + CheckMapsParameters const parameters(flags, maps); return new (zone()) Operator1( // -- IrOpcode::kCheckMaps, // opcode Operator::kNoThrow | Operator::kNoWrite, // flags diff --git a/src/compiler/simplified-operator.h b/src/compiler/simplified-operator.h index f4c7e7a2af..4ad44354f8 100644 --- a/src/compiler/simplified-operator.h +++ b/src/compiler/simplified-operator.h @@ -145,14 +145,28 @@ std::ostream& operator<<(std::ostream&, CheckForMinusZeroMode); CheckForMinusZeroMode CheckMinusZeroModeOf(const Operator*) WARN_UNUSED_RESULT; +// Flags for map checks. +enum class CheckMapsFlag : uint8_t { + kNone = 0u, + kTryMigrateInstance = 1u << 0, // Try instance migration. +}; +typedef base::Flags CheckMapsFlags; + +DEFINE_OPERATORS_FOR_FLAGS(CheckMapsFlags) + +std::ostream& operator<<(std::ostream&, CheckMapsFlags); + // A descriptor for map checks. class CheckMapsParameters final { public: - explicit CheckMapsParameters(ZoneHandleSet const& maps) : maps_(maps) {} + CheckMapsParameters(CheckMapsFlags flags, ZoneHandleSet const& maps) + : flags_(flags), maps_(maps) {} + CheckMapsFlags flags() const { return flags_; } ZoneHandleSet const& maps() const { return maps_; } private: + CheckMapsFlags const flags_; ZoneHandleSet const maps_; }; @@ -364,7 +378,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final const Operator* CheckIf(); const Operator* CheckBounds(); - const Operator* CheckMaps(ZoneHandleSet); + const Operator* CheckMaps(CheckMapsFlags, ZoneHandleSet); const Operator* CheckHeapObject(); const Operator* CheckInternalizedString();