Commit 1ff4a817 authored by bmeurer's avatar bmeurer Committed by Commit bot

[turbofan] Add support for (deferred) instance migration.

Fix a deoptimization loop in TurboFan, where we'd constantly fail the
same map check due to not trying instance migration, when there are
migration targets in the map check list. This deoptimization loop
showed up on the React test in Speedometer.

R=jarin@chromium.org

Review-Url: https://codereview.chromium.org/2621423006
Cr-Commit-Position: refs/heads/master@{#42323}
parent 124fbe55
...@@ -1058,25 +1058,79 @@ Node* EffectControlLinearizer::LowerCheckMaps(Node* node, Node* frame_state) { ...@@ -1058,25 +1058,79 @@ Node* EffectControlLinearizer::LowerCheckMaps(Node* node, Node* frame_state) {
Node* value = node->InputAt(0); Node* value = node->InputAt(0);
ZoneHandleSet<Map> const& maps = p.maps(); ZoneHandleSet<Map> const& maps = p.maps();
int const map_count = static_cast<int>(maps.size()); size_t const map_count = maps.size();
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);
// 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);
}
}
auto done = __ MakeLabelFor(GraphAssemblerLabelType::kNonDeferred, // Perform the (deferred) instance migration.
static_cast<size_t>(map_count)); __ 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);
}
// Load the current map of the {value}. // Reload the current map of the {value}.
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value); value_map = __ LoadField(AccessBuilder::ForMap(), value);
for (int i = 0; i < map_count; ++i) { // Perform the map checks again.
Node* map = __ HeapConstant(maps[i]); for (size_t i = 0; i < map_count; ++i) {
Node* check = __ WordEqual(value_map, map); Node* map = __ HeapConstant(maps[i]);
if (i == map_count - 1) { Node* check = __ WordEqual(value_map, map);
__ DeoptimizeUnless(DeoptimizeReason::kWrongMap, check, frame_state); if (i == map_count - 1) {
} else { __ DeoptimizeUnless(DeoptimizeReason::kWrongMap, check, frame_state);
__ GotoIf(check, &done); } 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; return value;
} }
......
...@@ -214,9 +214,11 @@ Reduction JSGlobalObjectSpecialization::ReduceJSStoreGlobal(Node* node) { ...@@ -214,9 +214,11 @@ Reduction JSGlobalObjectSpecialization::ReduceJSStoreGlobal(Node* node) {
value, effect, control); value, effect, control);
// Check {value} map agains the {property_cell} map. // Check {value} map agains the {property_cell} map.
effect = graph()->NewNode(simplified()->CheckMaps(ZoneHandleSet<Map>( effect =
property_cell_value_map)), graph()->NewNode(simplified()->CheckMaps(
value, effect, control); CheckMapsFlag::kNone,
ZoneHandleSet<Map>(property_cell_value_map)),
value, effect, control);
property_cell_value_type = Type::OtherInternal(); property_cell_value_type = Type::OtherInternal();
representation = MachineRepresentation::kTaggedPointer; representation = MachineRepresentation::kTaggedPointer;
} else { } else {
......
...@@ -1197,8 +1197,9 @@ JSNativeContextSpecialization::BuildPropertyAccess( ...@@ -1197,8 +1197,9 @@ JSNativeContextSpecialization::BuildPropertyAccess(
if (access_info.field_map().ToHandle(&field_map)) { if (access_info.field_map().ToHandle(&field_map)) {
// Emit a map check for the value. // Emit a map check for the value.
effect = graph()->NewNode( effect = graph()->NewNode(
simplified()->CheckMaps(ZoneHandleSet<Map>(field_map)), value, simplified()->CheckMaps(CheckMapsFlag::kNone,
effect, control); ZoneHandleSet<Map>(field_map)),
value, effect, control);
} }
field_access.write_barrier_kind = kPointerWriteBarrier; field_access.write_barrier_kind = kPointerWriteBarrier;
break; break;
...@@ -1519,9 +1520,11 @@ JSNativeContextSpecialization::BuildElementAccess( ...@@ -1519,9 +1520,11 @@ JSNativeContextSpecialization::BuildElementAccess(
if (access_mode == AccessMode::kStore && if (access_mode == AccessMode::kStore &&
IsFastSmiOrObjectElementsKind(elements_kind) && IsFastSmiOrObjectElementsKind(elements_kind) &&
store_mode != STORE_NO_TRANSITION_HANDLE_COW) { store_mode != STORE_NO_TRANSITION_HANDLE_COW) {
effect = graph()->NewNode(simplified()->CheckMaps(ZoneHandleSet<Map>( effect = graph()->NewNode(
factory()->fixed_array_map())), simplified()->CheckMaps(
elements, effect, control); CheckMapsFlag::kNone,
ZoneHandleSet<Map>(factory()->fixed_array_map())),
elements, effect, control);
} }
// Check if the {receiver} is a JSArray. // Check if the {receiver} is a JSArray.
...@@ -1748,11 +1751,15 @@ Node* JSNativeContextSpecialization::BuildCheckMaps( ...@@ -1748,11 +1751,15 @@ Node* JSNativeContextSpecialization::BuildCheckMaps(
} }
} }
ZoneHandleSet<Map> maps; ZoneHandleSet<Map> maps;
CheckMapsFlags flags = CheckMapsFlag::kNone;
for (Handle<Map> map : receiver_maps) { for (Handle<Map> map : receiver_maps) {
maps.insert(map, graph()->zone()); maps.insert(map, graph()->zone());
if (map->is_migration_target()) {
flags |= CheckMapsFlag::kTryMigrateInstance;
}
} }
return graph()->NewNode(simplified()->CheckMaps(maps), receiver, effect, return graph()->NewNode(simplified()->CheckMaps(flags, maps), receiver,
control); effect, control);
} }
void JSNativeContextSpecialization::AssumePrototypesStable( void JSNativeContextSpecialization::AssumePrototypesStable(
......
...@@ -234,9 +234,19 @@ std::ostream& operator<<(std::ostream& os, CheckForMinusZeroMode mode) { ...@@ -234,9 +234,19 @@ std::ostream& operator<<(std::ostream& os, CheckForMinusZeroMode mode) {
return os; 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, bool operator==(CheckMapsParameters const& lhs,
CheckMapsParameters const& rhs) { CheckMapsParameters const& rhs) {
return lhs.maps() == rhs.maps(); return lhs.flags() == rhs.flags() && lhs.maps() == rhs.maps();
} }
bool operator!=(CheckMapsParameters const& lhs, bool operator!=(CheckMapsParameters const& lhs,
...@@ -244,13 +254,15 @@ bool operator!=(CheckMapsParameters const& lhs, ...@@ -244,13 +254,15 @@ bool operator!=(CheckMapsParameters const& lhs,
return !(lhs == rhs); 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) { std::ostream& operator<<(std::ostream& os, CheckMapsParameters const& p) {
ZoneHandleSet<Map> const& maps = p.maps(); ZoneHandleSet<Map> const& maps = p.maps();
os << p.flags();
for (size_t i = 0; i < maps.size(); ++i) { for (size_t i = 0; i < maps.size(); ++i) {
if (i != 0) os << ", "; os << ", " << Brief(*maps[i]);
os << Brief(*maps[i]);
} }
return os; return os;
} }
...@@ -742,8 +754,9 @@ const Operator* SimplifiedOperatorBuilder::CheckedTaggedToFloat64( ...@@ -742,8 +754,9 @@ const Operator* SimplifiedOperatorBuilder::CheckedTaggedToFloat64(
return nullptr; return nullptr;
} }
const Operator* SimplifiedOperatorBuilder::CheckMaps(ZoneHandleSet<Map> maps) { const Operator* SimplifiedOperatorBuilder::CheckMaps(CheckMapsFlags flags,
CheckMapsParameters const parameters(maps); ZoneHandleSet<Map> maps) {
CheckMapsParameters const parameters(flags, maps);
return new (zone()) Operator1<CheckMapsParameters>( // -- return new (zone()) Operator1<CheckMapsParameters>( // --
IrOpcode::kCheckMaps, // opcode IrOpcode::kCheckMaps, // opcode
Operator::kNoThrow | Operator::kNoWrite, // flags Operator::kNoThrow | Operator::kNoWrite, // flags
......
...@@ -145,14 +145,28 @@ std::ostream& operator<<(std::ostream&, CheckForMinusZeroMode); ...@@ -145,14 +145,28 @@ std::ostream& operator<<(std::ostream&, CheckForMinusZeroMode);
CheckForMinusZeroMode CheckMinusZeroModeOf(const Operator*) WARN_UNUSED_RESULT; 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<CheckMapsFlag> CheckMapsFlags;
DEFINE_OPERATORS_FOR_FLAGS(CheckMapsFlags)
std::ostream& operator<<(std::ostream&, CheckMapsFlags);
// A descriptor for map checks. // A descriptor for map checks.
class CheckMapsParameters final { class CheckMapsParameters final {
public: public:
explicit CheckMapsParameters(ZoneHandleSet<Map> const& maps) : maps_(maps) {} CheckMapsParameters(CheckMapsFlags flags, ZoneHandleSet<Map> const& maps)
: flags_(flags), maps_(maps) {}
CheckMapsFlags flags() const { return flags_; }
ZoneHandleSet<Map> const& maps() const { return maps_; } ZoneHandleSet<Map> const& maps() const { return maps_; }
private: private:
CheckMapsFlags const flags_;
ZoneHandleSet<Map> const maps_; ZoneHandleSet<Map> const maps_;
}; };
...@@ -364,7 +378,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final ...@@ -364,7 +378,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* CheckIf(); const Operator* CheckIf();
const Operator* CheckBounds(); const Operator* CheckBounds();
const Operator* CheckMaps(ZoneHandleSet<Map>); const Operator* CheckMaps(CheckMapsFlags, ZoneHandleSet<Map>);
const Operator* CheckHeapObject(); const Operator* CheckHeapObject();
const Operator* CheckInternalizedString(); const Operator* CheckInternalizedString();
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment