Commit 04bb707e authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[turbofan] Preprocess feedback for global accesses (partially)

Main changes:
- Rename ProcessedFeedback to ElementAccessFeedback and introduce a base class
  with the old name ProcessedFeedback.
- Introduce another kind of ProcessedFeedback, namely GlobalAccessFeedback for
  the LoadGlobal/StoreGlobal IC. It's either a PropertyCell or a script context
  slot.
- Produce such processed feedback in the serializer, when visiting LdaGlobal and
  similar bytecodes.
- Consume it, and disallow heap access, in JSNativeContextSpecialization's
  ReduceJSLoadGlobal and ReduceJSStoreGlobal (for --concurrent-inlining).

Minor changes:
- Introduce a FeedbackSource class (pair of FeedbackVector and FeedbackSlot)
  that is used as the key of the processed feedback hash table. We already have
  two similar classes, FeedbackNexus and VectorSlotPair, but both are unsuitable
  for technical reasons (e.g. FeedbackNexus construction accesses the heap).
  Eventually we should remove VectorSlotPair.
- Processed feedback is now returned as a pointer, which is nullptr if the
  original feedback wasn't interesting (e.g. megamorphic).

The title says "partially" because the CL doesn't yet take into account named
accesses where the receiver happens to be the global proxy.

Bug: v8:7790
Change-Id: I4404d98636b91a8f2d5667115944bae4773a4770
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1518184
Commit-Queue: Georg Neis <neis@chromium.org>
Reviewed-by: 's avatarMaya Lekova <mslekova@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60240}
parent e8af602d
...@@ -260,38 +260,19 @@ bool AccessInfoFactory::ComputeElementAccessInfo( ...@@ -260,38 +260,19 @@ bool AccessInfoFactory::ComputeElementAccessInfo(
bool AccessInfoFactory::ComputeElementAccessInfos( bool AccessInfoFactory::ComputeElementAccessInfos(
FeedbackNexus nexus, MapHandles const& maps, AccessMode access_mode, FeedbackNexus nexus, MapHandles const& maps, AccessMode access_mode,
ZoneVector<ElementAccessInfo>* access_infos) const { ZoneVector<ElementAccessInfo>* access_infos) const {
ProcessedFeedback processed(broker()->zone()); ElementAccessFeedback const* processed;
if (FLAG_concurrent_inlining) { if (FLAG_concurrent_inlining) {
// TODO(neis): When concurrent inlining is ready,
// - change the printing below to not look into the heap,
// - remove the call to ProcessFeedbackMapsForElementAccess,
// - remove the Allow* scopes,
AllowCodeDependencyChange dependency_change_;
AllowHandleAllocation handle_allocation_;
AllowHandleDereference handle_dereference_;
AllowHeapAllocation heap_allocation_;
// We have already processed the feedback for this nexus during
// serialization. Use that data! We still process the incoming {maps} (even
// though we don't use them) so that we can print a comparison.
ProcessFeedbackMapsForElementAccess(broker(), maps, &processed);
ProcessedFeedback const& preprocessed = broker()->GetFeedback(nexus);
TRACE_BROKER(broker(), TRACE_BROKER(broker(),
"ComputeElementAccessInfos: using preprocessed feedback " "ComputeElementAccessInfos: using preprocessed feedback "
<< "(slot " << nexus.slot() << " of " << "(slot " << nexus.slot() << " of "
<< Brief(*nexus.vector_handle()) << "; " << "feedback vector handle "
<< preprocessed.receiver_maps.size() << "/" << nexus.vector_handle().address() << ").\n");
<< preprocessed.transitions.size() << " vs " processed = broker()->GetElementAccessFeedback(FeedbackSource(nexus));
<< processed.receiver_maps.size() << "/"
<< processed.transitions.size() << ").\n");
processed.receiver_maps = preprocessed.receiver_maps;
processed.transitions = preprocessed.transitions;
} else { } else {
ProcessFeedbackMapsForElementAccess(broker(), maps, &processed); processed = broker()->ProcessFeedbackMapsForElementAccess(maps);
} }
if (processed.receiver_maps.empty()) return false; if (processed == nullptr) return false;
if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) { if (access_mode == AccessMode::kLoad || access_mode == AccessMode::kHas) {
// For polymorphic loads of similar elements kinds (i.e. all tagged or all // For polymorphic loads of similar elements kinds (i.e. all tagged or all
...@@ -299,13 +280,13 @@ bool AccessInfoFactory::ComputeElementAccessInfos( ...@@ -299,13 +280,13 @@ bool AccessInfoFactory::ComputeElementAccessInfos(
// much faster than transitioning the elements to the worst case, trading a // much faster than transitioning the elements to the worst case, trading a
// TransitionElementsKind for a CheckMaps, avoiding mutation of the array. // TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
ElementAccessInfo access_info; ElementAccessInfo access_info;
if (ConsolidateElementLoad(processed, &access_info)) { if (ConsolidateElementLoad(*processed, &access_info)) {
access_infos->push_back(access_info); access_infos->push_back(access_info);
return true; return true;
} }
} }
for (Handle<Map> receiver_map : processed.receiver_maps) { for (Handle<Map> receiver_map : processed->receiver_maps) {
// Compute the element access information. // Compute the element access information.
ElementAccessInfo access_info; ElementAccessInfo access_info;
if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) { if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
...@@ -313,7 +294,7 @@ bool AccessInfoFactory::ComputeElementAccessInfos( ...@@ -313,7 +294,7 @@ bool AccessInfoFactory::ComputeElementAccessInfos(
} }
// Collect the possible transitions for the {receiver_map}. // Collect the possible transitions for the {receiver_map}.
for (auto transition : processed.transitions) { for (auto transition : processed->transitions) {
if (transition.second.equals(receiver_map)) { if (transition.second.equals(receiver_map)) {
access_info.AddTransitionSource(transition.first); access_info.AddTransitionSource(transition.first);
} }
...@@ -624,8 +605,9 @@ Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind, ...@@ -624,8 +605,9 @@ Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
} // namespace } // namespace
bool AccessInfoFactory::ConsolidateElementLoad( bool AccessInfoFactory::ConsolidateElementLoad(
ProcessedFeedback const& processed, ElementAccessInfo* access_info) const { ElementAccessFeedback const& processed,
ProcessedFeedback::MapIterator it = processed.all_maps(broker()); ElementAccessInfo* access_info) const {
ElementAccessFeedback::MapIterator it = processed.all_maps(broker());
MapRef first_map = it.current(); MapRef first_map = it.current();
InstanceType instance_type = first_map.instance_type(); InstanceType instance_type = first_map.instance_type();
ElementsKind elements_kind = first_map.elements_kind(); ElementsKind elements_kind = first_map.elements_kind();
......
...@@ -25,9 +25,9 @@ namespace compiler { ...@@ -25,9 +25,9 @@ namespace compiler {
// Forward declarations. // Forward declarations.
class CompilationDependencies; class CompilationDependencies;
class ElementAccessFeedback;
class Type; class Type;
class TypeCache; class TypeCache;
struct ProcessedFeedback;
// Whether we are loading a property or storing to a property. // Whether we are loading a property or storing to a property.
// For a store during literal creation, do not walk up the prototype chain. // For a store during literal creation, do not walk up the prototype chain.
...@@ -166,7 +166,7 @@ class AccessInfoFactory final { ...@@ -166,7 +166,7 @@ class AccessInfoFactory final {
ZoneVector<PropertyAccessInfo>* access_infos) const; ZoneVector<PropertyAccessInfo>* access_infos) const;
private: private:
bool ConsolidateElementLoad(ProcessedFeedback const& processed, bool ConsolidateElementLoad(ElementAccessFeedback const& processed,
ElementAccessInfo* access_info) const; ElementAccessInfo* access_info) const;
bool LookupSpecialFieldAccessor(Handle<Map> map, Handle<Name> name, bool LookupSpecialFieldAccessor(Handle<Map> map, Handle<Name> name,
PropertyAccessInfo* access_info) const; PropertyAccessInfo* access_info) const;
......
...@@ -24,6 +24,7 @@ ...@@ -24,6 +24,7 @@
#include "src/objects/js-regexp-inl.h" #include "src/objects/js-regexp-inl.h"
#include "src/objects/module-inl.h" #include "src/objects/module-inl.h"
#include "src/utils.h" #include "src/utils.h"
#include "src/vector-slot-pair.h"
namespace v8 { namespace v8 {
namespace internal { namespace internal {
...@@ -2841,6 +2842,12 @@ void JSBoundFunctionRef::Serialize() { ...@@ -2841,6 +2842,12 @@ void JSBoundFunctionRef::Serialize() {
data()->AsJSBoundFunction()->Serialize(broker()); data()->AsJSBoundFunction()->Serialize(broker());
} }
void PropertyCellRef::Serialize() {
if (broker()->mode() == JSHeapBroker::kDisabled) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsPropertyCell()->Serialize(broker());
}
bool CanInlineElementAccess(MapRef const& map) { bool CanInlineElementAccess(MapRef const& map) {
if (!map.IsJSObjectMap()) return false; if (!map.IsJSObjectMap()) return false;
if (map.is_access_check_needed()) return false; if (map.is_access_check_needed()) return false;
...@@ -2855,24 +2862,62 @@ bool CanInlineElementAccess(MapRef const& map) { ...@@ -2855,24 +2862,62 @@ bool CanInlineElementAccess(MapRef const& map) {
return false; return false;
} }
ProcessedFeedback::ProcessedFeedback(Zone* zone) GlobalAccessFeedback::GlobalAccessFeedback(PropertyCellRef cell)
: receiver_maps(zone), transitions(zone) {} : ProcessedFeedback(kGlobalAccess),
cell_or_context_(cell),
index_and_immutable_(0 /* doesn't matter */) {}
GlobalAccessFeedback::GlobalAccessFeedback(ContextRef script_context,
int slot_index, bool immutable)
: ProcessedFeedback(kGlobalAccess),
cell_or_context_(script_context),
index_and_immutable_(FeedbackNexus::SlotIndexBits::encode(slot_index) |
FeedbackNexus::ImmutabilityBit::encode(immutable)) {
DCHECK_EQ(this->slot_index(), slot_index);
DCHECK_EQ(this->immutable(), immutable);
}
bool GlobalAccessFeedback::IsPropertyCell() const {
return cell_or_context_.IsPropertyCell();
}
PropertyCellRef GlobalAccessFeedback::property_cell() const {
DCHECK(IsPropertyCell());
return cell_or_context_.AsPropertyCell();
}
ProcessedFeedback::MapIterator::MapIterator(ProcessedFeedback const& processed, ContextRef GlobalAccessFeedback::script_context() const {
JSHeapBroker* broker) DCHECK(IsScriptContextSlot());
return cell_or_context_.AsContext();
}
int GlobalAccessFeedback::slot_index() const {
CHECK(IsScriptContextSlot());
return FeedbackNexus::SlotIndexBits::decode(index_and_immutable_);
}
bool GlobalAccessFeedback::immutable() const {
CHECK(IsScriptContextSlot());
return FeedbackNexus::ImmutabilityBit::decode(index_and_immutable_);
}
ElementAccessFeedback::ElementAccessFeedback(Zone* zone)
: ProcessedFeedback(kElementAccess),
receiver_maps(zone),
transitions(zone) {}
ElementAccessFeedback::MapIterator::MapIterator(
ElementAccessFeedback const& processed, JSHeapBroker* broker)
: processed_(processed), broker_(broker) { : processed_(processed), broker_(broker) {
CHECK_LT(processed.receiver_maps.size(), CHECK_LT(processed.receiver_maps.size(),
std::numeric_limits<size_t>::max() - processed.transitions.size()); std::numeric_limits<size_t>::max() - processed.transitions.size());
} }
bool ProcessedFeedback::MapIterator::done() const { bool ElementAccessFeedback::MapIterator::done() const {
return index_ >= return index_ >=
processed_.receiver_maps.size() + processed_.transitions.size(); processed_.receiver_maps.size() + processed_.transitions.size();
} }
void ProcessedFeedback::MapIterator::advance() { index_++; } void ElementAccessFeedback::MapIterator::advance() { index_++; }
MapRef ProcessedFeedback::MapIterator::current() const { MapRef ElementAccessFeedback::MapIterator::current() const {
CHECK(!done()); CHECK(!done());
size_t receiver_maps_size = processed_.receiver_maps.size(); size_t receiver_maps_size = processed_.receiver_maps.size();
Handle<Map> map; Handle<Map> map;
...@@ -2884,73 +2929,140 @@ MapRef ProcessedFeedback::MapIterator::current() const { ...@@ -2884,73 +2929,140 @@ MapRef ProcessedFeedback::MapIterator::current() const {
return MapRef(broker_, map); return MapRef(broker_, map);
} }
ProcessedFeedback::MapIterator ProcessedFeedback::all_maps( ElementAccessFeedback::MapIterator ElementAccessFeedback::all_maps(
JSHeapBroker* broker) const { JSHeapBroker* broker) const {
return MapIterator(*this, broker); return MapIterator(*this, broker);
} }
ProcessedFeedback& JSHeapBroker::CreateEmptyFeedback( FeedbackSource::FeedbackSource(FeedbackNexus const& nexus)
FeedbackNexus const& nexus) { : vector(nexus.vector_handle()), slot(nexus.slot()) {}
auto insertion = feedback_.insert({nexus, ProcessedFeedback(zone())});
FeedbackSource::FeedbackSource(VectorSlotPair const& pair)
: vector(pair.vector()), slot(pair.slot()) {}
void JSHeapBroker::SetFeedback(FeedbackSource const& source,
ProcessedFeedback const* feedback) {
auto insertion = feedback_.insert({source, feedback});
CHECK(insertion.second); CHECK(insertion.second);
return insertion.first->second;
} }
bool JSHeapBroker::HasFeedback(FeedbackNexus const& nexus) const { bool JSHeapBroker::HasFeedback(FeedbackSource const& source) const {
return feedback_.find(nexus) != feedback_.end(); return feedback_.find(source) != feedback_.end();
} }
ProcessedFeedback& JSHeapBroker::GetFeedback(FeedbackNexus const& nexus) { ProcessedFeedback const* JSHeapBroker::GetFeedback(
auto it = feedback_.find(nexus); FeedbackSource const& source) const {
auto it = feedback_.find(source);
CHECK_NE(it, feedback_.end()); CHECK_NE(it, feedback_.end());
return it->second; return it->second;
} }
void ProcessFeedbackMapsForElementAccess(JSHeapBroker* broker, ElementAccessFeedback const* JSHeapBroker::GetElementAccessFeedback(
MapHandles const& maps, FeedbackSource const& source) const {
ProcessedFeedback* processed) { ProcessedFeedback const* feedback = GetFeedback(source);
CHECK(processed->receiver_maps.empty()); if (feedback == nullptr) return nullptr;
CHECK(processed->transitions.empty()); CHECK_EQ(feedback->kind(), ProcessedFeedback::kElementAccess);
return static_cast<ElementAccessFeedback const*>(feedback);
}
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);
}
ElementAccessFeedback const* JSHeapBroker::ProcessFeedbackMapsForElementAccess(
MapHandles const& maps) {
// Collect possible transition targets. // Collect possible transition targets.
MapHandles possible_transition_targets; MapHandles possible_transition_targets;
possible_transition_targets.reserve(maps.size()); possible_transition_targets.reserve(maps.size());
for (Handle<Map> map : maps) { for (Handle<Map> map : maps) {
if (CanInlineElementAccess(MapRef(broker, map)) && if (CanInlineElementAccess(MapRef(this, map)) &&
IsFastElementsKind(map->elements_kind()) && IsFastElementsKind(map->elements_kind()) &&
GetInitialFastElementsKind() != map->elements_kind()) { GetInitialFastElementsKind() != map->elements_kind()) {
possible_transition_targets.push_back(map); possible_transition_targets.push_back(map);
} }
} }
if (maps.empty()) return nullptr;
ElementAccessFeedback* result = new (zone()) ElementAccessFeedback(zone());
// Separate the actual receiver maps and the possible transition sources. // Separate the actual receiver maps and the possible transition sources.
for (Handle<Map> map : maps) { for (Handle<Map> map : maps) {
// Don't generate elements kind transitions from stable maps. // Don't generate elements kind transitions from stable maps.
Map transition_target = Map transition_target = map->is_stable()
map->is_stable() ? Map() ? Map()
: map->FindElementsKindTransitionedMap( : map->FindElementsKindTransitionedMap(
broker->isolate(), possible_transition_targets); isolate(), possible_transition_targets);
if (transition_target.is_null()) { if (transition_target.is_null()) {
processed->receiver_maps.push_back(map); result->receiver_maps.push_back(map);
} else { } else {
processed->transitions.emplace_back( result->transitions.emplace_back(map,
map, handle(transition_target, broker->isolate())); handle(transition_target, isolate()));
} }
} }
#ifdef ENABLE_SLOW_DCHECKS #ifdef ENABLE_SLOW_DCHECKS
// No transition sources appear in {receiver_maps}. // No transition sources appear in {receiver_maps}.
// All transition targets appear in {receiver_maps}. // All transition targets appear in {receiver_maps}.
for (auto& transition : processed->transitions) { for (auto& transition : result->transitions) {
USE(transition);
CHECK(std::none_of( CHECK(std::none_of(
processed->receiver_maps.cbegin(), processed->receiver_maps.cend(), result->receiver_maps.cbegin(), result->receiver_maps.cend(),
[&](Handle<Map> map) { return map.equals(transition.first); })); [&](Handle<Map> map) { return map.equals(transition.first); }));
CHECK(std::any_of( CHECK(std::any_of(
processed->receiver_maps.cbegin(), processed->receiver_maps.cend(), result->receiver_maps.cbegin(), result->receiver_maps.cend(),
[&](Handle<Map> map) { return map.equals(transition.second); })); [&](Handle<Map> map) { return map.equals(transition.second); }));
} }
#endif #endif
CHECK(!result->receiver_maps.empty());
return result;
}
GlobalAccessFeedback const* JSHeapBroker::ProcessFeedbackForGlobalAccess(
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.ic_state() != MONOMORPHIC || nexus.GetFeedback()->IsCleared()) {
return nullptr;
}
Handle<Object> feedback_value(nexus.GetFeedback()->GetHeapObjectOrSmi(),
isolate());
if (feedback_value->IsSmi()) {
// The wanted name belongs to a script-scope variable and the feedback tells
// us where to find its value.
int number = feedback_value->Number();
int const script_context_index =
FeedbackNexus::ContextIndexBits::decode(number);
int const context_slot_index = FeedbackNexus::SlotIndexBits::decode(number);
bool const immutable = FeedbackNexus::ImmutabilityBit::decode(number);
Handle<Context> context = ScriptContextTable::GetContext(
isolate(), native_context().script_context_table().object(),
script_context_index);
{
ObjectRef contents(this,
handle(context->get(context_slot_index), isolate()));
CHECK(!contents.equals(
ObjectRef(this, isolate()->factory()->the_hole_value())));
}
return new (zone()) GlobalAccessFeedback(ContextRef(this, context),
context_slot_index, immutable);
}
CHECK(feedback_value->IsPropertyCell());
// The wanted name belongs (or did belong) to a property on the global
// 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);
} }
#undef BIMODAL_ACCESSOR #undef BIMODAL_ACCESSOR
......
...@@ -21,6 +21,7 @@ namespace v8 { ...@@ -21,6 +21,7 @@ namespace v8 {
namespace internal { namespace internal {
class BytecodeArray; class BytecodeArray;
class VectorSlotPair;
class FixedDoubleArray; class FixedDoubleArray;
class HeapNumber; class HeapNumber;
class InternalizedString; class InternalizedString;
...@@ -186,6 +187,8 @@ class PropertyCellRef : public HeapObjectRef { ...@@ -186,6 +187,8 @@ class PropertyCellRef : public HeapObjectRef {
Handle<PropertyCell> object() const; Handle<PropertyCell> object() const;
PropertyDetails property_details() const; PropertyDetails property_details() const;
void Serialize();
ObjectRef value() const; ObjectRef value() const;
}; };
...@@ -622,8 +625,40 @@ class InternalizedStringRef : public StringRef { ...@@ -622,8 +625,40 @@ class InternalizedStringRef : public StringRef {
static const uint32_t kNotAnArrayIndex = -1; // 2^32-1 is not a valid index. static const uint32_t kNotAnArrayIndex = -1; // 2^32-1 is not a valid index.
}; };
struct ProcessedFeedback { class ProcessedFeedback : public ZoneObject {
explicit ProcessedFeedback(Zone* zone); public:
enum Kind { kElementAccess, kGlobalAccess };
Kind kind() const { return kind_; }
protected:
explicit ProcessedFeedback(Kind kind) : kind_(kind) {}
private:
Kind const kind_;
};
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;
private:
ObjectRef const cell_or_context_;
int const index_and_immutable_;
};
class ElementAccessFeedback : public ProcessedFeedback {
public:
explicit ElementAccessFeedback(Zone* zone);
// No transition sources appear in {receiver_maps}. // No transition sources appear in {receiver_maps}.
// All transition targets appear in {receiver_maps}. // All transition targets appear in {receiver_maps}.
...@@ -637,12 +672,12 @@ struct ProcessedFeedback { ...@@ -637,12 +672,12 @@ struct ProcessedFeedback {
MapRef current() const; MapRef current() const;
private: private:
friend struct ProcessedFeedback; friend class ElementAccessFeedback;
explicit MapIterator(ProcessedFeedback const& processed, explicit MapIterator(ElementAccessFeedback const& processed,
JSHeapBroker* broker); JSHeapBroker* broker);
ProcessedFeedback const& processed_; ElementAccessFeedback const& processed_;
JSHeapBroker* const broker_; JSHeapBroker* const broker_;
size_t index_ = 0; size_t index_ = 0;
}; };
...@@ -651,6 +686,29 @@ struct ProcessedFeedback { ...@@ -651,6 +686,29 @@ struct ProcessedFeedback {
MapIterator all_maps(JSHeapBroker* broker) const; MapIterator all_maps(JSHeapBroker* broker) const;
}; };
struct FeedbackSource {
FeedbackSource(Handle<FeedbackVector> vector_, FeedbackSlot slot_)
: vector(vector_), slot(slot_) {}
explicit FeedbackSource(FeedbackNexus const& nexus);
explicit FeedbackSource(VectorSlotPair const& pair);
Handle<FeedbackVector> const vector;
FeedbackSlot const slot;
struct Hash {
size_t operator()(FeedbackSource const& source) const {
return base::hash_combine(source.vector.address(), source.slot);
}
};
struct Equal {
bool operator()(FeedbackSource const& lhs,
FeedbackSource const& rhs) const {
return lhs.vector.equals(rhs.vector) && lhs.slot == rhs.slot;
}
};
};
class V8_EXPORT_PRIVATE JSHeapBroker : public NON_EXPORTED_BASE(ZoneObject) { class V8_EXPORT_PRIVATE JSHeapBroker : public NON_EXPORTED_BASE(ZoneObject) {
public: public:
JSHeapBroker(Isolate* isolate, Zone* broker_zone); JSHeapBroker(Isolate* isolate, Zone* broker_zone);
...@@ -681,9 +739,24 @@ class V8_EXPORT_PRIVATE JSHeapBroker : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -681,9 +739,24 @@ class V8_EXPORT_PRIVATE JSHeapBroker : public NON_EXPORTED_BASE(ZoneObject) {
// %ObjectPrototype%. // %ObjectPrototype%.
bool IsArrayOrObjectPrototype(const JSObjectRef& object) const; bool IsArrayOrObjectPrototype(const JSObjectRef& object) const;
ProcessedFeedback& CreateEmptyFeedback(FeedbackNexus const& nexus); bool HasFeedback(FeedbackSource const& source) const;
bool HasFeedback(FeedbackNexus const& nexus) const; // The processed {feedback} can be {nullptr}, indicating that the original
ProcessedFeedback& GetFeedback(FeedbackNexus const& nexus); // 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.
ElementAccessFeedback const* GetElementAccessFeedback(
FeedbackSource const& source) const;
GlobalAccessFeedback const* GetGlobalAccessFeedback(
FeedbackSource const& source) const;
// TODO(neis): Move these into serializer when we're always in the background.
ElementAccessFeedback const* ProcessFeedbackMapsForElementAccess(
MapHandles const& maps);
GlobalAccessFeedback const* ProcessFeedbackForGlobalAccess(
FeedbackSource const& source);
std::ostream& Trace(); std::ostream& Trace();
void IncrementTracingIndentation(); void IncrementTracingIndentation();
...@@ -697,18 +770,6 @@ class V8_EXPORT_PRIVATE JSHeapBroker : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -697,18 +770,6 @@ class V8_EXPORT_PRIVATE JSHeapBroker : public NON_EXPORTED_BASE(ZoneObject) {
void SerializeShareableObjects(); void SerializeShareableObjects();
void CollectArrayAndObjectPrototypes(); void CollectArrayAndObjectPrototypes();
struct FeedbackNexusHash {
size_t operator()(FeedbackNexus const& nexus) const {
return base::hash_combine(nexus.vector_handle().address(), nexus.slot());
}
};
struct FeedbackNexusEqual {
bool operator()(FeedbackNexus const& lhs, FeedbackNexus const& rhs) const {
return lhs.vector_handle().equals(rhs.vector_handle()) &&
lhs.slot() == rhs.slot();
}
};
Isolate* const isolate_; Isolate* const isolate_;
Zone* const broker_zone_; Zone* const broker_zone_;
Zone* current_zone_; Zone* current_zone_;
...@@ -721,8 +782,8 @@ class V8_EXPORT_PRIVATE JSHeapBroker : public NON_EXPORTED_BASE(ZoneObject) { ...@@ -721,8 +782,8 @@ class V8_EXPORT_PRIVATE JSHeapBroker : public NON_EXPORTED_BASE(ZoneObject) {
StdoutStream trace_out_; StdoutStream trace_out_;
unsigned trace_indentation_ = 0; unsigned trace_indentation_ = 0;
PerIsolateCompilerCache* compiler_cache_; PerIsolateCompilerCache* compiler_cache_;
ZoneUnorderedMap<FeedbackNexus, ProcessedFeedback, FeedbackNexusHash, ZoneUnorderedMap<FeedbackSource, ProcessedFeedback const*,
FeedbackNexusEqual> FeedbackSource::Hash, FeedbackSource::Equal>
feedback_; feedback_;
static const size_t kMinimalRefsBucketCount = 8; // must be power of 2 static const size_t kMinimalRefsBucketCount = 8; // must be power of 2
...@@ -743,9 +804,6 @@ Reduction NoChangeBecauseOfMissingData(JSHeapBroker* broker, ...@@ -743,9 +804,6 @@ Reduction NoChangeBecauseOfMissingData(JSHeapBroker* broker,
// Miscellaneous definitions that should be moved elsewhere once concurrent // Miscellaneous definitions that should be moved elsewhere once concurrent
// compilation is finished. // compilation is finished.
bool CanInlineElementAccess(MapRef const& map); bool CanInlineElementAccess(MapRef const& map);
void ProcessFeedbackMapsForElementAccess(JSHeapBroker* broker,
MapHandles const& maps,
ProcessedFeedback* processed);
#define TRACE_BROKER(broker, x) \ #define TRACE_BROKER(broker, x) \
do { \ do { \
......
...@@ -791,24 +791,27 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -791,24 +791,27 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
it.TryLookupCachedProperty(); it.TryLookupCachedProperty();
if (it.state() != LookupIterator::DATA) return NoChange(); if (it.state() != LookupIterator::DATA) return NoChange();
if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) return NoChange(); if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) return NoChange();
Handle<PropertyCell> property_cell = it.GetPropertyCell(); PropertyCellRef property_cell(broker(), it.GetPropertyCell());
property_cell.Serialize();
return ReduceGlobalAccess(node, receiver, value, name, access_mode, index, return ReduceGlobalAccess(node, receiver, value, name, access_mode, index,
property_cell); property_cell);
} }
Reduction JSNativeContextSpecialization::ReduceGlobalAccess( Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
Node* node, Node* receiver, Node* value, Handle<Name> name, Node* node, Node* receiver, Node* value, Handle<Name> name,
AccessMode access_mode, Node* index, Handle<PropertyCell> property_cell) { AccessMode access_mode, Node* index, PropertyCellRef const& property_cell) {
Node* effect = NodeProperties::GetEffectInput(node); Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node); Node* control = NodeProperties::GetControlInput(node);
Handle<Object> property_cell_value(property_cell->value(), isolate()); ObjectRef property_cell_value = property_cell.value();
if (property_cell_value.is_identical_to(factory()->the_hole_value())) { if (property_cell_value.IsHeapObject() &&
property_cell_value.AsHeapObject().map().oddball_type() ==
OddballType::kHole) {
// The property cell is no longer valid. // The property cell is no longer valid.
return NoChange(); return NoChange();
} }
PropertyDetails property_details = property_cell->property_details(); PropertyDetails property_details = property_cell.property_details();
PropertyCellType property_cell_type = property_details.cell_type(); PropertyCellType property_cell_type = property_details.cell_type();
DCHECK_EQ(kData, property_details.kind()); DCHECK_EQ(kData, property_details.kind());
...@@ -823,8 +826,8 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -823,8 +826,8 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
} else if (property_cell_type == PropertyCellType::kConstantType) { } else if (property_cell_type == PropertyCellType::kConstantType) {
// There's also no fast-path to store to a global cell which pretended // There's also no fast-path to store to a global cell which pretended
// to be stable, but is no longer stable now. // to be stable, but is no longer stable now.
if (property_cell_value->IsHeapObject() && if (property_cell_value.IsHeapObject() &&
!Handle<HeapObject>::cast(property_cell_value)->map()->is_stable()) { !property_cell_value.AsHeapObject().map().is_stable()) {
return NoChange(); return NoChange();
} }
} }
...@@ -866,8 +869,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -866,8 +869,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
// can be deleted or reconfigured to an accessor property). // can be deleted or reconfigured to an accessor property).
if (property_details.cell_type() != PropertyCellType::kMutable || if (property_details.cell_type() != PropertyCellType::kMutable ||
property_details.IsConfigurable()) { property_details.IsConfigurable()) {
dependencies()->DependOnGlobalProperty( dependencies()->DependOnGlobalProperty(property_cell);
PropertyCellRef(broker(), property_cell));
} }
// Load from constant/undefined global property can be constant-folded. // Load from constant/undefined global property can be constant-folded.
...@@ -876,8 +878,9 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -876,8 +878,9 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
value = access_mode == AccessMode::kHas value = access_mode == AccessMode::kHas
? jsgraph()->TrueConstant() ? jsgraph()->TrueConstant()
: jsgraph()->Constant(property_cell_value); : jsgraph()->Constant(property_cell_value);
CHECK( DCHECK(!property_cell_value.IsHeapObject() ||
!property_cell_value.is_identical_to(factory()->the_hole_value())); property_cell_value.AsHeapObject().map().oddball_type() !=
OddballType::kHole);
} else { } else {
DCHECK_NE(AccessMode::kHas, access_mode); DCHECK_NE(AccessMode::kHas, access_mode);
...@@ -887,16 +890,15 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -887,16 +890,15 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
MachineRepresentation representation = MachineRepresentation::kTagged; MachineRepresentation representation = MachineRepresentation::kTagged;
if (property_details.cell_type() == PropertyCellType::kConstantType) { if (property_details.cell_type() == PropertyCellType::kConstantType) {
// Compute proper type based on the current value in the cell. // Compute proper type based on the current value in the cell.
if (property_cell_value->IsSmi()) { if (property_cell_value.IsSmi()) {
property_cell_value_type = Type::SignedSmall(); property_cell_value_type = Type::SignedSmall();
representation = MachineRepresentation::kTaggedSigned; representation = MachineRepresentation::kTaggedSigned;
} else if (property_cell_value->IsNumber()) { } else if (property_cell_value.IsHeapNumber()) {
property_cell_value_type = Type::Number(); property_cell_value_type = Type::Number();
representation = MachineRepresentation::kTaggedPointer; representation = MachineRepresentation::kTaggedPointer;
} else { } else {
MapRef property_cell_value_map( MapRef property_cell_value_map =
broker(), handle(HeapObject::cast(*property_cell_value)->map(), property_cell_value.AsHeapObject().map();
isolate()));
property_cell_value_type = Type::For(property_cell_value_map); property_cell_value_type = Type::For(property_cell_value_map);
representation = MachineRepresentation::kTaggedPointer; representation = MachineRepresentation::kTaggedPointer;
...@@ -912,7 +914,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -912,7 +914,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
value = effect = graph()->NewNode( value = effect = graph()->NewNode(
simplified()->LoadField(ForPropertyCellValue( simplified()->LoadField(ForPropertyCellValue(
representation, property_cell_value_type, map, name)), representation, property_cell_value_type, map, name)),
jsgraph()->HeapConstant(property_cell), effect, control); jsgraph()->Constant(property_cell), effect, control);
} }
} }
} else { } else {
...@@ -926,8 +928,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -926,8 +928,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
case PropertyCellType::kConstant: { case PropertyCellType::kConstant: {
// Record a code dependency on the cell, and just deoptimize if the new // Record a code dependency on the cell, and just deoptimize if the new
// value doesn't match the previous value stored inside the cell. // value doesn't match the previous value stored inside the cell.
dependencies()->DependOnGlobalProperty( dependencies()->DependOnGlobalProperty(property_cell);
PropertyCellRef(broker(), property_cell));
Node* check = Node* check =
graph()->NewNode(simplified()->ReferenceEqual(), value, graph()->NewNode(simplified()->ReferenceEqual(), value,
jsgraph()->Constant(property_cell_value)); jsgraph()->Constant(property_cell_value));
...@@ -940,28 +941,25 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -940,28 +941,25 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
// Record a code dependency on the cell, and just deoptimize if the new // Record a code dependency on the cell, and just deoptimize if the new
// values' type doesn't match the type of the previous value in the // values' type doesn't match the type of the previous value in the
// cell. // cell.
dependencies()->DependOnGlobalProperty( dependencies()->DependOnGlobalProperty(property_cell);
PropertyCellRef(broker(), property_cell));
Type property_cell_value_type; Type property_cell_value_type;
MachineRepresentation representation = MachineRepresentation::kTagged; MachineRepresentation representation = MachineRepresentation::kTagged;
if (property_cell_value->IsHeapObject()) { if (property_cell_value.IsHeapObject()) {
// We cannot do anything if the {property_cell_value}s map is no // We cannot do anything if the {property_cell_value}s map is no
// longer stable. // longer stable.
Handle<Map> property_cell_value_map( MapRef property_cell_value_map =
Handle<HeapObject>::cast(property_cell_value)->map(), isolate()); property_cell_value.AsHeapObject().map();
DCHECK(property_cell_value_map->is_stable()); dependencies()->DependOnStableMap(property_cell_value_map);
dependencies()->DependOnStableMap(
MapRef(broker(), property_cell_value_map));
// Check that the {value} is a HeapObject. // Check that the {value} is a HeapObject.
value = effect = graph()->NewNode(simplified()->CheckHeapObject(), value = effect = graph()->NewNode(simplified()->CheckHeapObject(),
value, effect, control); value, effect, control);
// Check {value} map against the {property_cell} map. // Check {value} map against the {property_cell} map.
effect = effect = graph()->NewNode(
graph()->NewNode(simplified()->CheckMaps( simplified()->CheckMaps(
CheckMapsFlag::kNone, CheckMapsFlag::kNone,
ZoneHandleSet<Map>(property_cell_value_map)), ZoneHandleSet<Map>(property_cell_value_map.object())),
value, effect, control); value, effect, control);
property_cell_value_type = Type::OtherInternal(); property_cell_value_type = Type::OtherInternal();
representation = MachineRepresentation::kTaggedPointer; representation = MachineRepresentation::kTaggedPointer;
...@@ -975,20 +973,19 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -975,20 +973,19 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
effect = graph()->NewNode(simplified()->StoreField(ForPropertyCellValue( effect = graph()->NewNode(simplified()->StoreField(ForPropertyCellValue(
representation, property_cell_value_type, representation, property_cell_value_type,
MaybeHandle<Map>(), name)), MaybeHandle<Map>(), name)),
jsgraph()->HeapConstant(property_cell), value, jsgraph()->Constant(property_cell), value,
effect, control); effect, control);
break; break;
} }
case PropertyCellType::kMutable: { case PropertyCellType::kMutable: {
// Record a code dependency on the cell, and just deoptimize if the // Record a code dependency on the cell, and just deoptimize if the
// property ever becomes read-only. // property ever becomes read-only.
dependencies()->DependOnGlobalProperty( dependencies()->DependOnGlobalProperty(property_cell);
PropertyCellRef(broker(), property_cell));
effect = graph()->NewNode( effect = graph()->NewNode(
simplified()->StoreField(ForPropertyCellValue( simplified()->StoreField(ForPropertyCellValue(
MachineRepresentation::kTagged, Type::NonInternal(), MachineRepresentation::kTagged, Type::NonInternal(),
MaybeHandle<Map>(), name)), MaybeHandle<Map>(), name)),
jsgraph()->HeapConstant(property_cell), value, effect, control); jsgraph()->Constant(property_cell), value, effect, control);
break; break;
} }
} }
...@@ -1000,104 +997,83 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess( ...@@ -1000,104 +997,83 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) { Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode()); DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
Node* effect = NodeProperties::GetEffectInput(node); DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining);
LoadGlobalParameters const& p = LoadGlobalParametersOf(node->op()); LoadGlobalParameters const& p = LoadGlobalParametersOf(node->op());
if (!p.feedback().IsValid()) return NoChange(); if (!p.feedback().IsValid()) return NoChange();
FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); FeedbackSource source(p.feedback());
DCHECK(nexus.kind() == FeedbackSlotKind::kLoadGlobalInsideTypeof || GlobalAccessFeedback const* processed;
nexus.kind() == FeedbackSlotKind::kLoadGlobalNotInsideTypeof); if (FLAG_concurrent_inlining) {
if (nexus.ic_state() != MONOMORPHIC || nexus.GetFeedback()->IsCleared()) { processed = broker()->GetGlobalAccessFeedback(source);
return NoChange(); TRACE_BROKER(broker(), "ReduceJSLoadGlobal: using preprocessed feedback "
<< "(slot " << p.feedback().slot()
<< " of feedback vector handle "
<< p.feedback().vector().address() << ").\n");
} else {
processed = broker()->ProcessFeedbackForGlobalAccess(source);
} }
Handle<Object> feedback(nexus.GetFeedback()->GetHeapObjectOrSmi(), isolate());
if (feedback->IsSmi()) { if (processed == nullptr) return NoChange();
// The wanted name belongs to a script-scope variable and the feedback tells
// us where to find its value.
int number = feedback->Number(); if (processed->IsScriptContextSlot()) {
int const script_context_index = Node* effect = NodeProperties::GetEffectInput(node);
FeedbackNexus::ContextIndexBits::decode(number); Node* script_context = jsgraph()->Constant(processed->script_context());
int const context_slot_index = FeedbackNexus::SlotIndexBits::decode(number); Node* value = effect =
bool const immutable = FeedbackNexus::ImmutabilityBit::decode(number); graph()->NewNode(javascript()->LoadContext(0, processed->slot_index(),
Handle<Context> context = ScriptContextTable::GetContext( processed->immutable()),
isolate(), native_context().script_context_table().object(), script_context, effect);
script_context_index);
{
ObjectRef contents(broker(),
handle(context->get(context_slot_index), isolate()));
CHECK(!contents.equals(ObjectRef(broker(), factory()->the_hole_value())));
}
Node* context_constant = jsgraph()->Constant(context);
Node* value = effect = graph()->NewNode(
javascript()->LoadContext(0, context_slot_index, immutable),
context_constant, effect);
ReplaceWithValue(node, value, effect); ReplaceWithValue(node, value, effect);
return Replace(value); return Replace(value);
} }
CHECK(feedback->IsPropertyCell()); CHECK(processed->IsPropertyCell());
// The wanted name belongs (or did belong) to a property on the global object
// and the feedback is the cell holding its value.
return ReduceGlobalAccess(node, nullptr, nullptr, p.name(), AccessMode::kLoad, return ReduceGlobalAccess(node, nullptr, nullptr, p.name(), AccessMode::kLoad,
nullptr, Handle<PropertyCell>::cast(feedback)); nullptr, processed->property_cell());
} }
Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) { Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) {
DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode()); DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining);
Node* value = NodeProperties::GetValueInput(node, 0); Node* value = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
StoreGlobalParameters const& p = StoreGlobalParametersOf(node->op()); StoreGlobalParameters const& p = StoreGlobalParametersOf(node->op());
if (!p.feedback().IsValid()) return NoChange(); if (!p.feedback().IsValid()) return NoChange();
FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot()); FeedbackSource source(p.feedback());
DCHECK(nexus.kind() == FeedbackSlotKind::kStoreGlobalSloppy || GlobalAccessFeedback const* processed;
nexus.kind() == FeedbackSlotKind::kStoreGlobalStrict); if (FLAG_concurrent_inlining) {
if (nexus.ic_state() != MONOMORPHIC || nexus.GetFeedback()->IsCleared()) { processed = broker()->GetGlobalAccessFeedback(source);
return NoChange(); TRACE_BROKER(broker(), "ReduceJSStoreGlobal: using preprocessed feedback "
<< "(slot " << p.feedback().slot()
<< " of feedback vector handle "
<< p.feedback().vector().address() << ").\n");
} else {
processed = broker()->ProcessFeedbackForGlobalAccess(source);
} }
Handle<Object> feedback(nexus.GetFeedback()->GetHeapObjectOrSmi(), isolate());
if (feedback->IsSmi()) {
// The wanted name belongs to a script-scope variable and the feedback tells
// us where to find its value.
int const script_context_index = if (processed == nullptr) return NoChange();
FeedbackNexus::ContextIndexBits::decode(feedback->Number());
int const context_slot_index =
FeedbackNexus::SlotIndexBits::decode(feedback->Number());
bool const immutable =
FeedbackNexus::ImmutabilityBit::decode(feedback->Number());
Handle<Context> context = ScriptContextTable::GetContext(
isolate(), native_context().script_context_table().object(),
script_context_index);
if (immutable) return NoChange(); if (processed->IsScriptContextSlot()) {
if (processed->immutable()) return NoChange();
{ Node* effect = NodeProperties::GetEffectInput(node);
ObjectRef contents(broker(), Node* control = NodeProperties::GetControlInput(node);
handle(context->get(context_slot_index), isolate())); Node* script_context = jsgraph()->Constant(processed->script_context());
CHECK(!contents.equals(ObjectRef(broker(), factory()->the_hole_value()))); effect =
} graph()->NewNode(javascript()->StoreContext(0, processed->slot_index()),
value, script_context, effect, control);
Node* context_constant = jsgraph()->Constant(context);
effect = graph()->NewNode(javascript()->StoreContext(0, context_slot_index),
value, context_constant, effect, control);
ReplaceWithValue(node, value, effect, control); ReplaceWithValue(node, value, effect, control);
return Replace(value); return Replace(value);
} }
CHECK(feedback->IsPropertyCell()); if (processed->IsPropertyCell()) {
// The wanted name belongs (or did belong) to a property on the global object return ReduceGlobalAccess(node, nullptr, value, p.name(),
// and the feedback is the cell holding its value. AccessMode::kStore, nullptr,
return ReduceGlobalAccess(node, nullptr, value, p.name(), AccessMode::kStore, processed->property_cell());
nullptr, Handle<PropertyCell>::cast(feedback)); }
UNREACHABLE();
} }
Reduction JSNativeContextSpecialization::ReduceNamedAccess( Reduction JSNativeContextSpecialization::ReduceNamedAccess(
......
...@@ -116,7 +116,8 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final ...@@ -116,7 +116,8 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
Node* index = nullptr); Node* index = nullptr);
Reduction ReduceGlobalAccess(Node* node, Node* receiver, Node* value, Reduction ReduceGlobalAccess(Node* node, Node* receiver, Node* value,
Handle<Name> name, AccessMode access_mode, Handle<Name> name, AccessMode access_mode,
Node* index, Handle<PropertyCell> property_cell); Node* index,
PropertyCellRef const& property_cell);
Reduction ReduceKeyedLoadFromHeapConstant(Node* node, Node* index, Reduction ReduceKeyedLoadFromHeapConstant(Node* node, Node* index,
FeedbackNexus const& nexus, FeedbackNexus const& nexus,
AccessMode access_mode, AccessMode access_mode,
......
...@@ -683,13 +683,59 @@ void SerializerForBackgroundCompilation::VisitConstructWithSpread( ...@@ -683,13 +683,59 @@ void SerializerForBackgroundCompilation::VisitConstructWithSpread(
ProcessCallOrConstruct(callee, new_target, arguments, slot, true); ProcessCallOrConstruct(callee, new_target, arguments, slot, true);
} }
void SerializerForBackgroundCompilation::ProcessFeedbackForGlobalAccess(
FeedbackSlot slot) {
if (slot.IsInvalid()) return;
if (environment()->function().feedback_vector.is_null()) return;
FeedbackSource source(environment()->function().feedback_vector, slot);
if (!broker()->HasFeedback(source)) {
broker()->SetFeedback(source,
broker()->ProcessFeedbackForGlobalAccess(source));
}
// TODO(neis, mvstanton): In the case of an immutable script context slot, we
// must also serialize that slot such that ContextRef::get can retrieve the
// value.
}
void SerializerForBackgroundCompilation::VisitLdaGlobal(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(1);
ProcessFeedbackForGlobalAccess(slot);
// TODO(neis, mvstanton): In the case of an immutable script context slot, add
// the value as constant hint here and below
environment()->accumulator_hints().Clear();
}
void SerializerForBackgroundCompilation::VisitLdaGlobalInsideTypeof(
BytecodeArrayIterator* iterator) {
VisitLdaGlobal(iterator);
}
void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlot(
BytecodeArrayIterator* iterator) {
VisitLdaGlobal(iterator);
}
void SerializerForBackgroundCompilation::VisitLdaLookupGlobalSlotInsideTypeof(
BytecodeArrayIterator* iterator) {
VisitLdaGlobal(iterator);
}
void SerializerForBackgroundCompilation::VisitStaGlobal(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(1);
ProcessFeedbackForGlobalAccess(slot);
}
// Note: We never use the same feeedback slot for multiple access modes.
void SerializerForBackgroundCompilation::ProcessFeedbackForKeyedPropertyAccess( void SerializerForBackgroundCompilation::ProcessFeedbackForKeyedPropertyAccess(
FeedbackSlot slot, AccessMode mode) { FeedbackSlot slot, AccessMode mode) {
if (slot.IsInvalid()) return; if (slot.IsInvalid()) return;
if (environment()->function().feedback_vector.is_null()) return; if (environment()->function().feedback_vector.is_null()) return;
FeedbackNexus nexus(environment()->function().feedback_vector, slot); FeedbackNexus nexus(environment()->function().feedback_vector, slot);
if (broker()->HasFeedback(nexus)) return; FeedbackSource source(nexus);
if (broker()->HasFeedback(source)) return;
if (nexus.ic_state() == MEGAMORPHIC) return; if (nexus.ic_state() == MEGAMORPHIC) return;
...@@ -702,10 +748,12 @@ void SerializerForBackgroundCompilation::ProcessFeedbackForKeyedPropertyAccess( ...@@ -702,10 +748,12 @@ void SerializerForBackgroundCompilation::ProcessFeedbackForKeyedPropertyAccess(
MapHandles maps; MapHandles maps;
nexus.ExtractMaps(&maps); nexus.ExtractMaps(&maps);
ProcessedFeedback& processed = broker()->CreateEmptyFeedback(nexus); ElementAccessFeedback const* processed =
ProcessFeedbackMapsForElementAccess(broker(), maps, &processed); broker()->ProcessFeedbackMapsForElementAccess(maps);
broker()->SetFeedback(source, processed);
if (processed == nullptr) return;
for (ProcessedFeedback::MapIterator it = processed.all_maps(broker()); for (ElementAccessFeedback::MapIterator it = processed->all_maps(broker());
!it.done(); it.advance()) { !it.done(); it.advance()) {
switch (mode) { switch (mode) {
case AccessMode::kHas: case AccessMode::kHas:
......
...@@ -55,8 +55,6 @@ namespace compiler { ...@@ -55,8 +55,6 @@ namespace compiler {
V(CreateUnmappedArguments) \ V(CreateUnmappedArguments) \
V(LdaContextSlot) \ V(LdaContextSlot) \
V(LdaCurrentContextSlot) \ V(LdaCurrentContextSlot) \
V(LdaGlobal) \
V(LdaGlobalInsideTypeof) \
V(LdaImmutableContextSlot) \ V(LdaImmutableContextSlot) \
V(LdaImmutableCurrentContextSlot) \ V(LdaImmutableCurrentContextSlot) \
V(LdaNamedProperty) \ V(LdaNamedProperty) \
...@@ -122,7 +120,11 @@ namespace compiler { ...@@ -122,7 +120,11 @@ namespace compiler {
V(ExtraWide) \ V(ExtraWide) \
V(Illegal) \ V(Illegal) \
V(LdaConstant) \ V(LdaConstant) \
V(LdaGlobal) \
V(LdaGlobalInsideTypeof) \
V(LdaKeyedProperty) \ V(LdaKeyedProperty) \
V(LdaLookupGlobalSlot) \
V(LdaLookupGlobalSlotInsideTypeof) \
V(LdaNull) \ V(LdaNull) \
V(Ldar) \ V(Ldar) \
V(LdaSmi) \ V(LdaSmi) \
...@@ -131,6 +133,7 @@ namespace compiler { ...@@ -131,6 +133,7 @@ namespace compiler {
V(Mov) \ V(Mov) \
V(Return) \ V(Return) \
V(StackCheck) \ V(StackCheck) \
V(StaGlobal) \
V(StaInArrayLiteral) \ V(StaInArrayLiteral) \
V(StaKeyedProperty) \ V(StaKeyedProperty) \
V(Star) \ V(Star) \
...@@ -245,6 +248,7 @@ class SerializerForBackgroundCompilation { ...@@ -245,6 +248,7 @@ class SerializerForBackgroundCompilation {
base::Optional<Hints> new_target, base::Optional<Hints> new_target,
const HintsVector& arguments, bool with_spread); const HintsVector& arguments, bool with_spread);
void ProcessFeedbackForGlobalAccess(FeedbackSlot slot);
void ProcessFeedbackForKeyedPropertyAccess(FeedbackSlot slot, void ProcessFeedbackForKeyedPropertyAccess(FeedbackSlot slot,
AccessMode mode); AccessMode mode);
......
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