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(
bool AccessInfoFactory::ComputeElementAccessInfos(
FeedbackNexus nexus, MapHandles const& maps, AccessMode access_mode,
ZoneVector<ElementAccessInfo>* access_infos) const {
ProcessedFeedback processed(broker()->zone());
ElementAccessFeedback const* processed;
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(),
"ComputeElementAccessInfos: using preprocessed feedback "
<< "(slot " << nexus.slot() << " of "
<< Brief(*nexus.vector_handle()) << "; "
<< preprocessed.receiver_maps.size() << "/"
<< preprocessed.transitions.size() << " vs "
<< processed.receiver_maps.size() << "/"
<< processed.transitions.size() << ").\n");
processed.receiver_maps = preprocessed.receiver_maps;
processed.transitions = preprocessed.transitions;
<< "feedback vector handle "
<< nexus.vector_handle().address() << ").\n");
processed = broker()->GetElementAccessFeedback(FeedbackSource(nexus));
} 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) {
// For polymorphic loads of similar elements kinds (i.e. all tagged or all
......@@ -299,13 +280,13 @@ bool AccessInfoFactory::ComputeElementAccessInfos(
// much faster than transitioning the elements to the worst case, trading a
// TransitionElementsKind for a CheckMaps, avoiding mutation of the array.
ElementAccessInfo access_info;
if (ConsolidateElementLoad(processed, &access_info)) {
if (ConsolidateElementLoad(*processed, &access_info)) {
access_infos->push_back(access_info);
return true;
}
}
for (Handle<Map> receiver_map : processed.receiver_maps) {
for (Handle<Map> receiver_map : processed->receiver_maps) {
// Compute the element access information.
ElementAccessInfo access_info;
if (!ComputeElementAccessInfo(receiver_map, access_mode, &access_info)) {
......@@ -313,7 +294,7 @@ bool AccessInfoFactory::ComputeElementAccessInfos(
}
// Collect the possible transitions for the {receiver_map}.
for (auto transition : processed.transitions) {
for (auto transition : processed->transitions) {
if (transition.second.equals(receiver_map)) {
access_info.AddTransitionSource(transition.first);
}
......@@ -624,8 +605,9 @@ Maybe<ElementsKind> GeneralizeElementsKind(ElementsKind this_kind,
} // namespace
bool AccessInfoFactory::ConsolidateElementLoad(
ProcessedFeedback const& processed, ElementAccessInfo* access_info) const {
ProcessedFeedback::MapIterator it = processed.all_maps(broker());
ElementAccessFeedback const& processed,
ElementAccessInfo* access_info) const {
ElementAccessFeedback::MapIterator it = processed.all_maps(broker());
MapRef first_map = it.current();
InstanceType instance_type = first_map.instance_type();
ElementsKind elements_kind = first_map.elements_kind();
......
......@@ -25,9 +25,9 @@ namespace compiler {
// Forward declarations.
class CompilationDependencies;
class ElementAccessFeedback;
class Type;
class TypeCache;
struct ProcessedFeedback;
// Whether we are loading a property or storing to a property.
// For a store during literal creation, do not walk up the prototype chain.
......@@ -166,7 +166,7 @@ class AccessInfoFactory final {
ZoneVector<PropertyAccessInfo>* access_infos) const;
private:
bool ConsolidateElementLoad(ProcessedFeedback const& processed,
bool ConsolidateElementLoad(ElementAccessFeedback const& processed,
ElementAccessInfo* access_info) const;
bool LookupSpecialFieldAccessor(Handle<Map> map, Handle<Name> name,
PropertyAccessInfo* access_info) const;
......
......@@ -24,6 +24,7 @@
#include "src/objects/js-regexp-inl.h"
#include "src/objects/module-inl.h"
#include "src/utils.h"
#include "src/vector-slot-pair.h"
namespace v8 {
namespace internal {
......@@ -2841,6 +2842,12 @@ void JSBoundFunctionRef::Serialize() {
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) {
if (!map.IsJSObjectMap()) return false;
if (map.is_access_check_needed()) return false;
......@@ -2855,24 +2862,62 @@ bool CanInlineElementAccess(MapRef const& map) {
return false;
}
ProcessedFeedback::ProcessedFeedback(Zone* zone)
: receiver_maps(zone), transitions(zone) {}
GlobalAccessFeedback::GlobalAccessFeedback(PropertyCellRef cell)
: 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,
JSHeapBroker* broker)
ContextRef GlobalAccessFeedback::script_context() const {
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) {
CHECK_LT(processed.receiver_maps.size(),
std::numeric_limits<size_t>::max() - processed.transitions.size());
}
bool ProcessedFeedback::MapIterator::done() const {
bool ElementAccessFeedback::MapIterator::done() const {
return index_ >=
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());
size_t receiver_maps_size = processed_.receiver_maps.size();
Handle<Map> map;
......@@ -2884,73 +2929,140 @@ MapRef ProcessedFeedback::MapIterator::current() const {
return MapRef(broker_, map);
}
ProcessedFeedback::MapIterator ProcessedFeedback::all_maps(
ElementAccessFeedback::MapIterator ElementAccessFeedback::all_maps(
JSHeapBroker* broker) const {
return MapIterator(*this, broker);
}
ProcessedFeedback& JSHeapBroker::CreateEmptyFeedback(
FeedbackNexus const& nexus) {
auto insertion = feedback_.insert({nexus, ProcessedFeedback(zone())});
FeedbackSource::FeedbackSource(FeedbackNexus const& nexus)
: vector(nexus.vector_handle()), slot(nexus.slot()) {}
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);
return insertion.first->second;
}
bool JSHeapBroker::HasFeedback(FeedbackNexus const& nexus) const {
return feedback_.find(nexus) != feedback_.end();
bool JSHeapBroker::HasFeedback(FeedbackSource const& source) const {
return feedback_.find(source) != feedback_.end();
}
ProcessedFeedback& JSHeapBroker::GetFeedback(FeedbackNexus const& nexus) {
auto it = feedback_.find(nexus);
ProcessedFeedback const* JSHeapBroker::GetFeedback(
FeedbackSource const& source) const {
auto it = feedback_.find(source);
CHECK_NE(it, feedback_.end());
return it->second;
}
void ProcessFeedbackMapsForElementAccess(JSHeapBroker* broker,
MapHandles const& maps,
ProcessedFeedback* processed) {
CHECK(processed->receiver_maps.empty());
CHECK(processed->transitions.empty());
ElementAccessFeedback const* JSHeapBroker::GetElementAccessFeedback(
FeedbackSource const& source) const {
ProcessedFeedback const* feedback = GetFeedback(source);
if (feedback == nullptr) return nullptr;
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.
MapHandles possible_transition_targets;
possible_transition_targets.reserve(maps.size());
for (Handle<Map> map : maps) {
if (CanInlineElementAccess(MapRef(broker, map)) &&
if (CanInlineElementAccess(MapRef(this, map)) &&
IsFastElementsKind(map->elements_kind()) &&
GetInitialFastElementsKind() != map->elements_kind()) {
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.
for (Handle<Map> map : maps) {
// Don't generate elements kind transitions from stable maps.
Map transition_target =
map->is_stable() ? Map()
Map transition_target = map->is_stable()
? Map()
: map->FindElementsKindTransitionedMap(
broker->isolate(), possible_transition_targets);
isolate(), possible_transition_targets);
if (transition_target.is_null()) {
processed->receiver_maps.push_back(map);
result->receiver_maps.push_back(map);
} else {
processed->transitions.emplace_back(
map, handle(transition_target, broker->isolate()));
result->transitions.emplace_back(map,
handle(transition_target, isolate()));
}
}
#ifdef ENABLE_SLOW_DCHECKS
// No transition sources appear in {receiver_maps}.
// All transition targets appear in {receiver_maps}.
for (auto& transition : processed->transitions) {
USE(transition);
for (auto& transition : result->transitions) {
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); }));
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); }));
}
#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
......
......@@ -21,6 +21,7 @@ namespace v8 {
namespace internal {
class BytecodeArray;
class VectorSlotPair;
class FixedDoubleArray;
class HeapNumber;
class InternalizedString;
......@@ -186,6 +187,8 @@ class PropertyCellRef : public HeapObjectRef {
Handle<PropertyCell> object() const;
PropertyDetails property_details() const;
void Serialize();
ObjectRef value() const;
};
......@@ -622,8 +625,40 @@ class InternalizedStringRef : public StringRef {
static const uint32_t kNotAnArrayIndex = -1; // 2^32-1 is not a valid index.
};
struct ProcessedFeedback {
explicit ProcessedFeedback(Zone* zone);
class ProcessedFeedback : public ZoneObject {
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}.
// All transition targets appear in {receiver_maps}.
......@@ -637,12 +672,12 @@ struct ProcessedFeedback {
MapRef current() const;
private:
friend struct ProcessedFeedback;
friend class ElementAccessFeedback;
explicit MapIterator(ProcessedFeedback const& processed,
explicit MapIterator(ElementAccessFeedback const& processed,
JSHeapBroker* broker);
ProcessedFeedback const& processed_;
ElementAccessFeedback const& processed_;
JSHeapBroker* const broker_;
size_t index_ = 0;
};
......@@ -651,6 +686,29 @@ struct ProcessedFeedback {
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) {
public:
JSHeapBroker(Isolate* isolate, Zone* broker_zone);
......@@ -681,9 +739,24 @@ class V8_EXPORT_PRIVATE JSHeapBroker : public NON_EXPORTED_BASE(ZoneObject) {
// %ObjectPrototype%.
bool IsArrayOrObjectPrototype(const JSObjectRef& object) const;
ProcessedFeedback& CreateEmptyFeedback(FeedbackNexus const& nexus);
bool HasFeedback(FeedbackNexus const& nexus) const;
ProcessedFeedback& GetFeedback(FeedbackNexus const& nexus);
bool HasFeedback(FeedbackSource const& source) const;
// The processed {feedback} can be {nullptr}, indicating that the original
// 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();
void IncrementTracingIndentation();
......@@ -697,18 +770,6 @@ class V8_EXPORT_PRIVATE JSHeapBroker : public NON_EXPORTED_BASE(ZoneObject) {
void SerializeShareableObjects();
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_;
Zone* const broker_zone_;
Zone* current_zone_;
......@@ -721,8 +782,8 @@ class V8_EXPORT_PRIVATE JSHeapBroker : public NON_EXPORTED_BASE(ZoneObject) {
StdoutStream trace_out_;
unsigned trace_indentation_ = 0;
PerIsolateCompilerCache* compiler_cache_;
ZoneUnorderedMap<FeedbackNexus, ProcessedFeedback, FeedbackNexusHash,
FeedbackNexusEqual>
ZoneUnorderedMap<FeedbackSource, ProcessedFeedback const*,
FeedbackSource::Hash, FeedbackSource::Equal>
feedback_;
static const size_t kMinimalRefsBucketCount = 8; // must be power of 2
......@@ -743,9 +804,6 @@ Reduction NoChangeBecauseOfMissingData(JSHeapBroker* broker,
// Miscellaneous definitions that should be moved elsewhere once concurrent
// compilation is finished.
bool CanInlineElementAccess(MapRef const& map);
void ProcessFeedbackMapsForElementAccess(JSHeapBroker* broker,
MapHandles const& maps,
ProcessedFeedback* processed);
#define TRACE_BROKER(broker, x) \
do { \
......
......@@ -791,24 +791,27 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
it.TryLookupCachedProperty();
if (it.state() != LookupIterator::DATA) 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,
property_cell);
}
Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
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* control = NodeProperties::GetControlInput(node);
Handle<Object> property_cell_value(property_cell->value(), isolate());
if (property_cell_value.is_identical_to(factory()->the_hole_value())) {
ObjectRef property_cell_value = property_cell.value();
if (property_cell_value.IsHeapObject() &&
property_cell_value.AsHeapObject().map().oddball_type() ==
OddballType::kHole) {
// The property cell is no longer valid.
return NoChange();
}
PropertyDetails property_details = property_cell->property_details();
PropertyDetails property_details = property_cell.property_details();
PropertyCellType property_cell_type = property_details.cell_type();
DCHECK_EQ(kData, property_details.kind());
......@@ -823,8 +826,8 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
} else if (property_cell_type == PropertyCellType::kConstantType) {
// There's also no fast-path to store to a global cell which pretended
// to be stable, but is no longer stable now.
if (property_cell_value->IsHeapObject() &&
!Handle<HeapObject>::cast(property_cell_value)->map()->is_stable()) {
if (property_cell_value.IsHeapObject() &&
!property_cell_value.AsHeapObject().map().is_stable()) {
return NoChange();
}
}
......@@ -866,8 +869,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
// can be deleted or reconfigured to an accessor property).
if (property_details.cell_type() != PropertyCellType::kMutable ||
property_details.IsConfigurable()) {
dependencies()->DependOnGlobalProperty(
PropertyCellRef(broker(), property_cell));
dependencies()->DependOnGlobalProperty(property_cell);
}
// Load from constant/undefined global property can be constant-folded.
......@@ -876,8 +878,9 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
value = access_mode == AccessMode::kHas
? jsgraph()->TrueConstant()
: jsgraph()->Constant(property_cell_value);
CHECK(
!property_cell_value.is_identical_to(factory()->the_hole_value()));
DCHECK(!property_cell_value.IsHeapObject() ||
property_cell_value.AsHeapObject().map().oddball_type() !=
OddballType::kHole);
} else {
DCHECK_NE(AccessMode::kHas, access_mode);
......@@ -887,16 +890,15 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
MachineRepresentation representation = MachineRepresentation::kTagged;
if (property_details.cell_type() == PropertyCellType::kConstantType) {
// 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();
representation = MachineRepresentation::kTaggedSigned;
} else if (property_cell_value->IsNumber()) {
} else if (property_cell_value.IsHeapNumber()) {
property_cell_value_type = Type::Number();
representation = MachineRepresentation::kTaggedPointer;
} else {
MapRef property_cell_value_map(
broker(), handle(HeapObject::cast(*property_cell_value)->map(),
isolate()));
MapRef property_cell_value_map =
property_cell_value.AsHeapObject().map();
property_cell_value_type = Type::For(property_cell_value_map);
representation = MachineRepresentation::kTaggedPointer;
......@@ -912,7 +914,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
value = effect = graph()->NewNode(
simplified()->LoadField(ForPropertyCellValue(
representation, property_cell_value_type, map, name)),
jsgraph()->HeapConstant(property_cell), effect, control);
jsgraph()->Constant(property_cell), effect, control);
}
}
} else {
......@@ -926,8 +928,7 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
case PropertyCellType::kConstant: {
// Record a code dependency on the cell, and just deoptimize if the new
// value doesn't match the previous value stored inside the cell.
dependencies()->DependOnGlobalProperty(
PropertyCellRef(broker(), property_cell));
dependencies()->DependOnGlobalProperty(property_cell);
Node* check =
graph()->NewNode(simplified()->ReferenceEqual(), value,
jsgraph()->Constant(property_cell_value));
......@@ -940,28 +941,25 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
// 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
// cell.
dependencies()->DependOnGlobalProperty(
PropertyCellRef(broker(), property_cell));
dependencies()->DependOnGlobalProperty(property_cell);
Type property_cell_value_type;
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
// longer stable.
Handle<Map> property_cell_value_map(
Handle<HeapObject>::cast(property_cell_value)->map(), isolate());
DCHECK(property_cell_value_map->is_stable());
dependencies()->DependOnStableMap(
MapRef(broker(), property_cell_value_map));
MapRef property_cell_value_map =
property_cell_value.AsHeapObject().map();
dependencies()->DependOnStableMap(property_cell_value_map);
// Check that the {value} is a HeapObject.
value = effect = graph()->NewNode(simplified()->CheckHeapObject(),
value, effect, control);
// Check {value} map against the {property_cell} map.
effect =
graph()->NewNode(simplified()->CheckMaps(
effect = graph()->NewNode(
simplified()->CheckMaps(
CheckMapsFlag::kNone,
ZoneHandleSet<Map>(property_cell_value_map)),
ZoneHandleSet<Map>(property_cell_value_map.object())),
value, effect, control);
property_cell_value_type = Type::OtherInternal();
representation = MachineRepresentation::kTaggedPointer;
......@@ -975,20 +973,19 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
effect = graph()->NewNode(simplified()->StoreField(ForPropertyCellValue(
representation, property_cell_value_type,
MaybeHandle<Map>(), name)),
jsgraph()->HeapConstant(property_cell), value,
jsgraph()->Constant(property_cell), value,
effect, control);
break;
}
case PropertyCellType::kMutable: {
// Record a code dependency on the cell, and just deoptimize if the
// property ever becomes read-only.
dependencies()->DependOnGlobalProperty(
PropertyCellRef(broker(), property_cell));
dependencies()->DependOnGlobalProperty(property_cell);
effect = graph()->NewNode(
simplified()->StoreField(ForPropertyCellValue(
MachineRepresentation::kTagged, Type::NonInternal(),
MaybeHandle<Map>(), name)),
jsgraph()->HeapConstant(property_cell), value, effect, control);
jsgraph()->Constant(property_cell), value, effect, control);
break;
}
}
......@@ -1000,104 +997,83 @@ Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
Reduction JSNativeContextSpecialization::ReduceJSLoadGlobal(Node* node) {
DCHECK_EQ(IrOpcode::kJSLoadGlobal, node->opcode());
Node* effect = NodeProperties::GetEffectInput(node);
DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining);
LoadGlobalParameters const& p = LoadGlobalParametersOf(node->op());
if (!p.feedback().IsValid()) return NoChange();
FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
DCHECK(nexus.kind() == FeedbackSlotKind::kLoadGlobalInsideTypeof ||
nexus.kind() == FeedbackSlotKind::kLoadGlobalNotInsideTypeof);
if (nexus.ic_state() != MONOMORPHIC || nexus.GetFeedback()->IsCleared()) {
return NoChange();
FeedbackSource source(p.feedback());
GlobalAccessFeedback const* processed;
if (FLAG_concurrent_inlining) {
processed = broker()->GetGlobalAccessFeedback(source);
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()) {
// The wanted name belongs to a script-scope variable and the feedback tells
// us where to find its value.
if (processed == nullptr) return NoChange();
int number = feedback->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(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);
if (processed->IsScriptContextSlot()) {
Node* effect = NodeProperties::GetEffectInput(node);
Node* script_context = jsgraph()->Constant(processed->script_context());
Node* value = effect =
graph()->NewNode(javascript()->LoadContext(0, processed->slot_index(),
processed->immutable()),
script_context, effect);
ReplaceWithValue(node, value, effect);
return Replace(value);
}
CHECK(feedback->IsPropertyCell());
// The wanted name belongs (or did belong) to a property on the global object
// and the feedback is the cell holding its value.
CHECK(processed->IsPropertyCell());
return ReduceGlobalAccess(node, nullptr, nullptr, p.name(), AccessMode::kLoad,
nullptr, Handle<PropertyCell>::cast(feedback));
nullptr, processed->property_cell());
}
Reduction JSNativeContextSpecialization::ReduceJSStoreGlobal(Node* node) {
DCHECK_EQ(IrOpcode::kJSStoreGlobal, node->opcode());
DisallowHeapAccessIf no_heap_acess(FLAG_concurrent_inlining);
Node* value = NodeProperties::GetValueInput(node, 0);
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
StoreGlobalParameters const& p = StoreGlobalParametersOf(node->op());
if (!p.feedback().IsValid()) return NoChange();
FeedbackNexus nexus(p.feedback().vector(), p.feedback().slot());
DCHECK(nexus.kind() == FeedbackSlotKind::kStoreGlobalSloppy ||
nexus.kind() == FeedbackSlotKind::kStoreGlobalStrict);
if (nexus.ic_state() != MONOMORPHIC || nexus.GetFeedback()->IsCleared()) {
return NoChange();
FeedbackSource source(p.feedback());
GlobalAccessFeedback const* processed;
if (FLAG_concurrent_inlining) {
processed = broker()->GetGlobalAccessFeedback(source);
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 =
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 (processed == nullptr) return NoChange();
if (immutable) return NoChange();
{
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);
effect = graph()->NewNode(javascript()->StoreContext(0, context_slot_index),
value, context_constant, effect, control);
if (processed->IsScriptContextSlot()) {
if (processed->immutable()) return NoChange();
Node* effect = NodeProperties::GetEffectInput(node);
Node* control = NodeProperties::GetControlInput(node);
Node* script_context = jsgraph()->Constant(processed->script_context());
effect =
graph()->NewNode(javascript()->StoreContext(0, processed->slot_index()),
value, script_context, effect, control);
ReplaceWithValue(node, value, effect, control);
return Replace(value);
}
CHECK(feedback->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, value, p.name(), AccessMode::kStore,
nullptr, Handle<PropertyCell>::cast(feedback));
if (processed->IsPropertyCell()) {
return ReduceGlobalAccess(node, nullptr, value, p.name(),
AccessMode::kStore, nullptr,
processed->property_cell());
}
UNREACHABLE();
}
Reduction JSNativeContextSpecialization::ReduceNamedAccess(
......
......@@ -116,7 +116,8 @@ class V8_EXPORT_PRIVATE JSNativeContextSpecialization final
Node* index = nullptr);
Reduction ReduceGlobalAccess(Node* node, Node* receiver, Node* value,
Handle<Name> name, AccessMode access_mode,
Node* index, Handle<PropertyCell> property_cell);
Node* index,
PropertyCellRef const& property_cell);
Reduction ReduceKeyedLoadFromHeapConstant(Node* node, Node* index,
FeedbackNexus const& nexus,
AccessMode access_mode,
......
......@@ -683,13 +683,59 @@ void SerializerForBackgroundCompilation::VisitConstructWithSpread(
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(
FeedbackSlot slot, AccessMode mode) {
if (slot.IsInvalid()) return;
if (environment()->function().feedback_vector.is_null()) return;
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;
......@@ -702,10 +748,12 @@ void SerializerForBackgroundCompilation::ProcessFeedbackForKeyedPropertyAccess(
MapHandles maps;
nexus.ExtractMaps(&maps);
ProcessedFeedback& processed = broker()->CreateEmptyFeedback(nexus);
ProcessFeedbackMapsForElementAccess(broker(), maps, &processed);
ElementAccessFeedback const* 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()) {
switch (mode) {
case AccessMode::kHas:
......
......@@ -55,8 +55,6 @@ namespace compiler {
V(CreateUnmappedArguments) \
V(LdaContextSlot) \
V(LdaCurrentContextSlot) \
V(LdaGlobal) \
V(LdaGlobalInsideTypeof) \
V(LdaImmutableContextSlot) \
V(LdaImmutableCurrentContextSlot) \
V(LdaNamedProperty) \
......@@ -122,7 +120,11 @@ namespace compiler {
V(ExtraWide) \
V(Illegal) \
V(LdaConstant) \
V(LdaGlobal) \
V(LdaGlobalInsideTypeof) \
V(LdaKeyedProperty) \
V(LdaLookupGlobalSlot) \
V(LdaLookupGlobalSlotInsideTypeof) \
V(LdaNull) \
V(Ldar) \
V(LdaSmi) \
......@@ -131,6 +133,7 @@ namespace compiler {
V(Mov) \
V(Return) \
V(StackCheck) \
V(StaGlobal) \
V(StaInArrayLiteral) \
V(StaKeyedProperty) \
V(Star) \
......@@ -245,6 +248,7 @@ class SerializerForBackgroundCompilation {
base::Optional<Hints> new_target,
const HintsVector& arguments, bool with_spread);
void ProcessFeedbackForGlobalAccess(FeedbackSlot slot);
void ProcessFeedbackForKeyedPropertyAccess(FeedbackSlot slot,
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