Commit 4b1af9fc authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

Reland "[turbofan] Various serializer/broker improvements"

This is a reland of 29585a06 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/+/1758319Reviewed-by: 's avatarGeorg Neis <neis@chromium.org>
Commit-Queue: Georg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63241}
parent 098f85a4
......@@ -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;
}
if (!GeneralizeElementsKind(elements_kind, map.elements_kind())
.To(&elements_kind)) {
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());
}
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);
......
This diff is collapsed.
......@@ -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
......
......@@ -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
......
This diff is collapsed.
......@@ -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);
};
......
// 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_
......@@ -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);
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment