Commit 29585a06 authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[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: 's avatarGeorg Neis <neis@chromium.org>
Cr-Commit-Position: refs/heads/master@{#63232}
parent 356b42f0
......@@ -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,15 +685,20 @@ 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)) {
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())
......@@ -704,6 +707,7 @@ base::Optional<ElementAccessInfo> AccessInfoFactory::ConsolidateElementLoad(
}
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,
......@@ -6081,7 +6074,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());
......@@ -6149,7 +6142,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);
......
......@@ -284,11 +284,13 @@ class JSObjectData : public JSReceiverData {
return object_create_map_;
}
ObjectData* GetOwnConstantElement(JSHeapBroker* broker, uint32_t index,
bool serialize);
ObjectData* GetOwnDataProperty(JSHeapBroker* broker,
Representation representation,
FieldIndex field_index, bool serialize);
ObjectData* GetOwnConstantElement(
JSHeapBroker* broker, uint32_t index,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
ObjectData* GetOwnDataProperty(
JSHeapBroker* broker, Representation representation,
FieldIndex field_index,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
// This method is only used to assert our invariants.
bool cow_or_empty_elements_tenured() const;
......@@ -370,12 +372,12 @@ ObjectRef GetOwnDataPropertyFromHeap(JSHeapBroker* broker,
ObjectData* JSObjectData::GetOwnConstantElement(JSHeapBroker* broker,
uint32_t index,
bool serialize) {
SerializationPolicy policy) {
for (auto const& p : own_constant_elements_) {
if (p.first == index) return p.second;
}
if (!serialize) {
if (policy == SerializationPolicy::kAssumeSerialized) {
TRACE_MISSING(broker, "knowledge about index " << index << " on " << this);
return nullptr;
}
......@@ -390,11 +392,11 @@ ObjectData* JSObjectData::GetOwnConstantElement(JSHeapBroker* broker,
ObjectData* JSObjectData::GetOwnDataProperty(JSHeapBroker* broker,
Representation representation,
FieldIndex field_index,
bool serialize) {
SerializationPolicy policy) {
auto p = own_properties_.find(field_index.property_index());
if (p != own_properties_.end()) return p->second;
if (!serialize) {
if (policy == SerializationPolicy::kAssumeSerialized) {
TRACE_MISSING(broker, "knowledge about property with index "
<< field_index.property_index() << " on "
<< this);
......@@ -607,12 +609,15 @@ class ContextData : public HeapObjectData {
// {previous} will return the closest valid context possible to desired
// {depth}, decrementing {depth} for each previous link successfully followed.
// If {serialize} is true, it will serialize contexts along the way.
ContextData* previous(JSHeapBroker* broker, size_t* depth, bool serialize);
ContextData* previous(
JSHeapBroker* broker, size_t* depth,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
// Returns nullptr if the slot index isn't valid or wasn't serialized
// (unless {serialize} is true).
ObjectData* GetSlot(JSHeapBroker* broker, int index, bool serialize);
// Returns nullptr if the slot index isn't valid or wasn't serialized,
// unless {policy} is {kSerializeIfNeeded}.
ObjectData* GetSlot(
JSHeapBroker* broker, int index,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
private:
ZoneMap<int, ObjectData*> slots_;
......@@ -624,10 +629,11 @@ ContextData::ContextData(JSHeapBroker* broker, ObjectData** storage,
: HeapObjectData(broker, storage, object), slots_(broker->zone()) {}
ContextData* ContextData::previous(JSHeapBroker* broker, size_t* depth,
bool serialize) {
SerializationPolicy policy) {
if (*depth == 0) return this;
if (serialize && previous_ == nullptr) {
if (policy == SerializationPolicy::kSerializeIfNeeded &&
previous_ == nullptr) {
TraceScope tracer(broker, this, "ContextData::previous");
Handle<Context> context = Handle<Context>::cast(object());
Object prev = context->unchecked_previous();
......@@ -638,20 +644,20 @@ ContextData* ContextData::previous(JSHeapBroker* broker, size_t* depth,
if (previous_ != nullptr) {
*depth = *depth - 1;
return previous_->previous(broker, depth, serialize);
return previous_->previous(broker, depth, policy);
}
return this;
}
ObjectData* ContextData::GetSlot(JSHeapBroker* broker, int index,
bool serialize) {
SerializationPolicy policy) {
CHECK_GE(index, 0);
auto search = slots_.find(index);
if (search != slots_.end()) {
return search->second;
}
if (serialize) {
if (policy == SerializationPolicy::kSerializeIfNeeded) {
Handle<Context> context = Handle<Context>::cast(object());
if (index < context->length()) {
TraceScope tracer(broker, this, "ContextData::GetSlot");
......@@ -711,8 +717,9 @@ class StringData : public NameData {
bool is_external_string() const { return is_external_string_; }
bool is_seq_string() const { return is_seq_string_; }
StringData* GetCharAsString(JSHeapBroker* broker, uint32_t index,
bool serialize);
StringData* GetCharAsString(
JSHeapBroker* broker, uint32_t index,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
private:
int const length_;
......@@ -761,14 +768,14 @@ class InternalizedStringData : public StringData {
};
StringData* StringData::GetCharAsString(JSHeapBroker* broker, uint32_t index,
bool serialize) {
SerializationPolicy policy) {
if (index >= static_cast<uint32_t>(length())) return nullptr;
for (auto const& p : chars_as_strings_) {
if (p.first == index) return p.second;
}
if (!serialize) {
if (policy == SerializationPolicy::kAssumeSerialized) {
TRACE_MISSING(broker, "knowledge about index " << index << " on " << this);
return nullptr;
}
......@@ -1542,8 +1549,9 @@ class JSArrayData : public JSObjectData {
void Serialize(JSHeapBroker* broker);
ObjectData* length() const { return length_; }
ObjectData* GetOwnElement(JSHeapBroker* broker, uint32_t index,
bool serialize);
ObjectData* GetOwnElement(
JSHeapBroker* broker, uint32_t index,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
private:
bool serialized_ = false;
......@@ -1572,12 +1580,12 @@ void JSArrayData::Serialize(JSHeapBroker* broker) {
}
ObjectData* JSArrayData::GetOwnElement(JSHeapBroker* broker, uint32_t index,
bool serialize) {
SerializationPolicy policy) {
for (auto const& p : own_elements_) {
if (p.first == index) return p.second;
}
if (!serialize) {
if (policy == SerializationPolicy::kAssumeSerialized) {
TRACE_MISSING(broker, "knowledge about index " << index << " on " << this);
return nullptr;
}
......@@ -1794,8 +1802,9 @@ class JSGlobalProxyData : public JSObjectData {
JSGlobalProxyData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSGlobalProxy> object);
PropertyCellData* GetPropertyCell(JSHeapBroker* broker, NameData* name,
bool serialize);
PropertyCellData* GetPropertyCell(
JSHeapBroker* broker, NameData* name,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
private:
// Properties that either
......@@ -1825,15 +1834,14 @@ base::Optional<PropertyCellRef> GetPropertyCellFromHeap(JSHeapBroker* broker,
}
} // namespace
PropertyCellData* JSGlobalProxyData::GetPropertyCell(JSHeapBroker* broker,
NameData* name,
bool serialize) {
PropertyCellData* JSGlobalProxyData::GetPropertyCell(
JSHeapBroker* broker, NameData* name, SerializationPolicy policy) {
CHECK_NOT_NULL(name);
for (auto const& p : properties_) {
if (p.first == name) return p.second;
}
if (!serialize) {
if (policy == SerializationPolicy::kAssumeSerialized) {
TRACE_MISSING(broker, "knowledge about global property " << name);
return nullptr;
}
......@@ -2139,7 +2147,8 @@ bool ObjectRef::equals(const ObjectRef& other) const {
Isolate* ObjectRef::isolate() const { return broker()->isolate(); }
ContextRef ContextRef::previous(size_t* depth, bool serialize) const {
ContextRef ContextRef::previous(size_t* depth,
SerializationPolicy policy) const {
DCHECK_NOT_NULL(depth);
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
......@@ -2152,10 +2161,11 @@ ContextRef ContextRef::previous(size_t* depth, bool serialize) const {
return ContextRef(broker(), handle(current, broker()->isolate()));
}
ContextData* current = this->data()->AsContext();
return ContextRef(broker(), current->previous(broker(), depth, serialize));
return ContextRef(broker(), current->previous(broker(), depth, policy));
}
base::Optional<ObjectRef> ContextRef::get(int index, bool serialize) const {
base::Optional<ObjectRef> ContextRef::get(int index,
SerializationPolicy policy) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
......@@ -2163,7 +2173,7 @@ base::Optional<ObjectRef> ContextRef::get(int index, bool serialize) const {
return ObjectRef(broker(), value);
}
ObjectData* optional_slot =
data()->AsContext()->GetSlot(broker(), index, serialize);
data()->AsContext()->GetSlot(broker(), index, policy);
if (optional_slot != nullptr) {
return ObjectRef(broker(), optional_slot);
}
......@@ -2184,7 +2194,7 @@ JSHeapBroker::JSHeapBroker(Isolate* isolate, Zone* broker_zone,
ais_for_loading_exec_(zone()),
ais_for_loading_has_instance_(zone()),
ais_for_loading_then_(zone()),
property_access_infos_for_load_(zone()) {
property_access_infos_(zone()) {
// Note that this initialization of the refs_ pointer with the minimal
// initial capacity is redundant in the normal use case (concurrent
// compilation enabled, standard objects to be serialized), as the map
......@@ -3125,7 +3135,7 @@ bool FunctionTemplateInfoRef::has_call_code() const {
BIMODAL_ACCESSOR_C(FunctionTemplateInfo, bool, accept_any_receiver)
HolderLookupResult FunctionTemplateInfoRef::LookupHolderOfExpectedType(
MapRef receiver_map, bool serialize) {
MapRef receiver_map, SerializationPolicy policy) {
const HolderLookupResult not_found;
if (broker()->mode() == JSHeapBroker::kDisabled) {
......@@ -3161,7 +3171,7 @@ HolderLookupResult FunctionTemplateInfoRef::LookupHolderOfExpectedType(
if (lookup_it != fti_data->known_receivers().cend()) {
return lookup_it->second;
}
if (!serialize) {
if (policy == SerializationPolicy::kAssumeSerialized) {
TRACE_BROKER_MISSING(broker(),
"holder for receiver with map " << receiver_map);
return not_found;
......@@ -3386,7 +3396,7 @@ Maybe<double> ObjectRef::OddballToNumber() const {
}
base::Optional<ObjectRef> ObjectRef::GetOwnConstantElement(
uint32_t index, bool serialize) const {
uint32_t index, SerializationPolicy policy) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
return (IsJSObject() || IsString())
? GetOwnElementFromHeap(broker(), object(), index, true)
......@@ -3395,9 +3405,9 @@ base::Optional<ObjectRef> ObjectRef::GetOwnConstantElement(
ObjectData* element = nullptr;
if (IsJSObject()) {
element =
data()->AsJSObject()->GetOwnConstantElement(broker(), index, serialize);
data()->AsJSObject()->GetOwnConstantElement(broker(), index, policy);
} else if (IsString()) {
element = data()->AsString()->GetCharAsString(broker(), index, serialize);
element = data()->AsString()->GetCharAsString(broker(), index, policy);
}
if (element == nullptr) return base::nullopt;
return ObjectRef(broker(), element);
......@@ -3405,26 +3415,26 @@ base::Optional<ObjectRef> ObjectRef::GetOwnConstantElement(
base::Optional<ObjectRef> JSObjectRef::GetOwnDataProperty(
Representation field_representation, FieldIndex index,
bool serialize) const {
SerializationPolicy policy) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
return GetOwnDataPropertyFromHeap(broker(),
Handle<JSObject>::cast(object()),
field_representation, index);
}
ObjectData* property = data()->AsJSObject()->GetOwnDataProperty(
broker(), field_representation, index, serialize);
broker(), field_representation, index, policy);
if (property == nullptr) return base::nullopt;
return ObjectRef(broker(), property);
}
base::Optional<ObjectRef> JSArrayRef::GetOwnCowElement(uint32_t index,
bool serialize) const {
base::Optional<ObjectRef> JSArrayRef::GetOwnCowElement(
uint32_t index, SerializationPolicy policy) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
if (!object()->elements().IsCowArray()) return base::nullopt;
return GetOwnElementFromHeap(broker(), object(), index, false);
}
if (serialize) {
if (policy == SerializationPolicy::kSerializeIfNeeded) {
data()->AsJSObject()->SerializeElements(broker());
} else if (!data()->AsJSObject()->serialized_elements()) {
TRACE(broker(), "'elements' on " << this);
......@@ -3433,7 +3443,7 @@ base::Optional<ObjectRef> JSArrayRef::GetOwnCowElement(uint32_t index,
if (!elements().map().IsFixedCowArrayMap()) return base::nullopt;
ObjectData* element =
data()->AsJSArray()->GetOwnElement(broker(), index, serialize);
data()->AsJSArray()->GetOwnElement(broker(), index, policy);
if (element == nullptr) return base::nullopt;
return ObjectRef(broker(), element);
}
......@@ -3710,10 +3720,9 @@ bool JSFunctionRef::IsSerializedForCompilation() const {
shared().IsSerializedForCompilation(feedback_vector());
}
JSArrayRef SharedFunctionInfoRef::GetTemplateObject(ObjectRef description,
FeedbackVectorRef vector,
FeedbackSlot slot,
bool serialize) {
JSArrayRef SharedFunctionInfoRef::GetTemplateObject(
ObjectRef description, FeedbackVectorRef vector, FeedbackSlot slot,
SerializationPolicy policy) {
// Look in the feedback vector for the array. A Smi indicates that it's
// not yet cached here.
ObjectRef candidate = vector.get(slot);
......@@ -3736,7 +3745,7 @@ JSArrayRef SharedFunctionInfoRef::GetTemplateObject(ObjectRef description,
JSArrayData* array = data()->AsSharedFunctionInfo()->GetTemplateObject(slot);
if (array != nullptr) return JSArrayRef(broker(), array);
CHECK(serialize);
CHECK_EQ(policy, SerializationPolicy::kSerializeIfNeeded);
CHECK(broker()->SerializingAllowed());
Handle<TemplateObjectDescription> tod =
......@@ -3868,13 +3877,13 @@ void FunctionTemplateInfoRef::SerializeCallCode() {
}
base::Optional<PropertyCellRef> JSGlobalProxyRef::GetPropertyCell(
NameRef const& name, bool serialize) const {
NameRef const& name, SerializationPolicy policy) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
return GetPropertyCellFromHeap(broker(), name.object());
}
PropertyCellData* property_cell_data =
data()->AsJSGlobalProxy()->GetPropertyCell(
broker(), name.data()->AsName(), serialize);
data()->AsJSGlobalProxy()->GetPropertyCell(broker(),
name.data()->AsName(), policy);
if (property_cell_data == nullptr) return base::nullopt;
return PropertyCellRef(broker(), property_cell_data);
}
......@@ -3893,6 +3902,52 @@ bool CanInlineElementAccess(MapRef const& map) {
return false;
}
KeyedAccessMode ElementAccessFeedback::keyed_mode() const {
return keyed_mode_;
}
ZoneVector<ElementAccessFeedback::TransitionGroup> const&
ElementAccessFeedback::transition_groups() const {
return transition_groups_;
}
ElementAccessFeedback const& ElementAccessFeedback::Refine(
ZoneVector<Handle<Map>> const& inferred_maps, Zone* zone) const {
ElementAccessFeedback& refined_feedback =
*new (zone) ElementAccessFeedback(zone, keyed_mode());
if (inferred_maps.empty()) return refined_feedback;
ZoneUnorderedSet<Handle<Map>, Handle<Map>::hash, Handle<Map>::equal_to>
inferred(zone);
inferred.insert(inferred_maps.begin(), inferred_maps.end());
for (auto const& group : transition_groups()) {
DCHECK(!group.empty());
TransitionGroup new_group(zone);
for (size_t i = 1; i < group.size(); ++i) {
Handle<Map> source = group[i];
if (inferred.find(source) != inferred.end()) {
new_group.push_back(source);
}
}
Handle<Map> target = group.front();
bool const keep_target =
inferred.find(target) != inferred.end() || new_group.size() > 1;
if (keep_target) {
new_group.push_back(target);
// The target must be at the front, the order of sources doesn't matter.
std::swap(new_group[0], new_group[new_group.size() - 1]);
}
if (!new_group.empty()) {
DCHECK(new_group.size() == 1 || new_group.front().equals(target));
refined_feedback.transition_groups_.push_back(std::move(new_group));
}
}
return refined_feedback;
}
InsufficientFeedback::InsufficientFeedback()
: ProcessedFeedback(kInsufficient) {}
......@@ -3901,6 +3956,10 @@ GlobalAccessFeedback::GlobalAccessFeedback(PropertyCellRef cell)
cell_or_context_(cell),
index_and_immutable_(0 /* doesn't matter */) {}
GlobalAccessFeedback::GlobalAccessFeedback()
: ProcessedFeedback(kGlobalAccess),
index_and_immutable_(0 /* doesn't matter */) {}
GlobalAccessFeedback::GlobalAccessFeedback(ContextRef script_context,
int slot_index, bool immutable)
: ProcessedFeedback(kGlobalAccess),
......@@ -3911,33 +3970,40 @@ GlobalAccessFeedback::GlobalAccessFeedback(ContextRef script_context,
DCHECK_EQ(this->immutable(), immutable);
}
bool GlobalAccessFeedback::IsMegamorphic() const {
return !cell_or_context_.has_value();
}
bool GlobalAccessFeedback::IsPropertyCell() const {
return cell_or_context_.IsPropertyCell();
return cell_or_context_.has_value() && cell_or_context_->IsPropertyCell();
}
bool GlobalAccessFeedback::IsScriptContextSlot() const {
return cell_or_context_.has_value() && cell_or_context_->IsContext();
}
PropertyCellRef GlobalAccessFeedback::property_cell() const {
DCHECK(IsPropertyCell());
return cell_or_context_.AsPropertyCell();
CHECK(IsPropertyCell());
return cell_or_context_->AsPropertyCell();
}
ContextRef GlobalAccessFeedback::script_context() const {
DCHECK(IsScriptContextSlot());
return cell_or_context_.AsContext();
CHECK(IsScriptContextSlot());
return cell_or_context_->AsContext();
}
int GlobalAccessFeedback::slot_index() const {
CHECK(IsScriptContextSlot());
DCHECK(IsScriptContextSlot());
return FeedbackNexus::SlotIndexBits::decode(index_and_immutable_);
}
bool GlobalAccessFeedback::immutable() const {
CHECK(IsScriptContextSlot());
DCHECK(IsScriptContextSlot());
return FeedbackNexus::ImmutabilityBit::decode(index_and_immutable_);
}
base::Optional<ObjectRef> GlobalAccessFeedback::GetConstantHint() const {
if (IsScriptContextSlot()) {
if (immutable()) return script_context().get(slot_index());
} else {
if (IsPropertyCell()) {
return property_cell().value();
} else if (IsScriptContextSlot() && immutable()) {
return script_context().get(slot_index());
} else {
return base::nullopt;
}
return {};
}
KeyedAccessMode KeyedAccessMode::FromNexus(FeedbackNexus const& nexus) {
......@@ -3998,54 +4064,36 @@ KeyedAccessMode::KeyedAccessMode(AccessMode access_mode,
ElementAccessFeedback::ElementAccessFeedback(Zone* zone,
KeyedAccessMode const& keyed_mode)
: ProcessedFeedback(kElementAccess),
receiver_maps(zone),
transitions(zone),
keyed_mode(keyed_mode) {}
keyed_mode_(keyed_mode),
transition_groups_(zone) {}
ElementAccessFeedback::MapIterator::MapIterator(
ElementAccessFeedback const& processed, JSHeapBroker* broker)
: processed_(processed), broker_(broker) {
CHECK_LT(processed.receiver_maps.size(),
std::numeric_limits<size_t>::max() - processed.transitions.size());
}
bool ElementAccessFeedback::MapIterator::done() const {
return index_ >=
processed_.receiver_maps.size() + processed_.transitions.size();
}
void ElementAccessFeedback::MapIterator::advance() { index_++; }
MapRef ElementAccessFeedback::MapIterator::current() const {
CHECK(!done());
size_t receiver_maps_size = processed_.receiver_maps.size();
Handle<Map> map;
if (index_ < receiver_maps_size) {
map = processed_.receiver_maps[index_];
} else {
map = processed_.transitions[index_ - receiver_maps_size].first;
bool ElementAccessFeedback::HasOnlyStringMaps(JSHeapBroker* broker) const {
for (auto const& group : transition_groups()) {
for (Handle<Map> map : group) {
if (!MapRef(broker, map).IsStringMap()) return false;
}
}
return MapRef(broker_, map);
return true;
}
ElementAccessFeedback::MapIterator ElementAccessFeedback::all_maps(
JSHeapBroker* broker) const {
return MapIterator(*this, broker);
}
NamedAccessFeedback::NamedAccessFeedback(NameRef const& name,
ZoneVector<Handle<Map>> const& maps)
: ProcessedFeedback(kNamedAccess), name_(name), maps_(maps) {}
NamedAccessFeedback::NamedAccessFeedback(
NameRef const& name, ZoneVector<PropertyAccessInfo> const& access_infos)
: ProcessedFeedback(kNamedAccess),
name_(name),
access_infos_(access_infos) {
CHECK(!access_infos.empty());
FeedbackSource::FeedbackSource(Handle<FeedbackVector> vector_,
FeedbackSlot slot_)
: vector(vector_), slot(slot_) {
DCHECK(!slot.IsInvalid());
}
FeedbackSource::FeedbackSource(FeedbackVectorRef vector_, FeedbackSlot slot_)
: FeedbackSource(vector_.object(), slot_) {}
FeedbackSource::FeedbackSource(FeedbackNexus const& nexus)
: vector(nexus.vector_handle()), slot(nexus.slot()) {}
: FeedbackSource(nexus.vector_handle(), nexus.slot()) {}
FeedbackSource::FeedbackSource(VectorSlotPair const& pair)
: vector(pair.vector()), slot(pair.slot()) {}
: FeedbackSource(pair.vector(), pair.slot()) {}
void JSHeapBroker::SetFeedback(FeedbackSource const& source,
ProcessedFeedback const* feedback) {
......@@ -4057,80 +4105,78 @@ bool JSHeapBroker::HasFeedback(FeedbackSource const& source) const {
return feedback_.find(source) != feedback_.end();
}
ProcessedFeedback const* JSHeapBroker::GetFeedback(
ProcessedFeedback const& JSHeapBroker::GetFeedback(
FeedbackSource const& source) const {
auto it = feedback_.find(source);
CHECK_NE(it, feedback_.end());
return it->second;
return *it->second;
}
GlobalAccessFeedback const* JSHeapBroker::GetGlobalAccessFeedback(
FeedbackSource const& source) const {
ProcessedFeedback const* feedback = GetFeedback(source);
if (feedback == nullptr) return nullptr;
CHECK_EQ(feedback->kind(), ProcessedFeedback::kGlobalAccess);
return static_cast<GlobalAccessFeedback const*>(feedback);
bool JSHeapBroker::FeedbackIsInsufficient(FeedbackSource const& source) const {
return FLAG_concurrent_inlining
? GetFeedback(source).IsInsufficient()
: FeedbackNexus(source.vector, source.slot).IsUninitialized();
}
ElementAccessFeedback const* JSHeapBroker::ProcessFeedbackMapsForElementAccess(
MapHandles const& maps, KeyedAccessMode const& keyed_mode) {
DCHECK(!maps.empty());
// Collect possible transition targets.
MapHandles possible_transition_targets;
possible_transition_targets.reserve(maps.size());
namespace {
template <class MapContainer>
MapHandles GetRelevantReceiverMaps(Isolate* isolate, MapContainer const& maps) {
MapHandles result;
for (Handle<Map> map : maps) {
if (CanInlineElementAccess(MapRef(this, map)) &&
IsFastElementsKind(map->elements_kind()) &&
GetInitialFastElementsKind() != map->elements_kind()) {
possible_transition_targets.push_back(map);
if (Map::TryUpdate(isolate, map).ToHandle(&map) &&
!map->is_abandoned_prototype_map()) {
DCHECK(!map->is_deprecated());
result.push_back(map);
}
}
return result;
}
} // namespace
ElementAccessFeedback* result =
new (zone()) ElementAccessFeedback(zone(), keyed_mode);
// Separate the actual receiver maps and the possible transition sources.
for (Handle<Map> map : maps) {
// Don't generate elements kind transitions from stable maps.
Map transition_target = map->is_stable()
? Map()
: map->FindElementsKindTransitionedMap(
isolate(), possible_transition_targets);
if (transition_target.is_null()) {
result->receiver_maps.push_back(map);
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForPropertyAccess(
FeedbackSource const& source, AccessMode mode,
base::Optional<NameRef> static_name) {
FeedbackNexus nexus(source.vector, source.slot);
if (nexus.IsUninitialized()) return *new (zone()) InsufficientFeedback();
MapHandles maps;
nexus.ExtractMaps(&maps);
DCHECK_NE(nexus.ic_state(), PREMONOMORPHIC);
if (!maps.empty()) {
maps = GetRelevantReceiverMaps(isolate(), maps);
if (maps.empty()) return *new (zone()) InsufficientFeedback();
}
base::Optional<NameRef> name =
static_name.has_value() ? static_name : GetNameFeedback(nexus);
if (name.has_value()) {
return *new (zone()) NamedAccessFeedback(
*name, ZoneVector<Handle<Map>>(maps.begin(), maps.end(), zone()));
} else if (nexus.GetKeyType() == ELEMENT && !maps.empty()) {
return ProcessFeedbackMapsForElementAccess(
maps, KeyedAccessMode::FromNexus(nexus));
} else {
result->transitions.emplace_back(map,
handle(transition_target, isolate()));
}
// No actionable feedback.
DCHECK(maps.empty());
DCHECK(nexus.IsMegamorphic() || nexus.GetFeedback().IsCleared());
// TODO(neis): Investigate if we really want to treat cleared the same as
// megamorphic (also for global accesses).
// TODO(neis): Using ElementAccessFeedback here is kind of an abuse.
return *new (zone())
ElementAccessFeedback(zone(), KeyedAccessMode::FromNexus(nexus));
}
#ifdef ENABLE_SLOW_DCHECKS
// No transition sources appear in {receiver_maps}.
// All transition targets appear in {receiver_maps}.
for (auto& transition : result->transitions) {
CHECK(std::none_of(
result->receiver_maps.cbegin(), result->receiver_maps.cend(),
[&](Handle<Map> map) { return map.equals(transition.first); }));
CHECK(std::any_of(
result->receiver_maps.cbegin(), result->receiver_maps.cend(),
[&](Handle<Map> map) { return map.equals(transition.second); }));
}
#endif
CHECK(!result->receiver_maps.empty());
return result;
}
GlobalAccessFeedback const* JSHeapBroker::ProcessFeedbackForGlobalAccess(
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForGlobalAccess(
FeedbackSource const& source) {
FeedbackNexus nexus(source.vector, source.slot);
DCHECK(nexus.kind() == FeedbackSlotKind::kLoadGlobalInsideTypeof ||
nexus.kind() == FeedbackSlotKind::kLoadGlobalNotInsideTypeof ||
nexus.kind() == FeedbackSlotKind::kStoreGlobalSloppy ||
nexus.kind() == FeedbackSlotKind::kStoreGlobalStrict);
if (nexus.IsUninitialized()) return *new (zone()) InsufficientFeedback();
if (nexus.ic_state() != MONOMORPHIC || nexus.GetFeedback()->IsCleared()) {
return nullptr;
return *new (zone()) GlobalAccessFeedback();
}
Handle<Object> feedback_value(nexus.GetFeedback()->GetHeapObjectOrSmi(),
......@@ -4155,9 +4201,10 @@ GlobalAccessFeedback const* JSHeapBroker::ProcessFeedbackForGlobalAccess(
}
ContextRef context_ref(this, context);
if (immutable) {
context_ref.get(context_slot_index, true);
context_ref.get(context_slot_index,
SerializationPolicy::kSerializeIfNeeded);
}
return new (zone())
return *new (zone())
GlobalAccessFeedback(context_ref, context_slot_index, immutable);
}
......@@ -4166,7 +4213,251 @@ GlobalAccessFeedback const* JSHeapBroker::ProcessFeedbackForGlobalAccess(
// object and the feedback is the cell holding its value.
PropertyCellRef cell(this, Handle<PropertyCell>::cast(feedback_value));
cell.Serialize();
return new (zone()) GlobalAccessFeedback(cell);
return *new (zone()) GlobalAccessFeedback(cell);
}
BinaryOperationHint JSHeapBroker::ReadFeedbackForBinaryOperation(
FeedbackSource const& source) const {
return FeedbackNexus(source.vector, source.slot).GetBinaryOperationFeedback();
}
CompareOperationHint JSHeapBroker::ReadFeedbackForCompareOperation(
FeedbackSource const& source) const {
return FeedbackNexus(source.vector, source.slot)
.GetCompareOperationFeedback();
}
ForInHint JSHeapBroker::ReadFeedbackForForIn(
FeedbackSource const& source) const {
return FeedbackNexus(source.vector, source.slot).GetForInFeedback();
}
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForInstanceOf(
FeedbackSource const& source) {
FeedbackNexus nexus(source.vector, source.slot);
if (nexus.IsUninitialized()) return *new (zone()) InsufficientFeedback();
base::Optional<JSObjectRef> optional_constructor;
{
MaybeHandle<JSObject> maybe_constructor = nexus.GetConstructorFeedback();
Handle<JSObject> constructor;
if (maybe_constructor.ToHandle(&constructor)) {
optional_constructor = JSObjectRef(this, constructor);
}
}
return *new (zone()) InstanceOfFeedback(optional_constructor);
}
ProcessedFeedback const& JSHeapBroker::ReadFeedbackForCall(
FeedbackSource const& source) {
FeedbackNexus nexus(source.vector, source.slot);
if (nexus.IsUninitialized()) return *new (zone()) InsufficientFeedback();
base::Optional<HeapObjectRef> target_ref;
{
MaybeObject maybe_target = nexus.GetFeedback();
HeapObject target_object;
if (maybe_target->GetHeapObject(&target_object)) {
target_ref = HeapObjectRef(this, handle(target_object, isolate()));
}
}
float frequency = nexus.ComputeCallFrequency();
SpeculationMode mode = nexus.GetSpeculationMode();
return *new (zone()) CallFeedback(target_ref, frequency, mode);
}
BinaryOperationHint JSHeapBroker::GetFeedbackForBinaryOperation(
FeedbackSource const& source) const {
if (!FLAG_concurrent_inlining) return ReadFeedbackForBinaryOperation(source);
ProcessedFeedback const& feedback = GetFeedback(source);
return feedback.IsInsufficient() ? BinaryOperationHint::kNone
: feedback.AsBinaryOperation().value();
}
CompareOperationHint JSHeapBroker::GetFeedbackForCompareOperation(
FeedbackSource const& source) const {
if (!FLAG_concurrent_inlining) return ReadFeedbackForCompareOperation(source);
ProcessedFeedback const& feedback = GetFeedback(source);
return feedback.IsInsufficient() ? CompareOperationHint::kNone
: feedback.AsCompareOperation().value();
}
ForInHint JSHeapBroker::GetFeedbackForForIn(
FeedbackSource const& source) const {
if (!FLAG_concurrent_inlining) return ReadFeedbackForForIn(source);
ProcessedFeedback const& feedback = GetFeedback(source);
return feedback.IsInsufficient() ? ForInHint::kNone
: feedback.AsForIn().value();
}
ProcessedFeedback const& JSHeapBroker::GetFeedbackForPropertyAccess(
FeedbackSource const& source, AccessMode mode,
base::Optional<NameRef> static_name) {
return FLAG_concurrent_inlining
? GetFeedback(source)
: ReadFeedbackForPropertyAccess(source, mode, static_name);
}
ProcessedFeedback const& JSHeapBroker::GetFeedbackForInstanceOf(
FeedbackSource const& source) {
return FLAG_concurrent_inlining ? GetFeedback(source)
: ReadFeedbackForInstanceOf(source);
}
ProcessedFeedback const& JSHeapBroker::GetFeedbackForCall(
FeedbackSource const& source) {
return FLAG_concurrent_inlining ? GetFeedback(source)
: ReadFeedbackForCall(source);
}
ProcessedFeedback const& JSHeapBroker::GetFeedbackForGlobalAccess(
FeedbackSource const& source) {
return FLAG_concurrent_inlining ? GetFeedback(source)
: ReadFeedbackForGlobalAccess(source);
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForBinaryOperation(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
BinaryOperationHint hint = ReadFeedbackForBinaryOperation(source);
ProcessedFeedback const* feedback;
if (hint == BinaryOperationHint::kNone) {
feedback = new (zone()) InsufficientFeedback();
} else {
feedback = new (zone()) BinaryOperationFeedback(hint);
}
SetFeedback(source, feedback);
return *feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForCompareOperation(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
CompareOperationHint hint = ReadFeedbackForCompareOperation(source);
ProcessedFeedback const* feedback;
if (hint == CompareOperationHint::kNone) {
feedback = new (zone()) InsufficientFeedback();
} else {
feedback = new (zone()) CompareOperationFeedback(hint);
}
SetFeedback(source, feedback);
return *feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForForIn(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ForInHint hint = ReadFeedbackForForIn(source);
ProcessedFeedback const* feedback;
if (hint == ForInHint::kNone) {
feedback = new (zone()) InsufficientFeedback();
} else {
feedback = new (zone()) ForInFeedback(hint);
}
SetFeedback(source, feedback);
return *feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForPropertyAccess(
FeedbackSource const& source, AccessMode mode,
base::Optional<NameRef> static_name) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback =
ReadFeedbackForPropertyAccess(source, mode, static_name);
SetFeedback(source, &feedback);
return feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForInstanceOf(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback = ReadFeedbackForInstanceOf(source);
SetFeedback(source, &feedback);
return feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForCall(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback = ReadFeedbackForCall(source);
SetFeedback(source, &feedback);
return feedback;
}
ProcessedFeedback const& JSHeapBroker::ProcessFeedbackForGlobalAccess(
FeedbackSource const& source) {
if (HasFeedback(source)) return GetFeedback(source);
ProcessedFeedback const& feedback = ReadFeedbackForGlobalAccess(source);
SetFeedback(source, &feedback);
return feedback;
}
ElementAccessFeedback const& JSHeapBroker::ProcessFeedbackMapsForElementAccess(
MapHandles const& maps, KeyedAccessMode const& keyed_mode) {
DCHECK(!maps.empty());
// Collect possible transition targets.
MapHandles possible_transition_targets;
possible_transition_targets.reserve(maps.size());
for (Handle<Map> map : maps) {
if (CanInlineElementAccess(MapRef(this, map)) &&
IsFastElementsKind(map->elements_kind()) &&
GetInitialFastElementsKind() != map->elements_kind()) {
possible_transition_targets.push_back(map);
}
}
using TransitionGroup = ElementAccessFeedback::TransitionGroup;
ZoneUnorderedMap<Handle<Map>, TransitionGroup, Handle<Map>::hash,
Handle<Map>::equal_to>
transition_groups(zone());
// Separate the actual receiver maps and the possible transition sources.
for (Handle<Map> map : maps) {
// Don't generate elements kind transitions from stable maps.
Map transition_target = map->is_stable()
? Map()
: map->FindElementsKindTransitionedMap(
isolate(), possible_transition_targets);
if (transition_target.is_null()) {
TransitionGroup group(1, map, zone());
transition_groups.insert({map, group});
} else {
Handle<Map> target(transition_target, isolate());
TransitionGroup new_group(1, target, zone());
TransitionGroup& actual_group =
transition_groups.insert({target, new_group}).first->second;
actual_group.push_back(map);
}
}
ElementAccessFeedback* result =
new (zone()) ElementAccessFeedback(zone(), keyed_mode);
for (auto entry : transition_groups) {
result->AddGroup(std::move(entry.second));
}
CHECK(!result->transition_groups().empty());
return *result;
}
void ElementAccessFeedback::AddGroup(TransitionGroup&& group) {
CHECK(!group.empty());
transition_groups_.push_back(std::move(group));
#ifdef ENABLE_SLOW_DCHECKS
// Check that each of the group's maps occurs exactly once in the whole
// feedback. This implies that "a source is not a target".
for (Handle<Map> map : group) {
int count = 0;
for (TransitionGroup const& some_group : transition_groups()) {
count += std::count_if(
some_group.begin(), some_group.end(),
[&](Handle<Map> some_map) { return some_map.equals(map); });
}
CHECK_EQ(count, 1);
}
#endif
}
std::ostream& operator<<(std::ostream& os, const ObjectRef& ref) {
......@@ -4246,33 +4537,76 @@ PropertyAccessInfo const& JSHeapBroker::CreateAccessInfoForLoadingExec(
return ais_for_loading_exec_.insert({map, access_info}).first->second;
}
void JSHeapBroker::StorePropertyAccessInfoForLoad(
MapRef map, NameRef name, PropertyAccessInfo const& access_info) {
MapNameRefPair pair({map, name});
auto it = property_access_infos_for_load_.find(pair);
if (it != property_access_infos_for_load_.end()) {
return;
PropertyAccessInfo JSHeapBroker::GetPropertyAccessInfo(
MapRef map, NameRef name, AccessMode access_mode,
CompilationDependencies* dependencies, SerializationPolicy policy) {
PropertyAccessTarget target({map, name, access_mode});
auto it = property_access_infos_.find(target);
if (it != property_access_infos_.end()) return it->second;
if (policy == SerializationPolicy::kAssumeSerialized) {
TRACE_BROKER_MISSING(this, "PropertyAccessInfo for "
<< access_mode << " of property " << name
<< " on map " << map);
return PropertyAccessInfo::Invalid(zone());
}
auto inserted_ai =
property_access_infos_for_load_.insert(std::make_pair(pair, access_info));
DCHECK(inserted_ai.second);
USE(inserted_ai);
AccessInfoFactory factory(this, dependencies, zone());
PropertyAccessInfo access_info = factory.ComputePropertyAccessInfo(
map.object(), name.object(), access_mode);
if (FLAG_concurrent_inlining) {
CHECK(SerializingAllowed());
TRACE(this, "Storing PropertyAccessInfo for "
<< access_mode << " of property " << name << " on map "
<< map);
property_access_infos_.insert({target, access_info});
}
return access_info;
}
ElementAccessFeedback const* ProcessedFeedback::AsElementAccess() const {
BinaryOperationFeedback const& ProcessedFeedback::AsBinaryOperation() const {
CHECK_EQ(kBinaryOperation, kind());
return *static_cast<BinaryOperationFeedback const*>(this);
}
CallFeedback const& ProcessedFeedback::AsCall() const {
CHECK_EQ(kCall, kind());
return *static_cast<CallFeedback const*>(this);
}
CompareOperationFeedback const& ProcessedFeedback::AsCompareOperation() const {
CHECK_EQ(kCompareOperation, kind());
return *static_cast<CompareOperationFeedback const*>(this);
}
ElementAccessFeedback const& ProcessedFeedback::AsElementAccess() const {
CHECK_EQ(kElementAccess, kind());
return static_cast<ElementAccessFeedback const*>(this);
return *static_cast<ElementAccessFeedback const*>(this);
}
ForInFeedback const& ProcessedFeedback::AsForIn() const {
CHECK_EQ(kForIn, kind());
return *static_cast<ForInFeedback const*>(this);
}
GlobalAccessFeedback const& ProcessedFeedback::AsGlobalAccess() const {
CHECK_EQ(kGlobalAccess, kind());
return *static_cast<GlobalAccessFeedback const*>(this);
}
InstanceOfFeedback const& ProcessedFeedback::AsInstanceOf() const {
CHECK_EQ(kInstanceOf, kind());
return *static_cast<InstanceOfFeedback const*>(this);
}
NamedAccessFeedback const* ProcessedFeedback::AsNamedAccess() const {
NamedAccessFeedback const& ProcessedFeedback::AsNamedAccess() const {
CHECK_EQ(kNamedAccess, kind());
return static_cast<NamedAccessFeedback const*>(this);
return *static_cast<NamedAccessFeedback const*>(this);
}
BytecodeAnalysis const& JSHeapBroker::GetBytecodeAnalysis(
Handle<BytecodeArray> bytecode_array, BailoutId osr_bailout_id,
bool analyze_liveness, bool serialize) {
bool analyze_liveness, SerializationPolicy policy) {
ObjectData* bytecode_array_data = GetData(bytecode_array);
CHECK_NOT_NULL(bytecode_array_data);
......@@ -4292,7 +4626,7 @@ BytecodeAnalysis const& JSHeapBroker::GetBytecodeAnalysis(
return *it->second;
}
CHECK(serialize);
CHECK_EQ(policy, SerializationPolicy::kSerializeIfNeeded);
BytecodeAnalysis* analysis = new (zone()) BytecodeAnalysis(
bytecode_array, zone(), osr_bailout_id, analyze_liveness);
DCHECK_EQ(analysis->osr_bailout_id(), osr_bailout_id);
......
......@@ -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,
void StorePropertyAccessInfo(MapRef map, NameRef name, AccessMode mode,
PropertyAccessInfo const& access_info);
PropertyAccessInfo GetPropertyAccessInfo(
MapRef map, NameRef name, AccessMode access_mode,
CompilationDependencies* dependencies,
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
std::ostream& Trace();
void IncrementTracingIndentation();
......@@ -156,6 +193,23 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
friend class ObjectRef;
friend class ObjectData;
// Bottleneck FeedbackNexus access here, for storage in the broker
// or on-the-fly usage elsewhere in the compiler.
ForInHint ReadFeedbackForForIn(FeedbackSource const& source) const;
CompareOperationHint ReadFeedbackForCompareOperation(
FeedbackSource const& source) const;
BinaryOperationHint ReadFeedbackForBinaryOperation(
FeedbackSource const& source) const;
ProcessedFeedback const& ReadFeedbackForCall(FeedbackSource const& source);
ProcessedFeedback const& ReadFeedbackForGlobalAccess(
FeedbackSource const& source);
ProcessedFeedback const& ReadFeedbackForInstanceOf(
FeedbackSource const& source);
ProcessedFeedback const& ReadFeedbackForPropertyAccess(
FeedbackSource const& source, AccessMode mode,
base::Optional<NameRef> static_name);
void SerializeShareableObjects();
void CollectArrayAndObjectPrototypes();
......@@ -179,12 +233,15 @@ class V8_EXPORT_PRIVATE JSHeapBroker {
typedef ZoneUnorderedMap<MapRef, PropertyAccessInfo, ObjectRef::Hash,
ObjectRef::Equal>
MapToAccessInfos;
// TODO(neis): Replaceby uses of property_access_infos_.
MapToAccessInfos ais_for_loading_exec_;
MapToAccessInfos ais_for_loading_has_instance_;
MapToAccessInfos ais_for_loading_then_;
ZoneUnorderedMap<MapNameRefPair, PropertyAccessInfo, MapNameRefPair::Hash,
MapNameRefPair::Equal>
property_access_infos_for_load_;
ZoneUnorderedMap<PropertyAccessTarget, PropertyAccessInfo,
PropertyAccessTarget::Hash, PropertyAccessTarget::Equal>
property_access_infos_;
static const size_t kMinimalRefsBucketCount = 8; // must be power of 2
static const size_t kInitialRefsBucketCount = 1024; // must be power of 2
......
......@@ -52,17 +52,6 @@ bool HasOnlyJSArrayMaps(JSHeapBroker* broker,
return true;
}
void TryUpdateThenDropDeprecated(Isolate* isolate, MapHandles* maps) {
for (auto it = maps->begin(); it != maps->end();) {
if (Map::TryUpdate(isolate, *it).ToHandle(&*it)) {
DCHECK(!(*it)->is_deprecated());
++it;
} else {
it = maps->erase(it);
}
}
}
} // namespace
JSNativeContextSpecialization::JSNativeContextSpecialization(
......@@ -383,9 +372,7 @@ Reduction JSNativeContextSpecialization::ReduceJSGetSuperConstructor(
}
Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
// TODO(neis): Eliminate heap accesses.
AllowHandleDereference allow_handle_dereference;
AllowHandleAllocation allow_handle_allocation;
DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining);
DCHECK_EQ(IrOpcode::kJSInstanceOf, node->opcode());
FeedbackParameter const& p = FeedbackParameterOf(node->op());
......@@ -403,10 +390,13 @@ Reduction JSNativeContextSpecialization::ReduceJSInstanceOf(Node* node) {
if (m.HasValue() && m.Ref(broker()).IsJSObject()) {
receiver = m.Ref(broker()).AsJSObject().object();
} else if (p.feedback().IsValid()) {
FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
if (!nexus.GetConstructorFeedback().ToHandle(&receiver)) {
return NoChange();
}
ProcessedFeedback const& feedback =
broker()->GetFeedbackForInstanceOf(FeedbackSource(p.feedback()));
if (feedback.IsInsufficient()) return NoChange();
base::Optional<JSObjectRef> maybe_receiver =
feedback.AsInstanceOf().value();
if (!maybe_receiver.has_value()) return NoChange();
receiver = maybe_receiver->object();
} else {
return NoChange();
}
......@@ -1002,69 +992,88 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) {
DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining);
DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
LoadGlobalParameters const& p = LoadGlobalParametersOf(node->op());
if (!p.feedback().IsValid()) return NoChange();
FeedbackSource source(p.feedback());
// TODO(neis): Make consistent with other feedback processing code.
GlobalAccessFeedback const* processed =
FLAG_concurrent_inlining
? broker()->GetGlobalAccessFeedback(source)
: broker()->ProcessFeedbackForGlobalAccess(source);
if (processed == nullptr) return NoChange();
ProcessedFeedback const& processed =
broker()->GetFeedbackForGlobalAccess(FeedbackSource(p.feedback()));
if (processed.IsInsufficient()) return NoChange();
if (processed->IsScriptContextSlot()) {
GlobalAccessFeedback const& feedback = processed.AsGlobalAccess();
if (feedback.IsScriptContextSlot()) {
Node* effect = NodeProperties::GetEffectInput(node);
Node* script_context = jsgraph()->Constant(processed->script_context());
Node* script_context = jsgraph()->Constant(feedback.script_context());
Node* value = effect =
graph()->NewNode(javascript()->LoadContext(0, processed->slot_index(),
processed->immutable()),
graph()->NewNode(javascript()->LoadContext(0, feedback.slot_index(),
feedback.immutable()),
script_context, effect);
ReplaceWithValue(node, value, effect);
return Replace(value);
} else if (feedback.IsPropertyCell()) {
return ReduceGlobalAccess(node, nullptr, nullptr,
NameRef(broker(), p.name()), AccessMode::kLoad,
nullptr, feedback.property_cell());
} else {
DCHECK(feedback.IsMegamorphic());
return NoChange();
}
CHECK(processed->IsPropertyCell());
return ReduceGlobalAccess(node, nullptr, nullptr, NameRef(broker(), p.name()),
AccessMode::kLoad, nullptr,
processed->property_cell());
}
Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) {
DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining);
DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
Node* value = NodeProperties::GetValueInput(node, 0);
StoreGlobalParameters const& p = StoreGlobalParametersOf(node->op());
if (!p.feedback().IsValid()) return NoChange();
FeedbackSource source(p.feedback());
GlobalAccessFeedback const* processed =
FLAG_concurrent_inlining
? broker()->GetGlobalAccessFeedback(source)
: broker()->ProcessFeedbackForGlobalAccess(source);
if (processed == nullptr) return NoChange();
ProcessedFeedback const& processed =
broker()->GetFeedbackForGlobalAccess(FeedbackSource(p.feedback()));
if (processed.IsInsufficient()) return NoChange();
if (processed->IsScriptContextSlot()) {
if (processed->immutable()) return NoChange();
GlobalAccessFeedback const& feedback = processed.AsGlobalAccess();
if (feedback.IsScriptContextSlot()) {
if (feedback.immutable()) return NoChange();
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* script_context = jsgraph()->Constant(processed->script_context());
Node* script_context = jsgraph()->Constant(feedback.script_context());
effect =
graph()->NewNode(javascript()->StoreContext(0, processed->slot_index()),
graph()->NewNode(javascript()->StoreContext(0, feedback.slot_index()),
value, script_context, effect, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
if (processed->IsPropertyCell()) {
} else if (feedback.IsPropertyCell()) {
return ReduceGlobalAccess(node, nullptr, value, NameRef(broker(), p.name()),
AccessMode::kStore, nullptr,
processed->property_cell());
feedback.property_cell());
} else {
DCHECK(feedback.IsMegamorphic());
return NoChange();
}
}
UNREACHABLE();
void JSNativeContextSpecialization::FilterMapsAndGetPropertyAccessInfos(
NamedAccessFeedback const& feedback, AccessMode access_mode, Node* receiver,
Node* effect, ZoneVector<PropertyAccessInfo>* access_infos) {
ZoneVector<Handle<Map>> receiver_maps(zone());
// Either infer maps from the graph or use the feedback.
if (!InferReceiverMaps(receiver, effect, &receiver_maps)) {
receiver_maps = feedback.maps();
}
RemoveImpossibleReceiverMaps(receiver, &receiver_maps);
for (Handle<Map> map_handle : receiver_maps) {
MapRef map(broker(), map_handle);
CHECK(!map.is_deprecated());
PropertyAccessInfo access_info = broker()->GetPropertyAccessInfo(
map, feedback.name(), access_mode, dependencies(),
FLAG_concurrent_inlining ? SerializationPolicy::kAssumeSerialized
: SerializationPolicy::kSerializeIfNeeded);
access_infos->push_back(access_info);
}
}
Reduction JSNativeContextSpecialization::ReduceNamedAccess(
......@@ -1083,10 +1092,13 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
ZoneVector<PropertyAccessInfo> access_infos_for_feedback(zone());
ZoneVector<PropertyAccessInfo> access_infos(zone());
FilterMapsAndGetPropertyAccessInfos(feedback, access_mode, receiver, effect,
&access_infos_for_feedback);
AccessInfoFactory access_info_factory(broker(), dependencies(), zone());
if (!access_info_factory.FinalizePropertyAccessInfos(
feedback.access_infos(), access_mode, &access_infos)) {
access_infos_for_feedback, access_mode, &access_infos)) {
return NoChange();
}
......@@ -1459,9 +1471,48 @@ base::Optional<JSTypedArrayRef> GetTypedArrayConstant(JSHeapBroker* broker,
}
} // namespace
void JSNativeContextSpecialization::RemoveImpossibleReceiverMaps(
Node* receiver, ZoneVector<Handle<Map>>* receiver_maps) const {
// TODO(neis): Decide what to do about this for concurrent inlining.
AllowHandleAllocation allow_handle_allocation;
AllowHandleDereference allow_handle_dereference;
Handle<Map> root_map;
if (InferReceiverRootMap(receiver).ToHandle(&root_map)) {
DCHECK(!root_map->is_abandoned_prototype_map());
Isolate* isolate = this->isolate();
receiver_maps->erase(
std::remove_if(receiver_maps->begin(), receiver_maps->end(),
[root_map, isolate](Handle<Map> map) {
return map->is_abandoned_prototype_map() ||
map->FindRootMap(isolate) != *root_map;
}),
receiver_maps->end());
}
}
// Possibly refine the feedback using inferred map information from the graph.
ElementAccessFeedback const&
JSNativeContextSpecialization::TryRefineElementAccessFeedback(
ElementAccessFeedback const& feedback, Node* receiver, Node* effect) const {
AccessMode access_mode = feedback.keyed_mode().access_mode();
bool use_inference =
access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas;
if (!use_inference) return feedback;
ZoneVector<Handle<Map>> inferred_maps(zone());
if (!InferReceiverMaps(receiver, effect, &inferred_maps)) return feedback;
RemoveImpossibleReceiverMaps(receiver, &inferred_maps);
// TODO(neis): After Refine, the resulting feedback can still contain
// impossible maps when a target is kept only because more than one of its
// sources was inferred. Think of a way to completely rule out impossible
// maps.
return feedback.Refine(inferred_maps, zone());
}
Reduction JSNativeContextSpecialization::ReduceElementAccess(
Node* node, Node* index, Node* value,
ElementAccessFeedback const& processed) {
ElementAccessFeedback const& feedback) {
DisallowHeapAccessIf no_heap_access(FLAG_concurrent_inlining);
DCHECK(node->opcode() == IrOpcode::kJSLoadProperty ||
node->opcode() == IrOpcode::kJSStoreProperty ||
......@@ -1474,30 +1525,34 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
Node* frame_state =
NodeProperties::FindFrameStateBefore(node, jsgraph()->Dead());
AccessMode access_mode = processed.keyed_mode.access_mode();
// TODO(neis): It's odd that we do optimizations below that don't really care
// about the feedback, but we don't do them when the feedback is megamorphic.
if (feedback.transition_groups().empty()) return NoChange();
ElementAccessFeedback const& refined_feedback =
TryRefineElementAccessFeedback(feedback, receiver, effect);
AccessMode access_mode = refined_feedback.keyed_mode().access_mode();
if ((access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) &&
receiver->opcode() == IrOpcode::kHeapConstant) {
Reduction reduction = ReduceElementLoadFromHeapConstant(
node, index, access_mode, processed.keyed_mode.load_mode());
node, index, access_mode, refined_feedback.keyed_mode().load_mode());
if (reduction.Changed()) return reduction;
}
if (HasOnlyStringMaps(broker(), processed.receiver_maps)) {
DCHECK(processed.transitions.empty());
if (!refined_feedback.transition_groups().empty() &&
refined_feedback.HasOnlyStringMaps(broker())) {
return ReduceElementAccessOnString(node, index, value,
processed.keyed_mode);
refined_feedback.keyed_mode());
}
// Compute element access infos for the receiver maps.
AccessInfoFactory access_info_factory(broker(), dependencies(),
graph()->zone());
ZoneVector<ElementAccessInfo> access_infos(zone());
if (!access_info_factory.ComputeElementAccessInfos(processed, access_mode,
&access_infos)) {
if (!access_info_factory.ComputeElementAccessInfos(refined_feedback,
&access_infos) ||
access_infos.empty()) {
return NoChange();
} else if (access_infos.empty()) {
return ReduceSoftDeoptimize(
node, DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess);
}
// For holey stores or growing stores, we need to check that the prototype
......@@ -1516,7 +1571,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
// then we need to check that all prototypes have stable maps with
// fast elements (and we need to guard against changes to that below).
if ((IsHoleyOrDictionaryElementsKind(receiver_map.elements_kind()) ||
IsGrowStoreMode(processed.keyed_mode.store_mode())) &&
IsGrowStoreMode(feedback.keyed_mode().store_mode())) &&
!receiver_map.HasOnlyStablePrototypesWithFastElements(
&prototype_maps)) {
return NoChange();
......@@ -1587,7 +1642,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
// Access the actual element.
ValueEffectControl continuation =
BuildElementAccess(receiver, index, value, effect, control, access_info,
processed.keyed_mode);
feedback.keyed_mode());
value = continuation.value();
effect = continuation.effect();
control = continuation.control();
......@@ -1654,7 +1709,7 @@ Reduction JSNativeContextSpecialization::ReduceElementAccess(
// Access the actual element.
ValueEffectControl continuation =
BuildElementAccess(this_receiver, this_index, this_value, this_effect,
this_control, access_info, processed.keyed_mode);
this_control, access_info, feedback.keyed_mode());
values.push_back(continuation.value());
effects.push_back(continuation.effect());
controls.push_back(continuation.control());
......@@ -1772,58 +1827,21 @@ Reduction JSNativeContextSpecialization::ReducePropertyAccess(
node->opcode() == IrOpcode::kJSStoreNamedOwn ||
node->opcode() == IrOpcode::kJSGetIterator);
Node* receiver = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
ProcessedFeedback const* processed = nullptr;
if (FLAG_concurrent_inlining) {
processed = broker()->GetFeedback(source);
// TODO(neis): Infer maps from the graph and consolidate with feedback/hints
// and filter impossible candidates based on inferred root map.
} else {
// TODO(neis): Try to unify this with the similar code in the serializer.
FeedbackNexus nexus(source.vector, source.slot);
if (nexus.ic_state() == UNINITIALIZED) {
processed = new (zone()) InsufficientFeedback();
} else {
MapHandles receiver_maps;
if (!ExtractReceiverMaps(receiver, effect, nexus, &receiver_maps)) {
processed = new (zone()) InsufficientFeedback();
} else if (!receiver_maps.empty()) {
base::Optional<NameRef> name = static_name.has_value()
? static_name
: broker()->GetNameFeedback(nexus);
if (name.has_value()) {
ZoneVector<PropertyAccessInfo> access_infos(zone());
AccessInfoFactory access_info_factory(broker(), dependencies(),
zone());
access_info_factory.ComputePropertyAccessInfos(
receiver_maps, name->object(), access_mode, &access_infos);
processed = new (zone()) NamedAccessFeedback(*name, access_infos);
} else if (nexus.GetKeyType() == ELEMENT &&
MEGAMORPHIC != nexus.ic_state()) {
processed = broker()->ProcessFeedbackMapsForElementAccess(
receiver_maps, KeyedAccessMode::FromNexus(nexus));
}
}
}
}
if (processed == nullptr) return NoChange();
switch (processed->kind()) {
ProcessedFeedback const& feedback =
broker()->GetFeedbackForPropertyAccess(source, access_mode, static_name);
switch (feedback.kind()) {
case ProcessedFeedback::kInsufficient:
return ReduceSoftDeoptimize(
node,
DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess);
case ProcessedFeedback::kNamedAccess:
return ReduceNamedAccess(node, value, *processed->AsNamedAccess(),
return ReduceNamedAccess(node, value, feedback.AsNamedAccess(),
access_mode, key);
case ProcessedFeedback::kElementAccess:
CHECK_EQ(processed->AsElementAccess()->keyed_mode.access_mode(),
DCHECK_EQ(feedback.AsElementAccess().keyed_mode().access_mode(),
access_mode);
return ReduceElementAccess(node, key, value,
*processed->AsElementAccess());
case ProcessedFeedback::kGlobalAccess:
return ReduceElementAccess(node, key, value, feedback.AsElementAccess());
default:
UNREACHABLE();
}
}
......@@ -3235,47 +3253,9 @@ bool JSNativeContextSpecialization::CanTreatHoleAsUndefined(
return dependencies()->DependOnNoElementsProtector();
}
// Returns false iff we have insufficient feedback (uninitialized or obsolete).
bool JSNativeContextSpecialization::ExtractReceiverMaps(
Node* receiver, Node* effect, FeedbackNexus const& nexus,
MapHandles* receiver_maps) {
DCHECK(receiver_maps->empty());
if (nexus.IsUninitialized()) return false;
// See if we can infer a concrete type for the {receiver}. Solely relying on
// the inference is not safe for keyed stores, because we would potentially
// miss out on transitions that need to be performed.
{
FeedbackSlotKind kind = nexus.kind();
bool use_inference =
!IsKeyedStoreICKind(kind) && !IsStoreInArrayLiteralICKind(kind);
if (use_inference && InferReceiverMaps(receiver, effect, receiver_maps)) {
TryUpdateThenDropDeprecated(isolate(), receiver_maps);
return true;
}
}
if (nexus.ExtractMaps(receiver_maps) == 0) return true;
// Try to filter impossible candidates based on inferred root map.
Handle<Map> root_map;
if (InferReceiverRootMap(receiver).ToHandle(&root_map)) {
DCHECK(!root_map->is_abandoned_prototype_map());
Isolate* isolate = this->isolate();
receiver_maps->erase(
std::remove_if(receiver_maps->begin(), receiver_maps->end(),
[root_map, isolate](Handle<Map> map) {
return map->is_abandoned_prototype_map() ||
map->FindRootMap(isolate) != *root_map;
}),
receiver_maps->end());
}
TryUpdateThenDropDeprecated(isolate(), receiver_maps);
return !receiver_maps->empty();
}
bool JSNativeContextSpecialization::InferReceiverMaps(
Node* receiver, Node* effect, MapHandles* receiver_maps) {
Node* receiver, Node* effect,
ZoneVector<Handle<Map>>* receiver_maps) const {
ZoneHandleSet<Map> maps;
NodeProperties::InferReceiverMapsResult result =
NodeProperties::InferReceiverMapsUnsafe(broker(), receiver, effect,
......@@ -3301,7 +3281,7 @@ bool JSNativeContextSpecialization::InferReceiverMaps(
}
MaybeHandle<Map> JSNativeContextSpecialization::InferReceiverRootMap(
Node* receiver) {
Node* receiver) const {
HeapObjectMatcher m(receiver);
if (m.HasValue()) {
return handle(m.Value()->map().FindRootMap(isolate()), isolate());
......
......@@ -213,18 +213,25 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
// code dependencies and might use the array protector cell.
bool CanTreatHoleAsUndefined(ZoneVector<Handle<Map>> const& receiver_maps);
// Extract receiver maps from {nexus} and filter based on {receiver} if
// possible.
bool ExtractReceiverMaps(Node* receiver, Node* effect,
FeedbackNexus const& nexus,
MapHandles* receiver_maps);
void RemoveImpossibleReceiverMaps(
Node* receiver, ZoneVector<Handle<Map>>* receiver_maps) const;
ElementAccessFeedback const& TryRefineElementAccessFeedback(
ElementAccessFeedback const& feedback, Node* receiver,
Node* effect) const;
void FilterMapsAndGetPropertyAccessInfos(
NamedAccessFeedback const& feedback, AccessMode access_mode,
Node* receiver, Node* effect,
ZoneVector<PropertyAccessInfo>* access_infos);
// Try to infer maps for the given {receiver} at the current {effect}.
bool InferReceiverMaps(Node* receiver, Node* effect,
MapHandles* receiver_maps);
ZoneVector<Handle<Map>>* receiver_maps) const;
// Try to infer a root map for the {receiver} independent of the current
// program location.
MaybeHandle<Map> InferReceiverRootMap(Node* receiver);
MaybeHandle<Map> InferReceiverRootMap(Node* receiver) const;
// Checks if we know at compile time that the {receiver} either definitely
// has the {prototype} in it's prototype chain, or the {receiver} definitely
......
......@@ -6,6 +6,7 @@
#include "src/compiler/access-builder.h"
#include "src/compiler/js-graph.h"
#include "src/compiler/js-heap-broker.h"
#include "src/compiler/operator-properties.h"
#include "src/compiler/simplified-operator.h"
#include "src/objects/feedback-vector.h"
......@@ -78,16 +79,6 @@ class JSSpeculativeBinopBuilder final {
control_(control),
slot_(slot) {}
BinaryOperationHint GetBinaryOperationHint() {
FeedbackNexus nexus(feedback_vector(), slot_);
return nexus.GetBinaryOperationFeedback();
}
CompareOperationHint GetCompareOperationHint() {
FeedbackNexus nexus(feedback_vector(), slot_);
return nexus.GetCompareOperationFeedback();
}
bool GetBinaryNumberOperationHint(NumberOperationHint* hint) {
return BinaryOperationHintToNumberOperationHint(GetBinaryOperationHint(),
hint);
......@@ -239,34 +230,52 @@ class JSSpeculativeBinopBuilder final {
JSOperatorBuilder* javascript() { return jsgraph()->javascript(); }
SimplifiedOperatorBuilder* simplified() { return jsgraph()->simplified(); }
CommonOperatorBuilder* common() { return jsgraph()->common(); }
const Handle<FeedbackVector>& feedback_vector() const {
return lowering_->feedback_vector();
}
private:
const JSTypeHintLowering* lowering_;
const Operator* op_;
BinaryOperationHint GetBinaryOperationHint() {
return lowering_->GetBinaryOperationHint(slot_);
}
CompareOperationHint GetCompareOperationHint() {
return lowering_->GetCompareOperationHint(slot_);
}
JSTypeHintLowering const* const lowering_;
Operator const* const op_;
Node* left_;
Node* right_;
Node* effect_;
Node* control_;
FeedbackSlot slot_;
Node* const effect_;
Node* const control_;
FeedbackSlot const slot_;
};
JSTypeHintLowering::JSTypeHintLowering(JSGraph* jsgraph,
Handle<FeedbackVector> feedback_vector,
JSTypeHintLowering::JSTypeHintLowering(JSHeapBroker* broker, JSGraph* jsgraph,
FeedbackVectorRef feedback_vector,
Flags flags)
: jsgraph_(jsgraph), flags_(flags), feedback_vector_(feedback_vector) {}
: broker_(broker),
jsgraph_(jsgraph),
flags_(flags),
feedback_vector_(feedback_vector) {}
Isolate* JSTypeHintLowering::isolate() const { return jsgraph()->isolate(); }
BinaryOperationHint JSTypeHintLowering::GetBinaryOperationHint(
FeedbackSlot slot) const {
FeedbackSource source(feedback_vector(), slot);
return broker()->GetFeedbackForBinaryOperation(source);
}
CompareOperationHint JSTypeHintLowering::GetCompareOperationHint(
FeedbackSlot slot) const {
FeedbackSource source(feedback_vector(), slot);
return broker()->GetFeedbackForCompareOperation(source);
}
JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceUnaryOperation(
const Operator* op, Node* operand, Node* effect, Node* control,
FeedbackSlot slot) const {
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForUnaryOperation)) {
return LoweringResult::Exit(node);
}
......@@ -309,9 +318,7 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceUnaryOperation(
control, slot);
node = b.TryBuildNumberBinop();
if (!node) {
FeedbackNexus nexus(feedback_vector(), slot);
if (nexus.GetBinaryOperationFeedback() ==
BinaryOperationHint::kBigInt) {
if (GetBinaryOperationHint(slot) == BinaryOperationHint::kBigInt) {
const Operator* op = jsgraph()->simplified()->SpeculativeBigIntNegate(
BigIntOperationHint::kBigInt);
node = jsgraph()->graph()->NewNode(op, operand, effect, control);
......@@ -335,10 +342,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
FeedbackSlot slot) const {
switch (op->opcode()) {
case IrOpcode::kJSStrictEqual: {
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
return LoweringResult::Exit(node);
}
......@@ -351,10 +356,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
case IrOpcode::kJSGreaterThan:
case IrOpcode::kJSLessThanOrEqual:
case IrOpcode::kJSGreaterThanOrEqual: {
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
return LoweringResult::Exit(node);
}
......@@ -365,10 +368,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
break;
}
case IrOpcode::kJSInstanceOf: {
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForCompareOperation)) {
return LoweringResult::Exit(node);
}
......@@ -387,10 +388,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
case IrOpcode::kJSMultiply:
case IrOpcode::kJSDivide:
case IrOpcode::kJSModulus: {
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForBinaryOperation)) {
return LoweringResult::Exit(node);
}
......@@ -418,10 +417,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceBinaryOperation(
JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceForInNextOperation(
Node* receiver, Node* cache_array, Node* cache_type, Node* index,
Node* effect, Node* control, FeedbackSlot slot) const {
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
return LoweringResult::Exit(node);
}
......@@ -432,10 +429,8 @@ JSTypeHintLowering::LoweringResult
JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect,
Node* control,
FeedbackSlot slot) const {
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForForIn)) {
return LoweringResult::Exit(node);
}
......@@ -445,10 +440,9 @@ JSTypeHintLowering::ReduceForInPrepareOperation(Node* enumerator, Node* effect,
JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceToNumberOperation(
Node* input, Node* effect, Node* control, FeedbackSlot slot) const {
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
NumberOperationHint hint;
if (BinaryOperationHintToNumberOperationHint(
nexus.GetBinaryOperationFeedback(), &hint)) {
if (BinaryOperationHintToNumberOperationHint(GetBinaryOperationHint(slot),
&hint)) {
Node* node = jsgraph()->graph()->NewNode(
jsgraph()->simplified()->SpeculativeToNumber(hint, VectorSlotPair()),
input, effect, control);
......@@ -462,10 +456,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceCallOperation(
Node* control, FeedbackSlot slot) const {
DCHECK(op->opcode() == IrOpcode::kJSCall ||
op->opcode() == IrOpcode::kJSCallWithSpread);
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForCall)) {
return LoweringResult::Exit(node);
}
......@@ -477,10 +469,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceConstructOperation(
Node* control, FeedbackSlot slot) const {
DCHECK(op->opcode() == IrOpcode::kJSConstruct ||
op->opcode() == IrOpcode::kJSConstructWithSpread);
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForConstruct)) {
return LoweringResult::Exit(node);
}
......@@ -493,10 +483,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadNamedOperation(
// JSGetIterator involves a named load of the Symbol.iterator property.
DCHECK(op->opcode() == IrOpcode::kJSLoadNamed ||
op->opcode() == IrOpcode::kJSGetIterator);
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
return LoweringResult::Exit(node);
}
......@@ -507,10 +495,8 @@ JSTypeHintLowering::LoweringResult JSTypeHintLowering::ReduceLoadKeyedOperation(
const Operator* op, Node* obj, Node* key, Node* effect, Node* control,
FeedbackSlot slot) const {
DCHECK_EQ(IrOpcode::kJSLoadProperty, op->opcode());
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
return LoweringResult::Exit(node);
}
......@@ -524,10 +510,8 @@ JSTypeHintLowering::ReduceStoreNamedOperation(const Operator* op, Node* obj,
FeedbackSlot slot) const {
DCHECK(op->opcode() == IrOpcode::kJSStoreNamed ||
op->opcode() == IrOpcode::kJSStoreNamedOwn);
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForGenericNamedAccess)) {
return LoweringResult::Exit(node);
}
......@@ -541,20 +525,22 @@ JSTypeHintLowering::ReduceStoreKeyedOperation(const Operator* op, Node* obj,
FeedbackSlot slot) const {
DCHECK(op->opcode() == IrOpcode::kJSStoreProperty ||
op->opcode() == IrOpcode::kJSStoreInArrayLiteral);
DCHECK(!slot.IsInvalid());
FeedbackNexus nexus(feedback_vector(), slot);
if (Node* node = TryBuildSoftDeopt(
nexus, effect, control,
slot, effect, control,
DeoptimizeReason::kInsufficientTypeFeedbackForGenericKeyedAccess)) {
return LoweringResult::Exit(node);
}
return LoweringResult::NoChange();
}
Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus const& nexus,
Node* effect, Node* control,
Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackSlot slot, Node* effect,
Node* control,
DeoptimizeReason reason) const {
if ((flags() & kBailoutOnUninitialized) && nexus.IsUninitialized()) {
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()),
......@@ -563,8 +549,6 @@ Node* JSTypeHintLowering::TryBuildSoftDeopt(FeedbackNexus const& nexus,
NodeProperties::FindFrameStateBefore(deoptimize, jsgraph()->Dead());
deoptimize->ReplaceInput(0, frame_state);
return deoptimize;
}
return nullptr;
}
} // namespace compiler
......
......@@ -41,8 +41,8 @@ class JSTypeHintLowering {
enum Flag { kNoFlags = 0u, kBailoutOnUninitialized = 1u << 1 };
using Flags = base::Flags<Flag>;
JSTypeHintLowering(JSGraph* jsgraph, Handle<FeedbackVector> feedback_vector,
Flags flags);
JSTypeHintLowering(JSHeapBroker* broker, JSGraph* jsgraph,
FeedbackVectorRef feedback_vector, Flags flags);
// {LoweringResult} describes the result of lowering. The following outcomes
// are possible:
......@@ -153,19 +153,22 @@ class JSTypeHintLowering {
private:
friend class JSSpeculativeBinopBuilder;
Node* TryBuildSoftDeopt(FeedbackNexus const& nexus, Node* effect,
Node* control, DeoptimizeReason reson) const;
BinaryOperationHint GetBinaryOperationHint(FeedbackSlot slot) const;
CompareOperationHint GetCompareOperationHint(FeedbackSlot slot) const;
Node* TryBuildSoftDeopt(FeedbackSlot slot, Node* effect, Node* control,
DeoptimizeReason reson) const;
JSHeapBroker* broker() const { return broker_; }
JSGraph* jsgraph() const { return jsgraph_; }
Isolate* isolate() const;
Flags flags() const { return flags_; }
const Handle<FeedbackVector>& feedback_vector() const {
return feedback_vector_;
}
FeedbackVectorRef const& feedback_vector() const { return feedback_vector_; }
JSGraph* jsgraph_;
JSHeapBroker* const broker_;
JSGraph* const jsgraph_;
Flags const flags_;
Handle<FeedbackVector> feedback_vector_;
FeedbackVectorRef const feedback_vector_;
DISALLOW_COPY_AND_ASSIGN(JSTypeHintLowering);
};
......
// 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_
......@@ -39,15 +39,6 @@ namespace compiler {
V(Throw)
#define CLEAR_ACCUMULATOR_LIST(V) \
V(Add) \
V(AddSmi) \
V(BitwiseAnd) \
V(BitwiseAndSmi) \
V(BitwiseNot) \
V(BitwiseOr) \
V(BitwiseOrSmi) \
V(BitwiseXor) \
V(BitwiseXorSmi) \
V(CallRuntime) \
V(CloneObject) \
V(CreateArrayFromIterable) \
......@@ -56,39 +47,13 @@ namespace compiler {
V(CreateMappedArguments) \
V(CreateRestParameter) \
V(CreateUnmappedArguments) \
V(Dec) \
V(DeletePropertySloppy) \
V(DeletePropertyStrict) \
V(Div) \
V(DivSmi) \
V(Exp) \
V(ExpSmi) \
V(ForInContinue) \
V(ForInEnumerate) \
V(ForInNext) \
V(ForInStep) \
V(Inc) \
V(LogicalNot) \
V(Mod) \
V(ModSmi) \
V(Mul) \
V(MulSmi) \
V(Negate) \
V(SetPendingMessage) \
V(ShiftLeft) \
V(ShiftLeftSmi) \
V(ShiftRight) \
V(ShiftRightLogical) \
V(ShiftRightLogicalSmi) \
V(ShiftRightSmi) \
V(Sub) \
V(SubSmi) \
V(TestEqual) \
V(TestEqualStrict) \
V(TestGreaterThan) \
V(TestGreaterThanOrEqual) \
V(TestLessThan) \
V(TestLessThanOrEqual) \
V(TestNull) \
V(TestReferenceEqual) \
V(TestTypeOf) \
......@@ -96,8 +61,6 @@ namespace compiler {
V(TestUndetectable) \
V(ToBooleanLogicalNot) \
V(ToName) \
V(ToNumber) \
V(ToNumeric) \
V(ToString) \
V(TypeOf)
......@@ -140,6 +103,46 @@ namespace compiler {
V(Illegal) \
V(Wide)
#define BINARY_OP_LIST(V) \
V(Add) \
V(AddSmi) \
V(BitwiseAnd) \
V(BitwiseAndSmi) \
V(BitwiseOr) \
V(BitwiseOrSmi) \
V(BitwiseXor) \
V(BitwiseXorSmi) \
V(Div) \
V(DivSmi) \
V(Exp) \
V(ExpSmi) \
V(Mod) \
V(ModSmi) \
V(Mul) \
V(MulSmi) \
V(ShiftLeft) \
V(ShiftLeftSmi) \
V(ShiftRight) \
V(ShiftRightSmi) \
V(ShiftRightLogical) \
V(ShiftRightLogicalSmi) \
V(Sub) \
V(SubSmi)
#define UNARY_OP_LIST(V) \
V(BitwiseNot) \
V(Dec) \
V(Inc) \
V(Negate)
#define COMPARE_OP_LIST(V) \
V(TestEqual) \
V(TestEqualStrict) \
V(TestGreaterThan) \
V(TestGreaterThanOrEqual) \
V(TestLessThan) \
V(TestLessThanOrEqual)
#define SUPPORTED_BYTECODE_LIST(V) \
V(CallAnyReceiver) \
V(CallJSRuntime) \
......@@ -163,6 +166,8 @@ namespace compiler {
V(CreateObjectLiteral) \
V(CreateRegExpLiteral) \
V(CreateWithContext) \
V(ForInNext) \
V(ForInPrepare) \
V(GetIterator) \
V(GetSuperConstructor) \
V(GetTemplateObject) \
......@@ -212,11 +217,16 @@ namespace compiler {
V(TestIn) \
V(TestInstanceOf) \
V(ThrowReferenceErrorIfHole) \
V(ToNumber) \
V(ToNumeric) \
BINARY_OP_LIST(V) \
COMPARE_OP_LIST(V) \
CLEAR_ACCUMULATOR_LIST(V) \
CLEAR_ENVIRONMENT_LIST(V) \
CONDITIONAL_JUMPS_LIST(V) \
IGNORED_BYTECODE_LIST(V) \
KILL_ENVIRONMENT_LIST(V) \
UNARY_OP_LIST(V) \
UNCONDITIONAL_JUMPS_LIST(V) \
UNREACHABLE_BYTECODE_LIST(V)
......@@ -251,6 +261,8 @@ class Hints {
public:
explicit Hints(Zone* zone);
static Hints SingleConstant(Handle<Object> constant, Zone* zone);
const ConstantsSet& constants() const;
const MapsSet& maps() const;
const BlueprintsSet& function_blueprints() const;
......@@ -344,7 +356,7 @@ class SerializerForBackgroundCompilation {
const HintsVector& arguments,
SerializerForBackgroundCompilationFlags flags);
bool BailoutOnUninitialized(FeedbackSlot slot);
bool BailoutOnUninitialized(ProcessedFeedback const& feedback);
void TraverseBytecode();
......@@ -379,11 +391,16 @@ class SerializerForBackgroundCompilation {
void ProcessJump(interpreter::BytecodeArrayIterator* iterator);
void ProcessKeyedPropertyAccess(Hints const& receiver, Hints const& key,
FeedbackSlot slot, AccessMode mode);
void ProcessNamedPropertyAccess(interpreter::BytecodeArrayIterator* iterator,
AccessMode mode);
void ProcessNamedPropertyAccess(Hints const& receiver, NameRef const& name,
FeedbackSlot slot, AccessMode mode);
FeedbackSlot slot, AccessMode access_mode,
bool honor_bailout_on_uninitialized);
void ProcessNamedPropertyAccess(Hints receiver, NameRef const& name,
FeedbackSlot slot, AccessMode access_mode);
void ProcessNamedAccess(Hints receiver, NamedAccessFeedback const& feedback,
AccessMode access_mode, Hints* new_accumulator_hints);
void ProcessElementAccess(Hints receiver, Hints key,
ElementAccessFeedback const& feedback,
AccessMode access_mode);
void ProcessMapHintsForPromises(Hints const& receiver_hints);
void ProcessHintsForPromiseResolve(Hints const& resolution_hints);
void ProcessHintsForHasInPrototypeChain(Hints const& instance_hints);
......@@ -398,17 +415,16 @@ class SerializerForBackgroundCompilation {
void ProcessHintsForOrdinaryHasInstance(Hints const& constructor_hints,
Hints const& instance_hints);
GlobalAccessFeedback const* ProcessFeedbackForGlobalAccess(FeedbackSlot slot);
NamedAccessFeedback const* ProcessFeedbackMapsForNamedAccess(
const MapHandles& maps, AccessMode mode, NameRef const& name);
ElementAccessFeedback const* ProcessFeedbackMapsForElementAccess(
const MapHandles& maps, AccessMode mode,
KeyedAccessMode const& keyed_mode);
void ProcessFeedbackForPropertyAccess(FeedbackSlot slot, AccessMode mode,
base::Optional<NameRef> static_name);
void ProcessGlobalAccess(FeedbackSlot slot, bool is_load);
void ProcessCompareOperation(FeedbackSlot slot);
void ProcessForIn(FeedbackSlot slot);
void ProcessUnaryOrBinaryOperation(FeedbackSlot slot,
bool honor_bailout_on_uninitialized);
PropertyAccessInfo ProcessMapForNamedPropertyAccess(
MapRef const& receiver_map, NameRef const& name, AccessMode mode,
base::Optional<JSObjectRef> receiver = base::nullopt);
MapRef const& receiver_map, NameRef const& name, AccessMode access_mode,
base::Optional<JSObjectRef> receiver, Hints* new_accumulator_hints);
void ProcessCreateContext(interpreter::BytecodeArrayIterator* iterator,
int scopeinfo_operand_index);
......@@ -416,13 +432,14 @@ class SerializerForBackgroundCompilation {
enum ContextProcessingMode {
kIgnoreSlot,
kSerializeSlot,
kSerializeSlotAndAddToAccumulator
};
void ProcessContextAccess(const Hints& context_hints, int slot, int depth,
ContextProcessingMode mode);
void ProcessContextAccess(Hints const& context_hints, int slot, int depth,
ContextProcessingMode mode,
Hints* new_accumulator_hints = nullptr);
void ProcessImmutableLoad(ContextRef const& context, int slot,
ContextProcessingMode mode);
ContextProcessingMode mode,
Hints* new_accumulator_hints);
void ProcessLdaLookupGlobalSlot(interpreter::BytecodeArrayIterator* iterator);
void ProcessLdaLookupContextSlot(
interpreter::BytecodeArrayIterator* iterator);
......@@ -445,8 +462,10 @@ class SerializerForBackgroundCompilation {
void ContributeToJumpTargetEnvironment(int target_offset);
void IncorporateJumpTargetEnvironment(int target_offset);
Handle<FeedbackVector> feedback_vector() const;
Handle<BytecodeArray> bytecode_array() const;
BytecodeAnalysis const& GetBytecodeAnalysis(bool serialize);
BytecodeAnalysis const& GetBytecodeAnalysis(
SerializationPolicy policy = SerializationPolicy::kAssumeSerialized);
JSHeapBroker* broker() const { return broker_; }
CompilationDependencies* dependencies() const { return dependencies_; }
......@@ -521,6 +540,12 @@ bool Hints::Equals(Hints const& other) const {
}
#endif
Hints Hints::SingleConstant(Handle<Object> constant, Zone* zone) {
Hints result(zone);
result.AddConstant(constant);
return result;
}
const ConstantsSet& Hints::constants() const { return constants_; }
const MapsSet& Hints::maps() const { return maps_; }
......@@ -718,8 +743,8 @@ SerializerForBackgroundCompilation::Environment::Environment(
}
// Pad the rest with "undefined".
Hints undefined_hint(zone);
undefined_hint.AddConstant(isolate->factory()->undefined_value());
Hints undefined_hint =
Hints::SingleConstant(isolate->factory()->undefined_value(), zone);
for (size_t i = arguments.size(); i < param_count; ++i) {
ephemeral_hints_[i] = undefined_hint;
}
......@@ -851,7 +876,7 @@ SerializerForBackgroundCompilation::SerializerForBackgroundCompilation(
}
bool SerializerForBackgroundCompilation::BailoutOnUninitialized(
FeedbackSlot slot) {
ProcessedFeedback const& feedback) {
DCHECK(!environment()->IsDead());
if (!(flags() &
SerializerForBackgroundCompilationFlag::kBailoutOnUninitialized)) {
......@@ -862,16 +887,7 @@ bool SerializerForBackgroundCompilation::BailoutOnUninitialized(
// OSR entry point. TODO(neis): Support OSR?
return false;
}
FeedbackNexus nexus(environment()->function().feedback_vector(), slot);
if (!slot.IsInvalid() && nexus.IsUninitialized()) {
FeedbackSource source(nexus);
if (broker()->HasFeedback(source)) {
DCHECK_EQ(broker()->GetFeedback(source)->kind(),
ProcessedFeedback::kInsufficient);
} else {
broker()->SetFeedback(source,
new (broker()->zone()) InsufficientFeedback());
}
if (feedback.IsInsufficient()) {
environment()->Kill();
return true;
}
......@@ -881,15 +897,14 @@ bool SerializerForBackgroundCompilation::BailoutOnUninitialized(
Hints SerializerForBackgroundCompilation::Run() {
TraceScope tracer(broker(), this, "SerializerForBackgroundCompilation::Run");
SharedFunctionInfoRef shared(broker(), environment()->function().shared());
FeedbackVectorRef feedback_vector(
broker(), environment()->function().feedback_vector());
if (shared.IsSerializedForCompilation(feedback_vector)) {
FeedbackVectorRef feedback_vector_ref(broker(), feedback_vector());
if (shared.IsSerializedForCompilation(feedback_vector_ref)) {
TRACE_BROKER(broker(), "Already ran serializer for SharedFunctionInfo "
<< Brief(*shared.object())
<< ", bailing out.\n");
return Hints(zone());
}
shared.SetSerializedForCompilation(feedback_vector);
shared.SetSerializedForCompilation(feedback_vector_ref);
// We eagerly call the {EnsureSourcePositionsAvailable} for all serialized
// SFIs while still on the main thread. Source positions will later be used
......@@ -900,7 +915,7 @@ Hints SerializerForBackgroundCompilation::Run() {
shared.object());
}
feedback_vector.SerializeSlots();
feedback_vector_ref.SerializeSlots();
TraverseBytecode();
return environment()->return_value_hints();
}
......@@ -934,6 +949,11 @@ class ExceptionHandlerMatcher {
std::set<int>::const_iterator handlers_iterator_;
};
Handle<FeedbackVector> SerializerForBackgroundCompilation::feedback_vector()
const {
return environment()->function().feedback_vector();
}
Handle<BytecodeArray> SerializerForBackgroundCompilation::bytecode_array()
const {
return handle(environment()->function().shared()->GetBytecodeArray(),
......@@ -941,16 +961,17 @@ Handle<BytecodeArray> SerializerForBackgroundCompilation::bytecode_array()
}
BytecodeAnalysis const& SerializerForBackgroundCompilation::GetBytecodeAnalysis(
bool serialize) {
SerializationPolicy policy) {
return broker()->GetBytecodeAnalysis(
bytecode_array(), osr_offset(),
flags() &
SerializerForBackgroundCompilationFlag::kAnalyzeEnvironmentLiveness,
serialize);
policy);
}
void SerializerForBackgroundCompilation::TraverseBytecode() {
BytecodeAnalysis const& bytecode_analysis = GetBytecodeAnalysis(true);
BytecodeAnalysis const& bytecode_analysis =
GetBytecodeAnalysis(SerializationPolicy::kSerializeIfNeeded);
BytecodeArrayRef(broker(), bytecode_array()).SerializeForCompilation();
BytecodeArrayIterator iterator(bytecode_array());
......@@ -1030,11 +1051,11 @@ void SerializerForBackgroundCompilation::VisitGetTemplateObject(
ObjectRef description(
broker(), iterator->GetConstantForIndexOperand(0, broker()->isolate()));
FeedbackSlot slot = iterator->GetSlotOperand(1);
FeedbackVectorRef feedback_vector(
broker(), environment()->function().feedback_vector());
FeedbackVectorRef feedback_vector_ref(broker(), feedback_vector());
SharedFunctionInfoRef shared(broker(), environment()->function().shared());
JSArrayRef template_object =
shared.GetTemplateObject(description, feedback_vector, slot, true);
shared.GetTemplateObject(description, feedback_vector_ref, slot,
SerializationPolicy::kSerializeIfNeeded);
environment()->accumulator_hints().Clear();
environment()->accumulator_hints().AddConstant(template_object.object());
}
......@@ -1124,7 +1145,7 @@ void SerializerForBackgroundCompilation::VisitPushContext(
saved_context_hints.Clear();
saved_context_hints.Add(current_context_hints);
// New Context is in the accumulator. Put those hints into the current context
// New context is in the accumulator. Put those hints into the current context
// register hints.
current_context_hints.Clear();
current_context_hints.Add(environment()->accumulator_hints());
......@@ -1140,19 +1161,21 @@ void SerializerForBackgroundCompilation::VisitPopContext(
}
void SerializerForBackgroundCompilation::ProcessImmutableLoad(
ContextRef const& context_ref, int slot, ContextProcessingMode mode) {
DCHECK(mode == kSerializeSlot || mode == kSerializeSlotAndAddToAccumulator);
base::Optional<ObjectRef> slot_value = context_ref.get(slot, true);
ContextRef const& context_ref, int slot, ContextProcessingMode mode,
Hints* new_accumulator_hints) {
DCHECK_EQ(mode, kSerializeSlot);
base::Optional<ObjectRef> slot_value =
context_ref.get(slot, SerializationPolicy::kSerializeIfNeeded);
// Also, put the object into the constant hints for the accumulator.
if (mode == kSerializeSlotAndAddToAccumulator && slot_value.has_value()) {
environment()->accumulator_hints().AddConstant(slot_value.value().object());
// If requested, record the object as a new hint for the accumulator.
if (new_accumulator_hints != nullptr && slot_value.has_value()) {
new_accumulator_hints->AddConstant(slot_value.value().object());
}
}
void SerializerForBackgroundCompilation::ProcessContextAccess(
const Hints& context_hints, int slot, int depth,
ContextProcessingMode mode) {
Hints const& context_hints, int slot, int depth, ContextProcessingMode mode,
Hints* new_accumulator_hints) {
// This function is for JSContextSpecialization::ReduceJSLoadContext and
// ReduceJSStoreContext. Those reductions attempt to eliminate as many
// loads as possible by making use of constant Context objects. In the
......@@ -1163,9 +1186,10 @@ void SerializerForBackgroundCompilation::ProcessContextAccess(
// Walk this context to the given depth and serialize the slot found.
ContextRef context_ref(broker(), x);
size_t remaining_depth = depth;
context_ref = context_ref.previous(&remaining_depth, true);
context_ref = context_ref.previous(
&remaining_depth, SerializationPolicy::kSerializeIfNeeded);
if (remaining_depth == 0 && mode != kIgnoreSlot) {
ProcessImmutableLoad(context_ref, slot, mode);
ProcessImmutableLoad(context_ref, slot, mode, new_accumulator_hints);
}
}
}
......@@ -1173,9 +1197,10 @@ void SerializerForBackgroundCompilation::ProcessContextAccess(
if (x.distance <= static_cast<unsigned int>(depth)) {
ContextRef context_ref(broker(), x.context);
size_t remaining_depth = depth - x.distance;
context_ref = context_ref.previous(&remaining_depth, true);
context_ref = context_ref.previous(
&remaining_depth, SerializationPolicy::kSerializeIfNeeded);
if (remaining_depth == 0 && mode != kIgnoreSlot) {
ProcessImmutableLoad(context_ref, slot, mode);
ProcessImmutableLoad(context_ref, slot, mode, new_accumulator_hints);
}
}
}
......@@ -1183,60 +1208,68 @@ void SerializerForBackgroundCompilation::ProcessContextAccess(
void SerializerForBackgroundCompilation::VisitLdaContextSlot(
BytecodeArrayIterator* iterator) {
Hints& context_hints =
Hints const& context_hints =
environment()->register_hints(iterator->GetRegisterOperand(0));
const int slot = iterator->GetIndexOperand(1);
const int depth = iterator->GetUnsignedImmediateOperand(2);
Hints new_accumulator_hints(zone());
ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot,
&new_accumulator_hints);
environment()->accumulator_hints().Clear();
ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot);
environment()->accumulator_hints().Add(new_accumulator_hints);
}
void SerializerForBackgroundCompilation::VisitLdaCurrentContextSlot(
BytecodeArrayIterator* iterator) {
const int slot = iterator->GetIndexOperand(0);
const int depth = 0;
Hints& context_hints = environment()->current_context_hints();
Hints const& context_hints = environment()->current_context_hints();
Hints new_accumulator_hints(zone());
ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot,
&new_accumulator_hints);
environment()->accumulator_hints().Clear();
ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot);
environment()->accumulator_hints().Add(new_accumulator_hints);
}
void SerializerForBackgroundCompilation::VisitLdaImmutableContextSlot(
BytecodeArrayIterator* iterator) {
const int slot = iterator->GetIndexOperand(1);
const int depth = iterator->GetUnsignedImmediateOperand(2);
Hints& context_hints =
Hints const& context_hints =
environment()->register_hints(iterator->GetRegisterOperand(0));
Hints new_accumulator_hints(zone());
ProcessContextAccess(context_hints, slot, depth, kSerializeSlot,
&new_accumulator_hints);
environment()->accumulator_hints().Clear();
ProcessContextAccess(context_hints, slot, depth,
kSerializeSlotAndAddToAccumulator);
environment()->accumulator_hints().Add(new_accumulator_hints);
}
void SerializerForBackgroundCompilation::VisitLdaImmutableCurrentContextSlot(
BytecodeArrayIterator* iterator) {
const int slot = iterator->GetIndexOperand(0);
const int depth = 0;
Hints& context_hints = environment()->current_context_hints();
Hints const& context_hints = environment()->current_context_hints();
Hints new_accumulator_hints(zone());
ProcessContextAccess(context_hints, slot, depth, kSerializeSlot,
&new_accumulator_hints);
environment()->accumulator_hints().Clear();
ProcessContextAccess(context_hints, slot, depth,
kSerializeSlotAndAddToAccumulator);
environment()->accumulator_hints().Add(new_accumulator_hints);
}
void SerializerForBackgroundCompilation::VisitLdaModuleVariable(
BytecodeArrayIterator* iterator) {
const int slot = Context::EXTENSION_INDEX;
const int depth = iterator->GetUnsignedImmediateOperand(1);
// TODO(mvstanton): If we have a constant module, should we serialize the
// cell as well? Then we could put the value in the accumulator.
environment()->accumulator_hints().Clear();
ProcessContextAccess(environment()->current_context_hints(),
Context::EXTENSION_INDEX, depth, kSerializeSlot);
Hints const& context_hints = environment()->current_context_hints();
ProcessContextAccess(context_hints, slot, depth, kSerializeSlot);
}
void SerializerForBackgroundCompilation::VisitStaModuleVariable(
BytecodeArrayIterator* iterator) {
const int slot = Context::EXTENSION_INDEX;
const int depth = iterator->GetUnsignedImmediateOperand(1);
ProcessContextAccess(environment()->current_context_hints(),
Context::EXTENSION_INDEX, depth, kSerializeSlot);
Hints const& context_hints = environment()->current_context_hints();
ProcessContextAccess(context_hints, slot, depth, kSerializeSlot);
}
void SerializerForBackgroundCompilation::VisitStaLookupSlot(
......@@ -1250,7 +1283,7 @@ void SerializerForBackgroundCompilation::VisitStaContextSlot(
BytecodeArrayIterator* iterator) {
const int slot = iterator->GetIndexOperand(1);
const int depth = iterator->GetUnsignedImmediateOperand(2);
Hints& register_hints =
Hints const& register_hints =
environment()->register_hints(iterator->GetRegisterOperand(0));
ProcessContextAccess(register_hints, slot, depth, kIgnoreSlot);
}
......@@ -1259,7 +1292,7 @@ void SerializerForBackgroundCompilation::VisitStaCurrentContextSlot(
BytecodeArrayIterator* iterator) {
const int slot = iterator->GetIndexOperand(0);
const int depth = 0;
Hints& context_hints = environment()->current_context_hints();
Hints const& context_hints = environment()->current_context_hints();
ProcessContextAccess(context_hints, slot, depth, kIgnoreSlot);
}
......@@ -1337,6 +1370,18 @@ void SerializerForBackgroundCompilation::VisitCreateCatchContext(
ProcessCreateContext(iterator, 1);
}
void SerializerForBackgroundCompilation::VisitForInNext(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(3);
ProcessForIn(slot);
}
void SerializerForBackgroundCompilation::VisitForInPrepare(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(1);
ProcessForIn(slot);
}
void SerializerForBackgroundCompilation::ProcessCreateContext(
interpreter::BytecodeArrayIterator* iterator, int scopeinfo_operand_index) {
Handle<ScopeInfo> scope_info =
......@@ -1344,9 +1389,9 @@ void SerializerForBackgroundCompilation::ProcessCreateContext(
scopeinfo_operand_index, broker()->isolate()));
ScopeInfoRef scope_info_ref(broker(), scope_info);
Hints const& current_context_hints = environment()->current_context_hints();
Hints& accumulator_hints = environment()->accumulator_hints();
accumulator_hints.Clear();
Hints& current_context_hints = environment()->current_context_hints();
// For each constant context, we must create a virtual context from
// it of distance one.
......@@ -1367,24 +1412,20 @@ void SerializerForBackgroundCompilation::ProcessCreateContext(
void SerializerForBackgroundCompilation::VisitCreateClosure(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints().Clear();
Handle<SharedFunctionInfo> shared = Handle<SharedFunctionInfo>::cast(
iterator->GetConstantForIndexOperand(0, broker()->isolate()));
Handle<FeedbackCell> feedback_cell =
environment()->function().feedback_vector()->GetClosureFeedbackCell(
iterator->GetIndexOperand(1));
feedback_vector()->GetClosureFeedbackCell(iterator->GetIndexOperand(1));
FeedbackCellRef feedback_cell_ref(broker(), feedback_cell);
Handle<Object> cell_value(feedback_cell->value(), broker()->isolate());
ObjectRef cell_value_ref(broker(), cell_value);
environment()->accumulator_hints().Clear();
if (cell_value->IsFeedbackVector()) {
// Gather the context hints from the current context register hint
// structure.
FunctionBlueprint blueprint(shared,
Handle<FeedbackVector>::cast(cell_value),
environment()->current_context_hints());
environment()->accumulator_hints().AddFunctionBlueprint(blueprint);
}
}
......@@ -1400,9 +1441,8 @@ void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver0(
environment()->register_hints(iterator->GetRegisterOperand(0));
FeedbackSlot slot = iterator->GetSlotOperand(1);
Hints receiver(zone());
receiver.AddConstant(broker()->isolate()->factory()->undefined_value());
Hints receiver = Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(), zone());
HintsVector parameters({receiver}, zone());
ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}
......@@ -1415,9 +1455,8 @@ void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver1(
environment()->register_hints(iterator->GetRegisterOperand(1));
FeedbackSlot slot = iterator->GetSlotOperand(2);
Hints receiver(zone());
receiver.AddConstant(broker()->isolate()->factory()->undefined_value());
Hints receiver = Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(), zone());
HintsVector parameters({receiver, arg0}, zone());
ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}
......@@ -1432,9 +1471,8 @@ void SerializerForBackgroundCompilation::VisitCallUndefinedReceiver2(
environment()->register_hints(iterator->GetRegisterOperand(2));
FeedbackSlot slot = iterator->GetSlotOperand(3);
Hints receiver(zone());
receiver.AddConstant(broker()->isolate()->factory()->undefined_value());
Hints receiver = Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(), zone());
HintsVector parameters({receiver, arg0, arg1}, zone());
ProcessCallOrConstruct(callee, base::nullopt, parameters, slot);
}
......@@ -1498,12 +1536,12 @@ void SerializerForBackgroundCompilation::VisitCallWithSpread(
void SerializerForBackgroundCompilation::VisitCallJSRuntime(
BytecodeArrayIterator* iterator) {
environment()->accumulator_hints().Clear();
// BytecodeGraphBuilder::VisitCallJSRuntime needs the {runtime_index}
// slot in the native context to be serialized.
const int runtime_index = iterator->GetNativeContextIndexOperand(0);
broker()->native_context().get(runtime_index, true);
broker()->native_context().get(runtime_index,
SerializationPolicy::kSerializeIfNeeded);
environment()->accumulator_hints().Clear();
}
Hints SerializerForBackgroundCompilation::RunChildSerializer(
......@@ -1532,22 +1570,6 @@ Hints SerializerForBackgroundCompilation::RunChildSerializer(
return child_serializer.Run();
}
namespace {
base::Optional<HeapObjectRef> GetHeapObjectFeedback(
JSHeapBroker* broker, Handle<FeedbackVector> feedback_vector,
FeedbackSlot slot) {
if (slot.IsInvalid()) return base::nullopt;
FeedbackNexus nexus(feedback_vector, slot);
VectorSlotPair feedback(feedback_vector, slot, nexus.ic_state());
DCHECK(feedback.IsValid());
if (nexus.IsUninitialized()) return base::nullopt;
HeapObject object;
if (!nexus.GetFeedback()->GetHeapObject(&object)) return base::nullopt;
return HeapObjectRef(broker, handle(object, broker->isolate()));
}
} // namespace
bool SerializerForBackgroundCompilation::ProcessSFIForCallOrConstruct(
Handle<SharedFunctionInfo> shared, const HintsVector& arguments,
SpeculationMode speculation_mode) {
......@@ -1587,13 +1609,13 @@ MaybeHandle<JSFunction> UnrollBoundFunction(
target = target.AsJSBoundFunction().bound_target_function()) {
for (int i = target.AsJSBoundFunction().bound_arguments().length() - 1;
i >= 0; --i) {
Hints arg(broker->zone());
arg.AddConstant(
target.AsJSBoundFunction().bound_arguments().get(i).object());
Hints arg = Hints::SingleConstant(
target.AsJSBoundFunction().bound_arguments().get(i).object(),
broker->zone());
reversed_bound_arguments.push_back(arg);
}
Hints arg(broker->zone());
arg.AddConstant(target.AsJSBoundFunction().bound_this().object());
Hints arg = Hints::SingleConstant(
target.AsJSBoundFunction().bound_this().object(), broker->zone());
reversed_bound_arguments.push_back(arg);
}
......@@ -1613,26 +1635,30 @@ MaybeHandle<JSFunction> UnrollBoundFunction(
void SerializerForBackgroundCompilation::ProcessCallOrConstruct(
Hints callee, base::Optional<Hints> new_target,
const HintsVector& arguments, FeedbackSlot slot, bool with_spread) {
// TODO(neis): Make this part of ProcessFeedback*?
if (BailoutOnUninitialized(slot)) return;
// Incorporate feedback into hints.
base::Optional<HeapObjectRef> feedback = GetHeapObjectFeedback(
broker(), environment()->function().feedback_vector(), slot);
if (feedback.has_value() && feedback->map().is_callable()) {
FeedbackSource source(feedback_vector(), slot);
ProcessedFeedback const& feedback = broker()->ProcessFeedbackForCall(source);
if (BailoutOnUninitialized(feedback)) return;
// Incorporate feedback into hints copy to simplify processing.
SpeculationMode speculation_mode = SpeculationMode::kDisallowSpeculation;
if (!feedback.IsInsufficient()) {
speculation_mode = feedback.AsCall().speculation_mode();
base::Optional<HeapObjectRef> target = feedback.AsCall().target();
if (target.has_value() && target->map().is_callable()) {
// TODO(mvstanton): if the map isn't callable then we have an allocation
// site, and it may make sense to add the Array JSFunction constant.
if (new_target.has_value()) {
// Construct; feedback is new_target, which often is also the callee.
new_target->AddConstant(feedback->object());
callee.AddConstant(feedback->object());
new_target->AddConstant(target->object());
callee.AddConstant(target->object());
} else {
// Call; feedback is callee.
callee.AddConstant(feedback->object());
// Call; target is callee.
callee.AddConstant(target->object());
}
}
}
environment()->accumulator_hints().Clear();
FeedbackNexus nexus(environment()->function().feedback_vector(), slot);
SpeculationMode speculation_mode = nexus.GetSpeculationMode();
// For JSCallReducer::ReduceJSCall and JSCallReducer::ReduceJSConstruct.
for (auto hint : callee.constants()) {
......@@ -1688,9 +1714,8 @@ void SerializerForBackgroundCompilation::ProcessCallVarArgs(
// The receiver is either given in the first register or it is implicitly
// the {undefined} value.
if (receiver_mode == ConvertReceiverMode::kNullOrUndefined) {
Hints receiver(zone());
receiver.AddConstant(broker()->isolate()->factory()->undefined_value());
arguments.push_back(receiver);
arguments.push_back(Hints::SingleConstant(
broker()->isolate()->factory()->undefined_value(), zone()));
}
environment()->ExportRegisterHints(first_reg, reg_count, &arguments);
......@@ -1739,14 +1764,12 @@ void SerializerForBackgroundCompilation::ProcessApiCall(
void SerializerForBackgroundCompilation::ProcessReceiverMapForApiCall(
FunctionTemplateInfoRef target, Handle<Map> receiver) {
if (receiver->is_access_check_needed()) {
return;
}
if (!receiver->is_access_check_needed()) {
MapRef receiver_map(broker(), receiver);
TRACE_BROKER(broker(), "Serializing holder for target:" << target);
target.LookupHolderOfExpectedType(receiver_map, true);
target.LookupHolderOfExpectedType(receiver_map,
SerializationPolicy::kSerializeIfNeeded);
}
}
void SerializerForBackgroundCompilation::ProcessBuiltinCall(
......@@ -1910,7 +1933,8 @@ PropertyAccessInfo SerializerForBackgroundCompilation::ProcessMapForRegExpTest(
// The property is on the prototype chain.
JSObjectRef holder_ref(broker(), holder);
holder_ref.GetOwnDataProperty(ai_exec.field_representation(),
ai_exec.field_index(), true);
ai_exec.field_index(),
SerializationPolicy::kSerializeIfNeeded);
}
return ai_exec;
}
......@@ -1928,7 +1952,8 @@ void SerializerForBackgroundCompilation::ProcessHintsForRegExpTest(
// The property is on the object itself.
JSObjectRef holder_ref(broker(), regexp);
holder_ref.GetOwnDataProperty(ai_exec.field_representation(),
ai_exec.field_index(), true);
ai_exec.field_index(),
SerializationPolicy::kSerializeIfNeeded);
}
}
......@@ -2021,7 +2046,7 @@ void SerializerForBackgroundCompilation::VisitSwitchOnSmiNoFeedback(
void SerializerForBackgroundCompilation::VisitSwitchOnGeneratorState(
interpreter::BytecodeArrayIterator* iterator) {
for (const auto& target : GetBytecodeAnalysis(false).resume_jump_targets()) {
for (const auto& target : GetBytecodeAnalysis().resume_jump_targets()) {
ContributeToJumpTargetEnvironment(target.target_offset());
}
}
......@@ -2036,12 +2061,12 @@ void SerializerForBackgroundCompilation::Environment::ExportRegisterHints(
void SerializerForBackgroundCompilation::VisitConstruct(
BytecodeArrayIterator* iterator) {
const Hints& callee =
Hints const& new_target = environment()->accumulator_hints();
Hints const& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
size_t reg_count = iterator->GetRegisterCountOperand(2);
FeedbackSlot slot = iterator->GetSlotOperand(3);
const Hints& new_target = environment()->accumulator_hints();
HintsVector arguments(zone());
environment()->ExportRegisterHints(first_reg, reg_count, &arguments);
......@@ -2051,12 +2076,12 @@ void SerializerForBackgroundCompilation::VisitConstruct(
void SerializerForBackgroundCompilation::VisitConstructWithSpread(
BytecodeArrayIterator* iterator) {
const Hints& callee =
Hints const& new_target = environment()->accumulator_hints();
Hints const& callee =
environment()->register_hints(iterator->GetRegisterOperand(0));
interpreter::Register first_reg = iterator->GetRegisterOperand(1);
size_t reg_count = iterator->GetRegisterCountOperand(2);
FeedbackSlot slot = iterator->GetSlotOperand(3);
const Hints& new_target = environment()->accumulator_hints();
HintsVector arguments(zone());
environment()->ExportRegisterHints(first_reg, reg_count, &arguments);
......@@ -2064,39 +2089,36 @@ void SerializerForBackgroundCompilation::VisitConstructWithSpread(
ProcessCallOrConstruct(callee, new_target, arguments, slot, true);
}
GlobalAccessFeedback const*
SerializerForBackgroundCompilation::ProcessFeedbackForGlobalAccess(
FeedbackSlot slot) {
if (slot.IsInvalid()) return nullptr;
if (environment()->function().feedback_vector().is_null()) return nullptr;
FeedbackSource source(environment()->function().feedback_vector(), slot);
if (broker()->HasFeedback(source)) {
return broker()->GetGlobalAccessFeedback(source);
}
const GlobalAccessFeedback* feedback =
void SerializerForBackgroundCompilation::ProcessGlobalAccess(FeedbackSlot slot,
bool is_load) {
if (slot.IsInvalid() || feedback_vector().is_null()) return;
FeedbackSource source(feedback_vector(), slot);
ProcessedFeedback const& feedback =
broker()->ProcessFeedbackForGlobalAccess(source);
broker()->SetFeedback(source, feedback);
return feedback;
}
void SerializerForBackgroundCompilation::VisitLdaGlobal(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(1);
NameRef(broker(),
iterator->GetConstantForIndexOperand(0, broker()->isolate()));
if (is_load) {
environment()->accumulator_hints().Clear();
GlobalAccessFeedback const* feedback = ProcessFeedbackForGlobalAccess(slot);
if (feedback != nullptr) {
if (feedback.kind() == ProcessedFeedback::kGlobalAccess) {
// We may be able to contribute to accumulator constant hints.
base::Optional<ObjectRef> value = feedback->GetConstantHint();
base::Optional<ObjectRef> value =
feedback.AsGlobalAccess().GetConstantHint();
if (value.has_value()) {
environment()->accumulator_hints().AddConstant(value->object());
}
} else {
DCHECK(feedback.IsInsufficient());
}
}
}
void SerializerForBackgroundCompilation::VisitLdaGlobal(
BytecodeArrayIterator* iterator) {
NameRef(broker(),
iterator->GetConstantForIndexOperand(0, broker()->isolate()));
FeedbackSlot slot = iterator->GetSlotOperand(1);
ProcessGlobalAccess(slot, true);
}
void SerializerForBackgroundCompilation::VisitLdaGlobalInsideTypeof(
BytecodeArrayIterator* iterator) {
VisitLdaGlobal(iterator);
......@@ -2148,7 +2170,7 @@ void SerializerForBackgroundCompilation::VisitStaGlobal(
NameRef(broker(),
iterator->GetConstantForIndexOperand(0, broker()->isolate()));
FeedbackSlot slot = iterator->GetSlotOperand(1);
ProcessFeedbackForGlobalAccess(slot);
ProcessGlobalAccess(slot, false);
}
void SerializerForBackgroundCompilation::ProcessLdaLookupContextSlot(
......@@ -2158,9 +2180,9 @@ void SerializerForBackgroundCompilation::ProcessLdaLookupContextSlot(
NameRef(broker(),
iterator->GetConstantForIndexOperand(0, broker()->isolate()));
ProcessCheckContextExtensions(depth);
Hints& context_hints = environment()->current_context_hints();
environment()->accumulator_hints().Clear();
ProcessContextAccess(context_hints, slot_index, depth, kIgnoreSlot);
ProcessContextAccess(environment()->current_context_hints(), slot_index,
depth, kIgnoreSlot);
}
void SerializerForBackgroundCompilation::VisitLdaLookupContextSlot(
......@@ -2173,6 +2195,7 @@ void SerializerForBackgroundCompilation::VisitLdaLookupContextSlotInsideTypeof(
ProcessLdaLookupContextSlot(iterator);
}
// TODO(neis): Avoid duplicating this.
namespace {
template <class MapContainer>
MapHandles GetRelevantReceiverMaps(Isolate* isolate, MapContainer const& maps) {
......@@ -2188,197 +2211,81 @@ MapHandles GetRelevantReceiverMaps(Isolate* isolate, MapContainer const& maps) {
}
} // namespace
ElementAccessFeedback const*
SerializerForBackgroundCompilation::ProcessFeedbackMapsForElementAccess(
const MapHandles& maps, AccessMode mode,
KeyedAccessMode const& keyed_mode) {
ElementAccessFeedback const* result =
broker()->ProcessFeedbackMapsForElementAccess(maps, keyed_mode);
for (ElementAccessFeedback::MapIterator it = result->all_maps(broker());
!it.done(); it.advance()) {
switch (mode) {
case AccessMode::kHas:
case AccessMode::kLoad:
it.current().SerializeForElementLoad();
break;
case AccessMode::kStore:
it.current().SerializeForElementStore();
break;
case AccessMode::kStoreInLiteral:
// This operation is fairly local and simple, nothing to serialize.
break;
}
}
return result;
void SerializerForBackgroundCompilation::ProcessCompareOperation(
FeedbackSlot slot) {
if (slot.IsInvalid() || feedback_vector().is_null()) return;
FeedbackSource source(environment()->function().feedback_vector(), slot);
ProcessedFeedback const& feedback =
broker()->ProcessFeedbackForCompareOperation(source);
if (BailoutOnUninitialized(feedback)) return;
environment()->accumulator_hints().Clear();
}
NamedAccessFeedback const*
SerializerForBackgroundCompilation::ProcessFeedbackMapsForNamedAccess(
const MapHandles& maps, AccessMode mode, NameRef const& name) {
ZoneVector<PropertyAccessInfo> access_infos(broker()->zone());
for (Handle<Map> map : maps) {
MapRef map_ref(broker(), map);
PropertyAccessInfo info =
ProcessMapForNamedPropertyAccess(map_ref, name, mode);
access_infos.push_back(info);
// TODO(turbofan): We want to take receiver hints into account as well,
// not only the feedback maps.
// For JSNativeContextSpecialization::InlinePropertySetterCall
// and InlinePropertyGetterCall.
if (info.IsAccessorConstant() && !info.constant().is_null()) {
if (info.constant()->IsJSFunction()) {
JSFunctionRef function(broker(),
Handle<JSFunction>::cast(info.constant()));
// For JSCallReducer::ReduceJSCall.
function.Serialize();
// For JSCallReducer::ReduceCallApiFunction.
Handle<SharedFunctionInfo> sfi = function.shared().object();
if (sfi->IsApiFunction()) {
FunctionTemplateInfoRef fti_ref(
broker(), handle(sfi->get_api_func_data(), broker()->isolate()));
if (fti_ref.has_call_code()) fti_ref.SerializeCallCode();
ProcessReceiverMapForApiCall(fti_ref, map);
}
} else if (info.constant()->IsJSBoundFunction()) {
JSBoundFunctionRef function(
broker(), Handle<JSBoundFunction>::cast(info.constant()));
// For JSCallReducer::ReduceJSCall.
function.Serialize();
} else {
FunctionTemplateInfoRef fti_ref(
broker(), Handle<FunctionTemplateInfo>::cast(info.constant()));
if (fti_ref.has_call_code()) fti_ref.SerializeCallCode();
}
}
}
DCHECK(!access_infos.empty());
return new (broker()->zone()) NamedAccessFeedback(name, access_infos);
void SerializerForBackgroundCompilation::ProcessForIn(FeedbackSlot slot) {
if (slot.IsInvalid() || feedback_vector().is_null()) return;
FeedbackSource source(feedback_vector(), slot);
ProcessedFeedback const& feedback = broker()->ProcessFeedbackForForIn(source);
if (BailoutOnUninitialized(feedback)) return;
environment()->accumulator_hints().Clear();
}
void SerializerForBackgroundCompilation::ProcessFeedbackForPropertyAccess(
FeedbackSlot slot, AccessMode mode, base::Optional<NameRef> static_name) {
if (slot.IsInvalid()) return;
if (environment()->function().feedback_vector().is_null()) return;
FeedbackNexus nexus(environment()->function().feedback_vector(), slot);
FeedbackSource source(nexus);
if (broker()->HasFeedback(source)) return;
if (nexus.ic_state() == UNINITIALIZED) {
broker()->SetFeedback(source,
new (broker()->zone()) InsufficientFeedback());
void SerializerForBackgroundCompilation::ProcessUnaryOrBinaryOperation(
FeedbackSlot slot, bool honor_bailout_on_uninitialized) {
if (slot.IsInvalid() || feedback_vector().is_null()) return;
FeedbackSource source(feedback_vector(), slot);
// Internally V8 uses binary op feedback also for unary ops.
ProcessedFeedback const& feedback =
broker()->ProcessFeedbackForBinaryOperation(source);
if (honor_bailout_on_uninitialized && BailoutOnUninitialized(feedback)) {
return;
}
MapHandles maps;
if (nexus.ExtractMaps(&maps) == 0) { // Megamorphic.
broker()->SetFeedback(source, nullptr);
return;
}
maps = GetRelevantReceiverMaps(broker()->isolate(), maps);
if (maps.empty()) {
broker()->SetFeedback(source,
new (broker()->zone()) InsufficientFeedback());
return;
}
ProcessedFeedback const* processed = nullptr;
base::Optional<NameRef> name =
static_name.has_value() ? static_name : broker()->GetNameFeedback(nexus);
if (name.has_value()) {
processed = ProcessFeedbackMapsForNamedAccess(maps, mode, *name);
} else if (nexus.GetKeyType() == ELEMENT) {
DCHECK_NE(nexus.ic_state(), MEGAMORPHIC);
processed = ProcessFeedbackMapsForElementAccess(
maps, mode, KeyedAccessMode::FromNexus(nexus));
}
broker()->SetFeedback(source, processed);
}
void SerializerForBackgroundCompilation::ProcessKeyedPropertyAccess(
Hints const& receiver, Hints const& key, FeedbackSlot slot,
AccessMode mode) {
if (BailoutOnUninitialized(slot)) return;
for (Handle<Object> hint : receiver.constants()) {
ObjectRef receiver_ref(broker(), hint);
// For JSNativeContextSpecialization::ReduceElementAccess.
if (receiver_ref.IsJSTypedArray()) {
receiver_ref.AsJSTypedArray().Serialize();
}
// For JSNativeContextSpecialization::ReduceElementLoadFromHeapConstant.
if (mode == AccessMode::kLoad || mode == AccessMode::kHas) {
for (Handle<Object> hint : key.constants()) {
ObjectRef key_ref(broker(), hint);
// TODO(neis): Do this for integer-HeapNumbers too?
if (key_ref.IsSmi() && key_ref.AsSmi() >= 0) {
base::Optional<ObjectRef> element =
receiver_ref.GetOwnConstantElement(key_ref.AsSmi(), true);
if (!element.has_value() && receiver_ref.IsJSArray()) {
// We didn't find a constant element, but if the receiver is a
// cow-array we can exploit the fact that any future write to the
// element will replace the whole elements storage.
receiver_ref.AsJSArray().GetOwnCowElement(key_ref.AsSmi(), true);
}
}
}
}
}
environment()->accumulator_hints().Clear();
ProcessFeedbackForPropertyAccess(slot, mode, base::nullopt);
}
PropertyAccessInfo
SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess(
MapRef const& receiver_map, NameRef const& name, AccessMode mode,
base::Optional<JSObjectRef> receiver) {
MapRef const& receiver_map, NameRef const& name, AccessMode access_mode,
base::Optional<JSObjectRef> receiver, Hints* new_accumulator_hints) {
// For JSNativeContextSpecialization::ReduceNamedAccess.
if (receiver_map.IsMapOfCurrentGlobalProxy()) {
broker()->native_context().global_proxy_object().GetPropertyCell(name,
true);
broker()->native_context().global_proxy_object().GetPropertyCell(
name, SerializationPolicy::kSerializeIfNeeded);
}
AccessInfoFactory access_info_factory(broker(), dependencies(),
broker()->zone());
PropertyAccessInfo access_info(access_info_factory.ComputePropertyAccessInfo(
receiver_map.object(), name.object(), mode));
PropertyAccessInfo const& access_info = broker()->GetPropertyAccessInfo(
receiver_map, name, access_mode, dependencies(),
SerializationPolicy::kSerializeIfNeeded);
// For JSNativeContextSpecialization::InlinePropertySetterCall
// and InlinePropertyGetterCall.
if (access_info.IsAccessorConstant() && !access_info.constant().is_null()) {
if (access_info.constant()->IsJSFunction()) {
JSFunctionRef function(broker(), access_info.constant());
// For JSCallReducer::ReduceJSCall.
function.Serialize();
// For JSCallReducer::ReduceCallApiFunction.
Handle<SharedFunctionInfo> sfi(
handle(Handle<JSFunction>::cast(access_info.constant())->shared(),
broker()->isolate()));
Handle<SharedFunctionInfo> sfi = function.shared().object();
if (sfi->IsApiFunction()) {
FunctionTemplateInfoRef fti_ref(
broker(), handle(sfi->get_api_func_data(), broker()->isolate()));
if (fti_ref.has_call_code()) fti_ref.SerializeCallCode();
ProcessReceiverMapForApiCall(fti_ref, receiver_map.object());
}
} else if (access_info.constant()->IsJSBoundFunction()) {
JSBoundFunctionRef function(broker(), access_info.constant());
// For JSCallReducer::ReduceJSCall.
function.Serialize();
} else {
FunctionTemplateInfoRef fti_ref(
broker(), Handle<FunctionTemplateInfo>::cast(access_info.constant()));
if (fti_ref.has_call_code()) fti_ref.SerializeCallCode();
FunctionTemplateInfoRef fti(broker(), access_info.constant());
if (fti.has_call_code()) fti.SerializeCallCode();
}
}
// For PropertyAccessBuilder::TryBuildLoadConstantDataField
if (mode == AccessMode::kLoad) {
broker()->StorePropertyAccessInfoForLoad(receiver_map, name, access_info);
if (access_mode == AccessMode::kLoad) {
if (access_info.IsDataConstant()) {
base::Optional<JSObjectRef> holder;
Handle<JSObject> prototype;
......@@ -2391,11 +2298,11 @@ SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess(
}
if (holder.has_value()) {
base::Optional<ObjectRef> constant(
holder->GetOwnDataProperty(access_info.field_representation(),
access_info.field_index(), true));
base::Optional<ObjectRef> constant(holder->GetOwnDataProperty(
access_info.field_representation(), access_info.field_index(),
SerializationPolicy::kSerializeIfNeeded));
if (constant.has_value()) {
environment()->accumulator_hints().AddConstant(constant->object());
new_accumulator_hints->AddConstant(constant->object());
}
}
}
......@@ -2410,59 +2317,174 @@ void SerializerForBackgroundCompilation::VisitLdaKeyedProperty(
Hints const& receiver =
environment()->register_hints(iterator->GetRegisterOperand(0));
FeedbackSlot slot = iterator->GetSlotOperand(1);
ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kLoad);
ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kLoad, true);
}
void SerializerForBackgroundCompilation::ProcessKeyedPropertyAccess(
Hints const& receiver, Hints const& key, FeedbackSlot slot,
AccessMode access_mode, bool honor_bailout_on_uninitialized) {
if (slot.IsInvalid() || feedback_vector().is_null()) return;
FeedbackSource source(feedback_vector(), slot);
ProcessedFeedback const& feedback =
broker()->ProcessFeedbackForPropertyAccess(source, access_mode,
base::nullopt);
if (honor_bailout_on_uninitialized && BailoutOnUninitialized(feedback)) {
return;
}
Hints new_accumulator_hints(zone());
switch (feedback.kind()) {
case ProcessedFeedback::kElementAccess:
ProcessElementAccess(receiver, key, feedback.AsElementAccess(),
access_mode);
break;
case ProcessedFeedback::kNamedAccess:
ProcessNamedAccess(receiver, feedback.AsNamedAccess(), access_mode,
&new_accumulator_hints);
break;
case ProcessedFeedback::kInsufficient:
break;
default:
UNREACHABLE();
}
if (access_mode == AccessMode::kLoad) {
environment()->accumulator_hints().Clear();
environment()->accumulator_hints().Add(new_accumulator_hints);
} else {
DCHECK(new_accumulator_hints.IsEmpty());
}
}
void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess(
Hints const& receiver, NameRef const& name, FeedbackSlot slot,
AccessMode mode) {
if (BailoutOnUninitialized(slot)) return;
Hints receiver, NameRef const& name, FeedbackSlot slot,
AccessMode access_mode) {
if (slot.IsInvalid() || feedback_vector().is_null()) return;
FeedbackSource source(feedback_vector(), slot);
ProcessedFeedback const& feedback =
broker()->ProcessFeedbackForPropertyAccess(source, access_mode, name);
if (BailoutOnUninitialized(feedback)) return;
Hints new_accumulator_hints(zone());
switch (feedback.kind()) {
case ProcessedFeedback::kNamedAccess:
DCHECK(name.equals(feedback.AsNamedAccess().name()));
ProcessNamedAccess(receiver, feedback.AsNamedAccess(), access_mode,
&new_accumulator_hints);
break;
case ProcessedFeedback::kInsufficient:
break;
default:
UNREACHABLE();
}
if (access_mode == AccessMode::kLoad) {
environment()->accumulator_hints().Clear();
environment()->accumulator_hints().Add(new_accumulator_hints);
} else {
DCHECK(new_accumulator_hints.IsEmpty());
}
}
void SerializerForBackgroundCompilation::ProcessNamedAccess(
Hints receiver, NamedAccessFeedback const& feedback, AccessMode access_mode,
Hints* new_accumulator_hints) {
for (Handle<Map> map : feedback.AsNamedAccess().maps()) {
ProcessMapForNamedPropertyAccess(MapRef(broker(), map), feedback.name(),
access_mode, base::nullopt,
new_accumulator_hints);
}
for (Handle<Map> map :
GetRelevantReceiverMaps(broker()->isolate(), receiver.maps())) {
ProcessMapForNamedPropertyAccess(MapRef(broker(), map), feedback.name(),
access_mode, base::nullopt,
new_accumulator_hints);
}
JSGlobalProxyRef global_proxy =
broker()->native_context().global_proxy_object();
for (Handle<Object> hint : receiver.constants()) {
ObjectRef object(broker(), hint);
if (mode == AccessMode::kLoad && object.IsJSObject()) {
ProcessMapForNamedPropertyAccess(object.AsJSObject().map(), name, mode,
object.AsJSObject());
if (access_mode == AccessMode::kLoad && object.IsJSObject()) {
ProcessMapForNamedPropertyAccess(
object.AsJSObject().map(), feedback.name(), access_mode,
object.AsJSObject(), new_accumulator_hints);
}
// For JSNativeContextSpecialization::ReduceNamedAccessFromNexus.
// TODO(neis): This should be done even if megamorphic.
if (object.equals(global_proxy)) {
global_proxy.GetPropertyCell(name, true);
global_proxy.GetPropertyCell(feedback.name(),
SerializationPolicy::kSerializeIfNeeded);
}
// For JSNativeContextSpecialization::ReduceJSLoadNamed.
if (mode == AccessMode::kLoad && object.IsJSFunction() &&
name.equals(ObjectRef(
if (access_mode == AccessMode::kLoad && object.IsJSFunction() &&
feedback.name().equals(ObjectRef(
broker(), broker()->isolate()->factory()->prototype_string()))) {
object.AsJSFunction().Serialize();
}
}
}
DCHECK_NE(&environment()->accumulator_hints(), &receiver);
environment()->accumulator_hints().Clear();
void SerializerForBackgroundCompilation::ProcessElementAccess(
Hints receiver, Hints key, ElementAccessFeedback const& feedback,
AccessMode access_mode) {
for (auto const& group : feedback.transition_groups()) {
for (Handle<Map> map_handle : group) {
MapRef map(broker(), map_handle);
switch (access_mode) {
case AccessMode::kHas:
case AccessMode::kLoad:
map.SerializeForElementLoad();
break;
case AccessMode::kStore:
map.SerializeForElementStore();
break;
case AccessMode::kStoreInLiteral:
// This operation is fairly local and simple, nothing to serialize.
break;
}
}
}
ProcessFeedbackForPropertyAccess(slot, mode, name);
for (Handle<Object> hint : receiver.constants()) {
ObjectRef receiver_ref(broker(), hint);
for (Handle<Map> map :
GetRelevantReceiverMaps(broker()->isolate(), receiver.maps())) {
ProcessMapForNamedPropertyAccess(MapRef(broker(), map), name, mode);
// For JSNativeContextSpecialization::ReduceElementAccess.
if (receiver_ref.IsJSTypedArray()) {
receiver_ref.AsJSTypedArray().Serialize();
}
// For JSNativeContextSpecialization::ReduceElementLoadFromHeapConstant.
if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
for (Handle<Object> hint : key.constants()) {
ObjectRef key_ref(broker(), hint);
// TODO(neis): Do this for integer-HeapNumbers too?
if (key_ref.IsSmi() && key_ref.AsSmi() >= 0) {
base::Optional<ObjectRef> element =
receiver_ref.GetOwnConstantElement(
key_ref.AsSmi(), SerializationPolicy::kSerializeIfNeeded);
if (!element.has_value() && receiver_ref.IsJSArray()) {
// We didn't find a constant element, but if the receiver is a
// cow-array we can exploit the fact that any future write to the
// element will replace the whole elements storage.
receiver_ref.AsJSArray().GetOwnCowElement(
key_ref.AsSmi(), SerializationPolicy::kSerializeIfNeeded);
}
}
}
}
}
}
void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess(
BytecodeArrayIterator* iterator, AccessMode mode) {
void SerializerForBackgroundCompilation::VisitLdaNamedProperty(
BytecodeArrayIterator* iterator) {
Hints const& receiver =
environment()->register_hints(iterator->GetRegisterOperand(0));
NameRef name(broker(),
iterator->GetConstantForIndexOperand(1, broker()->isolate()));
FeedbackSlot slot = iterator->GetSlotOperand(2);
ProcessNamedPropertyAccess(receiver, name, slot, mode);
}
void SerializerForBackgroundCompilation::VisitLdaNamedProperty(
BytecodeArrayIterator* iterator) {
ProcessNamedPropertyAccess(iterator, AccessMode::kLoad);
ProcessNamedPropertyAccess(receiver, name, slot, AccessMode::kLoad);
}
void SerializerForBackgroundCompilation::VisitLdaNamedPropertyNoFeedback(
......@@ -2473,7 +2495,12 @@ void SerializerForBackgroundCompilation::VisitLdaNamedPropertyNoFeedback(
void SerializerForBackgroundCompilation::VisitStaNamedProperty(
BytecodeArrayIterator* iterator) {
ProcessNamedPropertyAccess(iterator, AccessMode::kStore);
Hints const& receiver =
environment()->register_hints(iterator->GetRegisterOperand(0));
NameRef name(broker(),
iterator->GetConstantForIndexOperand(1, broker()->isolate()));
FeedbackSlot slot = iterator->GetSlotOperand(2);
ProcessNamedPropertyAccess(receiver, name, slot, AccessMode::kStore);
}
void SerializerForBackgroundCompilation::VisitStaNamedPropertyNoFeedback(
......@@ -2484,7 +2511,12 @@ void SerializerForBackgroundCompilation::VisitStaNamedPropertyNoFeedback(
void SerializerForBackgroundCompilation::VisitStaNamedOwnProperty(
BytecodeArrayIterator* iterator) {
ProcessNamedPropertyAccess(iterator, AccessMode::kStoreInLiteral);
Hints const& receiver =
environment()->register_hints(iterator->GetRegisterOperand(0));
NameRef name(broker(),
iterator->GetConstantForIndexOperand(1, broker()->isolate()));
FeedbackSlot slot = iterator->GetSlotOperand(2);
ProcessNamedPropertyAccess(receiver, name, slot, AccessMode::kStoreInLiteral);
}
void SerializerForBackgroundCompilation::VisitTestIn(
......@@ -2493,7 +2525,7 @@ void SerializerForBackgroundCompilation::VisitTestIn(
Hints const& key =
environment()->register_hints(iterator->GetRegisterOperand(0));
FeedbackSlot slot = iterator->GetSlotOperand(1);
ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kHas);
ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kHas, false);
}
// For JSNativeContextSpecialization::ReduceJSOrdinaryHasInstance.
......@@ -2532,7 +2564,8 @@ void SerializerForBackgroundCompilation::ProcessConstantForInstanceOf(
JSObjectRef holder_ref = found_on_proto ? JSObjectRef(broker(), holder)
: constructor.AsJSObject();
base::Optional<ObjectRef> constant = holder_ref.GetOwnDataProperty(
access_info.field_representation(), access_info.field_index(), true);
access_info.field_representation(), access_info.field_index(),
SerializationPolicy::kSerializeIfNeeded);
CHECK(constant.has_value());
if (constant->IsJSFunction()) {
JSFunctionRef function = constant->AsJSFunction();
......@@ -2552,18 +2585,20 @@ void SerializerForBackgroundCompilation::VisitTestInstanceOf(
BytecodeArrayIterator* iterator) {
Hints const& lhs =
environment()->register_hints(iterator->GetRegisterOperand(0));
Hints& rhs = environment()->accumulator_hints();
Hints rhs = environment()->accumulator_hints();
FeedbackSlot slot = iterator->GetSlotOperand(1);
// Incorporate feedback (about the rhs of the operator) into hints.
{
Handle<FeedbackVector> feedback_vector =
environment()->function().feedback_vector();
FeedbackNexus nexus(feedback_vector, slot);
VectorSlotPair rhs_feedback(feedback_vector, slot, nexus.ic_state());
Handle<JSObject> constructor;
if (rhs_feedback.IsValid() &&
nexus.GetConstructorFeedback().ToHandle(&constructor)) {
Hints new_accumulator_hints(zone());
if (slot.IsInvalid() || feedback_vector().is_null()) return;
FeedbackSource source(feedback_vector(), slot);
ProcessedFeedback const& feedback =
broker()->ProcessFeedbackForInstanceOf(source);
// Incorporate feedback (about rhs) into hints copy to simplify processing.
if (!feedback.IsInsufficient()) {
InstanceOfFeedback const& rhs_feedback = feedback.AsInstanceOf();
if (rhs_feedback.value().has_value()) {
Handle<JSObject> constructor = rhs_feedback.value()->object();
rhs.AddConstant(constructor);
}
}
......@@ -2574,6 +2609,21 @@ void SerializerForBackgroundCompilation::VisitTestInstanceOf(
&walk_prototypes);
}
if (walk_prototypes) ProcessHintsForHasInPrototypeChain(lhs);
environment()->accumulator_hints().Clear();
environment()->accumulator_hints().Add(new_accumulator_hints);
}
void SerializerForBackgroundCompilation::VisitToNumeric(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(0);
ProcessUnaryOrBinaryOperation(slot, false);
}
void SerializerForBackgroundCompilation::VisitToNumber(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(0);
ProcessUnaryOrBinaryOperation(slot, false);
}
void SerializerForBackgroundCompilation::VisitThrowReferenceErrorIfHole(
......@@ -2589,7 +2639,7 @@ void SerializerForBackgroundCompilation::VisitStaKeyedProperty(
Hints const& key =
environment()->register_hints(iterator->GetRegisterOperand(1));
FeedbackSlot slot = iterator->GetSlotOperand(2);
ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStore);
ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStore, true);
}
void SerializerForBackgroundCompilation::VisitStaInArrayLiteral(
......@@ -2599,7 +2649,8 @@ void SerializerForBackgroundCompilation::VisitStaInArrayLiteral(
Hints const& key =
environment()->register_hints(iterator->GetRegisterOperand(1));
FeedbackSlot slot = iterator->GetSlotOperand(2);
ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStoreInLiteral);
ProcessKeyedPropertyAccess(receiver, key, slot, AccessMode::kStoreInLiteral,
true);
}
#define DEFINE_CLEAR_ENVIRONMENT(name, ...) \
......@@ -2657,14 +2708,44 @@ UNREACHABLE_BYTECODE_LIST(DEFINE_UNREACHABLE)
KILL_ENVIRONMENT_LIST(DEFINE_KILL)
#undef DEFINE_KILL
#undef CLEAR_ENVIRONMENT_LIST
#undef KILL_ENVIRONMENT_LIST
#define DEFINE_BINARY_OP(name, ...) \
void SerializerForBackgroundCompilation::Visit##name( \
BytecodeArrayIterator* iterator) { \
FeedbackSlot slot = iterator->GetSlotOperand(1); \
ProcessUnaryOrBinaryOperation(slot, true); \
}
BINARY_OP_LIST(DEFINE_BINARY_OP)
#undef DEFINE_BINARY_OP
#define DEFINE_COMPARE_OP(name, ...) \
void SerializerForBackgroundCompilation::Visit##name( \
BytecodeArrayIterator* iterator) { \
FeedbackSlot slot = iterator->GetSlotOperand(1); \
ProcessCompareOperation(slot); \
}
COMPARE_OP_LIST(DEFINE_COMPARE_OP)
#undef DEFINE_COMPARE_OP
#define DEFINE_UNARY_OP(name, ...) \
void SerializerForBackgroundCompilation::Visit##name( \
BytecodeArrayIterator* iterator) { \
FeedbackSlot slot = iterator->GetSlotOperand(0); \
ProcessUnaryOrBinaryOperation(slot, true); \
}
UNARY_OP_LIST(DEFINE_UNARY_OP)
#undef DEFINE_UNARY_OP
#undef BINARY_OP_LIST
#undef CLEAR_ACCUMULATOR_LIST
#undef UNCONDITIONAL_JUMPS_LIST
#undef CLEAR_ENVIRONMENT_LIST
#undef COMPARE_OP_LIST
#undef CONDITIONAL_JUMPS_LIST
#undef IGNORED_BYTECODE_LIST
#undef UNREACHABLE_BYTECODE_LIST
#undef KILL_ENVIRONMENT_LIST
#undef SUPPORTED_BYTECODE_LIST
#undef UNARY_OP_LIST
#undef UNCONDITIONAL_JUMPS_LIST
#undef UNREACHABLE_BYTECODE_LIST
} // namespace compiler
} // namespace internal
......
......@@ -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