[turbofan] Preprocess context slots for immutable global context loads

Main changes:
  ContextData class to hold a map of slots to ObjectData for known
    necessary lookups.
  LdaGlobal* and StaGlobal now receive an accumulator hint of the
    constant found at the lookup slot for immutable global context
    operations.

Bug: v8:7790
Change-Id: I63dc9eb8ebbbdfa4ce3b71c6aba63b3c06a3da9b
Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/1532074
Reviewed-by: Georg Neis <neis@chromium.org>
Commit-Queue: Michael Stanton <mvstanton@chromium.org>
Cr-Commit-Position: refs/heads/master@{#60386}
This commit is contained in:
Mike Stanton 2019-03-20 21:13:39 +01:00 committed by Commit Bot
parent c4eae87a1a
commit 9b929cdc65
6 changed files with 84 additions and 33 deletions

View File

@ -144,7 +144,7 @@ Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
// Now walk up the concrete context chain for the remaining depth.
ContextRef concrete = maybe_concrete.value();
concrete.Serialize(); // TODO(neis): Remove later.
concrete.SerializeContextChain(); // TODO(neis): Remove later.
for (; depth > 0; --depth) {
concrete = concrete.previous();
}
@ -158,6 +158,7 @@ Reduction JSContextSpecialization::ReduceJSLoadContext(Node* node) {
// This will hold the final value, if we can figure it out.
base::Optional<ObjectRef> maybe_value;
concrete.SerializeSlot(static_cast<int>(access.index()));
maybe_value = concrete.get(static_cast<int>(access.index()));
if (maybe_value.has_value() && !maybe_value->IsSmi()) {
// Even though the context slot is immutable, the context might have escaped
@ -206,7 +207,7 @@ Reduction JSContextSpecialization::ReduceJSStoreContext(Node* node) {
// Now walk up the concrete context chain for the remaining depth.
ContextRef concrete = maybe_concrete.value();
concrete.Serialize(); // TODO(neis): Remove later.
concrete.SerializeContextChain(); // TODO(neis): Remove later.
for (; depth > 0; --depth) {
concrete = concrete.previous();
}

View File

@ -406,37 +406,55 @@ class ContextData : public HeapObjectData {
public:
ContextData(JSHeapBroker* broker, ObjectData** storage,
Handle<Context> object);
void Serialize(JSHeapBroker* broker);
void SerializeContextChain(JSHeapBroker* broker);
ContextData* previous() const {
CHECK(serialized_);
CHECK(serialized_context_chain_);
return previous_;
}
void SerializeSlot(JSHeapBroker* broker, int index);
ObjectData* GetSlot(int index) {
auto search = slots_.find(index);
CHECK(search != slots_.end());
return search->second;
}
private:
bool serialized_ = false;
ZoneMap<int, ObjectData*> slots_;
bool serialized_context_chain_ = false;
ContextData* previous_ = nullptr;
};
ContextData::ContextData(JSHeapBroker* broker, ObjectData** storage,
Handle<Context> object)
: HeapObjectData(broker, storage, object) {}
: HeapObjectData(broker, storage, object), slots_(broker->zone()) {}
void ContextData::Serialize(JSHeapBroker* broker) {
if (serialized_) return;
serialized_ = true;
void ContextData::SerializeContextChain(JSHeapBroker* broker) {
if (serialized_context_chain_) return;
serialized_context_chain_ = true;
TraceScope tracer(broker, this, "ContextData::Serialize");
TraceScope tracer(broker, this, "ContextData::SerializeContextChain");
Handle<Context> context = Handle<Context>::cast(object());
DCHECK_NULL(previous_);
// Context::previous DCHECK-fails when called on the native context.
if (!context->IsNativeContext()) {
previous_ = broker->GetOrCreateData(context->previous())->AsContext();
previous_->Serialize(broker);
previous_->SerializeContextChain(broker);
}
}
void ContextData::SerializeSlot(JSHeapBroker* broker, int index) {
TraceScope tracer(broker, this, "ContextData::SerializeSlot");
TRACE(broker, "Serializing script context slot " << index << ".");
Handle<Context> context = Handle<Context>::cast(object());
CHECK(index >= 0 && index < context->length());
ObjectData* odata = broker->GetOrCreateData(context->get(index));
slots_.insert(std::make_pair(index, odata));
}
class NativeContextData : public ContextData {
public:
#define DECL_ACCESSOR(type, name) \
@ -1574,10 +1592,13 @@ ContextRef ContextRef::previous() const {
// Not needed for TypedLowering.
ObjectRef ContextRef::get(int index) const {
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
Handle<Object> value(object()->get(index), broker()->isolate());
return ObjectRef(broker(), value);
if (broker()->mode() == JSHeapBroker::kDisabled) {
AllowHandleAllocation handle_allocation;
AllowHandleDereference handle_dereference;
Handle<Object> value(object()->get(index), broker()->isolate());
return ObjectRef(broker(), value);
}
return ObjectRef(broker(), data()->AsContext()->GetSlot(index));
}
JSHeapBroker::JSHeapBroker(Isolate* isolate, Zone* broker_zone)
@ -2813,10 +2834,16 @@ void ModuleRef::Serialize() {
data()->AsModule()->Serialize(broker());
}
void ContextRef::Serialize() {
void ContextRef::SerializeContextChain() {
if (broker()->mode() == JSHeapBroker::kDisabled) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsContext()->Serialize(broker());
data()->AsContext()->SerializeContextChain(broker());
}
void ContextRef::SerializeSlot(int index) {
if (broker()->mode() == JSHeapBroker::kDisabled) return;
CHECK_EQ(broker()->mode(), JSHeapBroker::kSerializing);
data()->AsContext()->SerializeSlot(broker(), index);
}
void NativeContextRef::Serialize() {
@ -2898,6 +2925,15 @@ 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());
}
return {};
}
ElementAccessFeedback::ElementAccessFeedback(Zone* zone)
: ProcessedFeedback(kElementAccess),
receiver_maps(zone),
@ -3053,8 +3089,12 @@ GlobalAccessFeedback const* JSHeapBroker::ProcessFeedbackForGlobalAccess(
CHECK(!contents.equals(
ObjectRef(this, isolate()->factory()->the_hole_value())));
}
return new (zone()) GlobalAccessFeedback(ContextRef(this, context),
context_slot_index, immutable);
ContextRef context_ref(this, context);
if (immutable) {
context_ref.SerializeSlot(context_slot_index);
}
return new (zone())
GlobalAccessFeedback(context_ref, context_slot_index, immutable);
}
CHECK(feedback_value->IsPropertyCell());

View File

@ -286,8 +286,10 @@ class ContextRef : public HeapObjectRef {
using HeapObjectRef::HeapObjectRef;
Handle<Context> object() const;
void Serialize();
void SerializeContextChain();
ContextRef previous() const;
void SerializeSlot(int index);
ObjectRef get(int index) const;
};
@ -651,6 +653,8 @@ class GlobalAccessFeedback : public ProcessedFeedback {
int slot_index() const;
bool immutable() const;
base::Optional<ObjectRef> GetConstantValue() const;
private:
ObjectRef const cell_or_context_;
int const index_and_immutable_;

View File

@ -31,7 +31,7 @@ Reduction JSHeapCopyReducer::Reduce(Node* node) {
if (object.IsJSFunction()) object.AsJSFunction().Serialize();
if (object.IsJSObject()) object.AsJSObject().SerializeObjectCreateMap();
if (object.IsModule()) object.AsModule().Serialize();
if (object.IsContext()) object.AsContext().Serialize();
if (object.IsContext()) object.AsContext().SerializeContextChain();
break;
}
case IrOpcode::kJSCreateArray: {

View File

@ -683,27 +683,33 @@ void SerializerForBackgroundCompilation::VisitConstructWithSpread(
ProcessCallOrConstruct(callee, new_target, arguments, slot, true);
}
void SerializerForBackgroundCompilation::ProcessFeedbackForGlobalAccess(
GlobalAccessFeedback const*
SerializerForBackgroundCompilation::ProcessFeedbackForGlobalAccess(
FeedbackSlot slot) {
if (slot.IsInvalid()) return;
if (environment()->function().feedback_vector.is_null()) return;
if (slot.IsInvalid()) return nullptr;
if (environment()->function().feedback_vector.is_null()) return nullptr;
FeedbackSource source(environment()->function().feedback_vector, slot);
if (!broker()->HasFeedback(source)) {
broker()->SetFeedback(source,
broker()->ProcessFeedbackForGlobalAccess(source));
const GlobalAccessFeedback* feedback =
broker()->ProcessFeedbackForGlobalAccess(source);
broker()->SetFeedback(source, feedback);
return feedback;
}
// 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.
return nullptr;
}
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();
GlobalAccessFeedback const* feedback = ProcessFeedbackForGlobalAccess(slot);
if (feedback != nullptr) {
// We may be able to contribute to accumulator constant hints.
base::Optional<ObjectRef> value = feedback->GetConstantValue();
if (value.has_value()) {
environment()->accumulator_hints().AddConstant(value->object());
}
}
}
void SerializerForBackgroundCompilation::VisitLdaGlobalInsideTypeof(

View File

@ -248,7 +248,7 @@ class SerializerForBackgroundCompilation {
base::Optional<Hints> new_target,
const HintsVector& arguments, bool with_spread);
void ProcessFeedbackForGlobalAccess(FeedbackSlot slot);
GlobalAccessFeedback const* ProcessFeedbackForGlobalAccess(FeedbackSlot slot);
void ProcessFeedbackForKeyedPropertyAccess(FeedbackSlot slot,
AccessMode mode);