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) {
Node* value = node->InputAt(0);
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,
static_cast<size_t>(map_count));
// 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);
}
// Load the current map of the {value}.
Node* value_map = __ LoadField(AccessBuilder::ForMap(), value);
// Reload the current map of the {value}.
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 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;
}
......
......@@ -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<Map>(
property_cell_value_map)),
value, effect, control);
effect =
graph()->NewNode(simplified()->CheckMaps(
CheckMapsFlag::kNone,
ZoneHandleSet<Map>(property_cell_value_map)),
value, effect, control);
property_cell_value_type = Type::OtherInternal();
representation = MachineRepresentation::kTaggedPointer;
} else {
......
......@@ -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<Map>(field_map)), value,
effect, control);
simplified()->CheckMaps(CheckMapsFlag::kNone,
ZoneHandleSet<Map>(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<Map>(
factory()->fixed_array_map())),
elements, effect, control);
effect = graph()->NewNode(
simplified()->CheckMaps(
CheckMapsFlag::kNone,
ZoneHandleSet<Map>(factory()->fixed_array_map())),
elements, effect, control);
}
// Check if the {receiver} is a JSArray.
......@@ -1748,11 +1751,15 @@ Node* JSNativeContextSpecialization::BuildCheckMaps(
}
}
ZoneHandleSet<Map> maps;
CheckMapsFlags flags = CheckMapsFlag::kNone;
for (Handle<Map> 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(
......
......@@ -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<Map> 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<Map> maps) {
CheckMapsParameters const parameters(maps);
const Operator* SimplifiedOperatorBuilder::CheckMaps(CheckMapsFlags flags,
ZoneHandleSet<Map> maps) {
CheckMapsParameters const parameters(flags, maps);
return new (zone()) Operator1<CheckMapsParameters>( // --
IrOpcode::kCheckMaps, // opcode
Operator::kNoThrow | Operator::kNoWrite, // flags
......
......@@ -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<CheckMapsFlag> CheckMapsFlags;
DEFINE_OPERATORS_FOR_FLAGS(CheckMapsFlags)
std::ostream& operator<<(std::ostream&, CheckMapsFlags);
// A descriptor for map checks.
class CheckMapsParameters final {
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_; }
private:
CheckMapsFlags const flags_;
ZoneHandleSet<Map> const maps_;
};
......@@ -364,7 +378,7 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
const Operator* CheckIf();
const Operator* CheckBounds();
const Operator* CheckMaps(ZoneHandleSet<Map>);
const Operator* CheckMaps(CheckMapsFlags, ZoneHandleSet<Map>);
const Operator* CheckHeapObject();
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