Introduce a PREMONOMORPHIC state to StoreGlobalIC
It's used rather narrowly for now -- only when we run into an interceptor during the lookup. After the call to SetProperty, we know more. That is, the interceptor was only there because it's a new property, and the call to SetProperty ends up creating it. By delaying the initialization of the IC, we recognize the (now) created property, and can provide good feedback downstream to TurboFan. Bug: v8:8712 Change-Id: I4e10ba220c8363b393c6de84ce35fe5ef0e9c427 Reviewed-on: https://chromium-review.googlesource.com/c/1456090 Commit-Queue: Michael Stanton <mvstanton@chromium.org> Reviewed-by: Igor Sheludko <ishell@chromium.org> Cr-Commit-Position: refs/heads/master@{#59481}
This commit is contained in:
parent
154bb50c22
commit
ec06b5c456
@ -999,6 +999,7 @@ class RuntimeCallTimer final {
|
|||||||
V(LoadIC_StringWrapperLength) \
|
V(LoadIC_StringWrapperLength) \
|
||||||
V(StoreGlobalIC_SlowStub) \
|
V(StoreGlobalIC_SlowStub) \
|
||||||
V(StoreGlobalIC_StoreScriptContextField) \
|
V(StoreGlobalIC_StoreScriptContextField) \
|
||||||
|
V(StoreGlobalIC_Premonomorphic) \
|
||||||
V(StoreIC_HandlerCacheHit_Accessor) \
|
V(StoreIC_HandlerCacheHit_Accessor) \
|
||||||
V(StoreIC_NonReceiver) \
|
V(StoreIC_NonReceiver) \
|
||||||
V(StoreIC_Premonomorphic) \
|
V(StoreIC_Premonomorphic) \
|
||||||
|
@ -570,6 +570,13 @@ InlineCacheState FeedbackNexus::StateFromFeedback() const {
|
|||||||
case FeedbackSlotKind::kLoadGlobalInsideTypeof: {
|
case FeedbackSlotKind::kLoadGlobalInsideTypeof: {
|
||||||
if (feedback->IsSmi()) return MONOMORPHIC;
|
if (feedback->IsSmi()) return MONOMORPHIC;
|
||||||
|
|
||||||
|
if (feedback == MaybeObject::FromObject(
|
||||||
|
*FeedbackVector::PremonomorphicSentinel(isolate))) {
|
||||||
|
DCHECK(kind() == FeedbackSlotKind::kStoreGlobalSloppy ||
|
||||||
|
kind() == FeedbackSlotKind::kStoreGlobalStrict);
|
||||||
|
return PREMONOMORPHIC;
|
||||||
|
}
|
||||||
|
|
||||||
DCHECK(feedback->IsWeakOrCleared());
|
DCHECK(feedback->IsWeakOrCleared());
|
||||||
MaybeObject extra = GetFeedbackExtra();
|
MaybeObject extra = GetFeedbackExtra();
|
||||||
if (!feedback->IsCleared() ||
|
if (!feedback->IsCleared() ||
|
||||||
|
@ -2895,14 +2895,18 @@ void AccessorAssembler::StoreIC(const StoreICParameters* p) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void AccessorAssembler::StoreGlobalIC(const StoreICParameters* pp) {
|
void AccessorAssembler::StoreGlobalIC(const StoreICParameters* pp) {
|
||||||
Label if_lexical_var(this), if_property_cell(this);
|
Label if_lexical_var(this), if_heapobject(this);
|
||||||
TNode<MaybeObject> maybe_weak_ref =
|
TNode<MaybeObject> maybe_weak_ref =
|
||||||
LoadFeedbackVectorSlot(pp->vector, pp->slot, 0, SMI_PARAMETERS);
|
LoadFeedbackVectorSlot(pp->vector, pp->slot, 0, SMI_PARAMETERS);
|
||||||
Branch(TaggedIsSmi(maybe_weak_ref), &if_lexical_var, &if_property_cell);
|
Branch(TaggedIsSmi(maybe_weak_ref), &if_lexical_var, &if_heapobject);
|
||||||
|
|
||||||
BIND(&if_property_cell);
|
BIND(&if_heapobject);
|
||||||
{
|
{
|
||||||
Label try_handler(this), miss(this, Label::kDeferred);
|
Label try_handler(this), miss(this, Label::kDeferred);
|
||||||
|
GotoIf(
|
||||||
|
WordEqual(maybe_weak_ref, LoadRoot(RootIndex::kpremonomorphic_symbol)),
|
||||||
|
&miss);
|
||||||
|
|
||||||
CSA_ASSERT(this, IsWeakOrCleared(maybe_weak_ref));
|
CSA_ASSERT(this, IsWeakOrCleared(maybe_weak_ref));
|
||||||
TNode<PropertyCell> property_cell =
|
TNode<PropertyCell> property_cell =
|
||||||
CAST(GetHeapObjectAssumeWeak(maybe_weak_ref, &try_handler));
|
CAST(GetHeapObjectAssumeWeak(maybe_weak_ref, &try_handler));
|
||||||
|
18
src/ic/ic.cc
18
src/ic/ic.cc
@ -1471,6 +1471,24 @@ void StoreIC::UpdateCaches(LookupIterator* lookup, Handle<Object> value,
|
|||||||
}
|
}
|
||||||
handler = ComputeHandler(lookup);
|
handler = ComputeHandler(lookup);
|
||||||
} else {
|
} else {
|
||||||
|
if (state() == UNINITIALIZED && IsStoreGlobalIC() &&
|
||||||
|
lookup->state() == LookupIterator::INTERCEPTOR) {
|
||||||
|
InterceptorInfo info =
|
||||||
|
lookup->GetHolder<JSObject>()->GetNamedInterceptor();
|
||||||
|
if (!lookup->HolderIsReceiverOrHiddenPrototype() &&
|
||||||
|
!info->getter()->IsUndefined(isolate())) {
|
||||||
|
// Utilize premonomorphic state for global store ics that run into
|
||||||
|
// an interceptor because the property doesn't exist yet.
|
||||||
|
// After we actually set the property, we'll have more information.
|
||||||
|
// Premonomorphism gives us a chance to find more information the
|
||||||
|
// second time.
|
||||||
|
TRACE_HANDLER_STATS(isolate(), StoreGlobalIC_Premonomorphic);
|
||||||
|
ConfigureVectorState(receiver_map());
|
||||||
|
TraceIC("StoreGlobalIC", lookup->name());
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
set_slow_stub_reason("LookupForWrite said 'false'");
|
set_slow_stub_reason("LookupForWrite said 'false'");
|
||||||
// TODO(marja): change slow_stub to return MaybeObjectHandle.
|
// TODO(marja): change slow_stub to return MaybeObjectHandle.
|
||||||
handler = MaybeObjectHandle(slow_stub());
|
handler = MaybeObjectHandle(slow_stub());
|
||||||
|
@ -45,6 +45,8 @@
|
|||||||
#include "src/compilation-cache.h"
|
#include "src/compilation-cache.h"
|
||||||
#include "src/debug/debug.h"
|
#include "src/debug/debug.h"
|
||||||
#include "src/execution.h"
|
#include "src/execution.h"
|
||||||
|
#include "src/feedback-vector-inl.h"
|
||||||
|
#include "src/feedback-vector.h"
|
||||||
#include "src/futex-emulation.h"
|
#include "src/futex-emulation.h"
|
||||||
#include "src/global-handles.h"
|
#include "src/global-handles.h"
|
||||||
#include "src/heap/incremental-marking.h"
|
#include "src/heap/incremental-marking.h"
|
||||||
@ -10951,7 +10953,6 @@ static void ShadowIndexedGet(uint32_t index,
|
|||||||
static void ShadowNamedGet(Local<Name> key,
|
static void ShadowNamedGet(Local<Name> key,
|
||||||
const v8::PropertyCallbackInfo<v8::Value>&) {}
|
const v8::PropertyCallbackInfo<v8::Value>&) {}
|
||||||
|
|
||||||
|
|
||||||
THREADED_TEST(ShadowObject) {
|
THREADED_TEST(ShadowObject) {
|
||||||
shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
|
shadow_y = shadow_y_setter_call_count = shadow_y_getter_call_count = 0;
|
||||||
v8::Isolate* isolate = CcTest::isolate();
|
v8::Isolate* isolate = CcTest::isolate();
|
||||||
@ -11002,6 +11003,55 @@ THREADED_TEST(ShadowObject) {
|
|||||||
CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
|
CHECK_EQ(42, value->Int32Value(context.local()).FromJust());
|
||||||
}
|
}
|
||||||
|
|
||||||
|
THREADED_TEST(ShadowObjectAndDataProperty) {
|
||||||
|
// This test mimics the kind of shadow property the Chromium embedder
|
||||||
|
// uses for undeclared globals. The IC subsystem has special handling
|
||||||
|
// for this case, using a PREMONOMORPHIC state to delay entering
|
||||||
|
// MONOMORPHIC state until enough information is available to support
|
||||||
|
// efficient access and good feedback for optimization.
|
||||||
|
v8::Isolate* isolate = CcTest::isolate();
|
||||||
|
v8::HandleScope handle_scope(isolate);
|
||||||
|
|
||||||
|
Local<ObjectTemplate> global_template = v8::ObjectTemplate::New(isolate);
|
||||||
|
LocalContext context(nullptr, global_template);
|
||||||
|
|
||||||
|
Local<v8::FunctionTemplate> t = v8::FunctionTemplate::New(isolate);
|
||||||
|
t->InstanceTemplate()->SetHandler(
|
||||||
|
v8::NamedPropertyHandlerConfiguration(ShadowNamedGet));
|
||||||
|
|
||||||
|
Local<Value> o = t->GetFunction(context.local())
|
||||||
|
.ToLocalChecked()
|
||||||
|
->NewInstance(context.local())
|
||||||
|
.ToLocalChecked();
|
||||||
|
CHECK(context->Global()
|
||||||
|
->Set(context.local(), v8_str("__proto__"), o)
|
||||||
|
.FromJust());
|
||||||
|
|
||||||
|
CompileRun(
|
||||||
|
"function foo(x) { i = x; }"
|
||||||
|
"foo(0)");
|
||||||
|
|
||||||
|
i::Handle<i::JSFunction> foo(i::Handle<i::JSFunction>::cast(
|
||||||
|
v8::Utils::OpenHandle(*context->Global()
|
||||||
|
->Get(context.local(), v8_str("foo"))
|
||||||
|
.ToLocalChecked())));
|
||||||
|
CHECK(foo->has_feedback_vector());
|
||||||
|
i::FeedbackSlot slot = i::FeedbackVector::ToSlot(0);
|
||||||
|
i::FeedbackNexus nexus(foo->feedback_vector(), slot);
|
||||||
|
CHECK_EQ(i::FeedbackSlotKind::kStoreGlobalSloppy, nexus.kind());
|
||||||
|
CHECK_EQ(i::PREMONOMORPHIC, nexus.StateFromFeedback());
|
||||||
|
CompileRun("foo(1)");
|
||||||
|
CHECK_EQ(i::MONOMORPHIC, nexus.StateFromFeedback());
|
||||||
|
// We go a bit further, checking that the form of monomorphism is
|
||||||
|
// a PropertyCell in the vector. This is because we want to make sure
|
||||||
|
// we didn't settle for a "poor man's monomorphism," such as a
|
||||||
|
// slow_stub bailout which would mean a trip to the runtime on all
|
||||||
|
// subsequent stores, and a lack of feedback for the optimizing
|
||||||
|
// compiler downstream.
|
||||||
|
i::HeapObject heap_object;
|
||||||
|
CHECK(nexus.GetFeedback().GetHeapObject(&heap_object));
|
||||||
|
CHECK(heap_object->IsPropertyCell());
|
||||||
|
}
|
||||||
|
|
||||||
THREADED_TEST(SetPrototype) {
|
THREADED_TEST(SetPrototype) {
|
||||||
LocalContext context;
|
LocalContext context;
|
||||||
|
Loading…
Reference in New Issue
Block a user