Revert "[turbofan] Various serializer/broker improvements"

This reverts commit 29585a06cc.

Reason for revert: Breaks GC stress bots - 
https://ci.chromium.org/p/v8/builders/ci/V8%20Linux%20-%20gc%20stress/24009
https://ci.chromium.org/p/v8/builders/ci/V8%20Linux64%20GC%20Stress%20-%20custom%20snapshot/27281

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,neis@chromium.org

Change-Id: I88625d92fddf993db63661666c59af05a47b2b58
No-Presubmit: true
No-Tree-Checks: true
No-Try: true
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1758314
Reviewed-by: Maya Lekova <mslekova@chromium.org>
Commit-Queue: Maya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63237}
This commit is contained in:
Maya Lekova 2019-08-19 10:59:56 +00:00 committed by Commit Bot
parent f84a83ac47
commit 0645b26a3d
14 changed files with 1081 additions and 1641 deletions

View File

@ -290,32 +290,35 @@ base::Optional<ElementAccessInfo> AccessInfoFactory::ComputeElementAccessInfo(
}
bool AccessInfoFactory::ComputeElementAccessInfos(
ElementAccessFeedback const& feedback,
ElementAccessFeedback const& processed, AccessMode access_mode,
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(feedback);
ConsolidateElementLoad(processed);
if (access_info.has_value()) {
access_infos->push_back(*access_info);
return true;
}
}
for (auto const& group : feedback.transition_groups()) {
DCHECK(!group.empty());
Handle<Map> target = group.front();
for (Handle<Map> receiver_map : processed.receiver_maps) {
// Compute the element access information.
base::Optional<ElementAccessInfo> access_info =
ComputeElementAccessInfo(target, access_mode);
ComputeElementAccessInfo(receiver_map, access_mode);
if (!access_info.has_value()) return false;
for (size_t i = 1; i < group.size(); ++i) {
access_info->AddTransitionSource(group[i]);
// Collect the possible transitions for the {receiver_map}.
for (auto transition : processed.transitions) {
if (transition.second.equals(receiver_map)) {
access_info->AddTransitionSource(transition.first);
}
}
// Schedule the access information.
access_infos->push_back(*access_info);
}
return true;
@ -631,7 +634,6 @@ 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;
@ -685,28 +687,22 @@ Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
} // namespace
base::Optional<ElementAccessInfo> AccessInfoFactory::ConsolidateElementLoad(
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());
ElementAccessFeedback const& processed) const {
ElementAccessFeedback::MapIterator it = processed.all_maps(broker());
MapRef first_map = it.current();
InstanceType instance_type = first_map.instance_type();
ElementsKind elements_kind = first_map.elements_kind();
ZoneVector<Handle<Map>> maps(zone());
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());
for (; !it.done(); it.advance()) {
MapRef map = it.current();
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());
}
return ElementAccessInfo(std::move(maps), elements_kind, zone());

View File

@ -168,7 +168,7 @@ class AccessInfoFactory final {
base::Optional<ElementAccessInfo> ComputeElementAccessInfo(
Handle<Map> map, AccessMode access_mode) const;
bool ComputeElementAccessInfos(
ElementAccessFeedback const& feedback,
ElementAccessFeedback const& processed, AccessMode access_mode,
ZoneVector<ElementAccessInfo>* access_infos) const;
PropertyAccessInfo ComputePropertyAccessInfo(Handle<Map> map,
@ -196,7 +196,7 @@ class AccessInfoFactory final {
private:
base::Optional<ElementAccessInfo> ConsolidateElementLoad(
ElementAccessFeedback const& feedback) const;
ElementAccessFeedback const& processed) const;
PropertyAccessInfo LookupSpecialFieldAccessor(Handle<Map> map,
Handle<Name> name) const;
PropertyAccessInfo LookupTransition(Handle<Map> map, Handle<Name> name,

View File

@ -245,12 +245,11 @@ class BytecodeGraphBuilder {
ForInMode GetForInMode(int operand_index);
// Helper function to compute call frequency from the recorded type
// feedback. Returns unknown if invocation count is unknown. Returns 0 if
// feedback is insufficient.
// feedback.
CallFrequency ComputeCallFrequency(int slot_id) const;
// Helper function to extract the speculation mode from the recorded type
// feedback. Returns kDisallowSpeculation if feedback is insufficient.
// feedback.
SpeculationMode GetSpeculationMode(int slot_id) const;
// Control flow plumbing.
@ -951,7 +950,7 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
bytecode_array_(bytecode_array),
feedback_vector_(feedback_vector),
type_hint_lowering_(
broker, jsgraph, feedback_vector,
jsgraph, feedback_vector.object(),
(flags & BytecodeGraphBuilderFlag::kBailoutOnUninitialized)
? JSTypeHintLowering::kBailoutOnUninitialized
: JSTypeHintLowering::kNoFlags),
@ -964,8 +963,7 @@ BytecodeGraphBuilder::BytecodeGraphBuilder(
bytecode_analysis_(broker_->GetBytecodeAnalysis(
bytecode_array.object(), osr_offset,
flags & BytecodeGraphBuilderFlag::kAnalyzeEnvironmentLiveness,
FLAG_concurrent_inlining ? SerializationPolicy::kAssumeSerialized
: SerializationPolicy::kSerializeIfNeeded)),
!FLAG_concurrent_inlining)),
environment_(nullptr),
osr_(!osr_offset.IsNone()),
currently_peeled_loop_offset_(-1),
@ -1018,7 +1016,6 @@ 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());
}
@ -2152,11 +2149,11 @@ void BytecodeGraphBuilder::BuildCall(ConvertReceiverMode receiver_mode,
PrepareEagerCheckpoint();
VectorSlotPair feedback = CreateVectorSlotPair(slot_id);
CallFrequency frequency = ComputeCallFrequency(slot_id);
SpeculationMode speculation_mode = GetSpeculationMode(slot_id);
const Operator* op = javascript()->Call(arg_count, frequency, feedback,
receiver_mode, speculation_mode);
CallFrequency frequency = ComputeCallFrequency(slot_id);
const Operator* op =
javascript()->Call(arg_count, frequency, feedback, receiver_mode,
GetSpeculationMode(slot_id));
JSTypeHintLowering::LoweringResult lowering = TryBuildSimplifiedCall(
op, args, static_cast<int>(arg_count), feedback.slot());
if (lowering.IsExit()) return;
@ -2337,6 +2334,7 @@ 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);
@ -2650,23 +2648,23 @@ void BytecodeGraphBuilder::BuildBinaryOp(const Operator* op) {
BinaryOperationHint BytecodeGraphBuilder::GetBinaryOperationHint(
int operand_index) {
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(operand_index);
FeedbackSource source(feedback_vector(), slot);
return broker()->GetFeedbackForBinaryOperation(source);
FeedbackNexus nexus(feedback_vector().object(), slot);
return nexus.GetBinaryOperationFeedback();
}
// Helper function to create compare operation hint from the recorded type
// feedback.
CompareOperationHint BytecodeGraphBuilder::GetCompareOperationHint() {
FeedbackSlot slot = bytecode_iterator().GetSlotOperand(1);
FeedbackSource source(feedback_vector(), slot);
return broker()->GetFeedbackForCompareOperation(source);
FeedbackNexus nexus(feedback_vector().object(), slot);
return nexus.GetCompareOperationFeedback();
}
// 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);
FeedbackSource source(feedback_vector(), slot);
switch (broker()->GetFeedbackForForIn(source)) {
FeedbackNexus nexus(feedback_vector().object(), slot);
switch (nexus.GetForInFeedback()) {
case ForInHint::kNone:
case ForInHint::kEnumCacheKeysAndIndices:
return ForInMode::kUseEnumCacheKeysAndIndices;
@ -2680,12 +2678,11 @@ ForInMode BytecodeGraphBuilder::GetForInMode(int operand_index) {
CallFrequency BytecodeGraphBuilder::ComputeCallFrequency(int slot_id) const {
if (invocation_frequency_.IsUnknown()) return CallFrequency();
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.
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.
return CallFrequency(0.0f);
} else {
return CallFrequency(feedback_frequency * invocation_frequency_.value());
@ -2693,11 +2690,9 @@ CallFrequency BytecodeGraphBuilder::ComputeCallFrequency(int slot_id) const {
}
SpeculationMode BytecodeGraphBuilder::GetSpeculationMode(int slot_id) const {
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();
FeedbackNexus nexus(feedback_vector().object(),
FeedbackVector::ToSlot(slot_id));
return nexus.GetSpeculationMode();
}
void BytecodeGraphBuilder::VisitBitwiseNot() {

View File

@ -35,8 +35,6 @@ 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.
@ -139,9 +137,8 @@ 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, SerializationPolicy policy =
SerializationPolicy::kAssumeSerialized) const;
base::Optional<ObjectRef> GetOwnConstantElement(uint32_t index,
bool serialize = false) const;
Isolate* isolate() const;
@ -271,8 +268,7 @@ 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,
SerializationPolicy policy =
SerializationPolicy::kAssumeSerialized) const;
bool serialize = false) const;
FixedArrayBaseRef elements() const;
void SerializeElements();
void EnsureElementsTenured();
@ -374,14 +370,10 @@ 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,
SerializationPolicy policy =
SerializationPolicy::kAssumeSerialized) const;
ContextRef previous(size_t* depth, bool serialize = false) const;
// Only returns a value if the index is valid for this ContextRef.
base::Optional<ObjectRef> get(
int index, SerializationPolicy policy =
SerializationPolicy::kAssumeSerialized) const;
base::Optional<ObjectRef> get(int index, bool serialize = false) const;
// We only serialize the ScopeInfo if certain Promise
// builtins are called.
@ -653,9 +645,8 @@ class FunctionTemplateInfoRef : public HeapObjectRef {
void SerializeCallCode();
base::Optional<CallHandlerInfoRef> call_code() const;
HolderLookupResult LookupHolderOfExpectedType(
MapRef receiver_map,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
HolderLookupResult LookupHolderOfExpectedType(MapRef receiver_map,
bool serialize);
};
class FixedArrayBaseRef : public HeapObjectRef {
@ -743,9 +734,8 @@ 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, SerializationPolicy policy =
SerializationPolicy::kAssumeSerialized) const;
base::Optional<ObjectRef> GetOwnCowElement(uint32_t index,
bool serialize = false) const;
};
class ScopeInfoRef : public HeapObjectRef {
@ -792,9 +782,8 @@ 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,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
JSArrayRef GetTemplateObject(ObjectRef description, FeedbackVectorRef vector,
FeedbackSlot slot, bool serialize = false);
void SerializeFunctionTemplateInfo();
base::Optional<FunctionTemplateInfoRef> function_template_info() const;
@ -869,9 +858,8 @@ 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, SerializationPolicy policy =
SerializationPolicy::kAssumeSerialized) const;
base::Optional<PropertyCellRef> GetPropertyCell(NameRef const& name,
bool serialize = false) const;
};
class CodeRef : public HeapObjectRef {
@ -890,6 +878,120 @@ 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

View File

@ -2848,7 +2848,8 @@ Reduction JSCallReducer::ReduceCallApiFunction(
// See if we can constant-fold the compatible receiver checks.
HolderLookupResult api_holder =
function_template_info.LookupHolderOfExpectedType(first_receiver_map);
function_template_info.LookupHolderOfExpectedType(first_receiver_map,
false);
if (api_holder.lookup == CallOptimization::kHolderNotFound)
return inference.NoChange();
@ -2878,7 +2879,8 @@ 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);
function_template_info.LookupHolderOfExpectedType(receiver_map,
false);
if (api_holder.lookup != holder_i.lookup) return inference.NoChange();
if (!(api_holder.holder.has_value() && holder_i.holder.has_value()))
@ -3225,6 +3227,13 @@ 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) {
@ -3343,18 +3352,19 @@ 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();
ProcessedFeedback const& feedback =
broker()->GetFeedbackForCall(FeedbackSource(p.feedback()));
if (feedback.IsInsufficient()) {
FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
if (nexus.IsUninitialized()) {
return ReduceSoftDeoptimize(
node, DeoptimizeReason::kInsufficientTypeFeedbackForCall);
}
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);
base::Optional<HeapObjectRef> feedback =
GetHeapObjectFeedback(broker(), nexus);
if (feedback.has_value() && ShouldUseCallICFeedback(target) &&
feedback->map().is_callable()) {
Node* target_function = jsgraph()->Constant(*feedback);
// Check that the {target} is still the {target_function}.
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), target,
@ -3757,10 +3767,6 @@ 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());
@ -3770,16 +3776,17 @@ 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()) {
ProcessedFeedback const& feedback =
broker()->GetFeedbackForCall(FeedbackSource(p.feedback()));
if (feedback.IsInsufficient()) {
FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
if (nexus.IsUninitialized()) {
return ReduceSoftDeoptimize(
node, DeoptimizeReason::kInsufficientTypeFeedbackForConstruct);
}
base::Optional<HeapObjectRef> feedback_target = feedback.AsCall().target();
if (feedback_target.has_value() && feedback_target->IsAllocationSite()) {
base::Optional<HeapObjectRef> feedback =
GetHeapObjectFeedback(broker(), nexus);
if (feedback.has_value() && feedback->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
@ -3804,12 +3811,12 @@ Reduction JSCallReducer::ReduceJSConstruct(Node* node) {
NodeProperties::ReplaceValueInput(node, array_function, 1);
NodeProperties::ChangeOp(
node, javascript()->CreateArray(
arity, feedback_target->AsAllocationSite().object()));
arity, feedback->AsAllocationSite().object()));
return Changed(node);
} else if (feedback_target.has_value() &&
} else if (feedback.has_value() &&
!HeapObjectMatcher(new_target).HasValue() &&
feedback_target->map().is_constructor()) {
Node* new_target_feedback = jsgraph()->Constant(*feedback_target);
feedback->map().is_constructor()) {
Node* new_target_feedback = jsgraph()->Constant(*feedback);
// Check that the {new_target} is still the {new_target_feedback}.
Node* check = graph()->NewNode(simplified()->ReferenceEqual(), new_target,
@ -6074,7 +6081,7 @@ Reduction JSCallReducer::ReducePromisePrototypeFinally(Node* node) {
}
Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) {
DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining);
DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining);
DCHECK_EQ(IrOpcode::kJSCall, node->opcode());
CallParameters const& p = CallParametersOf(node->op());
@ -6142,7 +6149,7 @@ Reduction JSCallReducer::ReducePromisePrototypeThen(Node* node) {
// ES section #sec-promise.resolve
Reduction JSCallReducer::ReducePromiseResolveTrampoline(Node* node) {
DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining);
DisallowHeapAccessIf no_heap_acess(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

View File

@ -9,7 +9,6 @@
#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"
@ -28,8 +27,8 @@ class ObjectRef;
std::ostream& operator<<(std::ostream& os, const ObjectRef& ref);
struct FeedbackSource {
FeedbackSource(Handle<FeedbackVector> vector_, FeedbackSlot slot_);
FeedbackSource(FeedbackVectorRef vector_, FeedbackSlot slot_);
FeedbackSource(Handle<FeedbackVector> vector_, FeedbackSlot slot_)
: vector(vector_), slot(slot_) {}
explicit FeedbackSource(FeedbackNexus const& nexus);
explicit FeedbackSource(VectorSlotPair const& pair);
@ -62,24 +61,20 @@ struct FeedbackSource {
broker->Trace() << __FUNCTION__ << ": missing " << x << '\n'; \
} while (false)
struct PropertyAccessTarget {
struct MapNameRefPair {
MapRef map;
NameRef name;
AccessMode mode;
struct Hash {
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));
size_t operator()(const MapNameRefPair& pair) const {
return base::hash_combine(pair.map.object().address(),
pair.name.object().address());
}
};
struct Equal {
bool operator()(const PropertyAccessTarget& lhs,
const PropertyAccessTarget& rhs) const {
return lhs.map.equals(rhs.map) && lhs.name.equals(rhs.name) &&
lhs.mode == rhs.mode;
bool operator()(const MapNameRefPair& lhs,
const MapNameRefPair& rhs) const {
return lhs.map.equals(rhs.map) && lhs.name.equals(rhs.name);
}
};
};
@ -120,49 +115,21 @@ 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;
ProcessedFeedback const* GetFeedback(FeedbackSource const& source) const;
// Convenience wrappers around GetFeedback.
GlobalAccessFeedback const* GetGlobalAccessFeedback(
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,
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;
bool analyze_liveness, bool serialize);
base::Optional<NameRef> GetNameFeedback(FeedbackNexus const& nexus);
@ -177,12 +144,8 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
MapRef map, CompilationDependencies* dependencies);
void CreateAccessInfoForLoadingThen(MapRef map,
CompilationDependencies* dependencies);
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);
void StorePropertyAccessInfoForLoad(MapRef map, NameRef name,
PropertyAccessInfo const& access_info);
std::ostream& Trace();
void IncrementTracingIndentation();
@ -193,23 +156,6 @@ 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();
@ -233,15 +179,12 @@ 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<PropertyAccessTarget, PropertyAccessInfo,
PropertyAccessTarget::Hash, PropertyAccessTarget::Equal>
property_access_infos_;
ZoneUnorderedMap<MapNameRefPair, PropertyAccessInfo, MapNameRefPair::Hash,
MapNameRefPair::Equal>
property_access_infos_for_load_;
static const size_t kMinimalRefsBucketCount = 8; // must be power of 2
static const size_t kInitialRefsBucketCount = 1024; // must be power of 2

View File

@ -52,6 +52,17 @@ 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(
@ -372,7 +383,9 @@ Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor(
}
Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining);
// TODO(neis): Eliminate heap accesses.
AllowHandleDereference allow_handle_dereference;
AllowHandleAllocation allow_handle_allocation;
DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode());
FeedbackParameter const& p = FeedbackParameterOf(node->op());
@ -390,13 +403,10 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
if (m.HasValue() && m.Ref(broker()).IsJSObject()) {
receiver = m.Ref(broker()).AsJSObject().object();
} else if (p.feedback().IsValid()) {
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();
FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
if (!nexus.GetConstructorFeedback().ToHandle(&receiver)) {
return NoChange();
}
} else {
return NoChange();
}
@ -992,88 +1002,69 @@ 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());
ProcessedFeedback const& processed =
broker()->GetFeedbackForGlobalAccess(FeedbackSource(p.feedback()));
if (processed.IsInsufficient()) return NoChange();
// 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();
GlobalAccessFeedback const& feedback = processed.AsGlobalAccess();
if (feedback.IsScriptContextSlot()) {
if (processed->IsScriptContextSlot()) {
Node* effect = NodeProperties::GetEffectInput(node);
Node* script_context = jsgraph()->Constant(feedback.script_context());
Node* script_context = jsgraph()->Constant(processed->script_context());
Node* value = effect =
graph()->NewNode(javascript()->LoadContext(0, feedback.slot_index(),
feedback.immutable()),
graph()->NewNode(javascript()->LoadContext(0, processed->slot_index(),
processed->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());
ProcessedFeedback const& processed =
broker()->GetFeedbackForGlobalAccess(FeedbackSource(p.feedback()));
if (processed.IsInsufficient()) return NoChange();
GlobalAccessFeedback const* processed =
FLAG_concurrent_inlining
? broker()->GetGlobalAccessFeedback(source)
: broker()->ProcessFeedbackForGlobalAccess(source);
if (processed == nullptr) return NoChange();
GlobalAccessFeedback const& feedback = processed.AsGlobalAccess();
if (feedback.IsScriptContextSlot()) {
if (feedback.immutable()) return NoChange();
if (processed->IsScriptContextSlot()) {
if (processed->immutable()) return NoChange();
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* script_context = jsgraph()->Constant(feedback.script_context());
Node* script_context = jsgraph()->Constant(processed->script_context());
effect =
graph()->NewNode(javascript()->StoreContext(0, feedback.slot_index()),
graph()->NewNode(javascript()->StoreContext(0, processed->slot_index()),
value, script_context, effect, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
} else if (feedback.IsPropertyCell()) {
}
if (processed->IsPropertyCell()) {
return ReduceGlobalAccess(node, nullptr, value, NameRef(broker(), p.name()),
AccessMode::kStore, nullptr,
feedback.property_cell());
} else {
DCHECK(feedback.IsMegamorphic());
return NoChange();
processed->property_cell());
}
}
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);
}
UNREACHABLE();
}
Reduction JSNativeContextSpecialization::ReduceNamedAccess(
@ -1092,13 +1083,10 @@ 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(
access_infos_for_feedback, access_mode, &access_infos)) {
feedback.access_infos(), access_mode, &access_infos)) {
return NoChange();
}
@ -1471,48 +1459,9 @@ 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& feedback) {
ElementAccessFeedback const& processed) {
DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining);
DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
node->opcode() == IrOpcode::kJSStoreProperty ||
@ -1525,34 +1474,30 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
Node* frame_state =
NodeProperties::FindFrameStateBefore(node, jsgraph()->Dead());
// 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();
AccessMode access_mode = processed.keyed_mode.access_mode();
if ((access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) &&
receiver->opcode() == IrOpcode::kHeapConstant) {
Reduction reduction = ReduceElementLoadFromHeapConstant(
node, index, access_mode, refined_feedback.keyed_mode().load_mode());
node, index, access_mode, processed.keyed_mode.load_mode());
if (reduction.Changed()) return reduction;
}
if (!refined_feedback.transition_groups().empty() &&
refined_feedback.HasOnlyStringMaps(broker())) {
if (HasOnlyStringMaps(broker(), processed.receiver_maps)) {
DCHECK(processed.transitions.empty());
return ReduceElementAccessOnString(node, index, value,
refined_feedback.keyed_mode());
processed.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(refined_feedback,
&access_infos) ||
access_infos.empty()) {
if (!access_info_factory.ComputeElementAccessInfos(processed, access_mode,
&access_infos)) {
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
@ -1571,7 +1516,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(feedback.keyed_mode().store_mode())) &&
IsGrowStoreMode(processed.keyed_mode.store_mode())) &&
!receiver_map.HasOnlyStablePrototypesWithFastElements(
&prototype_maps)) {
return NoChange();
@ -1642,7 +1587,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
// Access the actual element.
ValueEffectControl continuation =
BuildElementAccess(receiver, index, value, effect, control, access_info,
feedback.keyed_mode());
processed.keyed_mode);
value = continuation.value();
effect = continuation.effect();
control = continuation.control();
@ -1709,7 +1654,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
// Access the actual element.
ValueEffectControl continuation =
BuildElementAccess(this_receiver, this_index, this_value, this_effect,
this_control, access_info, feedback.keyed_mode());
this_control, access_info, processed.keyed_mode);
values.push_back(continuation.value());
effects.push_back(continuation.effect());
controls.push_back(continuation.control());
@ -1827,21 +1772,58 @@ Reduction JSNativeContextSpecialization::ReducePropertyAccess(
node->opcode() == IrOpcode::kJSStoreNamedOwn ||
node->opcode() == IrOpcode::kJSGetIterator);
ProcessedFeedback const& feedback =
broker()->GetFeedbackForPropertyAccess(source, access_mode, static_name);
switch (feedback.kind()) {
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()) {
case ProcessedFeedback::kInsufficient:
return ReduceSoftDeoptimize(
node,
DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
case ProcessedFeedback::kNamedAccess:
return ReduceNamedAccess(node, value, feedback.AsNamedAccess(),
return ReduceNamedAccess(node, value, *processed->AsNamedAccess(),
access_mode, key);
case ProcessedFeedback::kElementAccess:
DCHECK_EQ(feedback.AsElementAccess().keyed_mode().access_mode(),
access_mode);
return ReduceElementAccess(node, key, value, feedback.AsElementAccess());
default:
CHECK_EQ(processed->AsElementAccess()->keyed_mode.access_mode(),
access_mode);
return ReduceElementAccess(node, key, value,
*processed->AsElementAccess());
case ProcessedFeedback::kGlobalAccess:
UNREACHABLE();
}
}
@ -3253,9 +3235,47 @@ 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,
ZoneVector<Handle<Map>>* receiver_maps) const {
Node* receiver, Node* effect, MapHandles* receiver_maps) {
ZoneHandleSet<Map> maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMapsUnsafe(broker(), receiver, effect,
@ -3281,7 +3301,7 @@ bool JSNativeContextSpecialization::InferReceiverMaps(
}
MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverRootMap(
Node* receiver) const {
Node* receiver) {
HeapObjectMatcher m(receiver);
if (m.HasValue()) {
return handle(m.Value()->map().FindRootMap(isolate()), isolate());

View File

@ -213,25 +213,18 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
// code dependencies and might use the array protector cell.
bool CanTreatHoleAsUndefined(ZoneVector<Handle<Map>> const& 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);
// Extract receiver maps from {nexus} and filter based on {receiver} if
// possible.
bool ExtractReceiverMaps(Node* receiver, Node* effect,
FeedbackNexus const& nexus,
MapHandles* receiver_maps);
// Try to infer maps for the given {receiver} at the current {effect}.
bool InferReceiverMaps(Node* receiver, Node* effect,
ZoneVector<Handle<Map>>* receiver_maps) const;
MapHandles* receiver_maps);
// Try to infer a root map for the {receiver} independent of the current
// program location.
MaybeHandle<Map> InferReceiverRootMap(Node* receiver) const;
MaybeHandle<Map> InferReceiverRootMap(Node* receiver);
// Checks if we know at compile time that the {receiver} either definitely
// has the {prototype} in it's prototype chain, or the {receiver} definitely

View File

@ -6,7 +6,6 @@
#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"
@ -79,6 +78,16 @@ 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);
@ -230,52 +239,34 @@ 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:
BinaryOperationHint GetBinaryOperationHint() {
return lowering_->GetBinaryOperationHint(slot_);
}
CompareOperationHint GetCompareOperationHint() {
return lowering_->GetCompareOperationHint(slot_);
}
JSTypeHintLowering const* const lowering_;
Operator const* const op_;
const JSTypeHintLowering* lowering_;
const Operator* op_;
Node* left_;
Node* right_;
Node* const effect_;
Node* const control_;
FeedbackSlot const slot_;
Node* effect_;
Node* control_;
FeedbackSlot slot_;
};
JSTypeHintLowering::JSTypeHintLowering(JSHeapBroker* broker, JSGraph* jsgraph,
FeedbackVectorRef feedback_vector,
JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
Handle<FeedbackVector> feedback_vector,
Flags flags)
: broker_(broker),
jsgraph_(jsgraph),
flags_(flags),
feedback_vector_(feedback_vector) {}
: 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(
slot, effect, control,
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForUnaryOperation)) {
return LoweringResult::Exit(node);
}
@ -318,7 +309,9 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceUnaryOperation(
control, slot);
node = b.TryBuildNumberBinop();
if (!node) {
if (GetBinaryOperationHint(slot) == BinaryOperationHint::kBigInt) {
FeedbackNexus nexus(feedback_vector(), slot);
if (nexus.GetBinaryOperationFeedback() ==
BinaryOperationHint::kBigInt) {
const Operator* op = jsgraph()->simplified()->SpeculativeBigIntNegate(
BigIntOperationHint::kBigInt);
node = jsgraph()->graph()->NewNode(op, operand, effect, control);
@ -342,8 +335,10 @@ 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(
slot, effect, control,
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
return LoweringResult::Exit(node);
}
@ -356,8 +351,10 @@ 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(
slot, effect, control,
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
return LoweringResult::Exit(node);
}
@ -368,8 +365,10 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
break;
}
case IrOpcode::kJSInstanceOf: {
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
slot, effect, control,
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
return LoweringResult::Exit(node);
}
@ -388,8 +387,10 @@ 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(
slot, effect, control,
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForBinaryOperation)) {
return LoweringResult::Exit(node);
}
@ -417,8 +418,10 @@ 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(
slot, effect, control,
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
return LoweringResult::Exit(node);
}
@ -429,8 +432,10 @@ 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(
slot, effect, control,
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
return LoweringResult::Exit(node);
}
@ -440,9 +445,10 @@ 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(GetBinaryOperationHint(slot),
&hint)) {
if (BinaryOperationHintToNumberOperationHint(
nexus.GetBinaryOperationFeedback(), &hint)) {
Node* node = jsgraph()->graph()->NewNode(
jsgraph()->simplified()->SpeculativeToNumber(hint, VectorSlotPair()),
input, effect, control);
@ -456,8 +462,10 @@ 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(
slot, effect, control,
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForCall)) {
return LoweringResult::Exit(node);
}
@ -469,8 +477,10 @@ 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(
slot, effect, control,
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForConstruct)) {
return LoweringResult::Exit(node);
}
@ -483,8 +493,10 @@ 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(
slot, effect, control,
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
return LoweringResult::Exit(node);
}
@ -495,8 +507,10 @@ 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(
slot, effect, control,
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
return LoweringResult::Exit(node);
}
@ -510,8 +524,10 @@ 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(
slot, effect, control,
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
return LoweringResult::Exit(node);
}
@ -525,30 +541,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(
slot, effect, control,
nexus, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
return LoweringResult::Exit(node);
}
return LoweringResult::NoChange();
}
Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackSlot slot, Node* effect,
Node* control,
Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus const& nexus,
Node* effect, Node* control,
DeoptimizeReason reason) const {
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;
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;
}
} // namespace compiler

View File

@ -41,8 +41,8 @@ class JSTypeHintLowering {
enum Flag { kNoFlags = 0u, kBailoutOnUninitialized = 1u << 1 };
using Flags = base::Flags<Flag>;
JSTypeHintLowering(JSHeapBroker* broker, JSGraph* jsgraph,
FeedbackVectorRef feedback_vector, Flags flags);
JSTypeHintLowering(JSGraph* jsgraph, Handle<FeedbackVector> feedback_vector,
Flags flags);
// {LoweringResult} describes the result of lowering. The following outcomes
// are possible:
@ -153,22 +153,19 @@ 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_; }
FeedbackVectorRef const& feedback_vector() const { return feedback_vector_; }
const Handle<FeedbackVector>& feedback_vector() const {
return feedback_vector_;
}
JSHeapBroker* const broker_;
JSGraph* const jsgraph_;
JSGraph* jsgraph_;
Flags const flags_;
FeedbackVectorRef const feedback_vector_;
Handle<FeedbackVector> feedback_vector_;
DISALLOW_COPY_AND_ASSIGN(JSTypeHintLowering);
};

View File

@ -1,215 +0,0 @@
// 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

View File

@ -887,7 +887,8 @@ float FeedbackNexus::ComputeCallFrequency() {
double const invocation_count = vector().invocation_count();
double const call_count = GetCallCount();
if (invocation_count == 0.0) { // Prevent division by 0.
if (invocation_count == 0) {
// Prevent division by 0.
return 0.0f;
}
return static_cast<float>(call_count / invocation_count);