Reland "[turbofan] Various serializer/broker improvements"
This is a reland of 29585a06cc
after
removing an incorrect DCHECK.
Original change's description:
> [turbofan] Various serializer/broker improvements
>
> They are all somewhat entangled, sorry for the big CL.
>
> - Brokerize remaining feedback vector slots.
> - Introduce Hints::SingleConstant helper.
> - Introduce SerializationPolicy enum.
> - Eliminate use of nullptr for megamorphic load/store ic feedback.
> Instead use the corresponding ProcessedFeedback with an empty list
> of maps or the like. new class MegamorphicFeedback.
> - Separate processing of feedback from serialization. This eliminates
> code duplication.
> - Be very careful when clearing hints not to overwrite hints that are
> being processed.
> - Move AccessInfos out of NamedAccessFeedback. Always store them in
> property_access_infos_ map on broker. (This was actually unused
> before, somewhat by mistake.)
> - Support map inference in concurrent inlining. Rewrite
> ElementAccessFeedback such that we can refine it with the set of
> inferred maps.
>
> TBR: mvstanton@chromium.org
> Change-Id: I05e9eb250bdffc6dff29db01742550a86a41cb31
> Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1752853
> Commit-Queue: Georg Neis <neis@chromium.org>
> Reviewed-by: Georg Neis <neis@chromium.org>
> Cr-Commit-Position: refs/heads/master@{#63232}
TBR: mvstanton@chromium.org
Bug: v8:7790
Change-Id: Ia4acd31b339a941ee065e1ae4835bb7b85d5685e
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1758319
Reviewed-by: Georg Neis <neis@chromium.org>
Commit-Queue: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63241}
This commit is contained in:
parent
098f85a464
commit
4b1af9fcc2
@ -290,35 +290,32 @@ base::Optional<ElementAccessInfo> AccessInfoFactory::ComputeElementAccessInfo(
|
||||
}
|
||||
|
||||
bool AccessInfoFactory::ComputeElementAccessInfos(
|
||||
ElementAccessFeedback const& processed, AccessMode access_mode,
|
||||
ElementAccessFeedback const& feedback,
|
||||
ZoneVector<ElementAccessInfo>* access_infos) const {
|
||||
AccessMode access_mode = feedback.keyed_mode().access_mode();
|
||||
if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
|
||||
// For polymorphic loads of similar elements kinds (i.e. all tagged or all
|
||||
// double), always use the "worst case" code without a transition. This is
|
||||
// much faster than transitioning the elements to the worst case, trading a
|
||||
// TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
|
||||
base::Optional<ElementAccessInfo> access_info =
|
||||
ConsolidateElementLoad(processed);
|
||||
ConsolidateElementLoad(feedback);
|
||||
if (access_info.has_value()) {
|
||||
access_infos->push_back(*access_info);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
for (Handle<Map> receiver_map : processed.receiver_maps) {
|
||||
// Compute the element access information.
|
||||
for (auto const& group : feedback.transition_groups()) {
|
||||
DCHECK(!group.empty());
|
||||
Handle<Map> target = group.front();
|
||||
base::Optional<ElementAccessInfo> access_info =
|
||||
ComputeElementAccessInfo(receiver_map, access_mode);
|
||||
ComputeElementAccessInfo(target, access_mode);
|
||||
if (!access_info.has_value()) return false;
|
||||
|
||||
// Collect the possible transitions for the {receiver_map}.
|
||||
for (auto transition : processed.transitions) {
|
||||
if (transition.second.equals(receiver_map)) {
|
||||
access_info->AddTransitionSource(transition.first);
|
||||
}
|
||||
for (size_t i = 1; i < group.size(); ++i) {
|
||||
access_info->AddTransitionSource(group[i]);
|
||||
}
|
||||
|
||||
// Schedule the access information.
|
||||
access_infos->push_back(*access_info);
|
||||
}
|
||||
return true;
|
||||
@ -634,6 +631,7 @@ void PropertyAccessInfo::RecordDependencies(
|
||||
bool AccessInfoFactory::FinalizePropertyAccessInfos(
|
||||
ZoneVector<PropertyAccessInfo> access_infos, AccessMode access_mode,
|
||||
ZoneVector<PropertyAccessInfo>* result) const {
|
||||
if (access_infos.empty()) return false;
|
||||
MergePropertyAccessInfos(access_infos, access_mode, result);
|
||||
for (PropertyAccessInfo const& info : *result) {
|
||||
if (info.IsInvalid()) return false;
|
||||
@ -687,22 +685,28 @@ Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
|
||||
} // namespace
|
||||
|
||||
base::Optional<ElementAccessInfo> AccessInfoFactory::ConsolidateElementLoad(
|
||||
ElementAccessFeedback const& processed) const {
|
||||
ElementAccessFeedback::MapIterator it = processed.all_maps(broker());
|
||||
MapRef first_map = it.current();
|
||||
ElementAccessFeedback const& feedback) const {
|
||||
if (feedback.transition_groups().empty()) return base::nullopt;
|
||||
|
||||
DCHECK(!feedback.transition_groups().front().empty());
|
||||
MapRef first_map(broker(), feedback.transition_groups().front().front());
|
||||
InstanceType instance_type = first_map.instance_type();
|
||||
ElementsKind elements_kind = first_map.elements_kind();
|
||||
|
||||
ZoneVector<Handle<Map>> maps(zone());
|
||||
for (; !it.done(); it.advance()) {
|
||||
MapRef map = it.current();
|
||||
if (map.instance_type() != instance_type || !CanInlineElementAccess(map)) {
|
||||
return base::nullopt;
|
||||
for (auto const& group : feedback.transition_groups()) {
|
||||
for (Handle<Map> map_handle : group) {
|
||||
MapRef map(broker(), map_handle);
|
||||
if (map.instance_type() != instance_type ||
|
||||
!CanInlineElementAccess(map)) {
|
||||
return base::nullopt;
|
||||
}
|
||||
if (!GeneralizeElementsKind(elements_kind, map.elements_kind())
|
||||
.To(&elements_kind)) {
|
||||
return base::nullopt;
|
||||
}
|
||||
maps.push_back(map.object());
|
||||
}
|
||||
if (!GeneralizeElementsKind(elements_kind, map.elements_kind())
|
||||
.To(&elements_kind)) {
|
||||
return base::nullopt;
|
||||
}
|
||||
maps.push_back(map.object());
|
||||
}
|
||||
|
||||
return ElementAccessInfo(std::move(maps), elements_kind, zone());
|
||||
|
@ -168,7 +168,7 @@ class AccessInfoFactory final {
|
||||
base::Optional<ElementAccessInfo> ComputeElementAccessInfo(
|
||||
Handle<Map> map, AccessMode access_mode) const;
|
||||
bool ComputeElementAccessInfos(
|
||||
ElementAccessFeedback const& processed, AccessMode access_mode,
|
||||
ElementAccessFeedback const& feedback,
|
||||
ZoneVector<ElementAccessInfo>* access_infos) const;
|
||||
|
||||
PropertyAccessInfo ComputePropertyAccessInfo(Handle<Map> map,
|
||||
@ -196,7 +196,7 @@ class AccessInfoFactory final {
|
||||
|
||||
private:
|
||||
base::Optional<ElementAccessInfo> ConsolidateElementLoad(
|
||||
ElementAccessFeedback const& processed) const;
|
||||
ElementAccessFeedback const& feedback) const;
|
||||
PropertyAccessInfo LookupSpecialFieldAccessor(Handle<Map> map,
|
||||
Handle<Name> name) const;
|
||||
PropertyAccessInfo LookupTransition(Handle<Map> map, Handle<Name> name,
|
||||
|
@ -245,11 +245,12 @@ class BytecodeGraphBuilder {
|
||||
ForInMode GetForInMode(int operand_index);
|
||||
|
||||
// Helper function to compute call frequency from the recorded type
|
||||
// feedback.
|
||||
// feedback. Returns unknown if invocation count is unknown. Returns 0 if
|
||||
// feedback is insufficient.
|
||||
CallFrequency ComputeCallFrequency(int slot_id) const;
|
||||
|
||||
// Helper function to extract the speculation mode from the recorded type
|
||||
// feedback.
|
||||
// feedback. Returns kDisallowSpeculation if feedback is insufficient.
|
||||
SpeculationMode GetSpeculationMode(int slot_id) const;
|
||||
|
||||
// Control flow plumbing.
|
||||
@ -950,7 +951,7 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
|
||||
bytecode_array_(bytecode_array),
|
||||
feedback_vector_(feedback_vector),
|
||||
type_hint_lowering_(
|
||||
jsgraph, feedback_vector.object(),
|
||||
broker, jsgraph, feedback_vector,
|
||||
(flags & BytecodeGraphBuilderFlag::kBailoutOnUninitialized)
|
||||
? JSTypeHintLowering::kBailoutOnUninitialized
|
||||
: JSTypeHintLowering::kNoFlags),
|
||||
@ -963,7 +964,8 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
|
||||
bytecode_analysis_(broker_->GetBytecodeAnalysis(
|
||||
bytecode_array.object(), osr_offset,
|
||||
flags & BytecodeGraphBuilderFlag::kAnalyzeEnvironmentLiveness,
|
||||
!FLAG_concurrent_inlining)),
|
||||
FLAG_concurrent_inlining ? SerializationPolicy::kAssumeSerialized
|
||||
: SerializationPolicy::kSerializeIfNeeded)),
|
||||
environment_(nullptr),
|
||||
osr_(!osr_offset.IsNone()),
|
||||
currently_peeled_loop_offset_(-1),
|
||||
@ -1016,6 +1018,7 @@ Node* BytecodeGraphBuilder::BuildLoadNativeContextField(int index) {
|
||||
|
||||
VectorSlotPair BytecodeGraphBuilder::CreateVectorSlotPair(int slot_id) {
|
||||
FeedbackSlot slot = FeedbackVector::ToSlot(slot_id);
|
||||
// TODO(mvstanton): eliminate this use of a FeedbackNexus.
|
||||
FeedbackNexus nexus(feedback_vector().object(), slot);
|
||||
return VectorSlotPair(feedback_vector().object(), slot, nexus.ic_state());
|
||||
}
|
||||
@ -2149,11 +2152,11 @@ void BytecodeGraphBuilder::BuildCall(ConvertReceiverMode receiver_mode,
|
||||
PrepareEagerCheckpoint();
|
||||
|
||||
VectorSlotPair feedback = CreateVectorSlotPair(slot_id);
|
||||
|
||||
CallFrequency frequency = ComputeCallFrequency(slot_id);
|
||||
const Operator* op =
|
||||
javascript()->Call(arg_count, frequency, feedback, receiver_mode,
|
||||
GetSpeculationMode(slot_id));
|
||||
SpeculationMode speculation_mode = GetSpeculationMode(slot_id);
|
||||
const Operator* op = javascript()->Call(arg_count, frequency, feedback,
|
||||
receiver_mode, speculation_mode);
|
||||
|
||||
JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedCall(
|
||||
op, args, static_cast<int>(arg_count), feedback.slot());
|
||||
if (lowering.IsExit()) return;
|
||||
@ -2334,7 +2337,6 @@ void BytecodeGraphBuilder::VisitCallWithSpread() {
|
||||
first_arg, arg_count);
|
||||
int const slot_id = bytecode_iterator().GetIndexOperand(3);
|
||||
VectorSlotPair feedback = CreateVectorSlotPair(slot_id);
|
||||
|
||||
CallFrequency frequency = ComputeCallFrequency(slot_id);
|
||||
const Operator* op = javascript()->CallWithSpread(
|
||||
static_cast<int>(reg_count + 1), frequency, feedback);
|
||||
@ -2648,23 +2650,23 @@ void BytecodeGraphBuilder::BuildBinaryOp(const Operator* op) {
|
||||
BinaryOperationHint BytecodeGraphBuilder::GetBinaryOperationHint(
|
||||
int operand_index) {
|
||||
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(operand_index);
|
||||
FeedbackNexus nexus(feedback_vector().object(), slot);
|
||||
return nexus.GetBinaryOperationFeedback();
|
||||
FeedbackSource source(feedback_vector(), slot);
|
||||
return broker()->GetFeedbackForBinaryOperation(source);
|
||||
}
|
||||
|
||||
// Helper function to create compare operation hint from the recorded type
|
||||
// feedback.
|
||||
CompareOperationHint BytecodeGraphBuilder::GetCompareOperationHint() {
|
||||
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1);
|
||||
FeedbackNexus nexus(feedback_vector().object(), slot);
|
||||
return nexus.GetCompareOperationFeedback();
|
||||
FeedbackSource source(feedback_vector(), slot);
|
||||
return broker()->GetFeedbackForCompareOperation(source);
|
||||
}
|
||||
|
||||
// Helper function to create for-in mode from the recorded type feedback.
|
||||
ForInMode BytecodeGraphBuilder::GetForInMode(int operand_index) {
|
||||
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(operand_index);
|
||||
FeedbackNexus nexus(feedback_vector().object(), slot);
|
||||
switch (nexus.GetForInFeedback()) {
|
||||
FeedbackSource source(feedback_vector(), slot);
|
||||
switch (broker()->GetFeedbackForForIn(source)) {
|
||||
case ForInHint::kNone:
|
||||
case ForInHint::kEnumCacheKeysAndIndices:
|
||||
return ForInMode::kUseEnumCacheKeysAndIndices;
|
||||
@ -2678,11 +2680,12 @@ ForInMode BytecodeGraphBuilder::GetForInMode(int operand_index) {
|
||||
|
||||
CallFrequency BytecodeGraphBuilder::ComputeCallFrequency(int slot_id) const {
|
||||
if (invocation_frequency_.IsUnknown()) return CallFrequency();
|
||||
FeedbackNexus nexus(feedback_vector().object(),
|
||||
FeedbackVector::ToSlot(slot_id));
|
||||
float feedback_frequency = nexus.ComputeCallFrequency();
|
||||
if (feedback_frequency == 0.0f) {
|
||||
// This is to prevent multiplying zero and infinity.
|
||||
FeedbackSlot slot = FeedbackVector::ToSlot(slot_id);
|
||||
FeedbackSource source(feedback_vector(), slot);
|
||||
ProcessedFeedback const& feedback = broker()->GetFeedbackForCall(source);
|
||||
float feedback_frequency =
|
||||
feedback.IsInsufficient() ? 0.0f : feedback.AsCall().frequency();
|
||||
if (feedback_frequency == 0.0f) { // Prevent multiplying zero and infinity.
|
||||
return CallFrequency(0.0f);
|
||||
} else {
|
||||
return CallFrequency(feedback_frequency * invocation_frequency_.value());
|
||||
@ -2690,9 +2693,11 @@ CallFrequency BytecodeGraphBuilder::ComputeCallFrequency(int slot_id) const {
|
||||
}
|
||||
|
||||
SpeculationMode BytecodeGraphBuilder::GetSpeculationMode(int slot_id) const {
|
||||
FeedbackNexus nexus(feedback_vector().object(),
|
||||
FeedbackVector::ToSlot(slot_id));
|
||||
return nexus.GetSpeculationMode();
|
||||
FeedbackSlot slot = FeedbackVector::ToSlot(slot_id);
|
||||
FeedbackSource source(feedback_vector(), slot);
|
||||
ProcessedFeedback const& feedback = broker()->GetFeedbackForCall(source);
|
||||
return feedback.IsInsufficient() ? SpeculationMode::kDisallowSpeculation
|
||||
: feedback.AsCall().speculation_mode();
|
||||
}
|
||||
|
||||
void BytecodeGraphBuilder::VisitBitwiseNot() {
|
||||
|
@ -35,6 +35,8 @@ namespace compiler {
|
||||
// For a store during literal creation, do not walk up the prototype chain.
|
||||
enum class AccessMode { kLoad, kStore, kStoreInLiteral, kHas };
|
||||
|
||||
enum class SerializationPolicy { kAssumeSerialized, kSerializeIfNeeded };
|
||||
|
||||
enum class OddballType : uint8_t {
|
||||
kNone, // Not an Oddball.
|
||||
kBoolean, // True or False.
|
||||
@ -137,8 +139,9 @@ class V8_EXPORT_PRIVATE ObjectRef {
|
||||
|
||||
// Return the element at key {index} if {index} is known to be an own data
|
||||
// property of the object that is non-writable and non-configurable.
|
||||
base::Optional<ObjectRef> GetOwnConstantElement(uint32_t index,
|
||||
bool serialize = false) const;
|
||||
base::Optional<ObjectRef> GetOwnConstantElement(
|
||||
uint32_t index, SerializationPolicy policy =
|
||||
SerializationPolicy::kAssumeSerialized) const;
|
||||
|
||||
Isolate* isolate() const;
|
||||
|
||||
@ -268,7 +271,8 @@ class JSObjectRef : public JSReceiverRef {
|
||||
// if {index} is known to be an own data property of the object.
|
||||
base::Optional<ObjectRef> GetOwnDataProperty(
|
||||
Representation field_representation, FieldIndex index,
|
||||
bool serialize = false) const;
|
||||
SerializationPolicy policy =
|
||||
SerializationPolicy::kAssumeSerialized) const;
|
||||
FixedArrayBaseRef elements() const;
|
||||
void SerializeElements();
|
||||
void EnsureElementsTenured();
|
||||
@ -370,10 +374,14 @@ class ContextRef : public HeapObjectRef {
|
||||
// followed. If {depth} != 0 on function return, then it only got
|
||||
// partway to the desired depth. If {serialize} is true, then
|
||||
// {previous} will cache its findings.
|
||||
ContextRef previous(size_t* depth, bool serialize = false) const;
|
||||
ContextRef previous(size_t* depth,
|
||||
SerializationPolicy policy =
|
||||
SerializationPolicy::kAssumeSerialized) const;
|
||||
|
||||
// Only returns a value if the index is valid for this ContextRef.
|
||||
base::Optional<ObjectRef> get(int index, bool serialize = false) const;
|
||||
base::Optional<ObjectRef> get(
|
||||
int index, SerializationPolicy policy =
|
||||
SerializationPolicy::kAssumeSerialized) const;
|
||||
|
||||
// We only serialize the ScopeInfo if certain Promise
|
||||
// builtins are called.
|
||||
@ -645,8 +653,9 @@ class FunctionTemplateInfoRef : public HeapObjectRef {
|
||||
void SerializeCallCode();
|
||||
base::Optional<CallHandlerInfoRef> call_code() const;
|
||||
|
||||
HolderLookupResult LookupHolderOfExpectedType(MapRef receiver_map,
|
||||
bool serialize);
|
||||
HolderLookupResult LookupHolderOfExpectedType(
|
||||
MapRef receiver_map,
|
||||
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
|
||||
};
|
||||
|
||||
class FixedArrayBaseRef : public HeapObjectRef {
|
||||
@ -734,8 +743,9 @@ class JSArrayRef : public JSObjectRef {
|
||||
|
||||
// Return the element at key {index} if the array has a copy-on-write elements
|
||||
// storage and {index} is known to be an own data property.
|
||||
base::Optional<ObjectRef> GetOwnCowElement(uint32_t index,
|
||||
bool serialize = false) const;
|
||||
base::Optional<ObjectRef> GetOwnCowElement(
|
||||
uint32_t index, SerializationPolicy policy =
|
||||
SerializationPolicy::kAssumeSerialized) const;
|
||||
};
|
||||
|
||||
class ScopeInfoRef : public HeapObjectRef {
|
||||
@ -782,8 +792,9 @@ class V8_EXPORT_PRIVATE SharedFunctionInfoRef : public HeapObjectRef {
|
||||
// Template objects may not be created at compilation time. This method
|
||||
// wraps the retrieval of the template object and creates it if
|
||||
// necessary.
|
||||
JSArrayRef GetTemplateObject(ObjectRef description, FeedbackVectorRef vector,
|
||||
FeedbackSlot slot, bool serialize = false);
|
||||
JSArrayRef GetTemplateObject(
|
||||
ObjectRef description, FeedbackVectorRef vector, FeedbackSlot slot,
|
||||
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
|
||||
|
||||
void SerializeFunctionTemplateInfo();
|
||||
base::Optional<FunctionTemplateInfoRef> function_template_info() const;
|
||||
@ -858,8 +869,9 @@ class JSGlobalProxyRef : public JSObjectRef {
|
||||
// If {serialize} is true:
|
||||
// Like above but potentially access the heap and serialize the necessary
|
||||
// information.
|
||||
base::Optional<PropertyCellRef> GetPropertyCell(NameRef const& name,
|
||||
bool serialize = false) const;
|
||||
base::Optional<PropertyCellRef> GetPropertyCell(
|
||||
NameRef const& name, SerializationPolicy policy =
|
||||
SerializationPolicy::kAssumeSerialized) const;
|
||||
};
|
||||
|
||||
class CodeRef : public HeapObjectRef {
|
||||
@ -878,120 +890,6 @@ class InternalizedStringRef : public StringRef {
|
||||
|
||||
#undef DEFINE_REF_CONSTRUCTOR
|
||||
|
||||
class ElementAccessFeedback;
|
||||
class NamedAccessFeedback;
|
||||
|
||||
class ProcessedFeedback : public ZoneObject {
|
||||
public:
|
||||
enum Kind { kInsufficient, kGlobalAccess, kNamedAccess, kElementAccess };
|
||||
Kind kind() const { return kind_; }
|
||||
|
||||
ElementAccessFeedback const* AsElementAccess() const;
|
||||
NamedAccessFeedback const* AsNamedAccess() const;
|
||||
|
||||
protected:
|
||||
explicit ProcessedFeedback(Kind kind) : kind_(kind) {}
|
||||
|
||||
private:
|
||||
Kind const kind_;
|
||||
};
|
||||
|
||||
class InsufficientFeedback final : public ProcessedFeedback {
|
||||
public:
|
||||
InsufficientFeedback();
|
||||
};
|
||||
|
||||
class GlobalAccessFeedback : public ProcessedFeedback {
|
||||
public:
|
||||
explicit GlobalAccessFeedback(PropertyCellRef cell);
|
||||
GlobalAccessFeedback(ContextRef script_context, int slot_index,
|
||||
bool immutable);
|
||||
|
||||
bool IsPropertyCell() const;
|
||||
PropertyCellRef property_cell() const;
|
||||
|
||||
bool IsScriptContextSlot() const { return !IsPropertyCell(); }
|
||||
ContextRef script_context() const;
|
||||
int slot_index() const;
|
||||
bool immutable() const;
|
||||
|
||||
base::Optional<ObjectRef> GetConstantHint() const;
|
||||
|
||||
private:
|
||||
ObjectRef const cell_or_context_;
|
||||
int const index_and_immutable_;
|
||||
};
|
||||
|
||||
class KeyedAccessMode {
|
||||
public:
|
||||
static KeyedAccessMode FromNexus(FeedbackNexus const& nexus);
|
||||
|
||||
AccessMode access_mode() const;
|
||||
bool IsLoad() const;
|
||||
bool IsStore() const;
|
||||
KeyedAccessLoadMode load_mode() const;
|
||||
KeyedAccessStoreMode store_mode() const;
|
||||
|
||||
private:
|
||||
AccessMode const access_mode_;
|
||||
union LoadStoreMode {
|
||||
LoadStoreMode(KeyedAccessLoadMode load_mode);
|
||||
LoadStoreMode(KeyedAccessStoreMode store_mode);
|
||||
KeyedAccessLoadMode load_mode;
|
||||
KeyedAccessStoreMode store_mode;
|
||||
} const load_store_mode_;
|
||||
|
||||
KeyedAccessMode(AccessMode access_mode, KeyedAccessLoadMode load_mode);
|
||||
KeyedAccessMode(AccessMode access_mode, KeyedAccessStoreMode store_mode);
|
||||
};
|
||||
|
||||
class ElementAccessFeedback : public ProcessedFeedback {
|
||||
public:
|
||||
ElementAccessFeedback(Zone* zone, KeyedAccessMode const& keyed_mode);
|
||||
|
||||
// No transition sources appear in {receiver_maps}.
|
||||
// All transition targets appear in {receiver_maps}.
|
||||
ZoneVector<Handle<Map>> receiver_maps;
|
||||
ZoneVector<std::pair<Handle<Map>, Handle<Map>>> transitions;
|
||||
|
||||
KeyedAccessMode const keyed_mode;
|
||||
|
||||
class MapIterator {
|
||||
public:
|
||||
bool done() const;
|
||||
void advance();
|
||||
MapRef current() const;
|
||||
|
||||
private:
|
||||
friend class ElementAccessFeedback;
|
||||
|
||||
explicit MapIterator(ElementAccessFeedback const& processed,
|
||||
JSHeapBroker* broker);
|
||||
|
||||
ElementAccessFeedback const& processed_;
|
||||
JSHeapBroker* const broker_;
|
||||
size_t index_ = 0;
|
||||
};
|
||||
|
||||
// Iterator over all maps: first {receiver_maps}, then transition sources.
|
||||
MapIterator all_maps(JSHeapBroker* broker) const;
|
||||
};
|
||||
|
||||
class NamedAccessFeedback : public ProcessedFeedback {
|
||||
public:
|
||||
NamedAccessFeedback(NameRef const& name,
|
||||
ZoneVector<PropertyAccessInfo> const& access_infos);
|
||||
|
||||
NameRef const& name() const { return name_; }
|
||||
ZoneVector<PropertyAccessInfo> const& access_infos() const {
|
||||
return access_infos_;
|
||||
}
|
||||
|
||||
private:
|
||||
NameRef const name_;
|
||||
ZoneVector<PropertyAccessInfo> const access_infos_;
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
@ -2848,8 +2848,7 @@ Reduction JSCallReducer::ReduceCallApiFunction(
|
||||
|
||||
// See if we can constant-fold the compatible receiver checks.
|
||||
HolderLookupResult api_holder =
|
||||
function_template_info.LookupHolderOfExpectedType(first_receiver_map,
|
||||
false);
|
||||
function_template_info.LookupHolderOfExpectedType(first_receiver_map);
|
||||
if (api_holder.lookup == CallOptimization::kHolderNotFound)
|
||||
return inference.NoChange();
|
||||
|
||||
@ -2879,8 +2878,7 @@ Reduction JSCallReducer::ReduceCallApiFunction(
|
||||
for (size_t i = 1; i < receiver_maps.size(); ++i) {
|
||||
MapRef receiver_map(broker(), receiver_maps[i]);
|
||||
HolderLookupResult holder_i =
|
||||
function_template_info.LookupHolderOfExpectedType(receiver_map,
|
||||
false);
|
||||
function_template_info.LookupHolderOfExpectedType(receiver_map);
|
||||
|
||||
if (api_holder.lookup != holder_i.lookup) return inference.NoChange();
|
||||
if (!(api_holder.holder.has_value() && holder_i.holder.has_value()))
|
||||
@ -3227,13 +3225,6 @@ bool ShouldUseCallICFeedback(Node* node) {
|
||||
return true;
|
||||
}
|
||||
|
||||
base::Optional<HeapObjectRef> GetHeapObjectFeedback(
|
||||
JSHeapBroker* broker, const FeedbackNexus& nexus) {
|
||||
HeapObject object;
|
||||
if (!nexus.GetFeedback()->GetHeapObject(&object)) return base::nullopt;
|
||||
return HeapObjectRef(broker, handle(object, broker->isolate()));
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
Reduction JSCallReducer::ReduceJSCall(Node* node) {
|
||||
@ -3352,19 +3343,18 @@ Reduction JSCallReducer::ReduceJSCall(Node* node) {
|
||||
return reduction.Changed() ? reduction : Changed(node);
|
||||
}
|
||||
|
||||
// Extract feedback from the {node} using the FeedbackNexus.
|
||||
if (!p.feedback().IsValid()) return NoChange();
|
||||
FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
|
||||
if (nexus.IsUninitialized()) {
|
||||
ProcessedFeedback const& feedback =
|
||||
broker()->GetFeedbackForCall(FeedbackSource(p.feedback()));
|
||||
if (feedback.IsInsufficient()) {
|
||||
return ReduceSoftDeoptimize(
|
||||
node, DeoptimizeReason::kInsufficientTypeFeedbackForCall);
|
||||
}
|
||||
|
||||
base::Optional<HeapObjectRef> feedback =
|
||||
GetHeapObjectFeedback(broker(), nexus);
|
||||
if (feedback.has_value() && ShouldUseCallICFeedback(target) &&
|
||||
feedback->map().is_callable()) {
|
||||
Node* target_function = jsgraph()->Constant(*feedback);
|
||||
base::Optional<HeapObjectRef> feedback_target = feedback.AsCall().target();
|
||||
if (feedback_target.has_value() && ShouldUseCallICFeedback(target) &&
|
||||
feedback_target->map().is_callable()) {
|
||||
Node* target_function = jsgraph()->Constant(*feedback_target);
|
||||
|
||||
// Check that the {target} is still the {target_function}.
|
||||
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
|
||||
@ -3767,6 +3757,10 @@ Reduction JSCallReducer::ReduceJSCallWithSpread(Node* node) {
|
||||
}
|
||||
|
||||
Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
|
||||
// TODO(mslekova): Remove once ReduceJSConstruct is brokerized.
|
||||
AllowHandleDereference allow_handle_dereference;
|
||||
AllowHandleAllocation allow_handle_allocation;
|
||||
|
||||
DCHECK_EQ(IrOpcode::kJSConstruct, node->opcode());
|
||||
ConstructParameters const& p = ConstructParametersOf(node->op());
|
||||
DCHECK_LE(2u, p.arity());
|
||||
@ -3776,17 +3770,16 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
|
||||
Node* effect = NodeProperties::GetEffectInput(node);
|
||||
Node* control = NodeProperties::GetControlInput(node);
|
||||
|
||||
// Extract feedback from the {node} using the FeedbackNexus.
|
||||
if (p.feedback().IsValid()) {
|
||||
FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
|
||||
if (nexus.IsUninitialized()) {
|
||||
ProcessedFeedback const& feedback =
|
||||
broker()->GetFeedbackForCall(FeedbackSource(p.feedback()));
|
||||
if (feedback.IsInsufficient()) {
|
||||
return ReduceSoftDeoptimize(
|
||||
node, DeoptimizeReason::kInsufficientTypeFeedbackForConstruct);
|
||||
}
|
||||
|
||||
base::Optional<HeapObjectRef> feedback =
|
||||
GetHeapObjectFeedback(broker(), nexus);
|
||||
if (feedback.has_value() && feedback->IsAllocationSite()) {
|
||||
base::Optional<HeapObjectRef> feedback_target = feedback.AsCall().target();
|
||||
if (feedback_target.has_value() && feedback_target->IsAllocationSite()) {
|
||||
// The feedback is an AllocationSite, which means we have called the
|
||||
// Array function and collected transition (and pretenuring) feedback
|
||||
// for the resulting arrays. This has to be kept in sync with the
|
||||
@ -3811,12 +3804,12 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
|
||||
NodeProperties::ReplaceValueInput(node, array_function, 1);
|
||||
NodeProperties::ChangeOp(
|
||||
node, javascript()->CreateArray(
|
||||
arity, feedback->AsAllocationSite().object()));
|
||||
arity, feedback_target->AsAllocationSite().object()));
|
||||
return Changed(node);
|
||||
} else if (feedback.has_value() &&
|
||||
} else if (feedback_target.has_value() &&
|
||||
!HeapObjectMatcher(new_target).HasValue() &&
|
||||
feedback->map().is_constructor()) {
|
||||
Node* new_target_feedback = jsgraph()->Constant(*feedback);
|
||||
feedback_target->map().is_constructor()) {
|
||||
Node* new_target_feedback = jsgraph()->Constant(*feedback_target);
|
||||
|
||||
// Check that the {new_target} is still the {new_target_feedback}.
|
||||
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), new_target,
|
||||
@ -6085,7 +6078,7 @@ Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) {
|
||||
}
|
||||
|
||||
Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) {
|
||||
DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining);
|
||||
DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining);
|
||||
|
||||
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
|
||||
CallParameters const& p = CallParametersOf(node->op());
|
||||
@ -6153,7 +6146,7 @@ Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) {
|
||||
|
||||
// ES section #sec-promise.resolve
|
||||
Reduction JSCallReducer::ReducePromiseResolveTrampoline(Node* node) {
|
||||
DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining);
|
||||
DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining);
|
||||
|
||||
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
|
||||
Node* receiver = NodeProperties::GetValueInput(node, 1);
|
||||
|
File diff suppressed because it is too large
Load Diff
@ -9,6 +9,7 @@
|
||||
#include "src/base/optional.h"
|
||||
#include "src/common/globals.h"
|
||||
#include "src/compiler/access-info.h"
|
||||
#include "src/compiler/processed-feedback.h"
|
||||
#include "src/compiler/refs-map.h"
|
||||
#include "src/handles/handles.h"
|
||||
#include "src/interpreter/bytecode-array-accessor.h"
|
||||
@ -27,8 +28,8 @@ class ObjectRef;
|
||||
std::ostream& operator<<(std::ostream& os, const ObjectRef& ref);
|
||||
|
||||
struct FeedbackSource {
|
||||
FeedbackSource(Handle<FeedbackVector> vector_, FeedbackSlot slot_)
|
||||
: vector(vector_), slot(slot_) {}
|
||||
FeedbackSource(Handle<FeedbackVector> vector_, FeedbackSlot slot_);
|
||||
FeedbackSource(FeedbackVectorRef vector_, FeedbackSlot slot_);
|
||||
explicit FeedbackSource(FeedbackNexus const& nexus);
|
||||
explicit FeedbackSource(VectorSlotPair const& pair);
|
||||
|
||||
@ -61,20 +62,24 @@ struct FeedbackSource {
|
||||
broker->Trace() << __FUNCTION__ << ": missing " << x << '\n'; \
|
||||
} while (false)
|
||||
|
||||
struct MapNameRefPair {
|
||||
struct PropertyAccessTarget {
|
||||
MapRef map;
|
||||
NameRef name;
|
||||
AccessMode mode;
|
||||
|
||||
struct Hash {
|
||||
size_t operator()(const MapNameRefPair& pair) const {
|
||||
return base::hash_combine(pair.map.object().address(),
|
||||
pair.name.object().address());
|
||||
size_t operator()(const PropertyAccessTarget& pair) const {
|
||||
return base::hash_combine(
|
||||
base::hash_combine(pair.map.object().address(),
|
||||
pair.name.object().address()),
|
||||
static_cast<int>(pair.mode));
|
||||
}
|
||||
};
|
||||
struct Equal {
|
||||
bool operator()(const MapNameRefPair& lhs,
|
||||
const MapNameRefPair& rhs) const {
|
||||
return lhs.map.equals(rhs.map) && lhs.name.equals(rhs.name);
|
||||
bool operator()(const PropertyAccessTarget& lhs,
|
||||
const PropertyAccessTarget& rhs) const {
|
||||
return lhs.map.equals(rhs.map) && lhs.name.equals(rhs.name) &&
|
||||
lhs.mode == rhs.mode;
|
||||
}
|
||||
};
|
||||
};
|
||||
@ -115,21 +120,49 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
|
||||
// feedback didn't contain information relevant for Turbofan.
|
||||
void SetFeedback(FeedbackSource const& source,
|
||||
ProcessedFeedback const* feedback);
|
||||
ProcessedFeedback const* GetFeedback(FeedbackSource const& source) const;
|
||||
|
||||
// Convenience wrappers around GetFeedback.
|
||||
GlobalAccessFeedback const* GetGlobalAccessFeedback(
|
||||
FeedbackSource const& source) const;
|
||||
ProcessedFeedback const& GetFeedback(FeedbackSource const& source) const;
|
||||
|
||||
// TODO(neis): Move these into serializer when we're always in the background.
|
||||
ElementAccessFeedback const* ProcessFeedbackMapsForElementAccess(
|
||||
ElementAccessFeedback const& ProcessFeedbackMapsForElementAccess(
|
||||
MapHandles const& maps, KeyedAccessMode const& keyed_mode);
|
||||
GlobalAccessFeedback const* ProcessFeedbackForGlobalAccess(
|
||||
FeedbackSource const& source);
|
||||
|
||||
BytecodeAnalysis const& GetBytecodeAnalysis(
|
||||
Handle<BytecodeArray> bytecode_array, BailoutId osr_offset,
|
||||
bool analyze_liveness, bool serialize);
|
||||
bool analyze_liveness,
|
||||
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
|
||||
|
||||
// Binary, comparison and for-in hints can be fully expressed via
|
||||
// an enum. Insufficient feedback is signaled by <Hint enum>::kNone.
|
||||
BinaryOperationHint GetFeedbackForBinaryOperation(
|
||||
FeedbackSource const& source) const;
|
||||
CompareOperationHint GetFeedbackForCompareOperation(
|
||||
FeedbackSource const& source) const;
|
||||
ForInHint GetFeedbackForForIn(FeedbackSource const& source) const;
|
||||
|
||||
ProcessedFeedback const& GetFeedbackForCall(FeedbackSource const& source);
|
||||
ProcessedFeedback const& GetFeedbackForGlobalAccess(
|
||||
FeedbackSource const& source);
|
||||
ProcessedFeedback const& GetFeedbackForInstanceOf(
|
||||
FeedbackSource const& source);
|
||||
ProcessedFeedback const& GetFeedbackForPropertyAccess(
|
||||
FeedbackSource const& source, AccessMode mode,
|
||||
base::Optional<NameRef> static_name);
|
||||
|
||||
ProcessedFeedback const& ProcessFeedbackForBinaryOperation(
|
||||
FeedbackSource const& source);
|
||||
ProcessedFeedback const& ProcessFeedbackForCall(FeedbackSource const& source);
|
||||
ProcessedFeedback const& ProcessFeedbackForCompareOperation(
|
||||
FeedbackSource const& source);
|
||||
ProcessedFeedback const& ProcessFeedbackForForIn(
|
||||
FeedbackSource const& source);
|
||||
ProcessedFeedback const& ProcessFeedbackForGlobalAccess(
|
||||
FeedbackSource const& source);
|
||||
ProcessedFeedback const& ProcessFeedbackForInstanceOf(
|
||||
FeedbackSource const& source);
|
||||
ProcessedFeedback const& ProcessFeedbackForPropertyAccess(
|
||||
FeedbackSource const& source, AccessMode mode,
|
||||
base::Optional<NameRef> static_name);
|
||||
|
||||
bool FeedbackIsInsufficient(FeedbackSource const& source) const;
|
||||
|
||||
base::Optional<NameRef> GetNameFeedback(FeedbackNexus const& nexus);
|
||||
|
||||
@ -144,8 +177,12 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
|
||||
MapRef map, CompilationDependencies* dependencies);
|
||||
void CreateAccessInfoForLoadingThen(MapRef map,
|
||||
CompilationDependencies* dependencies);
|
||||
void StorePropertyAccessInfoForLoad(MapRef map, NameRef name,
|
||||
PropertyAccessInfo const& access_info);
|
||||
void StorePropertyAccessInfo(MapRef map, NameRef name, AccessMode mode,
|
||||
PropertyAccessInfo const& access_info);
|
||||
PropertyAccessInfo GetPropertyAccessInfo(
|
||||
MapRef map, NameRef name, AccessMode access_mode,
|
||||
CompilationDependencies* dependencies,
|
||||
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
|
||||
|
||||
std::ostream& Trace();
|
||||
void IncrementTracingIndentation();
|
||||
@ -156,6 +193,23 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
|
||||
friend class ObjectRef;
|
||||
friend class ObjectData;
|
||||
|
||||
// Bottleneck FeedbackNexus access here, for storage in the broker
|
||||
// or on-the-fly usage elsewhere in the compiler.
|
||||
ForInHint ReadFeedbackForForIn(FeedbackSource const& source) const;
|
||||
CompareOperationHint ReadFeedbackForCompareOperation(
|
||||
FeedbackSource const& source) const;
|
||||
BinaryOperationHint ReadFeedbackForBinaryOperation(
|
||||
FeedbackSource const& source) const;
|
||||
|
||||
ProcessedFeedback const& ReadFeedbackForCall(FeedbackSource const& source);
|
||||
ProcessedFeedback const& ReadFeedbackForGlobalAccess(
|
||||
FeedbackSource const& source);
|
||||
ProcessedFeedback const& ReadFeedbackForInstanceOf(
|
||||
FeedbackSource const& source);
|
||||
ProcessedFeedback const& ReadFeedbackForPropertyAccess(
|
||||
FeedbackSource const& source, AccessMode mode,
|
||||
base::Optional<NameRef> static_name);
|
||||
|
||||
void SerializeShareableObjects();
|
||||
void CollectArrayAndObjectPrototypes();
|
||||
|
||||
@ -179,12 +233,15 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
|
||||
typedef ZoneUnorderedMap<MapRef, PropertyAccessInfo, ObjectRef::Hash,
|
||||
ObjectRef::Equal>
|
||||
MapToAccessInfos;
|
||||
|
||||
// TODO(neis): Replaceby uses of property_access_infos_.
|
||||
MapToAccessInfos ais_for_loading_exec_;
|
||||
MapToAccessInfos ais_for_loading_has_instance_;
|
||||
MapToAccessInfos ais_for_loading_then_;
|
||||
ZoneUnorderedMap<MapNameRefPair, PropertyAccessInfo, MapNameRefPair::Hash,
|
||||
MapNameRefPair::Equal>
|
||||
property_access_infos_for_load_;
|
||||
|
||||
ZoneUnorderedMap<PropertyAccessTarget, PropertyAccessInfo,
|
||||
PropertyAccessTarget::Hash, PropertyAccessTarget::Equal>
|
||||
property_access_infos_;
|
||||
|
||||
static const size_t kMinimalRefsBucketCount = 8; // must be power of 2
|
||||
static const size_t kInitialRefsBucketCount = 1024; // must be power of 2
|
||||
|
@ -52,17 +52,6 @@ bool HasOnlyJSArrayMaps(JSHeapBroker* broker,
|
||||
return true;
|
||||
}
|
||||
|
||||
void TryUpdateThenDropDeprecated(Isolate* isolate, MapHandles* maps) {
|
||||
for (auto it = maps->begin(); it != maps->end();) {
|
||||
if (Map::TryUpdate(isolate, *it).ToHandle(&*it)) {
|
||||
DCHECK(!(*it)->is_deprecated());
|
||||
++it;
|
||||
} else {
|
||||
it = maps->erase(it);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
JSNativeContextSpecialization::JSNativeContextSpecialization(
|
||||
@ -383,9 +372,7 @@ Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor(
|
||||
}
|
||||
|
||||
Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
|
||||
// TODO(neis): Eliminate heap accesses.
|
||||
AllowHandleDereference allow_handle_dereference;
|
||||
AllowHandleAllocation allow_handle_allocation;
|
||||
DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining);
|
||||
|
||||
DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode());
|
||||
FeedbackParameter const& p = FeedbackParameterOf(node->op());
|
||||
@ -403,10 +390,13 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
|
||||
if (m.HasValue() && m.Ref(broker()).IsJSObject()) {
|
||||
receiver = m.Ref(broker()).AsJSObject().object();
|
||||
} else if (p.feedback().IsValid()) {
|
||||
FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
|
||||
if (!nexus.GetConstructorFeedback().ToHandle(&receiver)) {
|
||||
return NoChange();
|
||||
}
|
||||
ProcessedFeedback const& feedback =
|
||||
broker()->GetFeedbackForInstanceOf(FeedbackSource(p.feedback()));
|
||||
if (feedback.IsInsufficient()) return NoChange();
|
||||
base::Optional<JSObjectRef> maybe_receiver =
|
||||
feedback.AsInstanceOf().value();
|
||||
if (!maybe_receiver.has_value()) return NoChange();
|
||||
receiver = maybe_receiver->object();
|
||||
} else {
|
||||
return NoChange();
|
||||
}
|
||||
@ -1002,69 +992,88 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
|
||||
|
||||
Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) {
|
||||
DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining);
|
||||
|
||||
DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
|
||||
LoadGlobalParameters const& p = LoadGlobalParametersOf(node->op());
|
||||
if (!p.feedback().IsValid()) return NoChange();
|
||||
FeedbackSource source(p.feedback());
|
||||
|
||||
// TODO(neis): Make consistent with other feedback processing code.
|
||||
GlobalAccessFeedback const* processed =
|
||||
FLAG_concurrent_inlining
|
||||
? broker()->GetGlobalAccessFeedback(source)
|
||||
: broker()->ProcessFeedbackForGlobalAccess(source);
|
||||
if (processed == nullptr) return NoChange();
|
||||
ProcessedFeedback const& processed =
|
||||
broker()->GetFeedbackForGlobalAccess(FeedbackSource(p.feedback()));
|
||||
if (processed.IsInsufficient()) return NoChange();
|
||||
|
||||
if (processed->IsScriptContextSlot()) {
|
||||
GlobalAccessFeedback const& feedback = processed.AsGlobalAccess();
|
||||
if (feedback.IsScriptContextSlot()) {
|
||||
Node* effect = NodeProperties::GetEffectInput(node);
|
||||
Node* script_context = jsgraph()->Constant(processed->script_context());
|
||||
Node* script_context = jsgraph()->Constant(feedback.script_context());
|
||||
Node* value = effect =
|
||||
graph()->NewNode(javascript()->LoadContext(0, processed->slot_index(),
|
||||
processed->immutable()),
|
||||
graph()->NewNode(javascript()->LoadContext(0, feedback.slot_index(),
|
||||
feedback.immutable()),
|
||||
script_context, effect);
|
||||
ReplaceWithValue(node, value, effect);
|
||||
return Replace(value);
|
||||
} else if (feedback.IsPropertyCell()) {
|
||||
return ReduceGlobalAccess(node, nullptr, nullptr,
|
||||
NameRef(broker(), p.name()), AccessMode::kLoad,
|
||||
nullptr, feedback.property_cell());
|
||||
} else {
|
||||
DCHECK(feedback.IsMegamorphic());
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
CHECK(processed->IsPropertyCell());
|
||||
return ReduceGlobalAccess(node, nullptr, nullptr, NameRef(broker(), p.name()),
|
||||
AccessMode::kLoad, nullptr,
|
||||
processed->property_cell());
|
||||
}
|
||||
|
||||
Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) {
|
||||
DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining);
|
||||
|
||||
DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
|
||||
Node* value = NodeProperties::GetValueInput(node, 0);
|
||||
|
||||
StoreGlobalParameters const& p = StoreGlobalParametersOf(node->op());
|
||||
if (!p.feedback().IsValid()) return NoChange();
|
||||
FeedbackSource source(p.feedback());
|
||||
|
||||
GlobalAccessFeedback const* processed =
|
||||
FLAG_concurrent_inlining
|
||||
? broker()->GetGlobalAccessFeedback(source)
|
||||
: broker()->ProcessFeedbackForGlobalAccess(source);
|
||||
if (processed == nullptr) return NoChange();
|
||||
ProcessedFeedback const& processed =
|
||||
broker()->GetFeedbackForGlobalAccess(FeedbackSource(p.feedback()));
|
||||
if (processed.IsInsufficient()) return NoChange();
|
||||
|
||||
if (processed->IsScriptContextSlot()) {
|
||||
if (processed->immutable()) return NoChange();
|
||||
GlobalAccessFeedback const& feedback = processed.AsGlobalAccess();
|
||||
if (feedback.IsScriptContextSlot()) {
|
||||
if (feedback.immutable()) return NoChange();
|
||||
Node* effect = NodeProperties::GetEffectInput(node);
|
||||
Node* control = NodeProperties::GetControlInput(node);
|
||||
Node* script_context = jsgraph()->Constant(processed->script_context());
|
||||
Node* script_context = jsgraph()->Constant(feedback.script_context());
|
||||
effect =
|
||||
graph()->NewNode(javascript()->StoreContext(0, processed->slot_index()),
|
||||
graph()->NewNode(javascript()->StoreContext(0, feedback.slot_index()),
|
||||
value, script_context, effect, control);
|
||||
ReplaceWithValue(node, value, effect, control);
|
||||
return Replace(value);
|
||||
}
|
||||
|
||||
if (processed->IsPropertyCell()) {
|
||||
} else if (feedback.IsPropertyCell()) {
|
||||
return ReduceGlobalAccess(node, nullptr, value, NameRef(broker(), p.name()),
|
||||
AccessMode::kStore, nullptr,
|
||||
processed->property_cell());
|
||||
feedback.property_cell());
|
||||
} else {
|
||||
DCHECK(feedback.IsMegamorphic());
|
||||
return NoChange();
|
||||
}
|
||||
}
|
||||
|
||||
UNREACHABLE();
|
||||
void JSNativeContextSpecialization::FilterMapsAndGetPropertyAccessInfos(
|
||||
NamedAccessFeedback const& feedback, AccessMode access_mode, Node* receiver,
|
||||
Node* effect, ZoneVector<PropertyAccessInfo>* access_infos) {
|
||||
ZoneVector<Handle<Map>> receiver_maps(zone());
|
||||
|
||||
// Either infer maps from the graph or use the feedback.
|
||||
if (!InferReceiverMaps(receiver, effect, &receiver_maps)) {
|
||||
receiver_maps = feedback.maps();
|
||||
}
|
||||
RemoveImpossibleReceiverMaps(receiver, &receiver_maps);
|
||||
|
||||
for (Handle<Map> map_handle : receiver_maps) {
|
||||
MapRef map(broker(), map_handle);
|
||||
CHECK(!map.is_deprecated());
|
||||
PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo(
|
||||
map, feedback.name(), access_mode, dependencies(),
|
||||
FLAG_concurrent_inlining ? SerializationPolicy::kAssumeSerialized
|
||||
: SerializationPolicy::kSerializeIfNeeded);
|
||||
access_infos->push_back(access_info);
|
||||
}
|
||||
}
|
||||
|
||||
Reduction JSNativeContextSpecialization::ReduceNamedAccess(
|
||||
@ -1083,10 +1092,13 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
|
||||
Node* effect = NodeProperties::GetEffectInput(node);
|
||||
Node* control = NodeProperties::GetControlInput(node);
|
||||
|
||||
ZoneVector<PropertyAccessInfo> access_infos_for_feedback(zone());
|
||||
ZoneVector<PropertyAccessInfo> access_infos(zone());
|
||||
FilterMapsAndGetPropertyAccessInfos(feedback, access_mode, receiver, effect,
|
||||
&access_infos_for_feedback);
|
||||
AccessInfoFactory access_info_factory(broker(), dependencies(), zone());
|
||||
if (!access_info_factory.FinalizePropertyAccessInfos(
|
||||
feedback.access_infos(), access_mode, &access_infos)) {
|
||||
access_infos_for_feedback, access_mode, &access_infos)) {
|
||||
return NoChange();
|
||||
}
|
||||
|
||||
@ -1459,9 +1471,48 @@ base::Optional<JSTypedArrayRef> GetTypedArrayConstant(JSHeapBroker* broker,
|
||||
}
|
||||
} // namespace
|
||||
|
||||
void JSNativeContextSpecialization::RemoveImpossibleReceiverMaps(
|
||||
Node* receiver, ZoneVector<Handle<Map>>* receiver_maps) const {
|
||||
// TODO(neis): Decide what to do about this for concurrent inlining.
|
||||
AllowHandleAllocation allow_handle_allocation;
|
||||
AllowHandleDereference allow_handle_dereference;
|
||||
Handle<Map> root_map;
|
||||
if (InferReceiverRootMap(receiver).ToHandle(&root_map)) {
|
||||
DCHECK(!root_map->is_abandoned_prototype_map());
|
||||
Isolate* isolate = this->isolate();
|
||||
receiver_maps->erase(
|
||||
std::remove_if(receiver_maps->begin(), receiver_maps->end(),
|
||||
[root_map, isolate](Handle<Map> map) {
|
||||
return map->is_abandoned_prototype_map() ||
|
||||
map->FindRootMap(isolate) != *root_map;
|
||||
}),
|
||||
receiver_maps->end());
|
||||
}
|
||||
}
|
||||
|
||||
// Possibly refine the feedback using inferred map information from the graph.
|
||||
ElementAccessFeedback const&
|
||||
JSNativeContextSpecialization::TryRefineElementAccessFeedback(
|
||||
ElementAccessFeedback const& feedback, Node* receiver, Node* effect) const {
|
||||
AccessMode access_mode = feedback.keyed_mode().access_mode();
|
||||
bool use_inference =
|
||||
access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas;
|
||||
if (!use_inference) return feedback;
|
||||
|
||||
ZoneVector<Handle<Map>> inferred_maps(zone());
|
||||
if (!InferReceiverMaps(receiver, effect, &inferred_maps)) return feedback;
|
||||
|
||||
RemoveImpossibleReceiverMaps(receiver, &inferred_maps);
|
||||
// TODO(neis): After Refine, the resulting feedback can still contain
|
||||
// impossible maps when a target is kept only because more than one of its
|
||||
// sources was inferred. Think of a way to completely rule out impossible
|
||||
// maps.
|
||||
return feedback.Refine(inferred_maps, zone());
|
||||
}
|
||||
|
||||
Reduction JSNativeContextSpecialization::ReduceElementAccess(
|
||||
Node* node, Node* index, Node* value,
|
||||
ElementAccessFeedback const& processed) {
|
||||
ElementAccessFeedback const& feedback) {
|
||||
DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining);
|
||||
DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
|
||||
node->opcode() == IrOpcode::kJSStoreProperty ||
|
||||
@ -1474,30 +1525,34 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
|
||||
Node* frame_state =
|
||||
NodeProperties::FindFrameStateBefore(node, jsgraph()->Dead());
|
||||
|
||||
AccessMode access_mode = processed.keyed_mode.access_mode();
|
||||
// TODO(neis): It's odd that we do optimizations below that don't really care
|
||||
// about the feedback, but we don't do them when the feedback is megamorphic.
|
||||
if (feedback.transition_groups().empty()) return NoChange();
|
||||
|
||||
ElementAccessFeedback const& refined_feedback =
|
||||
TryRefineElementAccessFeedback(feedback, receiver, effect);
|
||||
|
||||
AccessMode access_mode = refined_feedback.keyed_mode().access_mode();
|
||||
if ((access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) &&
|
||||
receiver->opcode() == IrOpcode::kHeapConstant) {
|
||||
Reduction reduction = ReduceElementLoadFromHeapConstant(
|
||||
node, index, access_mode, processed.keyed_mode.load_mode());
|
||||
node, index, access_mode, refined_feedback.keyed_mode().load_mode());
|
||||
if (reduction.Changed()) return reduction;
|
||||
}
|
||||
|
||||
if (HasOnlyStringMaps(broker(), processed.receiver_maps)) {
|
||||
DCHECK(processed.transitions.empty());
|
||||
if (!refined_feedback.transition_groups().empty() &&
|
||||
refined_feedback.HasOnlyStringMaps(broker())) {
|
||||
return ReduceElementAccessOnString(node, index, value,
|
||||
processed.keyed_mode);
|
||||
refined_feedback.keyed_mode());
|
||||
}
|
||||
|
||||
// Compute element access infos for the receiver maps.
|
||||
AccessInfoFactory access_info_factory(broker(), dependencies(),
|
||||
graph()->zone());
|
||||
ZoneVector<ElementAccessInfo> access_infos(zone());
|
||||
if (!access_info_factory.ComputeElementAccessInfos(processed, access_mode,
|
||||
&access_infos)) {
|
||||
if (!access_info_factory.ComputeElementAccessInfos(refined_feedback,
|
||||
&access_infos) ||
|
||||
access_infos.empty()) {
|
||||
return NoChange();
|
||||
} else if (access_infos.empty()) {
|
||||
return ReduceSoftDeoptimize(
|
||||
node, DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess);
|
||||
}
|
||||
|
||||
// For holey stores or growing stores, we need to check that the prototype
|
||||
@ -1516,7 +1571,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
|
||||
// then we need to check that all prototypes have stable maps with
|
||||
// fast elements (and we need to guard against changes to that below).
|
||||
if ((IsHoleyOrDictionaryElementsKind(receiver_map.elements_kind()) ||
|
||||
IsGrowStoreMode(processed.keyed_mode.store_mode())) &&
|
||||
IsGrowStoreMode(feedback.keyed_mode().store_mode())) &&
|
||||
!receiver_map.HasOnlyStablePrototypesWithFastElements(
|
||||
&prototype_maps)) {
|
||||
return NoChange();
|
||||
@ -1587,7 +1642,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
|
||||
// Access the actual element.
|
||||
ValueEffectControl continuation =
|
||||
BuildElementAccess(receiver, index, value, effect, control, access_info,
|
||||
processed.keyed_mode);
|
||||
feedback.keyed_mode());
|
||||
value = continuation.value();
|
||||
effect = continuation.effect();
|
||||
control = continuation.control();
|
||||
@ -1654,7 +1709,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
|
||||
// Access the actual element.
|
||||
ValueEffectControl continuation =
|
||||
BuildElementAccess(this_receiver, this_index, this_value, this_effect,
|
||||
this_control, access_info, processed.keyed_mode);
|
||||
this_control, access_info, feedback.keyed_mode());
|
||||
values.push_back(continuation.value());
|
||||
effects.push_back(continuation.effect());
|
||||
controls.push_back(continuation.control());
|
||||
@ -1772,58 +1827,21 @@ Reduction JSNativeContextSpecialization::ReducePropertyAccess(
|
||||
node->opcode() == IrOpcode::kJSStoreNamedOwn ||
|
||||
node->opcode() == IrOpcode::kJSGetIterator);
|
||||
|
||||
Node* receiver = NodeProperties::GetValueInput(node, 0);
|
||||
Node* effect = NodeProperties::GetEffectInput(node);
|
||||
|
||||
ProcessedFeedback const* processed = nullptr;
|
||||
if (FLAG_concurrent_inlining) {
|
||||
processed = broker()->GetFeedback(source);
|
||||
// TODO(neis): Infer maps from the graph and consolidate with feedback/hints
|
||||
// and filter impossible candidates based on inferred root map.
|
||||
} else {
|
||||
// TODO(neis): Try to unify this with the similar code in the serializer.
|
||||
FeedbackNexus nexus(source.vector, source.slot);
|
||||
if (nexus.ic_state() == UNINITIALIZED) {
|
||||
processed = new (zone()) InsufficientFeedback();
|
||||
} else {
|
||||
MapHandles receiver_maps;
|
||||
if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) {
|
||||
processed = new (zone()) InsufficientFeedback();
|
||||
} else if (!receiver_maps.empty()) {
|
||||
base::Optional<NameRef> name = static_name.has_value()
|
||||
? static_name
|
||||
: broker()->GetNameFeedback(nexus);
|
||||
if (name.has_value()) {
|
||||
ZoneVector<PropertyAccessInfo> access_infos(zone());
|
||||
AccessInfoFactory access_info_factory(broker(), dependencies(),
|
||||
zone());
|
||||
access_info_factory.ComputePropertyAccessInfos(
|
||||
receiver_maps, name->object(), access_mode, &access_infos);
|
||||
processed = new (zone()) NamedAccessFeedback(*name, access_infos);
|
||||
} else if (nexus.GetKeyType() == ELEMENT &&
|
||||
MEGAMORPHIC != nexus.ic_state()) {
|
||||
processed = broker()->ProcessFeedbackMapsForElementAccess(
|
||||
receiver_maps, KeyedAccessMode::FromNexus(nexus));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (processed == nullptr) return NoChange();
|
||||
switch (processed->kind()) {
|
||||
ProcessedFeedback const& feedback =
|
||||
broker()->GetFeedbackForPropertyAccess(source, access_mode, static_name);
|
||||
switch (feedback.kind()) {
|
||||
case ProcessedFeedback::kInsufficient:
|
||||
return ReduceSoftDeoptimize(
|
||||
node,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
|
||||
case ProcessedFeedback::kNamedAccess:
|
||||
return ReduceNamedAccess(node, value, *processed->AsNamedAccess(),
|
||||
return ReduceNamedAccess(node, value, feedback.AsNamedAccess(),
|
||||
access_mode, key);
|
||||
case ProcessedFeedback::kElementAccess:
|
||||
CHECK_EQ(processed->AsElementAccess()->keyed_mode.access_mode(),
|
||||
access_mode);
|
||||
return ReduceElementAccess(node, key, value,
|
||||
*processed->AsElementAccess());
|
||||
case ProcessedFeedback::kGlobalAccess:
|
||||
DCHECK_EQ(feedback.AsElementAccess().keyed_mode().access_mode(),
|
||||
access_mode);
|
||||
return ReduceElementAccess(node, key, value, feedback.AsElementAccess());
|
||||
default:
|
||||
UNREACHABLE();
|
||||
}
|
||||
}
|
||||
@ -3235,47 +3253,9 @@ bool JSNativeContextSpecialization::CanTreatHoleAsUndefined(
|
||||
return dependencies()->DependOnNoElementsProtector();
|
||||
}
|
||||
|
||||
// Returns false iff we have insufficient feedback (uninitialized or obsolete).
|
||||
bool JSNativeContextSpecialization::ExtractReceiverMaps(
|
||||
Node* receiver, Node* effect, FeedbackNexus const& nexus,
|
||||
MapHandles* receiver_maps) {
|
||||
DCHECK(receiver_maps->empty());
|
||||
if (nexus.IsUninitialized()) return false;
|
||||
|
||||
// See if we can infer a concrete type for the {receiver}. Solely relying on
|
||||
// the inference is not safe for keyed stores, because we would potentially
|
||||
// miss out on transitions that need to be performed.
|
||||
{
|
||||
FeedbackSlotKind kind = nexus.kind();
|
||||
bool use_inference =
|
||||
!IsKeyedStoreICKind(kind) && !IsStoreInArrayLiteralICKind(kind);
|
||||
if (use_inference && InferReceiverMaps(receiver, effect, receiver_maps)) {
|
||||
TryUpdateThenDropDeprecated(isolate(), receiver_maps);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
if (nexus.ExtractMaps(receiver_maps) == 0) return true;
|
||||
|
||||
// Try to filter impossible candidates based on inferred root map.
|
||||
Handle<Map> root_map;
|
||||
if (InferReceiverRootMap(receiver).ToHandle(&root_map)) {
|
||||
DCHECK(!root_map->is_abandoned_prototype_map());
|
||||
Isolate* isolate = this->isolate();
|
||||
receiver_maps->erase(
|
||||
std::remove_if(receiver_maps->begin(), receiver_maps->end(),
|
||||
[root_map, isolate](Handle<Map> map) {
|
||||
return map->is_abandoned_prototype_map() ||
|
||||
map->FindRootMap(isolate) != *root_map;
|
||||
}),
|
||||
receiver_maps->end());
|
||||
}
|
||||
TryUpdateThenDropDeprecated(isolate(), receiver_maps);
|
||||
return !receiver_maps->empty();
|
||||
}
|
||||
|
||||
bool JSNativeContextSpecialization::InferReceiverMaps(
|
||||
Node* receiver, Node* effect, MapHandles* receiver_maps) {
|
||||
Node* receiver, Node* effect,
|
||||
ZoneVector<Handle<Map>>* receiver_maps) const {
|
||||
ZoneHandleSet<Map> maps;
|
||||
NodeProperties::InferReceiverMapsResult result =
|
||||
NodeProperties::InferReceiverMapsUnsafe(broker(), receiver, effect,
|
||||
@ -3301,7 +3281,7 @@ bool JSNativeContextSpecialization::InferReceiverMaps(
|
||||
}
|
||||
|
||||
MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverRootMap(
|
||||
Node* receiver) {
|
||||
Node* receiver) const {
|
||||
HeapObjectMatcher m(receiver);
|
||||
if (m.HasValue()) {
|
||||
return handle(m.Value()->map().FindRootMap(isolate()), isolate());
|
||||
|
@ -213,18 +213,25 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
|
||||
// code dependencies and might use the array protector cell.
|
||||
bool CanTreatHoleAsUndefined(ZoneVector<Handle<Map>> const& receiver_maps);
|
||||
|
||||
// Extract receiver maps from {nexus} and filter based on {receiver} if
|
||||
// possible.
|
||||
bool ExtractReceiverMaps(Node* receiver, Node* effect,
|
||||
FeedbackNexus const& nexus,
|
||||
MapHandles* receiver_maps);
|
||||
void RemoveImpossibleReceiverMaps(
|
||||
Node* receiver, ZoneVector<Handle<Map>>* receiver_maps) const;
|
||||
|
||||
ElementAccessFeedback const& TryRefineElementAccessFeedback(
|
||||
ElementAccessFeedback const& feedback, Node* receiver,
|
||||
Node* effect) const;
|
||||
|
||||
void FilterMapsAndGetPropertyAccessInfos(
|
||||
NamedAccessFeedback const& feedback, AccessMode access_mode,
|
||||
Node* receiver, Node* effect,
|
||||
ZoneVector<PropertyAccessInfo>* access_infos);
|
||||
|
||||
// Try to infer maps for the given {receiver} at the current {effect}.
|
||||
bool InferReceiverMaps(Node* receiver, Node* effect,
|
||||
MapHandles* receiver_maps);
|
||||
ZoneVector<Handle<Map>>* receiver_maps) const;
|
||||
|
||||
// Try to infer a root map for the {receiver} independent of the current
|
||||
// program location.
|
||||
MaybeHandle<Map> InferReceiverRootMap(Node* receiver);
|
||||
MaybeHandle<Map> InferReceiverRootMap(Node* receiver) const;
|
||||
|
||||
// Checks if we know at compile time that the {receiver} either definitely
|
||||
// has the {prototype} in it's prototype chain, or the {receiver} definitely
|
||||
|
@ -6,6 +6,7 @@
|
||||
|
||||
#include "src/compiler/access-builder.h"
|
||||
#include "src/compiler/js-graph.h"
|
||||
#include "src/compiler/js-heap-broker.h"
|
||||
#include "src/compiler/operator-properties.h"
|
||||
#include "src/compiler/simplified-operator.h"
|
||||
#include "src/objects/feedback-vector.h"
|
||||
@ -78,16 +79,6 @@ class JSSpeculativeBinopBuilder final {
|
||||
control_(control),
|
||||
slot_(slot) {}
|
||||
|
||||
BinaryOperationHint GetBinaryOperationHint() {
|
||||
FeedbackNexus nexus(feedback_vector(), slot_);
|
||||
return nexus.GetBinaryOperationFeedback();
|
||||
}
|
||||
|
||||
CompareOperationHint GetCompareOperationHint() {
|
||||
FeedbackNexus nexus(feedback_vector(), slot_);
|
||||
return nexus.GetCompareOperationFeedback();
|
||||
}
|
||||
|
||||
bool GetBinaryNumberOperationHint(NumberOperationHint* hint) {
|
||||
return BinaryOperationHintToNumberOperationHint(GetBinaryOperationHint(),
|
||||
hint);
|
||||
@ -239,34 +230,52 @@ class JSSpeculativeBinopBuilder final {
|
||||
JSOperatorBuilder* javascript() { return jsgraph()->javascript(); }
|
||||
SimplifiedOperatorBuilder* simplified() { return jsgraph()->simplified(); }
|
||||
CommonOperatorBuilder* common() { return jsgraph()->common(); }
|
||||
const Handle<FeedbackVector>& feedback_vector() const {
|
||||
return lowering_->feedback_vector();
|
||||
}
|
||||
|
||||
private:
|
||||
const JSTypeHintLowering* lowering_;
|
||||
const Operator* op_;
|
||||
BinaryOperationHint GetBinaryOperationHint() {
|
||||
return lowering_->GetBinaryOperationHint(slot_);
|
||||
}
|
||||
|
||||
CompareOperationHint GetCompareOperationHint() {
|
||||
return lowering_->GetCompareOperationHint(slot_);
|
||||
}
|
||||
|
||||
JSTypeHintLowering const* const lowering_;
|
||||
Operator const* const op_;
|
||||
Node* left_;
|
||||
Node* right_;
|
||||
Node* effect_;
|
||||
Node* control_;
|
||||
FeedbackSlot slot_;
|
||||
Node* const effect_;
|
||||
Node* const control_;
|
||||
FeedbackSlot const slot_;
|
||||
};
|
||||
|
||||
JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
|
||||
Handle<FeedbackVector> feedback_vector,
|
||||
JSTypeHintLowering::JSTypeHintLowering(JSHeapBroker* broker, JSGraph* jsgraph,
|
||||
FeedbackVectorRef feedback_vector,
|
||||
Flags flags)
|
||||
: jsgraph_(jsgraph), flags_(flags), feedback_vector_(feedback_vector) {}
|
||||
: broker_(broker),
|
||||
jsgraph_(jsgraph),
|
||||
flags_(flags),
|
||||
feedback_vector_(feedback_vector) {}
|
||||
|
||||
Isolate* JSTypeHintLowering::isolate() const { return jsgraph()->isolate(); }
|
||||
|
||||
BinaryOperationHint JSTypeHintLowering::GetBinaryOperationHint(
|
||||
FeedbackSlot slot) const {
|
||||
FeedbackSource source(feedback_vector(), slot);
|
||||
return broker()->GetFeedbackForBinaryOperation(source);
|
||||
}
|
||||
|
||||
CompareOperationHint JSTypeHintLowering::GetCompareOperationHint(
|
||||
FeedbackSlot slot) const {
|
||||
FeedbackSource source(feedback_vector(), slot);
|
||||
return broker()->GetFeedbackForCompareOperation(source);
|
||||
}
|
||||
|
||||
JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceUnaryOperation(
|
||||
const Operator* op, Node* operand, Node* effect, Node* control,
|
||||
FeedbackSlot slot) const {
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
nexus, effect, control,
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForUnaryOperation)) {
|
||||
return LoweringResult::Exit(node);
|
||||
}
|
||||
@ -309,9 +318,7 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceUnaryOperation(
|
||||
control, slot);
|
||||
node = b.TryBuildNumberBinop();
|
||||
if (!node) {
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (nexus.GetBinaryOperationFeedback() ==
|
||||
BinaryOperationHint::kBigInt) {
|
||||
if (GetBinaryOperationHint(slot) == BinaryOperationHint::kBigInt) {
|
||||
const Operator* op = jsgraph()->simplified()->SpeculativeBigIntNegate(
|
||||
BigIntOperationHint::kBigInt);
|
||||
node = jsgraph()->graph()->NewNode(op, operand, effect, control);
|
||||
@ -335,10 +342,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
|
||||
FeedbackSlot slot) const {
|
||||
switch (op->opcode()) {
|
||||
case IrOpcode::kJSStrictEqual: {
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
nexus, effect, control,
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
|
||||
return LoweringResult::Exit(node);
|
||||
}
|
||||
@ -351,10 +356,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
|
||||
case IrOpcode::kJSGreaterThan:
|
||||
case IrOpcode::kJSLessThanOrEqual:
|
||||
case IrOpcode::kJSGreaterThanOrEqual: {
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
nexus, effect, control,
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
|
||||
return LoweringResult::Exit(node);
|
||||
}
|
||||
@ -365,10 +368,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
|
||||
break;
|
||||
}
|
||||
case IrOpcode::kJSInstanceOf: {
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
nexus, effect, control,
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
|
||||
return LoweringResult::Exit(node);
|
||||
}
|
||||
@ -387,10 +388,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
|
||||
case IrOpcode::kJSMultiply:
|
||||
case IrOpcode::kJSDivide:
|
||||
case IrOpcode::kJSModulus: {
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
nexus, effect, control,
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForBinaryOperation)) {
|
||||
return LoweringResult::Exit(node);
|
||||
}
|
||||
@ -418,10 +417,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
|
||||
JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceForInNextOperation(
|
||||
Node* receiver, Node* cache_array, Node* cache_type, Node* index,
|
||||
Node* effect, Node* control, FeedbackSlot slot) const {
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
nexus, effect, control,
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
|
||||
return LoweringResult::Exit(node);
|
||||
}
|
||||
@ -432,10 +429,8 @@ JSTypeHintLowering::LoweringResult
|
||||
JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect,
|
||||
Node* control,
|
||||
FeedbackSlot slot) const {
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
nexus, effect, control,
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
|
||||
return LoweringResult::Exit(node);
|
||||
}
|
||||
@ -445,10 +440,9 @@ JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect,
|
||||
JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceToNumberOperation(
|
||||
Node* input, Node* effect, Node* control, FeedbackSlot slot) const {
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
NumberOperationHint hint;
|
||||
if (BinaryOperationHintToNumberOperationHint(
|
||||
nexus.GetBinaryOperationFeedback(), &hint)) {
|
||||
if (BinaryOperationHintToNumberOperationHint(GetBinaryOperationHint(slot),
|
||||
&hint)) {
|
||||
Node* node = jsgraph()->graph()->NewNode(
|
||||
jsgraph()->simplified()->SpeculativeToNumber(hint, VectorSlotPair()),
|
||||
input, effect, control);
|
||||
@ -462,10 +456,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceCallOperation(
|
||||
Node* control, FeedbackSlot slot) const {
|
||||
DCHECK(op->opcode() == IrOpcode::kJSCall ||
|
||||
op->opcode() == IrOpcode::kJSCallWithSpread);
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
nexus, effect, control,
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForCall)) {
|
||||
return LoweringResult::Exit(node);
|
||||
}
|
||||
@ -477,10 +469,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceConstructOperation(
|
||||
Node* control, FeedbackSlot slot) const {
|
||||
DCHECK(op->opcode() == IrOpcode::kJSConstruct ||
|
||||
op->opcode() == IrOpcode::kJSConstructWithSpread);
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
nexus, effect, control,
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForConstruct)) {
|
||||
return LoweringResult::Exit(node);
|
||||
}
|
||||
@ -493,10 +483,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadNamedOperation(
|
||||
// JSGetIterator involves a named load of the Symbol.iterator property.
|
||||
DCHECK(op->opcode() == IrOpcode::kJSLoadNamed ||
|
||||
op->opcode() == IrOpcode::kJSGetIterator);
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
nexus, effect, control,
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
|
||||
return LoweringResult::Exit(node);
|
||||
}
|
||||
@ -507,10 +495,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadKeyedOperation(
|
||||
const Operator* op, Node* obj, Node* key, Node* effect, Node* control,
|
||||
FeedbackSlot slot) const {
|
||||
DCHECK_EQ(IrOpcode::kJSLoadProperty, op->opcode());
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
nexus, effect, control,
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
|
||||
return LoweringResult::Exit(node);
|
||||
}
|
||||
@ -524,10 +510,8 @@ JSTypeHintLowering::ReduceStoreNamedOperation(const Operator* op, Node* obj,
|
||||
FeedbackSlot slot) const {
|
||||
DCHECK(op->opcode() == IrOpcode::kJSStoreNamed ||
|
||||
op->opcode() == IrOpcode::kJSStoreNamedOwn);
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
nexus, effect, control,
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
|
||||
return LoweringResult::Exit(node);
|
||||
}
|
||||
@ -541,30 +525,30 @@ JSTypeHintLowering::ReduceStoreKeyedOperation(const Operator* op, Node* obj,
|
||||
FeedbackSlot slot) const {
|
||||
DCHECK(op->opcode() == IrOpcode::kJSStoreProperty ||
|
||||
op->opcode() == IrOpcode::kJSStoreInArrayLiteral);
|
||||
DCHECK(!slot.IsInvalid());
|
||||
FeedbackNexus nexus(feedback_vector(), slot);
|
||||
if (Node* node = TryBuildSoftDeopt(
|
||||
nexus, effect, control,
|
||||
slot, effect, control,
|
||||
DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
|
||||
return LoweringResult::Exit(node);
|
||||
}
|
||||
return LoweringResult::NoChange();
|
||||
}
|
||||
|
||||
Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus const& nexus,
|
||||
Node* effect, Node* control,
|
||||
Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackSlot slot, Node* effect,
|
||||
Node* control,
|
||||
DeoptimizeReason reason) const {
|
||||
if ((flags() & kBailoutOnUninitialized) && nexus.IsUninitialized()) {
|
||||
Node* deoptimize = jsgraph()->graph()->NewNode(
|
||||
jsgraph()->common()->Deoptimize(DeoptimizeKind::kSoft, reason,
|
||||
VectorSlotPair()),
|
||||
jsgraph()->Dead(), effect, control);
|
||||
Node* frame_state =
|
||||
NodeProperties::FindFrameStateBefore(deoptimize, jsgraph()->Dead());
|
||||
deoptimize->ReplaceInput(0, frame_state);
|
||||
return deoptimize;
|
||||
}
|
||||
return nullptr;
|
||||
if (!(flags() & kBailoutOnUninitialized)) return nullptr;
|
||||
|
||||
FeedbackSource source(feedback_vector(), slot);
|
||||
if (!broker()->FeedbackIsInsufficient(source)) return nullptr;
|
||||
|
||||
Node* deoptimize = jsgraph()->graph()->NewNode(
|
||||
jsgraph()->common()->Deoptimize(DeoptimizeKind::kSoft, reason,
|
||||
VectorSlotPair()),
|
||||
jsgraph()->Dead(), effect, control);
|
||||
Node* frame_state =
|
||||
NodeProperties::FindFrameStateBefore(deoptimize, jsgraph()->Dead());
|
||||
deoptimize->ReplaceInput(0, frame_state);
|
||||
return deoptimize;
|
||||
}
|
||||
|
||||
} // namespace compiler
|
||||
|
@ -41,8 +41,8 @@ class JSTypeHintLowering {
|
||||
enum Flag { kNoFlags = 0u, kBailoutOnUninitialized = 1u << 1 };
|
||||
using Flags = base::Flags<Flag>;
|
||||
|
||||
JSTypeHintLowering(JSGraph* jsgraph, Handle<FeedbackVector> feedback_vector,
|
||||
Flags flags);
|
||||
JSTypeHintLowering(JSHeapBroker* broker, JSGraph* jsgraph,
|
||||
FeedbackVectorRef feedback_vector, Flags flags);
|
||||
|
||||
// {LoweringResult} describes the result of lowering. The following outcomes
|
||||
// are possible:
|
||||
@ -153,19 +153,22 @@ class JSTypeHintLowering {
|
||||
|
||||
private:
|
||||
friend class JSSpeculativeBinopBuilder;
|
||||
Node* TryBuildSoftDeopt(FeedbackNexus const& nexus, Node* effect,
|
||||
Node* control, DeoptimizeReason reson) const;
|
||||
|
||||
BinaryOperationHint GetBinaryOperationHint(FeedbackSlot slot) const;
|
||||
CompareOperationHint GetCompareOperationHint(FeedbackSlot slot) const;
|
||||
Node* TryBuildSoftDeopt(FeedbackSlot slot, Node* effect, Node* control,
|
||||
DeoptimizeReason reson) const;
|
||||
|
||||
JSHeapBroker* broker() const { return broker_; }
|
||||
JSGraph* jsgraph() const { return jsgraph_; }
|
||||
Isolate* isolate() const;
|
||||
Flags flags() const { return flags_; }
|
||||
const Handle<FeedbackVector>& feedback_vector() const {
|
||||
return feedback_vector_;
|
||||
}
|
||||
FeedbackVectorRef const& feedback_vector() const { return feedback_vector_; }
|
||||
|
||||
JSGraph* jsgraph_;
|
||||
JSHeapBroker* const broker_;
|
||||
JSGraph* const jsgraph_;
|
||||
Flags const flags_;
|
||||
Handle<FeedbackVector> feedback_vector_;
|
||||
FeedbackVectorRef const feedback_vector_;
|
||||
|
||||
DISALLOW_COPY_AND_ASSIGN(JSTypeHintLowering);
|
||||
};
|
||||
|
215
src/compiler/processed-feedback.h
Normal file
215
src/compiler/processed-feedback.h
Normal file
@ -0,0 +1,215 @@
|
||||
// Copyright 2019 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.
|
||||
|
||||
#ifndef V8_COMPILER_PROCESSED_FEEDBACK_H_
|
||||
#define V8_COMPILER_PROCESSED_FEEDBACK_H_
|
||||
|
||||
#include "src/compiler/heap-refs.h"
|
||||
|
||||
namespace v8 {
|
||||
namespace internal {
|
||||
namespace compiler {
|
||||
|
||||
class BinaryOperationFeedback;
|
||||
class CallFeedback;
|
||||
class CompareOperationFeedback;
|
||||
class ElementAccessFeedback;
|
||||
class ForInFeedback;
|
||||
class GlobalAccessFeedback;
|
||||
class InstanceOfFeedback;
|
||||
class NamedAccessFeedback;
|
||||
|
||||
class ProcessedFeedback : public ZoneObject {
|
||||
public:
|
||||
enum Kind {
|
||||
kInsufficient,
|
||||
kBinaryOperation,
|
||||
kCall,
|
||||
kCompareOperation,
|
||||
kElementAccess,
|
||||
kForIn,
|
||||
kGlobalAccess,
|
||||
kInstanceOf,
|
||||
kNamedAccess,
|
||||
};
|
||||
Kind kind() const { return kind_; }
|
||||
|
||||
bool IsInsufficient() const { return kind() == kInsufficient; }
|
||||
|
||||
BinaryOperationFeedback const& AsBinaryOperation() const;
|
||||
CallFeedback const& AsCall() const;
|
||||
CompareOperationFeedback const& AsCompareOperation() const;
|
||||
ElementAccessFeedback const& AsElementAccess() const;
|
||||
ForInFeedback const& AsForIn() const;
|
||||
GlobalAccessFeedback const& AsGlobalAccess() const;
|
||||
InstanceOfFeedback const& AsInstanceOf() const;
|
||||
NamedAccessFeedback const& AsNamedAccess() const;
|
||||
|
||||
protected:
|
||||
explicit ProcessedFeedback(Kind kind) : kind_(kind) {}
|
||||
|
||||
private:
|
||||
Kind const kind_;
|
||||
};
|
||||
|
||||
class InsufficientFeedback final : public ProcessedFeedback {
|
||||
public:
|
||||
InsufficientFeedback();
|
||||
};
|
||||
|
||||
class GlobalAccessFeedback : public ProcessedFeedback {
|
||||
public:
|
||||
explicit GlobalAccessFeedback(PropertyCellRef cell);
|
||||
GlobalAccessFeedback(ContextRef script_context, int slot_index,
|
||||
bool immutable);
|
||||
GlobalAccessFeedback(); // Megamorphic
|
||||
|
||||
bool IsMegamorphic() const;
|
||||
|
||||
bool IsPropertyCell() const;
|
||||
PropertyCellRef property_cell() const;
|
||||
|
||||
bool IsScriptContextSlot() const;
|
||||
ContextRef script_context() const;
|
||||
int slot_index() const;
|
||||
bool immutable() const;
|
||||
|
||||
base::Optional<ObjectRef> GetConstantHint() const;
|
||||
|
||||
private:
|
||||
base::Optional<ObjectRef> const cell_or_context_;
|
||||
int const index_and_immutable_;
|
||||
};
|
||||
|
||||
class KeyedAccessMode {
|
||||
public:
|
||||
static KeyedAccessMode FromNexus(FeedbackNexus const& nexus);
|
||||
|
||||
AccessMode access_mode() const;
|
||||
bool IsLoad() const;
|
||||
bool IsStore() const;
|
||||
KeyedAccessLoadMode load_mode() const;
|
||||
KeyedAccessStoreMode store_mode() const;
|
||||
|
||||
private:
|
||||
AccessMode const access_mode_;
|
||||
union LoadStoreMode {
|
||||
LoadStoreMode(KeyedAccessLoadMode load_mode);
|
||||
LoadStoreMode(KeyedAccessStoreMode store_mode);
|
||||
KeyedAccessLoadMode load_mode;
|
||||
KeyedAccessStoreMode store_mode;
|
||||
} const load_store_mode_;
|
||||
|
||||
KeyedAccessMode(AccessMode access_mode, KeyedAccessLoadMode load_mode);
|
||||
KeyedAccessMode(AccessMode access_mode, KeyedAccessStoreMode store_mode);
|
||||
};
|
||||
|
||||
class ElementAccessFeedback : public ProcessedFeedback {
|
||||
public:
|
||||
ElementAccessFeedback(Zone* zone, KeyedAccessMode const& keyed_mode);
|
||||
|
||||
KeyedAccessMode keyed_mode() const;
|
||||
|
||||
// A transition group is a target and a possibly empty set of sources that can
|
||||
// transition to the target. It is represented as a non-empty vector with the
|
||||
// target at index 0.
|
||||
using TransitionGroup = ZoneVector<Handle<Map>>;
|
||||
ZoneVector<TransitionGroup> const& transition_groups() const;
|
||||
|
||||
bool HasOnlyStringMaps(JSHeapBroker* broker) const;
|
||||
|
||||
void AddGroup(TransitionGroup&& group);
|
||||
|
||||
// Refine {this} by trying to restrict it to the maps in {inferred_maps}. A
|
||||
// transition group's target is kept iff it is in {inferred_maps} or if more
|
||||
// than one of its sources is in {inferred_maps}. Here's an (unrealistic)
|
||||
// example showing all the possible situations:
|
||||
//
|
||||
// inferred_maps = [a0, a2, c1, c2, d1, e0, e1]
|
||||
//
|
||||
// Groups before: Groups after:
|
||||
// [a0, a1, a2] [a0, a2]
|
||||
// [b0]
|
||||
// [c0, c1, c2, c3] [c0, c1, c2]
|
||||
// [d0, d1] [d1]
|
||||
// [e0, e1] [e0, e1]
|
||||
//
|
||||
ElementAccessFeedback const& Refine(
|
||||
ZoneVector<Handle<Map>> const& inferred_maps, Zone* zone) const;
|
||||
|
||||
private:
|
||||
KeyedAccessMode const keyed_mode_;
|
||||
ZoneVector<TransitionGroup> transition_groups_;
|
||||
};
|
||||
|
||||
class NamedAccessFeedback : public ProcessedFeedback {
|
||||
public:
|
||||
NamedAccessFeedback(NameRef const& name, ZoneVector<Handle<Map>> const& maps);
|
||||
|
||||
NameRef const& name() const { return name_; }
|
||||
ZoneVector<Handle<Map>> const& maps() const { return maps_; }
|
||||
|
||||
private:
|
||||
NameRef const name_;
|
||||
ZoneVector<Handle<Map>> const maps_;
|
||||
};
|
||||
|
||||
class CallFeedback : public ProcessedFeedback {
|
||||
public:
|
||||
CallFeedback(base::Optional<HeapObjectRef> target, float frequency,
|
||||
SpeculationMode mode)
|
||||
: ProcessedFeedback(kCall),
|
||||
target_(target),
|
||||
frequency_(frequency),
|
||||
mode_(mode) {}
|
||||
|
||||
base::Optional<HeapObjectRef> target() const { return target_; }
|
||||
float frequency() const { return frequency_; }
|
||||
SpeculationMode speculation_mode() const { return mode_; }
|
||||
|
||||
private:
|
||||
base::Optional<HeapObjectRef> const target_;
|
||||
float const frequency_;
|
||||
SpeculationMode const mode_;
|
||||
};
|
||||
|
||||
template <class T, ProcessedFeedback::Kind K>
|
||||
class SingleValueFeedback : public ProcessedFeedback {
|
||||
public:
|
||||
explicit SingleValueFeedback(T value) : ProcessedFeedback(K), value_(value) {}
|
||||
|
||||
T value() const { return value_; }
|
||||
|
||||
private:
|
||||
T const value_;
|
||||
};
|
||||
|
||||
class InstanceOfFeedback
|
||||
: public SingleValueFeedback<base::Optional<JSObjectRef>,
|
||||
ProcessedFeedback::kInstanceOf> {
|
||||
using SingleValueFeedback::SingleValueFeedback;
|
||||
};
|
||||
|
||||
class BinaryOperationFeedback
|
||||
: public SingleValueFeedback<BinaryOperationHint,
|
||||
ProcessedFeedback::kBinaryOperation> {
|
||||
using SingleValueFeedback::SingleValueFeedback;
|
||||
};
|
||||
|
||||
class CompareOperationFeedback
|
||||
: public SingleValueFeedback<CompareOperationHint,
|
||||
ProcessedFeedback::kCompareOperation> {
|
||||
using SingleValueFeedback::SingleValueFeedback;
|
||||
};
|
||||
|
||||
class ForInFeedback
|
||||
: public SingleValueFeedback<ForInHint, ProcessedFeedback::kForIn> {
|
||||
using SingleValueFeedback::SingleValueFeedback;
|
||||
};
|
||||
|
||||
} // namespace compiler
|
||||
} // namespace internal
|
||||
} // namespace v8
|
||||
|
||||
#endif // V8_COMPILER_PROCESSED_FEEDBACK_H_
|
File diff suppressed because it is too large
Load Diff
@ -887,8 +887,7 @@ float FeedbackNexus::ComputeCallFrequency() {
|
||||
|
||||
double const invocation_count = vector().invocation_count();
|
||||
double const call_count = GetCallCount();
|
||||
if (invocation_count == 0) {
|
||||
// Prevent division by 0.
|
||||
if (invocation_count == 0.0) { // Prevent division by 0.
|
||||
return 0.0f;
|
||||
}
|
||||
return static_cast<float>(call_count / invocation_count);
|
||||
|
Loading…
Reference in New Issue
Block a user