Commit 95b09e3e authored by Georg Neis's avatar Georg Neis Committed by Commit Bot

[turbofan] Support named access on global proxy in serializer

Process feedback and hints for Lda/StaNamed bytecodes w.r.t. access on
the global proxy. This stores the property cells (or their absence) on
the JSGlobalProxyData.

Bug: v8:7790
Change-Id: Iadedea5494611c1b2ed38b6ce75687e084cc27f9
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1499499
Commit-Queue: Jaroslav Sevcik <jarin@chromium.org>
Reviewed-by: 's avatarJaroslav Sevcik <jarin@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60411}
parent 4f2586b9
......@@ -702,6 +702,9 @@ class MapData : public HeapObjectData {
bool supports_fast_array_resize() const {
return supports_fast_array_resize_;
}
bool IsMapOfCurrentGlobalProxy() const {
return is_map_of_current_global_proxy_;
}
// Extra information.
......@@ -750,6 +753,7 @@ class MapData : public HeapObjectData {
int const unused_property_fields_;
bool const supports_fast_array_iteration_;
bool const supports_fast_array_resize_;
bool const is_map_of_current_global_proxy_;
bool serialized_elements_kind_generalizations_ = false;
ZoneVector<MapData*> elements_kind_generalizations_;
......@@ -865,6 +869,8 @@ MapData::MapData(JSHeapBroker* broker, ObjectData** storage, Handle<Map> object)
SupportsFastArrayIteration(broker->isolate(), object)),
supports_fast_array_resize_(
SupportsFastArrayResize(broker->isolate(), object)),
is_map_of_current_global_proxy_(
object->IsMapOfGlobalProxy(broker->isolate()->native_context())),
elements_kind_generalizations_(broker->zone()) {}
JSFunctionData::JSFunctionData(JSHeapBroker* broker, ObjectData** storage,
......@@ -1328,10 +1334,65 @@ void CellData::Serialize(JSHeapBroker* broker) {
class JSGlobalProxyData : public JSObjectData {
public:
JSGlobalProxyData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSGlobalProxy> object)
: JSObjectData(broker, storage, object) {}
Handle<JSGlobalProxy> object);
PropertyCellData* GetPropertyCell(JSHeapBroker* broker, NameData* name,
bool serialize);
private:
// Properties that either
// (1) are known to exist as property cells on the global object, or
// (2) are known not to (possibly they don't exist at all).
// In case (2), the second pair component is nullptr.
ZoneVector<std::pair<NameData*, PropertyCellData*>> properties_;
};
JSGlobalProxyData::JSGlobalProxyData(JSHeapBroker* broker, ObjectData** storage,
Handle<JSGlobalProxy> object)
: JSObjectData(broker, storage, object), properties_(broker->zone()) {}
base::Optional<PropertyCellRef> GetPropertyCellFromHeap(JSHeapBroker* broker,
Handle<Name> name) {
LookupIterator it(broker->isolate(),
handle(broker->native_context().object()->global_object(),
broker->isolate()),
name, LookupIterator::OWN);
it.TryLookupCachedProperty();
if (it.state() == LookupIterator::DATA &&
it.GetHolder<JSObject>()->IsJSGlobalObject()) {
return PropertyCellRef(broker, it.GetPropertyCell());
}
return base::nullopt;
}
PropertyCellData* JSGlobalProxyData::GetPropertyCell(JSHeapBroker* broker,
NameData* name,
bool serialize) {
CHECK_NOT_NULL(name);
for (auto const& p : properties_) {
if (p.first == name) return p.second;
}
if (!serialize) {
AllowHandleAllocation handle_allocation_;
AllowHandleDereference handle_dereference_;
AllowHeapAllocation heap_allocation_;
TRACE(broker, "GetPropertyCell: missing knowledge about global property "
<< Brief(*name->object()) << "\n");
return nullptr;
}
PropertyCellData* result = nullptr;
base::Optional<PropertyCellRef> cell =
GetPropertyCellFromHeap(broker, Handle<Name>::cast(name->object()));
if (cell.has_value()) {
cell->Serialize();
result = cell->data()->AsPropertyCell();
}
properties_.push_back({name, result});
return result;
}
class CodeData : public HeapObjectData {
public:
CodeData(JSHeapBroker* broker, ObjectData** storage, Handle<Code> object)
......@@ -2019,6 +2080,15 @@ bool MapRef::supports_fast_array_resize() const {
return data()->AsMap()->supports_fast_array_resize();
}
bool MapRef::IsMapOfCurrentGlobalProxy() const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference allow_handle_dereference;
AllowHandleAllocation handle_allocation;
return object()->IsMapOfGlobalProxy(broker()->isolate()->native_context());
}
return data()->AsMap()->IsMapOfCurrentGlobalProxy();
}
int JSFunctionRef::InitialMapInstanceSizeWithMinSlack() const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleDereference allow_handle_dereference;
......@@ -2875,6 +2945,18 @@ void PropertyCellRef::Serialize() {
data()->AsPropertyCell()->Serialize(broker());
}
base::Optional<PropertyCellRef> JSGlobalProxyRef::GetPropertyCell(
NameRef const& name, bool serialize) const {
if (broker()->mode() == JSHeapBroker::kDisabled) {
return GetPropertyCellFromHeap(broker(), name.object());
}
PropertyCellData* property_cell_data =
data()->AsJSGlobalProxy()->GetPropertyCell(
broker(), name.data()->AsName(), serialize);
if (property_cell_data == nullptr) return base::nullopt;
return PropertyCellRef(broker(), property_cell_data);
}
bool CanInlineElementAccess(MapRef const& map) {
if (!map.IsJSObjectMap()) return false;
if (map.is_access_check_needed()) return false;
......@@ -2911,7 +2993,6 @@ PropertyCellRef GlobalAccessFeedback::property_cell() const {
DCHECK(IsPropertyCell());
return cell_or_context_.AsPropertyCell();
}
ContextRef GlobalAccessFeedback::script_context() const {
DCHECK(IsScriptContextSlot());
return cell_or_context_.AsContext();
......@@ -2925,11 +3006,11 @@ bool GlobalAccessFeedback::immutable() const {
return FeedbackNexus::ImmutabilityBit::decode(index_and_immutable_);
}
base::Optional<ObjectRef> GlobalAccessFeedback::GetConstantValue() const {
if (IsScriptContextSlot() && immutable()) {
// Return the value of this global variable if it's guaranteed to be
// constant.
return script_context().get(slot_index());
base::Optional<ObjectRef> GlobalAccessFeedback::GetConstantHint() const {
if (IsScriptContextSlot()) {
if (immutable()) return script_context().get(slot_index());
} else {
return property_cell().value();
}
return {};
}
......
......@@ -132,6 +132,8 @@ class ObjectRef {
ObjectData* data_; // Should be used only by object() getters.
private:
friend class JSGlobalProxyRef;
friend class JSGlobalProxyData;
JSHeapBroker* broker_;
};
......@@ -448,6 +450,7 @@ class MapRef : public HeapObjectRef {
bool is_migration_target() const;
bool supports_fast_array_iteration() const;
bool supports_fast_array_resize() const;
bool IsMapOfCurrentGlobalProxy() const;
#define DEF_TESTER(Type, ...) bool Is##Type##Map() const;
INSTANCE_TYPE_CHECKERS(DEF_TESTER)
......@@ -610,6 +613,16 @@ class JSGlobalProxyRef : public JSObjectRef {
public:
using JSObjectRef::JSObjectRef;
Handle<JSGlobalProxy> object() const;
// If {serialize} is false:
// If the property is known to exist as a property cell (on the global
// object), return that property cell. Otherwise (not known to exist as a
// property cell or known not to exist as a property cell) return nothing.
// 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;
};
class CodeRef : public HeapObjectRef {
......@@ -653,7 +666,7 @@ class GlobalAccessFeedback : public ProcessedFeedback {
int slot_index() const;
bool immutable() const;
base::Optional<ObjectRef> GetConstantValue() const;
base::Optional<ObjectRef> GetConstantHint() const;
private:
ObjectRef const cell_or_context_;
......
......@@ -785,16 +785,12 @@ FieldAccess ForPropertyCellValue(MachineRepresentation representation,
Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
Node* node, Node* receiver, Node* value, Handle<Name> name,
AccessMode access_mode, Node* index) {
// Lookup on the global object. We only deal with own data properties
// of the global object here (represented as PropertyCell).
LookupIterator it(isolate(), global_object(), name, LookupIterator::OWN);
it.TryLookupCachedProperty();
if (it.state() != LookupIterator::DATA) return NoChange();
if (!it.GetHolder<JSObject>()->IsJSGlobalObject()) return NoChange();
PropertyCellRef property_cell(broker(), it.GetPropertyCell());
property_cell.Serialize();
return ReduceGlobalAccess(node, receiver, value, name, access_mode, index,
property_cell);
NameRef name_ref(broker(), name);
base::Optional<PropertyCellRef> cell =
native_context().global_proxy_object().GetPropertyCell(name_ref);
return cell.has_value() ? ReduceGlobalAccess(node, receiver, value, name,
access_mode, index, *cell)
: NoChange();
}
Reduction JSNativeContextSpecialization::ReduceGlobalAccess(
......@@ -1095,16 +1091,10 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccess(
// native contexts' global proxy, and turn that into a direct access
// to the current native contexts' global object instead.
if (receiver_maps.size() == 1) {
Handle<Map> receiver_map = receiver_maps.front();
if (receiver_map->IsJSGlobalProxyMap()) {
Object maybe_constructor = receiver_map->GetConstructor();
// Detached global proxies have |null| as their constructor.
if (maybe_constructor->IsJSFunction() &&
JSFunction::cast(maybe_constructor)->native_context() ==
*native_context().object()) {
return ReduceGlobalAccess(node, receiver, value, name, access_mode,
index);
}
MapRef receiver_map(broker(), receiver_maps.front());
if (receiver_map.IsMapOfCurrentGlobalProxy()) {
return ReduceGlobalAccess(node, receiver, value, name, access_mode,
index);
}
}
......@@ -1358,7 +1348,8 @@ Reduction JSNativeContextSpecialization::ReduceNamedAccessFromNexus(
// Check if we are accessing the current native contexts' global proxy.
HeapObjectMatcher m(receiver);
if (m.HasValue() && m.Value().is_identical_to(global_proxy())) {
if (m.HasValue() &&
m.Ref(broker()).equals(native_context().global_proxy_object())) {
// Optimize accesses to the current native contexts' global proxy.
return ReduceGlobalAccess(node, nullptr, value, name, access_mode);
}
......
......@@ -705,7 +705,7 @@ void SerializerForBackgroundCompilation::VisitLdaGlobal(
GlobalAccessFeedback const* feedback = ProcessFeedbackForGlobalAccess(slot);
if (feedback != nullptr) {
// We may be able to contribute to accumulator constant hints.
base::Optional<ObjectRef> value = feedback->GetConstantValue();
base::Optional<ObjectRef> value = feedback->GetConstantHint();
if (value.has_value()) {
environment()->accumulator_hints().AddConstant(value->object());
}
......@@ -733,7 +733,22 @@ void SerializerForBackgroundCompilation::VisitStaGlobal(
ProcessFeedbackForGlobalAccess(slot);
}
// Note: We never use the same feeedback slot for multiple access modes.
namespace {
template <class MapContainer>
MapHandles GetRelevantReceiverMaps(Isolate* isolate, MapContainer const& maps) {
MapHandles result;
for (Handle<Map> map : maps) {
if (Map::TryUpdate(isolate, map).ToHandle(&map) &&
!map->is_abandoned_prototype_map()) {
DCHECK(!map->is_deprecated());
result.push_back(map);
}
}
return result;
}
} // namespace
// Note: We never use the same feedback slot for multiple access modes.
void SerializerForBackgroundCompilation::ProcessFeedbackForKeyedPropertyAccess(
FeedbackSlot slot, AccessMode mode) {
if (slot.IsInvalid()) return;
......@@ -743,8 +758,6 @@ void SerializerForBackgroundCompilation::ProcessFeedbackForKeyedPropertyAccess(
FeedbackSource source(nexus);
if (broker()->HasFeedback(source)) return;
if (nexus.ic_state() == MEGAMORPHIC) return;
if (nexus.GetKeyType() == PROPERTY) {
CHECK_NE(mode, AccessMode::kStoreInLiteral);
return; // TODO(neis): Support named access.
......@@ -755,7 +768,8 @@ void SerializerForBackgroundCompilation::ProcessFeedbackForKeyedPropertyAccess(
MapHandles maps;
nexus.ExtractMaps(&maps);
ElementAccessFeedback const* processed =
broker()->ProcessFeedbackMapsForElementAccess(maps);
broker()->ProcessFeedbackMapsForElementAccess(
GetRelevantReceiverMaps(broker()->isolate(), maps));
broker()->SetFeedback(source, processed);
if (processed == nullptr) return;
......@@ -776,6 +790,36 @@ void SerializerForBackgroundCompilation::ProcessFeedbackForKeyedPropertyAccess(
}
}
void SerializerForBackgroundCompilation::ProcessMapForNamedPropertyAccess(
MapRef const& map, NameRef const& name) {
// For JSNativeContextSpecialization::ReduceNamedAccess.
if (map.IsMapOfCurrentGlobalProxy()) {
broker()->native_context().global_proxy_object().GetPropertyCell(name,
true);
}
}
// Note: We never use the same feedback slot for multiple names.
void SerializerForBackgroundCompilation::ProcessFeedbackForNamedPropertyAccess(
FeedbackSlot slot, NameRef const& 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;
MapHandles maps;
nexus.ExtractMaps(&maps);
for (Handle<Map> map : GetRelevantReceiverMaps(broker()->isolate(), maps)) {
ProcessMapForNamedPropertyAccess(MapRef(broker(), map), name);
}
// NamedProperty support is still WIP. For now we don't have any actual data
// to store, so use nullptr to at least record that we processed the feedback.
broker()->SetFeedback(source, nullptr);
}
void SerializerForBackgroundCompilation::VisitLdaKeyedProperty(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(1);
......@@ -783,6 +827,42 @@ void SerializerForBackgroundCompilation::VisitLdaKeyedProperty(
environment()->accumulator_hints().Clear();
}
void SerializerForBackgroundCompilation::ProcessNamedPropertyAccess(
Hints const& receiver, NameRef const& name, FeedbackSlot slot) {
if (!slot.IsInvalid()) ProcessFeedbackForNamedPropertyAccess(slot, name);
for (Handle<Map> map :
GetRelevantReceiverMaps(broker()->isolate(), receiver.maps())) {
ProcessMapForNamedPropertyAccess(MapRef(broker(), map), name);
}
JSGlobalProxyRef global_proxy =
broker()->native_context().global_proxy_object();
for (Handle<Object> object : receiver.constants()) {
// For JSNativeContextSpecialization::ReduceNamedAccessFromNexus.
if (object.equals(global_proxy.object())) {
global_proxy.GetPropertyCell(name, true);
}
}
environment()->accumulator_hints().Clear();
}
void SerializerForBackgroundCompilation::VisitLdaNamedProperty(
BytecodeArrayIterator* iterator) {
Hints const& receiver =
environment()->register_hints(iterator->GetRegisterOperand(0));
Handle<Name> name(Name::cast(iterator->GetConstantForIndexOperand(1)),
broker()->isolate());
FeedbackSlot slot = iterator->GetSlotOperand(2);
ProcessNamedPropertyAccess(receiver, NameRef(broker(), name), slot);
}
void SerializerForBackgroundCompilation::VisitStaNamedProperty(
BytecodeArrayIterator* iterator) {
VisitLdaNamedProperty(iterator);
}
void SerializerForBackgroundCompilation::VisitTestIn(
BytecodeArrayIterator* iterator) {
FeedbackSlot slot = iterator->GetSlotOperand(1);
......@@ -798,8 +878,8 @@ void SerializerForBackgroundCompilation::VisitStaKeyedProperty(
ProcessFeedbackForKeyedPropertyAccess(slot, AccessMode::kStore);
// Process hints about constants.
for (Handle<Object> object : receiver.constants()) {
// For JSNativeContextSpecialization::ReduceElementAccess.
if (object->IsJSTypedArray()) JSTypedArrayRef(broker(), object).Serialize();
}
......
......@@ -48,17 +48,15 @@ namespace compiler {
V(SwitchOnGeneratorState) \
V(Throw)
#define CLEAR_ACCUMULATOR_LIST(V) \
V(CreateEmptyObjectLiteral) \
V(CreateMappedArguments) \
V(CreateRestParameter) \
V(CreateUnmappedArguments) \
V(LdaContextSlot) \
V(LdaCurrentContextSlot) \
V(LdaImmutableContextSlot) \
V(LdaImmutableCurrentContextSlot) \
V(LdaNamedProperty) \
V(LdaNamedPropertyNoFeedback)
#define CLEAR_ACCUMULATOR_LIST(V) \
V(CreateEmptyObjectLiteral) \
V(CreateMappedArguments) \
V(CreateRestParameter) \
V(CreateUnmappedArguments) \
V(LdaContextSlot) \
V(LdaCurrentContextSlot) \
V(LdaImmutableContextSlot) \
V(LdaImmutableCurrentContextSlot)
#define UNCONDITIONAL_JUMPS_LIST(V) \
V(Jump) \
......@@ -86,6 +84,8 @@ namespace compiler {
V(JumpIfUndefinedConstant)
#define INGORED_BYTECODE_LIST(V) \
V(LdaNamedPropertyNoFeedback) \
V(StaNamedPropertyNoFeedback) \
V(TestEqual) \
V(TestEqualStrict) \
V(TestLessThan) \
......@@ -125,6 +125,7 @@ namespace compiler {
V(LdaKeyedProperty) \
V(LdaLookupGlobalSlot) \
V(LdaLookupGlobalSlotInsideTypeof) \
V(LdaNamedProperty) \
V(LdaNull) \
V(Ldar) \
V(LdaSmi) \
......@@ -136,6 +137,7 @@ namespace compiler {
V(StaGlobal) \
V(StaInArrayLiteral) \
V(StaKeyedProperty) \
V(StaNamedProperty) \
V(Star) \
V(TestIn) \
V(Wide) \
......@@ -243,6 +245,8 @@ class SerializerForBackgroundCompilation {
bool with_spread = false);
void ProcessJump(interpreter::BytecodeArrayIterator* iterator);
void MergeAfterJump(interpreter::BytecodeArrayIterator* iterator);
void ProcessNamedPropertyAccess(Hints const& receiver, NameRef const& name,
FeedbackSlot slot);
Hints RunChildSerializer(CompilationSubject function,
base::Optional<Hints> new_target,
......@@ -251,6 +255,9 @@ class SerializerForBackgroundCompilation {
GlobalAccessFeedback const* ProcessFeedbackForGlobalAccess(FeedbackSlot slot);
void ProcessFeedbackForKeyedPropertyAccess(FeedbackSlot slot,
AccessMode mode);
void ProcessFeedbackForNamedPropertyAccess(FeedbackSlot slot,
NameRef const& name);
void ProcessMapForNamedPropertyAccess(MapRef const& map, NameRef const& name);
JSHeapBroker* broker() const { return broker_; }
Zone* zone() const { return zone_; }
......
......@@ -56,6 +56,18 @@ MaybeHandle<JSFunction> Map::GetConstructorFunction(
return MaybeHandle<JSFunction>();
}
bool Map::IsMapOfGlobalProxy(Handle<NativeContext> native_context) const {
DisallowHeapAllocation no_gc;
if (IsJSGlobalProxyMap()) {
Object maybe_constructor = GetConstructor();
// Detached global proxies have |null| as their constructor.
return maybe_constructor.IsJSFunction() &&
JSFunction::cast(maybe_constructor).native_context() ==
*native_context;
}
return false;
}
void Map::PrintReconfiguration(Isolate* isolate, FILE* file, int modify_index,
PropertyKind kind,
PropertyAttributes attributes) {
......
......@@ -895,6 +895,9 @@ class Map : public HeapObject {
InstanceType instance_type);
inline bool CanHaveFastTransitionableElementsKind() const;
// Whether this is the map of the given native context's global proxy.
bool IsMapOfGlobalProxy(Handle<NativeContext> native_context) const;
private:
// This byte encodes either the instance size without the in-object slack or
// the slack size in properties backing store.
......
......@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
// Flags: --allow-natives-syntax
function Foo(a, b) {
var bname = "b";
this["a"] = a;
......@@ -29,3 +31,29 @@ for (var i = 0; i < 6; i++) {
var f = new Foo(i + "", (i + 2) + "");
assertEquals((i + "") + ((i + 2) + ""), f.x);
}
{
function Global(i) { this.bla = i }
Global(0);
Global(1);
%OptimizeFunctionOnNextCall(Global);
Global(2);
assertEquals(bla, 2);
}
{
function access(obj) { obj.bla = 42 }
access({a: 0});
access({b: 0});
access({c: 0});
access({d: 0});
access({e: 0});
var global = this;
function foo() { access(global) };
foo();
foo();
%OptimizeFunctionOnNextCall(foo);
foo();
}
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