[turboprop] Migrate deprecated maps in dynamic map check operator
If incoming map is deprecated, generate code to migrate the map. Since this involves generating additional code and a call to runtime, we only do this if one of the receiver maps was a migration target when optimizing this function. If not, we deoptimize and discard the optimized code if we see a deprecated map. This is to avoid bailout loops when we see deprecated maps. This change does the following: // We generated code to migrate deprecated maps only if one of the maps // in feedback vector is a migration target. if ( there are migration targets in feedback) { if (checkMaps fails) { if (incoming map is deprecated) { migrate the map checkMaps with the new map } else { bailout } } } else { if (checkMaps fails) bailout; } Bug: v8:10582, v8:9684 Change-Id: I8a04c77ed209dd2fb0300a783d844f2335a678c8 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2292231 Reviewed-by: Sathya Gunasekaran <gsathya@chromium.org> Reviewed-by: Georg Neis <neis@chromium.org> Commit-Queue: Mythri Alle <mythria@chromium.org> Cr-Commit-Position: refs/heads/master@{#69179}
This commit is contained in:
parent
065dde954a
commit
9b9ba19e3c
@ -272,6 +272,22 @@ class EffectControlLinearizer {
|
||||
void TransitionElementsTo(Node* node, Node* array, ElementsKind from,
|
||||
ElementsKind to);
|
||||
|
||||
// This function tries to migrate |value| if its map |value_map| is
|
||||
// deprecated. It deopts, if either |value_map| isn't deprecated or migration
|
||||
// fails.
|
||||
void MigrateInstanceOrDeopt(Node* value, Node* value_map, Node* frame_state,
|
||||
FeedbackSource const& feedback_source,
|
||||
DeoptimizeReason reason);
|
||||
|
||||
// Helper functions used in LowerDynamicCheckMaps
|
||||
void CheckPolymorphic(Node* feedback, Node* value_map, Node* handler,
|
||||
GraphAssemblerLabel<0>* migrate,
|
||||
GraphAssemblerLabel<0>* done, Node* frame_state);
|
||||
void CheckMonomorphic(Node* feedback, Node* value_map, Node* handler,
|
||||
GraphAssemblerLabel<0>* done,
|
||||
GraphAssemblerLabel<0>* map_check_failed,
|
||||
Node* frame_state, int slot, Node* vector);
|
||||
|
||||
bool should_maintain_schedule() const {
|
||||
return maintain_schedule_ == MaintainSchedule::kMaintain;
|
||||
}
|
||||
@ -1763,6 +1779,29 @@ Node* EffectControlLinearizer::LowerCheckClosure(Node* node,
|
||||
return value;
|
||||
}
|
||||
|
||||
void EffectControlLinearizer::MigrateInstanceOrDeopt(
|
||||
Node* value, Node* value_map, Node* frame_state,
|
||||
FeedbackSource const& feedback_source, DeoptimizeReason reason) {
|
||||
// If map is not deprecated the migration attempt does not make sense.
|
||||
Node* bitfield3 = __ LoadField(AccessBuilder::ForMapBitField3(), value_map);
|
||||
Node* is_not_deprecated = __ Word32Equal(
|
||||
__ Word32And(bitfield3,
|
||||
__ Int32Constant(Map::Bits3::IsDeprecatedBit::kMask)),
|
||||
__ Int32Constant(0));
|
||||
__ DeoptimizeIf(reason, feedback_source, is_not_deprecated, frame_state,
|
||||
IsSafetyCheck::kCriticalSafetyCheck);
|
||||
Operator::Properties properties = Operator::kNoDeopt | Operator::kNoThrow;
|
||||
Runtime::FunctionId id = Runtime::kTryMigrateInstance;
|
||||
auto call_descriptor = Linkage::GetRuntimeCallDescriptor(
|
||||
graph()->zone(), id, 1, properties, CallDescriptor::kNoFlags);
|
||||
Node* result = __ Call(call_descriptor, __ CEntryStubConstant(1), value,
|
||||
__ ExternalConstant(ExternalReference::Create(id)),
|
||||
__ Int32Constant(1), __ NoContextConstant());
|
||||
Node* check = ObjectIsSmi(result);
|
||||
__ DeoptimizeIf(DeoptimizeReason::kInstanceMigrationFailed, feedback_source,
|
||||
check, frame_state, IsSafetyCheck::kCriticalSafetyCheck);
|
||||
}
|
||||
|
||||
void EffectControlLinearizer::LowerCheckMaps(Node* node, Node* frame_state) {
|
||||
CheckMapsParameters const& p = CheckMapsParametersOf(node->op());
|
||||
Node* value = node->InputAt(0);
|
||||
@ -1792,29 +1831,8 @@ void EffectControlLinearizer::LowerCheckMaps(Node* node, Node* frame_state) {
|
||||
|
||||
// Perform the (deferred) instance migration.
|
||||
__ Bind(&migrate);
|
||||
{
|
||||
// If map is not deprecated the migration attempt does not make sense.
|
||||
Node* bitfield3 =
|
||||
__ LoadField(AccessBuilder::ForMapBitField3(), value_map);
|
||||
Node* if_not_deprecated = __ Word32Equal(
|
||||
__ Word32And(bitfield3,
|
||||
__ Int32Constant(Map::Bits3::IsDeprecatedBit::kMask)),
|
||||
__ Int32Constant(0));
|
||||
__ DeoptimizeIf(DeoptimizeReason::kWrongMap, p.feedback(),
|
||||
if_not_deprecated, frame_state,
|
||||
IsSafetyCheck::kCriticalSafetyCheck);
|
||||
|
||||
Operator::Properties properties = Operator::kNoDeopt | Operator::kNoThrow;
|
||||
Runtime::FunctionId id = Runtime::kTryMigrateInstance;
|
||||
auto call_descriptor = Linkage::GetRuntimeCallDescriptor(
|
||||
graph()->zone(), id, 1, properties, CallDescriptor::kNoFlags);
|
||||
Node* result = __ Call(call_descriptor, __ CEntryStubConstant(1), value,
|
||||
__ ExternalConstant(ExternalReference::Create(id)),
|
||||
__ Int32Constant(1), __ NoContextConstant());
|
||||
Node* check = ObjectIsSmi(result);
|
||||
__ DeoptimizeIf(DeoptimizeReason::kInstanceMigrationFailed, p.feedback(),
|
||||
check, frame_state, IsSafetyCheck::kCriticalSafetyCheck);
|
||||
}
|
||||
MigrateInstanceOrDeopt(value, value_map, frame_state, p.feedback(),
|
||||
DeoptimizeReason::kWrongMap);
|
||||
|
||||
// Reload the current map of the {value}.
|
||||
value_map = __ LoadField(AccessBuilder::ForMap(), value);
|
||||
@ -1859,6 +1877,80 @@ void EffectControlLinearizer::LowerCheckMaps(Node* node, Node* frame_state) {
|
||||
}
|
||||
}
|
||||
|
||||
void EffectControlLinearizer::CheckPolymorphic(Node* feedback_slot,
|
||||
Node* value_map, Node* handler,
|
||||
GraphAssemblerLabel<0>* migrate,
|
||||
GraphAssemblerLabel<0>* done,
|
||||
Node* frame_state) {
|
||||
Node* feedback_slot_map =
|
||||
__ LoadField(AccessBuilder::ForMap(), feedback_slot);
|
||||
Node* is_weak_fixed_array_check =
|
||||
__ TaggedEqual(feedback_slot_map, __ WeakFixedArrayMapConstant());
|
||||
__ DeoptimizeIfNot(DeoptimizeReason::kTransitionedToMegamorphicIC,
|
||||
FeedbackSource(), is_weak_fixed_array_check, frame_state,
|
||||
IsSafetyCheck::kCriticalSafetyCheck);
|
||||
|
||||
Node* length = ChangeSmiToInt32(
|
||||
__ LoadField(AccessBuilder::ForWeakFixedArrayLength(), feedback_slot));
|
||||
auto loop = __ MakeLoopLabel(MachineRepresentation::kWord32);
|
||||
__ Goto(&loop, __ Int32Constant(0));
|
||||
__ Bind(&loop);
|
||||
{
|
||||
Node* index = loop.PhiAt(0);
|
||||
Node* check = __ Int32LessThan(index, length);
|
||||
if (migrate != nullptr) {
|
||||
__ GotoIfNot(check, migrate);
|
||||
} else {
|
||||
__ DeoptimizeIfNot(DeoptimizeKind::kBailout,
|
||||
DeoptimizeReason::kMissingMap, FeedbackSource(), check,
|
||||
frame_state, IsSafetyCheck::kCriticalSafetyCheck);
|
||||
}
|
||||
|
||||
Node* maybe_map = __ LoadElement(AccessBuilder::ForWeakFixedArrayElement(),
|
||||
feedback_slot, index);
|
||||
auto continue_loop = __ MakeLabel();
|
||||
|
||||
__ GotoIfNot(BuildIsWeakReferenceTo(maybe_map, value_map), &continue_loop);
|
||||
constexpr int kHandlerOffsetInEntry = 1;
|
||||
Node* maybe_handler = __ LoadElement(
|
||||
AccessBuilder::ForWeakFixedArrayElement(), feedback_slot,
|
||||
__ Int32Add(index, __ Int32Constant(kHandlerOffsetInEntry)));
|
||||
Node* handler_check = __ TaggedEqual(maybe_handler, handler);
|
||||
__ DeoptimizeIfNot(DeoptimizeReason::kWrongHandler, FeedbackSource(),
|
||||
handler_check, frame_state,
|
||||
IsSafetyCheck::kCriticalSafetyCheck);
|
||||
|
||||
__ Goto(done);
|
||||
|
||||
__ Bind(&continue_loop);
|
||||
constexpr int kEntrySize = 2;
|
||||
index = __ Int32Add(index, __ Int32Constant(kEntrySize));
|
||||
__ Goto(&loop, index);
|
||||
}
|
||||
}
|
||||
|
||||
void EffectControlLinearizer::CheckMonomorphic(
|
||||
Node* feedback, Node* value_map, Node* handler,
|
||||
GraphAssemblerLabel<0>* done, GraphAssemblerLabel<0>* map_check_failed,
|
||||
Node* frame_state, int slot, Node* vector) {
|
||||
Node* mono_check = BuildIsWeakReferenceTo(feedback, value_map);
|
||||
if (map_check_failed != nullptr) {
|
||||
__ GotoIfNot(mono_check, map_check_failed);
|
||||
} else {
|
||||
__ DeoptimizeIfNot(DeoptimizeKind::kBailout, DeoptimizeReason::kMissingMap,
|
||||
FeedbackSource(), mono_check, frame_state,
|
||||
IsSafetyCheck::kCriticalSafetyCheck);
|
||||
}
|
||||
|
||||
Node* feedback_slot_handler =
|
||||
__ LoadField(AccessBuilder::ForFeedbackVectorSlot(slot + 1), vector);
|
||||
Node* handler_check = __ TaggedEqual(handler, feedback_slot_handler);
|
||||
__ DeoptimizeIfNot(DeoptimizeReason::kWrongHandler, FeedbackSource(),
|
||||
handler_check, frame_state,
|
||||
IsSafetyCheck::kCriticalSafetyCheck);
|
||||
__ Goto(done);
|
||||
}
|
||||
|
||||
void EffectControlLinearizer::LowerDynamicCheckMaps(Node* node,
|
||||
Node* frame_state) {
|
||||
DynamicCheckMapsParameters const& p =
|
||||
@ -1881,17 +1973,8 @@ void EffectControlLinearizer::LowerDynamicCheckMaps(Node* node,
|
||||
// case the current state is polymorphic, and if we ever go back to
|
||||
// monomorphic start, we will deopt and reoptimize the code.
|
||||
if (p.state() == DynamicCheckMapsParameters::kMonomorphic) {
|
||||
Node* mono_check = BuildIsWeakReferenceTo(feedback_slot, value_map);
|
||||
__ GotoIfNot(mono_check, &maybe_poly);
|
||||
|
||||
Node* feedback_slot_handler = __ LoadField(
|
||||
AccessBuilder::ForFeedbackVectorSlot(feedback.index() + 1), vector);
|
||||
mono_check = __ TaggedEqual(handler, feedback_slot_handler);
|
||||
__ DeoptimizeIfNot(DeoptimizeReason::kWrongHandler, FeedbackSource(),
|
||||
mono_check, frame_state,
|
||||
IsSafetyCheck::kCriticalSafetyCheck);
|
||||
|
||||
__ Goto(&done);
|
||||
CheckMonomorphic(feedback_slot, value_map, handler, &done, &maybe_poly,
|
||||
frame_state, feedback.index(), vector);
|
||||
} else {
|
||||
DCHECK(p.state() == DynamicCheckMapsParameters::kPolymorphic);
|
||||
__ Goto(&maybe_poly);
|
||||
@ -1899,62 +1982,47 @@ void EffectControlLinearizer::LowerDynamicCheckMaps(Node* node,
|
||||
|
||||
__ Bind(&maybe_poly);
|
||||
{
|
||||
Node* poly_check = BuildIsStrongReference(feedback_slot);
|
||||
if (p.state() == DynamicCheckMapsParameters::kMonomorphic) {
|
||||
__ DeoptimizeIfNot(DeoptimizeKind::kBailout,
|
||||
DeoptimizeReason::kMissingMap, FeedbackSource(),
|
||||
poly_check, frame_state,
|
||||
IsSafetyCheck::kCriticalSafetyCheck);
|
||||
Node* is_poly_or_megamorphic = BuildIsStrongReference(feedback_slot);
|
||||
// If the IC state at code generation time is not monomorphic, we don't
|
||||
// handle monomorphic states and just deoptimize if IC transitions to
|
||||
// monomorphic. For polymorphic ICs it is not required to migrate deprecated
|
||||
// maps since ICs don't discard deprecated maps from feedback. Only generate
|
||||
// codeneed to migrate maps for Monomoprhic state.
|
||||
if (p.flags() & CheckMapsFlag::kTryMigrateInstance &&
|
||||
p.state() == DynamicCheckMapsParameters::kMonomorphic) {
|
||||
auto migrate = __ MakeDeferredLabel();
|
||||
|
||||
__ GotoIfNot(is_poly_or_megamorphic, &migrate);
|
||||
// TODO(mythria): ICs don't drop deprecated maps from feedback vector.
|
||||
// So it is not equired to migrate the instance for polymorphic case.
|
||||
// When we change dynamic map checks to check only four maps re-evaluate
|
||||
// if this is required.
|
||||
CheckPolymorphic(feedback_slot, value_map, handler, nullptr, &done,
|
||||
frame_state);
|
||||
|
||||
__ Bind(&migrate);
|
||||
{
|
||||
MigrateInstanceOrDeopt(value, value_map, frame_state, FeedbackSource(),
|
||||
DeoptimizeReason::kMissingMap);
|
||||
Node* new_value_map = __ LoadField(AccessBuilder::ForMap(), value);
|
||||
|
||||
// Check if new map matches.
|
||||
CheckMonomorphic(feedback_slot, new_value_map, handler, &done, nullptr,
|
||||
frame_state, feedback.index(), vector);
|
||||
}
|
||||
} else {
|
||||
__ DeoptimizeIfNot(DeoptimizeReason::kTransitionedToMonomorphicIC,
|
||||
FeedbackSource(), poly_check, frame_state,
|
||||
IsSafetyCheck::kCriticalSafetyCheck);
|
||||
}
|
||||
Node* feedback_slot_map =
|
||||
__ LoadField(AccessBuilder::ForMap(), feedback_slot);
|
||||
Node* is_weak_fixed_array_check =
|
||||
__ TaggedEqual(feedback_slot_map, __ WeakFixedArrayMapConstant());
|
||||
__ DeoptimizeIfNot(DeoptimizeReason::kTransitionedToMegamorphicIC,
|
||||
FeedbackSource(), is_weak_fixed_array_check, frame_state,
|
||||
IsSafetyCheck::kCriticalSafetyCheck);
|
||||
|
||||
Node* length = ChangeSmiToInt32(
|
||||
__ LoadField(AccessBuilder::ForWeakFixedArrayLength(), feedback_slot));
|
||||
auto loop = __ MakeLoopLabel(MachineRepresentation::kWord32);
|
||||
__ Goto(&loop, __ Int32Constant(0));
|
||||
__ Bind(&loop);
|
||||
{
|
||||
Node* index = loop.PhiAt(0);
|
||||
Node* check = __ Int32LessThan(index, length);
|
||||
__ DeoptimizeIfNot(DeoptimizeKind::kBailout,
|
||||
DeoptimizeReason::kMissingMap, FeedbackSource(), check,
|
||||
DeoptimizeReason reason = DeoptimizeReason::kMissingMap;
|
||||
DeoptimizeKind kind = DeoptimizeKind::kBailout;
|
||||
if (p.state() != DynamicCheckMapsParameters::kMonomorphic) {
|
||||
reason = DeoptimizeReason::kTransitionedToMonomorphicIC;
|
||||
kind = DeoptimizeKind::kEager;
|
||||
}
|
||||
__ DeoptimizeIfNot(kind, reason, FeedbackSource(), is_poly_or_megamorphic,
|
||||
frame_state, IsSafetyCheck::kCriticalSafetyCheck);
|
||||
|
||||
Node* maybe_map = __ LoadElement(
|
||||
AccessBuilder::ForWeakFixedArrayElement(), feedback_slot, index);
|
||||
auto continue_loop = __ MakeLabel();
|
||||
|
||||
__ GotoIfNot(BuildIsWeakReferenceTo(maybe_map, value_map),
|
||||
&continue_loop);
|
||||
constexpr int kHandlerOffsetInEntry = 1;
|
||||
Node* maybe_handler = __ LoadElement(
|
||||
AccessBuilder::ForWeakFixedArrayElement(), feedback_slot,
|
||||
__ Int32Add(index, __ Int32Constant(kHandlerOffsetInEntry)));
|
||||
|
||||
Node* handler_check = __ TaggedEqual(maybe_handler, handler);
|
||||
__ DeoptimizeIfNot(DeoptimizeReason::kWrongHandler, FeedbackSource(),
|
||||
handler_check, frame_state,
|
||||
IsSafetyCheck::kCriticalSafetyCheck);
|
||||
__ Goto(&done);
|
||||
|
||||
__ Bind(&continue_loop);
|
||||
constexpr int kEntrySize = 2;
|
||||
index = __ Int32Add(index, __ Int32Constant(kEntrySize));
|
||||
__ Goto(&loop, index);
|
||||
CheckPolymorphic(feedback_slot, value_map, handler, nullptr, &done,
|
||||
frame_state);
|
||||
}
|
||||
}
|
||||
|
||||
__ Bind(&done);
|
||||
}
|
||||
|
||||
|
@ -4676,11 +4676,12 @@ bool ElementAccessFeedback::HasOnlyStringMaps(JSHeapBroker* broker) const {
|
||||
|
||||
MinimorphicLoadPropertyAccessFeedback::MinimorphicLoadPropertyAccessFeedback(
|
||||
NameRef const& name, FeedbackSlotKind slot_kind, bool is_monomorphic,
|
||||
Handle<Object> handler)
|
||||
Handle<Object> handler, bool has_migration_target_maps)
|
||||
: ProcessedFeedback(kMinimorphicPropertyAccess, slot_kind),
|
||||
name_(name),
|
||||
is_monomorphic_(is_monomorphic),
|
||||
handler_(handler) {
|
||||
handler_(handler),
|
||||
has_migration_target_maps_(has_migration_target_maps) {
|
||||
DCHECK(IsLoadICKind(slot_kind));
|
||||
}
|
||||
|
||||
@ -4779,6 +4780,13 @@ MaybeObjectHandle TryGetMinimorphicHandler(
|
||||
}
|
||||
return initial_handler;
|
||||
}
|
||||
|
||||
bool HasMigrationTargets(const MapHandles& maps) {
|
||||
for (Handle<Map> map : maps) {
|
||||
if (map->is_migration_target()) return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
} // namespace
|
||||
|
||||
bool JSHeapBroker::CanUseFeedback(const FeedbackNexus& nexus) const {
|
||||
@ -4801,19 +4809,20 @@ ProcessedFeedback const& JSHeapBroker::ReadFeedbackForPropertyAccess(
|
||||
|
||||
std::vector<MapAndHandler> maps_and_handlers;
|
||||
nexus.ExtractMapsAndFeedback(&maps_and_handlers);
|
||||
MapHandles maps;
|
||||
for (auto const& entry : maps_and_handlers) {
|
||||
maps.push_back(entry.first);
|
||||
}
|
||||
|
||||
base::Optional<NameRef> name =
|
||||
static_name.has_value() ? static_name : GetNameFeedback(nexus);
|
||||
MaybeObjectHandle handler = TryGetMinimorphicHandler(maps_and_handlers, kind);
|
||||
if (!handler.is_null()) {
|
||||
return *zone()->New<MinimorphicLoadPropertyAccessFeedback>(
|
||||
*name, kind, nexus.ic_state() == MONOMORPHIC, handler.object());
|
||||
*name, kind, nexus.ic_state() == MONOMORPHIC, handler.object(),
|
||||
HasMigrationTargets(maps));
|
||||
}
|
||||
|
||||
MapHandles maps;
|
||||
for (auto const& entry : maps_and_handlers) {
|
||||
maps.push_back(entry.first);
|
||||
}
|
||||
FilterRelevantReceiverMaps(isolate(), &maps);
|
||||
|
||||
// If no maps were found for a non-megamorphic access, then our maps died
|
||||
|
@ -1067,9 +1067,13 @@ Reduction JSNativeContextSpecialization::ReduceMinimorphicPropertyAccess(
|
||||
if (access_info.IsInvalid()) return NoChange();
|
||||
|
||||
PropertyAccessBuilder access_builder(jsgraph(), broker(), nullptr);
|
||||
CheckMapsFlags flags = CheckMapsFlag::kNone;
|
||||
if (feedback.has_migration_target_maps()) {
|
||||
flags |= CheckMapsFlag::kTryMigrateInstance;
|
||||
}
|
||||
effect = graph()->NewNode(
|
||||
simplified()->DynamicCheckMaps(
|
||||
feedback.handler(), source,
|
||||
flags, feedback.handler(), source,
|
||||
feedback.is_monomorphic()
|
||||
? DynamicCheckMapsParameters::ICState::kMonomorphic
|
||||
: DynamicCheckMapsParameters::ICState::kPolymorphic),
|
||||
|
@ -178,16 +178,19 @@ class MinimorphicLoadPropertyAccessFeedback : public ProcessedFeedback {
|
||||
MinimorphicLoadPropertyAccessFeedback(NameRef const& name,
|
||||
FeedbackSlotKind slot_kind,
|
||||
bool is_monomorphic,
|
||||
Handle<Object> handler);
|
||||
Handle<Object> handler,
|
||||
bool has_migration_target_maps);
|
||||
|
||||
NameRef const& name() const { return name_; }
|
||||
bool is_monomorphic() const { return is_monomorphic_; }
|
||||
Handle<Object> handler() const { return handler_; }
|
||||
bool has_migration_target_maps() const { return has_migration_target_maps_; }
|
||||
|
||||
private:
|
||||
NameRef const name_;
|
||||
bool is_monomorphic_;
|
||||
Handle<Object> handler_;
|
||||
bool const is_monomorphic_;
|
||||
Handle<Object> const handler_;
|
||||
bool const has_migration_target_maps_;
|
||||
};
|
||||
|
||||
class CallFeedback : public ProcessedFeedback {
|
||||
|
@ -1474,9 +1474,11 @@ const Operator* SimplifiedOperatorBuilder::CheckMaps(
|
||||
}
|
||||
|
||||
const Operator* SimplifiedOperatorBuilder::DynamicCheckMaps(
|
||||
Handle<Object> handler, const FeedbackSource& feedback,
|
||||
CheckMapsFlags flags, Handle<Object> handler,
|
||||
const FeedbackSource& feedback,
|
||||
DynamicCheckMapsParameters::ICState ic_state) {
|
||||
DynamicCheckMapsParameters const parameters(handler, feedback, ic_state);
|
||||
DynamicCheckMapsParameters const parameters(flags, handler, feedback,
|
||||
ic_state);
|
||||
return zone()->New<Operator1<DynamicCheckMapsParameters>>( // --
|
||||
IrOpcode::kDynamicCheckMaps, // opcode
|
||||
Operator::kNoThrow | Operator::kNoWrite, // flags
|
||||
|
@ -431,15 +431,17 @@ class DynamicCheckMapsParameters final {
|
||||
public:
|
||||
enum ICState { kMonomorphic, kPolymorphic };
|
||||
|
||||
DynamicCheckMapsParameters(Handle<Object> handler,
|
||||
DynamicCheckMapsParameters(CheckMapsFlags flags, Handle<Object> handler,
|
||||
const FeedbackSource& feedback, ICState state)
|
||||
: handler_(handler), feedback_(feedback), state_(state) {}
|
||||
: flags_(flags), handler_(handler), feedback_(feedback), state_(state) {}
|
||||
|
||||
CheckMapsFlags flags() const { return flags_; }
|
||||
Handle<Object> handler() const { return handler_; }
|
||||
FeedbackSource const& feedback() const { return feedback_; }
|
||||
ICState const& state() const { return state_; }
|
||||
|
||||
private:
|
||||
CheckMapsFlags const flags_;
|
||||
Handle<Object> const handler_;
|
||||
FeedbackSource const feedback_;
|
||||
ICState const state_;
|
||||
@ -874,7 +876,8 @@ class V8_EXPORT_PRIVATE SimplifiedOperatorBuilder final
|
||||
const Operator* CheckMaps(CheckMapsFlags, ZoneHandleSet<Map>,
|
||||
const FeedbackSource& = FeedbackSource());
|
||||
const Operator* DynamicCheckMaps(
|
||||
Handle<Object> handler, const FeedbackSource& feedback,
|
||||
CheckMapsFlags flags, Handle<Object> handler,
|
||||
const FeedbackSource& feedback,
|
||||
DynamicCheckMapsParameters::ICState ic_state);
|
||||
const Operator* CheckNotTaggedHole();
|
||||
const Operator* CheckNumber(const FeedbackSource& feedback);
|
||||
|
@ -63,6 +63,7 @@ namespace internal {
|
||||
V(WrongInstanceType, "wrong instance type") \
|
||||
V(WrongMap, "wrong map") \
|
||||
V(MissingMap, "missing map") \
|
||||
V(DeprecatedMap, "deprecated map") \
|
||||
V(WrongHandler, "wrong handler") \
|
||||
V(WrongName, "wrong name") \
|
||||
V(WrongValue, "wrong value") \
|
||||
|
@ -0,0 +1,35 @@
|
||||
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --turboprop --dynamic-map-checks --opt
|
||||
// Flags: --no-always-opt
|
||||
|
||||
function f(o) {
|
||||
return o.b;
|
||||
}
|
||||
|
||||
var o = {a:10, b:20};
|
||||
var o1 = {a:10, b:20};
|
||||
var o2 = {a:10, b:20};
|
||||
var o3 = {a:10, b:20, c:30};
|
||||
%PrepareFunctionForOptimization(f);
|
||||
// Transition IC state to polymorphic.
|
||||
f(o);
|
||||
f(o3);
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
f(o);
|
||||
assertOptimized(f);
|
||||
f(o);
|
||||
|
||||
// Deprecates O's map.
|
||||
o1.b = 10.23;
|
||||
// Deoptimizes but retains code.
|
||||
f(o1);
|
||||
assertOptimized(f);
|
||||
|
||||
// Continues to use optimized code since deprecated map is still in the
|
||||
// feedback. ICs don't drop deprecated maps in the polymoprhic case.
|
||||
f(o);
|
||||
f(o);
|
||||
assertOptimized(f);
|
@ -0,0 +1,41 @@
|
||||
// Copyright 2020 the V8 project authors. All rights reserved.
|
||||
// Use of this source code is governed by a BSD-style license that can be
|
||||
// found in the LICENSE file.
|
||||
|
||||
// Flags: --allow-natives-syntax --turboprop --dynamic-map-checks --opt
|
||||
// Flags: --no-always-opt
|
||||
|
||||
function f(o) {
|
||||
return o.b;
|
||||
}
|
||||
|
||||
var o = {a:10, b:20};
|
||||
var o1 = {a:10, b:20};
|
||||
var o2 = {a:10, b:20};
|
||||
%PrepareFunctionForOptimization(f);
|
||||
f(o);
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
f(o);
|
||||
assertOptimized(f);
|
||||
%PrepareFunctionForOptimization(f);
|
||||
f(o);
|
||||
|
||||
// Deprecates O's map.
|
||||
o1.b = 10.23;
|
||||
// Deoptimizes but retains code.
|
||||
f(o1);
|
||||
assertOptimized(f);
|
||||
|
||||
// Deoptimizes and discards code.
|
||||
f(o);
|
||||
f(o);
|
||||
assertUnoptimized(f);
|
||||
|
||||
// When we reoptimize we should include code for migrating deprecated maps.
|
||||
%OptimizeFunctionOnNextCall(f);
|
||||
f(o);
|
||||
assertOptimized(f);
|
||||
|
||||
f(o2);
|
||||
f(o2);
|
||||
assertOptimized(f);
|
Loading…
Reference in New Issue
Block a user