| 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);
|
| }
|
|
|
|
|
|
|