Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(531)

Unified Diff: src/ic.cc

Issue 6894003: Better support for 'polymorphic' JS and external arrays (Closed) Base URL: https://v8.googlecode.com/svn/branches/bleeding_edge
Patch Set: final review feedback Created 9 years, 7 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/ic.h ('k') | src/list.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/ic.cc
diff --git a/src/ic.cc b/src/ic.cc
index 3f5326b002304e3d3d9ae9f5a47cd2afeceaffcc..8506c0ec0a3d2818c85fda73e2d522d0b2d8e106 100644
--- a/src/ic.cc
+++ b/src/ic.cc
@@ -1,4 +1,4 @@
-// Copyright 2006-2009 the V8 project authors. All rights reserved.
+// Copyright 2011 the V8 project authors. All rights reserved.
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
@@ -67,7 +67,33 @@ void IC::TraceIC(const char* type,
State new_state = StateFrom(new_target,
HEAP->undefined_value(),
HEAP->undefined_value());
- PrintF("[%s (%c->%c)%s", type,
+ PrintF("[%s in ", type);
+ StackFrameIterator it;
+ while (it.frame()->fp() != this->fp()) it.Advance();
+ StackFrame* raw_frame = it.frame();
+ if (raw_frame->is_internal()) {
+ Isolate* isolate = new_target->GetIsolate();
+ Code* apply_builtin = isolate->builtins()->builtin(
+ Builtins::kFunctionApply);
+ if (raw_frame->unchecked_code() == apply_builtin) {
+ PrintF("apply from ");
+ it.Advance();
+ raw_frame = it.frame();
+ }
+ }
+ if (raw_frame->is_java_script()) {
+ JavaScriptFrame* frame = JavaScriptFrame::cast(raw_frame);
+ Code* js_code = frame->unchecked_code();
+ // Find the function on the stack and both the active code for the
+ // function and the original code.
+ JSFunction* function = JSFunction::cast(frame->function());
+ function->PrintName();
+ int code_offset = address() - js_code->instruction_start();
+ PrintF("+%d", code_offset);
+ } else {
+ PrintF("<unknown>");
+ }
+ PrintF(" (%c->%c)%s",
TransitionMarkFromState(old_state),
TransitionMarkFromState(new_state),
extra_info);
@@ -274,11 +300,9 @@ void IC::Clear(Address address) {
switch (target->kind()) {
case Code::LOAD_IC: return LoadIC::Clear(address, target);
case Code::KEYED_LOAD_IC:
- case Code::KEYED_EXTERNAL_ARRAY_LOAD_IC:
return KeyedLoadIC::Clear(address, target);
case Code::STORE_IC: return StoreIC::Clear(address, target);
case Code::KEYED_STORE_IC:
- case Code::KEYED_EXTERNAL_ARRAY_STORE_IC:
return KeyedStoreIC::Clear(address, target);
case Code::CALL_IC: return CallIC::Clear(address, target);
case Code::KEYED_CALL_IC: return KeyedCallIC::Clear(address, target);
@@ -1032,9 +1056,49 @@ void LoadIC::UpdateCaches(LookupResult* lookup,
}
+String* KeyedLoadIC::GetStubNameForCache(IC::State ic_state) {
+ if (ic_state == MONOMORPHIC) {
+ return isolate()->heap()->KeyedLoadSpecializedMonomorphic_symbol();
+ } else {
+ ASSERT(ic_state == MEGAMORPHIC);
+ return isolate()->heap()->KeyedLoadSpecializedPolymorphic_symbol();
+ }
+}
+
+
+MaybeObject* KeyedLoadIC::GetFastElementStubWithoutMapCheck(
+ bool is_js_array) {
+ return KeyedLoadFastElementStub().TryGetCode();
+}
+
+
+MaybeObject* KeyedLoadIC::GetExternalArrayStubWithoutMapCheck(
+ ExternalArrayType array_type) {
+ return KeyedLoadExternalArrayStub(array_type).TryGetCode();
+}
+
+
+MaybeObject* KeyedLoadIC::ConstructMegamorphicStub(
+ MapList* receiver_maps,
+ CodeList* targets,
+ StrictModeFlag strict_mode) {
+ Object* object;
+ KeyedLoadStubCompiler compiler;
+ MaybeObject* maybe_code = compiler.CompileLoadMegamorphic(receiver_maps,
+ targets);
+ if (!maybe_code->ToObject(&object)) return maybe_code;
+ isolate()->counters()->keyed_load_polymorphic_stubs()->Increment();
+ PROFILE(isolate(), CodeCreateEvent(
+ Logger::KEYED_LOAD_MEGAMORPHIC_IC_TAG,
+ Code::cast(object), 0));
+ return object;
+}
+
+
MaybeObject* KeyedLoadIC::Load(State state,
Handle<Object> object,
- Handle<Object> key) {
+ Handle<Object> key,
+ bool force_generic_stub) {
// Check for values that can be converted into a symbol.
// TODO(1295): Remove this code.
HandleScope scope(isolate());
@@ -1160,34 +1224,31 @@ MaybeObject* KeyedLoadIC::Load(State state,
if (use_ic) {
Code* stub = generic_stub();
- if (state == UNINITIALIZED) {
+ if (!force_generic_stub) {
if (object->IsString() && key->IsNumber()) {
- stub = string_stub();
+ if (state == UNINITIALIZED) {
+ stub = string_stub();
+ }
} else if (object->IsJSObject()) {
- Handle<JSObject> receiver = Handle<JSObject>::cast(object);
- if (receiver->HasExternalArrayElements()) {
- MaybeObject* probe =
- isolate()->stub_cache()->ComputeKeyedLoadOrStoreExternalArray(
- *receiver, false, kNonStrictMode);
- stub = probe->IsFailure() ?
- NULL : Code::cast(probe->ToObjectUnchecked());
- } else if (receiver->HasIndexedInterceptor()) {
+ JSObject* receiver = JSObject::cast(*object);
+ if (receiver->HasIndexedInterceptor()) {
stub = indexed_interceptor_stub();
- } else if (key->IsSmi() &&
- receiver->map()->has_fast_elements()) {
- MaybeObject* probe =
- isolate()->stub_cache()->ComputeKeyedLoadSpecialized(*receiver);
- stub = probe->IsFailure() ?
- NULL : Code::cast(probe->ToObjectUnchecked());
+ } else if (key->IsSmi()) {
+ MaybeObject* maybe_stub = ComputeStub(receiver,
+ false,
+ kNonStrictMode,
+ stub);
+ stub = maybe_stub->IsFailure() ?
+ NULL : Code::cast(maybe_stub->ToObjectUnchecked());
}
}
}
if (stub != NULL) set_target(stub);
+ }
#ifdef DEBUG
- TraceIC("KeyedLoadIC", key, state, target());
+ TraceIC("KeyedLoadIC", key, state, target());
#endif // DEBUG
- }
// Get the property.
return Runtime::GetObjectProperty(isolate(), object, key);
@@ -1484,11 +1545,256 @@ void StoreIC::UpdateCaches(LookupResult* lookup,
}
+static bool AddOneReceiverMapIfMissing(MapList* receiver_maps,
+ Map* new_receiver_map) {
+ for (int current = 0; current < receiver_maps->length(); ++current) {
+ if (receiver_maps->at(current) == new_receiver_map) {
+ return false;
+ }
+ }
+ receiver_maps->Add(new_receiver_map);
+ return true;
+}
+
+
+void KeyedIC::GetReceiverMapsForStub(Code* stub, MapList* result) {
+ ASSERT(stub->is_inline_cache_stub());
+ if (stub == string_stub()) {
+ return result->Add(isolate()->heap()->string_map());
+ } else if (stub->is_keyed_load_stub() || stub->is_keyed_store_stub()) {
+ if (stub->ic_state() == MONOMORPHIC) {
+ result->Add(Map::cast(stub->FindFirstMap()));
+ } else {
+ ASSERT(stub->ic_state() == MEGAMORPHIC);
+ AssertNoAllocation no_allocation;
+ int mask = RelocInfo::ModeMask(RelocInfo::EMBEDDED_OBJECT);
+ for (RelocIterator it(stub, mask); !it.done(); it.next()) {
+ RelocInfo* info = it.rinfo();
+ Object* object = info->target_object();
+ ASSERT(object->IsMap());
+ result->Add(Map::cast(object));
+ }
+ }
+ }
+}
+
+
+MaybeObject* KeyedIC::ComputeStub(JSObject* receiver,
+ bool is_store,
+ StrictModeFlag strict_mode,
+ Code* generic_stub) {
+ State ic_state = target()->ic_state();
+ Code* monomorphic_stub;
+ // Always compute the MONOMORPHIC stub, even if the MEGAMORPHIC stub ends up
+ // being used. This is necessary because the megamorphic stub needs to have
+ // access to more information than what is stored in the receiver map in some
+ // cases (external arrays need the array type from the MONOMORPHIC stub).
+ MaybeObject* maybe_stub = ComputeMonomorphicStub(receiver,
+ is_store,
+ strict_mode,
+ generic_stub);
+ if (!maybe_stub->To(&monomorphic_stub)) return maybe_stub;
+
+ if (ic_state == UNINITIALIZED || ic_state == PREMONOMORPHIC) {
+ return monomorphic_stub;
+ }
+ ASSERT(target() != generic_stub);
+
+ // Don't handle megamorphic property accesses for INTERCEPTORS or CALLBACKS
+ // via megamorphic stubs, since they don't have a map in their relocation info
+ // and so the stubs can't be harvested for the object needed for a map check.
+ if (target()->type() != NORMAL) {
+ return generic_stub;
+ }
+
+ // Determine the list of receiver maps that this call site has seen,
+ // adding the map that was just encountered.
+ MapList target_receiver_maps;
+ GetReceiverMapsForStub(target(), &target_receiver_maps);
+ if (!AddOneReceiverMapIfMissing(&target_receiver_maps, receiver->map())) {
+ // If the miss wasn't due to an unseen map, a MEGAMORPHIC stub
+ // won't help, use the generic stub.
+ return generic_stub;
+ }
+
+ // TODO(1385): Currently MEGAMORPHIC stubs are cached in the receiver map stub
+ // cache, but that can put receiver types together from unrelated call sites
+ // into the same stub--they always handle the union of all receiver maps seen
+ // at all call sites involving the receiver map. This is only an
+ // approximation: ideally, there would be a global cache that mapped sets of
+ // receiver maps to MEGAMORPHIC stubs. The complexity of the MEGAMORPHIC stub
+ // computation also leads to direct manipulation of the stub cache from the IC
+ // code, which the global cache solution would avoid.
+ Code::Kind kind = this->kind();
+ Code::Flags flags = Code::ComputeFlags(kind,
+ NOT_IN_LOOP,
+ MEGAMORPHIC,
+ strict_mode);
+ String* megamorphic_name = GetStubNameForCache(MEGAMORPHIC);
+ Object* maybe_cached_stub = receiver->map()->FindInCodeCache(megamorphic_name,
+ flags);
+
+ // Create a set of all receiver maps that have been seen at the IC call site
+ // and those seen by the MEGAMORPHIC cached stub, if that's the stub that's
+ // been selected.
+ MapList receiver_maps;
+ if (!maybe_cached_stub->IsUndefined()) {
+ GetReceiverMapsForStub(Code::cast(maybe_cached_stub), &receiver_maps);
+ }
+ bool added_map = false;
+ for (int i = 0; i < target_receiver_maps.length(); ++i) {
+ if (AddOneReceiverMapIfMissing(&receiver_maps,
+ target_receiver_maps.at(i))) {
+ added_map = true;
+ }
+ }
+ ASSERT(receiver_maps.length() > 0);
+
+ // If the maximum number of receiver maps has been exceeded, use the Generic
+ // version of the IC.
+ if (receiver_maps.length() > KeyedIC::kMaxKeyedPolymorphism) {
+ return generic_stub;
+ }
+
+ // If no maps have been seen at the call site that aren't in the cached
+ // stub, then use it.
+ if (!added_map) {
+ ASSERT(!maybe_cached_stub->IsUndefined());
+ ASSERT(maybe_cached_stub->IsCode());
+ return Code::cast(maybe_cached_stub);
+ }
+
+ // Lookup all of the receiver maps in the cache, they should all already
+ // have MONOMORPHIC stubs.
+ CodeList handler_ics(KeyedIC::kMaxKeyedPolymorphism);
+ for (int current = 0; current < receiver_maps.length(); ++current) {
+ Map* receiver_map(receiver_maps.at(current));
+ MaybeObject* maybe_cached_stub = ComputeMonomorphicStubWithoutMapCheck(
+ receiver_map,
+ strict_mode,
+ generic_stub);
+ Code* cached_stub;
+ if (!maybe_cached_stub->To(&cached_stub)) {
+ return maybe_cached_stub;
+ }
+ handler_ics.Add(cached_stub);
+ }
+
+ Code* stub;
+ // Build the MEGAMORPHIC stub.
+ maybe_stub = ConstructMegamorphicStub(&receiver_maps,
+ &handler_ics,
+ strict_mode);
+ if (!maybe_stub->To(&stub)) return maybe_stub;
+
+ MaybeObject* maybe_update = receiver->UpdateMapCodeCache(
+ megamorphic_name,
+ stub);
+ if (maybe_update->IsFailure()) return maybe_update;
+ return stub;
+}
+
+
+MaybeObject* KeyedIC::ComputeMonomorphicStubWithoutMapCheck(
+ Map* receiver_map,
+ StrictModeFlag strict_mode,
+ Code* generic_stub) {
+ if ((receiver_map->instance_type() & kNotStringTag) == 0) {
+ ASSERT(string_stub() != NULL);
+ return string_stub();
+ } else if (receiver_map->has_external_array_elements()) {
+ // Determine the array type from the default MONOMORPHIC already generated
+ // stub. There is no other way to determine the type of the external array
+ // directly from the receiver type.
+ Code::Kind kind = this->kind();
+ Code::Flags flags = Code::ComputeMonomorphicFlags(kind,
+ NORMAL,
+ strict_mode);
+ String* monomorphic_name = GetStubNameForCache(MONOMORPHIC);
+ Object* maybe_default_stub = receiver_map->FindInCodeCache(monomorphic_name,
+ flags);
+ if (maybe_default_stub->IsUndefined()) {
+ return generic_stub;
+ }
+ Code* default_stub = Code::cast(maybe_default_stub);
+ return GetExternalArrayStubWithoutMapCheck(
+ default_stub->external_array_type());
+ } else if (receiver_map->has_fast_elements()) {
+ bool is_js_array = receiver_map->instance_type() == JS_ARRAY_TYPE;
+ return GetFastElementStubWithoutMapCheck(is_js_array);
+ } else {
+ return generic_stub;
+ }
+}
+
+
+MaybeObject* KeyedIC::ComputeMonomorphicStub(JSObject* receiver,
+ bool is_store,
+ StrictModeFlag strict_mode,
+ Code* generic_stub) {
+ Code* result = NULL;
+ if (receiver->HasExternalArrayElements()) {
+ MaybeObject* maybe_stub =
+ isolate()->stub_cache()->ComputeKeyedLoadOrStoreExternalArray(
+ receiver, is_store, strict_mode);
+ if (!maybe_stub->To(&result)) return maybe_stub;
+ } else if (receiver->map()->has_fast_elements()) {
+ MaybeObject* maybe_stub =
+ isolate()->stub_cache()->ComputeKeyedLoadOrStoreFastElement(
+ receiver, is_store, strict_mode);
+ if (!maybe_stub->To(&result)) return maybe_stub;
+ } else {
+ result = generic_stub;
+ }
+ return result;
+}
+
+
+String* KeyedStoreIC::GetStubNameForCache(IC::State ic_state) {
+ if (ic_state == MONOMORPHIC) {
+ return isolate()->heap()->KeyedStoreSpecializedMonomorphic_symbol();
+ } else {
+ ASSERT(ic_state == MEGAMORPHIC);
+ return isolate()->heap()->KeyedStoreSpecializedPolymorphic_symbol();
+ }
+}
+
+
+MaybeObject* KeyedStoreIC::GetFastElementStubWithoutMapCheck(
+ bool is_js_array) {
+ return KeyedStoreFastElementStub(is_js_array).TryGetCode();
+}
+
+
+MaybeObject* KeyedStoreIC::GetExternalArrayStubWithoutMapCheck(
+ ExternalArrayType array_type) {
+ return KeyedStoreExternalArrayStub(array_type).TryGetCode();
+}
+
+
+MaybeObject* KeyedStoreIC::ConstructMegamorphicStub(
+ MapList* receiver_maps,
+ CodeList* targets,
+ StrictModeFlag strict_mode) {
+ Object* object;
+ KeyedStoreStubCompiler compiler(strict_mode);
+ MaybeObject* maybe_code = compiler.CompileStoreMegamorphic(receiver_maps,
+ targets);
+ if (!maybe_code->ToObject(&object)) return maybe_code;
+ isolate()->counters()->keyed_store_polymorphic_stubs()->Increment();
+ PROFILE(isolate(), CodeCreateEvent(
+ Logger::KEYED_STORE_MEGAMORPHIC_IC_TAG,
+ Code::cast(object), 0));
+ return object;
+}
+
+
MaybeObject* KeyedStoreIC::Store(State state,
StrictModeFlag strict_mode,
Handle<Object> object,
Handle<Object> key,
- Handle<Object> value) {
+ Handle<Object> value,
+ bool force_generic) {
if (key->IsSymbol()) {
Handle<String> name = Handle<String>::cast(key);
@@ -1530,29 +1836,27 @@ MaybeObject* KeyedStoreIC::Store(State state,
ASSERT(!(use_ic && object->IsJSGlobalProxy()));
if (use_ic) {
- Code* stub =
- (strict_mode == kStrictMode) ? generic_stub_strict() : generic_stub();
- if (state == UNINITIALIZED) {
- if (object->IsJSObject()) {
- Handle<JSObject> receiver = Handle<JSObject>::cast(object);
- if (receiver->HasExternalArrayElements()) {
- MaybeObject* probe =
- isolate()->stub_cache()->ComputeKeyedLoadOrStoreExternalArray(
- *receiver, true, strict_mode);
- stub = probe->IsFailure() ?
- NULL : Code::cast(probe->ToObjectUnchecked());
- } else if (key->IsSmi() && receiver->map()->has_fast_elements()) {
- MaybeObject* probe =
- isolate()->stub_cache()->ComputeKeyedStoreSpecialized(
- *receiver, strict_mode);
- stub = probe->IsFailure() ?
- NULL : Code::cast(probe->ToObjectUnchecked());
- }
+ Code* stub = (strict_mode == kStrictMode)
+ ? generic_stub_strict()
+ : generic_stub();
+ if (!force_generic) {
+ if (object->IsJSObject() && key->IsSmi()) {
+ JSObject* receiver = JSObject::cast(*object);
+ MaybeObject* maybe_stub = ComputeStub(receiver,
+ true,
+ strict_mode,
+ stub);
+ stub = maybe_stub->IsFailure() ?
+ NULL : Code::cast(maybe_stub->ToObjectUnchecked());
}
}
if (stub != NULL) set_target(stub);
}
+#ifdef DEBUG
+ TraceIC("KeyedStoreIC", key, state, target());
+#endif
+
// Set the property.
return Runtime::SetObjectProperty(
isolate(), object , key, value, NONE, strict_mode);
@@ -1721,7 +2025,16 @@ RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_Miss) {
ASSERT(args.length() == 2);
KeyedLoadIC ic(isolate);
IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
- return ic.Load(state, args.at<Object>(0), args.at<Object>(1));
+ return ic.Load(state, args.at<Object>(0), args.at<Object>(1), false);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, KeyedLoadIC_MissForceGeneric) {
+ NoHandleAllocation na;
+ ASSERT(args.length() == 2);
+ KeyedLoadIC ic(isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ return ic.Load(state, args.at<Object>(0), args.at<Object>(1), true);
}
@@ -1805,7 +2118,42 @@ RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_Miss) {
static_cast<StrictModeFlag>(extra_ic_state & kStrictMode),
args.at<Object>(0),
args.at<Object>(1),
- args.at<Object>(2));
+ args.at<Object>(2),
+ false);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_Slow) {
+ NoHandleAllocation na;
+ ASSERT(args.length() == 3);
+ KeyedStoreIC ic(isolate);
+ Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
+ Handle<Object> object = args.at<Object>(0);
+ Handle<Object> key = args.at<Object>(1);
+ Handle<Object> value = args.at<Object>(2);
+ StrictModeFlag strict_mode =
+ static_cast<StrictModeFlag>(extra_ic_state & kStrictMode);
+ return Runtime::SetObjectProperty(isolate,
+ object,
+ key,
+ value,
+ NONE,
+ strict_mode);
+}
+
+
+RUNTIME_FUNCTION(MaybeObject*, KeyedStoreIC_MissForceGeneric) {
+ NoHandleAllocation na;
+ ASSERT(args.length() == 3);
+ KeyedStoreIC ic(isolate);
+ IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
+ Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
+ return ic.Store(state,
+ static_cast<StrictModeFlag>(extra_ic_state & kStrictMode),
+ args.at<Object>(0),
+ args.at<Object>(1),
+ args.at<Object>(2),
+ true);
}
« no previous file with comments | « src/ic.h ('k') | src/list.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698