[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}
This commit is contained in:
bmeurer 2017-01-13 04:09:52 -08:00 committed by Commit bot
parent 124fbe55ec
commit 1ff4a8178e
5 changed files with 122 additions and 32 deletions

View File

@ -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();
auto done = __ MakeLabelFor(GraphAssemblerLabelType::kNonDeferred,
static_cast<size_t>(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;
}

View File

@ -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 {

View File

@ -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(

View File

@ -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

View File

@ -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();