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

Side by Side Diff: src/ic.cc

Issue 6344005: Introduce extra IC state to record additional feedback from IC-s. (Closed)
Patch Set: Shared miss stub Created 9 years, 11 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 unified diff | Download patch
OLDNEW
1 // Copyright 2006-2009 the V8 project authors. All rights reserved. 1 // Copyright 2006-2009 the V8 project authors. All rights reserved.
2 // Redistribution and use in source and binary forms, with or without 2 // Redistribution and use in source and binary forms, with or without
3 // modification, are permitted provided that the following conditions are 3 // modification, are permitted provided that the following conditions are
4 // met: 4 // met:
5 // 5 //
6 // * Redistributions of source code must retain the above copyright 6 // * Redistributions of source code must retain the above copyright
7 // notice, this list of conditions and the following disclaimer. 7 // notice, this list of conditions and the following disclaimer.
8 // * Redistributions in binary form must reproduce the above 8 // * Redistributions in binary form must reproduce the above
9 // copyright notice, this list of conditions and the following 9 // copyright notice, this list of conditions and the following
10 // disclaimer in the documentation and/or other materials provided 10 // disclaimer in the documentation and/or other materials provided
(...skipping 136 matching lines...) Expand 10 before | Expand all | Expand 10 after
147 !current->IsJSGlobalProxy() && 147 !current->IsJSGlobalProxy() &&
148 !current->IsJSGlobalObject()) { 148 !current->IsJSGlobalObject()) {
149 return true; 149 return true;
150 } 150 }
151 } 151 }
152 152
153 return false; 153 return false;
154 } 154 }
155 155
156 156
157 IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) { 157 static bool TryRemoveInvalidPrototypeDependentStub(Code* target,
158 IC::State state = target->ic_state(); 158 Object* receiver,
159 159 Object* name) {
160 if (state != MONOMORPHIC || !name->IsString()) return state;
161 if (receiver->IsUndefined() || receiver->IsNull()) return state;
162
163 InlineCacheHolderFlag cache_holder = 160 InlineCacheHolderFlag cache_holder =
164 Code::ExtractCacheHolderFromFlags(target->flags()); 161 Code::ExtractCacheHolderFromFlags(target->flags());
165 162
166
167 if (cache_holder == OWN_MAP && !receiver->IsJSObject()) { 163 if (cache_holder == OWN_MAP && !receiver->IsJSObject()) {
168 // The stub was generated for JSObject but called for non-JSObject. 164 // The stub was generated for JSObject but called for non-JSObject.
169 // IC::GetCodeCacheHolder is not applicable. 165 // IC::GetCodeCacheHolder is not applicable.
170 return MONOMORPHIC; 166 return false;
171 } else if (cache_holder == PROTOTYPE_MAP && 167 } else if (cache_holder == PROTOTYPE_MAP &&
172 receiver->GetPrototype()->IsNull()) { 168 receiver->GetPrototype()->IsNull()) {
173 // IC::GetCodeCacheHolder is not applicable. 169 // IC::GetCodeCacheHolder is not applicable.
174 return MONOMORPHIC; 170 return false;
175 } 171 }
176 Map* map = IC::GetCodeCacheHolder(receiver, cache_holder)->map(); 172 Map* map = IC::GetCodeCacheHolder(receiver, cache_holder)->map();
177 173
178 // Decide whether the inline cache failed because of changes to the 174 // Decide whether the inline cache failed because of changes to the
179 // receiver itself or changes to one of its prototypes. 175 // receiver itself or changes to one of its prototypes.
180 // 176 //
181 // If there are changes to the receiver itself, the map of the 177 // If there are changes to the receiver itself, the map of the
182 // receiver will have changed and the current target will not be in 178 // receiver will have changed and the current target will not be in
183 // the receiver map's code cache. Therefore, if the current target 179 // the receiver map's code cache. Therefore, if the current target
184 // is in the receiver map's code cache, the inline cache failed due 180 // is in the receiver map's code cache, the inline cache failed due
185 // to prototype check failure. 181 // to prototype check failure.
186 int index = map->IndexInCodeCache(name, target); 182 int index = map->IndexInCodeCache(name, target);
187 if (index >= 0) { 183 if (index >= 0) {
188 // For keyed load/store/call, the most likely cause of cache failure is 184 map->RemoveFromCodeCache(String::cast(name), target, index);
189 // that the key has changed. We do not distinguish between 185 return true;
190 // prototype and non-prototype failures for keyed access. 186 }
191 Code::Kind kind = target->kind();
192 if (kind == Code::KEYED_LOAD_IC ||
193 kind == Code::KEYED_STORE_IC ||
194 kind == Code::KEYED_CALL_IC) {
195 return MONOMORPHIC;
196 }
197 187
198 // Remove the target from the code cache to avoid hitting the same 188 return false;
199 // invalid stub again. 189 }
200 map->RemoveFromCodeCache(String::cast(name), target, index);
201 190
191
192 IC::State IC::StateFrom(Code* target, Object* receiver, Object* name) {
193 IC::State state = target->ic_state();
194
195 if (state != MONOMORPHIC || !name->IsString()) return state;
196 if (receiver->IsUndefined() || receiver->IsNull()) return state;
197
198 // For keyed load/store/call, the most likely cause of cache failure is
199 // that the key has changed. We do not distinguish between
200 // prototype and non-prototype failures for keyed access.
201 Code::Kind kind = target->kind();
202 if (kind == Code::KEYED_LOAD_IC ||
203 kind == Code::KEYED_STORE_IC ||
204 kind == Code::KEYED_CALL_IC) {
205 return MONOMORPHIC;
206 }
207
208 // Remove the target from the code cache if it became invalid
209 // because of changes in the prototype chain to avoid hitting it
210 // again.
211 // Call stubs handle this later to allow extra IC state
212 // transitions.
213 if (kind != Code::CALL_IC &&
214 TryRemoveInvalidPrototypeDependentStub(target, receiver, name)) {
202 return MONOMORPHIC_PROTOTYPE_FAILURE; 215 return MONOMORPHIC_PROTOTYPE_FAILURE;
203 } 216 }
204 217
205 // The builtins object is special. It only changes when JavaScript 218 // The builtins object is special. It only changes when JavaScript
206 // builtins are loaded lazily. It is important to keep inline 219 // builtins are loaded lazily. It is important to keep inline
207 // caches for the builtins object monomorphic. Therefore, if we get 220 // caches for the builtins object monomorphic. Therefore, if we get
208 // an inline cache miss for the builtins object after lazily loading 221 // an inline cache miss for the builtins object after lazily loading
209 // JavaScript builtins, we return uninitialized as the state to 222 // JavaScript builtins, we return uninitialized as the state to
210 // force the inline cache back to monomorphic state. 223 // force the inline cache back to monomorphic state.
211 if (receiver->IsJSBuiltinsObject()) { 224 if (receiver->IsJSBuiltinsObject()) {
(...skipping 263 matching lines...) Expand 10 before | Expand all | Expand 10 after
475 // Change the receiver to the result of calling ToObject on it. 488 // Change the receiver to the result of calling ToObject on it.
476 const int argc = this->target()->arguments_count(); 489 const int argc = this->target()->arguments_count();
477 StackFrameLocator locator; 490 StackFrameLocator locator;
478 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0); 491 JavaScriptFrame* frame = locator.FindJavaScriptFrame(0);
479 int index = frame->ComputeExpressionsCount() - (argc + 1); 492 int index = frame->ComputeExpressionsCount() - (argc + 1);
480 frame->SetExpression(index, *Factory::ToObject(object)); 493 frame->SetExpression(index, *Factory::ToObject(object));
481 } 494 }
482 495
483 496
484 MaybeObject* CallICBase::LoadFunction(State state, 497 MaybeObject* CallICBase::LoadFunction(State state,
498 Code::ExtraICState extra_ic_state,
485 Handle<Object> object, 499 Handle<Object> object,
486 Handle<String> name) { 500 Handle<String> name) {
487 // If the object is undefined or null it's illegal to try to get any 501 // If the object is undefined or null it's illegal to try to get any
488 // of its properties; throw a TypeError in that case. 502 // of its properties; throw a TypeError in that case.
489 if (object->IsUndefined() || object->IsNull()) { 503 if (object->IsUndefined() || object->IsNull()) {
490 return TypeError("non_object_property_call", object, name); 504 return TypeError("non_object_property_call", object, name);
491 } 505 }
492 506
493 if (object->IsString() || object->IsNumber() || object->IsBoolean()) { 507 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
494 ReceiverToObject(object); 508 ReceiverToObject(object);
(...skipping 25 matching lines...) Expand all
520 // If the object does not have the requested property, check which 534 // If the object does not have the requested property, check which
521 // exception we need to throw. 535 // exception we need to throw.
522 if (IsContextual(object)) { 536 if (IsContextual(object)) {
523 return ReferenceError("not_defined", name); 537 return ReferenceError("not_defined", name);
524 } 538 }
525 return TypeError("undefined_method", object, name); 539 return TypeError("undefined_method", object, name);
526 } 540 }
527 541
528 // Lookup is valid: Update inline cache and stub cache. 542 // Lookup is valid: Update inline cache and stub cache.
529 if (FLAG_use_ic) { 543 if (FLAG_use_ic) {
530 UpdateCaches(&lookup, state, object, name); 544 UpdateCaches(&lookup, state, extra_ic_state, object, name);
531 } 545 }
532 546
533 // Get the property. 547 // Get the property.
534 PropertyAttributes attr; 548 PropertyAttributes attr;
535 Object* result; 549 Object* result;
536 { MaybeObject* maybe_result = 550 { MaybeObject* maybe_result =
537 object->GetProperty(*object, &lookup, *name, &attr); 551 object->GetProperty(*object, &lookup, *name, &attr);
538 if (!maybe_result->ToObject(&result)) return maybe_result; 552 if (!maybe_result->ToObject(&result)) return maybe_result;
539 } 553 }
540 if (lookup.type() == INTERCEPTOR) { 554 if (lookup.type() == INTERCEPTOR) {
(...skipping 28 matching lines...) Expand all
569 // Try to find a suitable function delegate for the object at hand. 583 // Try to find a suitable function delegate for the object at hand.
570 result = TryCallAsFunction(result); 584 result = TryCallAsFunction(result);
571 MaybeObject* answer = result; 585 MaybeObject* answer = result;
572 if (!result->IsJSFunction()) { 586 if (!result->IsJSFunction()) {
573 answer = TypeError("property_not_function", object, name); 587 answer = TypeError("property_not_function", object, name);
574 } 588 }
575 return answer; 589 return answer;
576 } 590 }
577 591
578 592
593 bool CallICBase::TryUpdateExtraICState(LookupResult* lookup,
594 Handle<Object> object,
595 Code::ExtraICState* extra_ic_state) {
596 ASSERT(kind_ == Code::CALL_IC);
597 if (lookup->type() != CONSTANT_FUNCTION) return false;
598 JSFunction* function = lookup->GetConstantFunction();
599 if (!function->shared()->HasBuiltinFunctionId()) return false;
600
601 // Fetch the arguments passed to the called function.
602 const int argc = target()->arguments_count();
603 Address entry = Top::c_entry_fp(Top::GetCurrentThread());
604 Address fp = Memory::Address_at(entry + ExitFrameConstants::kCallerFPOffset);
605 Arguments args(argc + 1,
606 &Memory::Object_at(fp +
607 StandardFrameConstants::kCallerSPOffset +
608 argc * kPointerSize));
609 switch (function->shared()->builtin_function_id()) {
610 case kStringCharCodeAt:
611 case kStringCharAt:
612 if (object->IsString()) {
613 String* string = String::cast(*object);
614 // Check that there's the right wrapper in the receiver slot.
615 ASSERT(string == JSValue::cast(args[0])->value());
616 // If we're in the default (fastest) state and the index is
617 // out of bounds, update the state to record this fact.
618 if (*extra_ic_state == DEFAULT_STRING_STUB &&
619 argc >= 1 && args[1]->IsNumber()) {
620 double index;
621 if (args[1]->IsSmi()) {
622 index = Smi::cast(args[1])->value();
623 } else {
624 ASSERT(args[1]->IsHeapNumber());
625 index = DoubleToInteger(HeapNumber::cast(args[1])->value());
626 }
627 if (index < 0 || index >= string->length()) {
628 *extra_ic_state = STRING_INDEX_OUT_OF_BOUNDS;
629 return true;
630 }
631 }
632 }
633 break;
634 default:
635 return false;
636 }
637 return false;
638 }
639
640
579 void CallICBase::UpdateCaches(LookupResult* lookup, 641 void CallICBase::UpdateCaches(LookupResult* lookup,
580 State state, 642 State state,
643 Code::ExtraICState extra_ic_state,
581 Handle<Object> object, 644 Handle<Object> object,
582 Handle<String> name) { 645 Handle<String> name) {
583 // Bail out if we didn't find a result. 646 // Bail out if we didn't find a result.
584 if (!lookup->IsProperty() || !lookup->IsCacheable()) return; 647 if (!lookup->IsProperty() || !lookup->IsCacheable()) return;
585 648
586 if (lookup->holder() != *object && 649 if (lookup->holder() != *object &&
587 HasNormalObjectsInPrototypeChain(lookup, object->GetPrototype())) { 650 HasNormalObjectsInPrototypeChain(lookup, object->GetPrototype())) {
588 // Suppress optimization for prototype chains with slow properties objects 651 // Suppress optimization for prototype chains with slow properties objects
589 // in the middle. 652 // in the middle.
590 return; 653 return;
591 } 654 }
592 655
656
657 if (kind_ == Code::CALL_IC && state == MONOMORPHIC) {
Mads Ager (chromium) 2011/01/18 12:54:00 This is non-trivial and requires a comment. Woul
658 if (TryUpdateExtraICState(lookup, object, &extra_ic_state)) {
659 state = PREMONOMORPHIC;
660 } else if (TryRemoveInvalidPrototypeDependentStub(target(),
661 *object,
662 *name)) {
663 state = MONOMORPHIC_PROTOTYPE_FAILURE;
664 }
665 }
666
593 // Compute the number of arguments. 667 // Compute the number of arguments.
594 int argc = target()->arguments_count(); 668 int argc = target()->arguments_count();
595 InLoopFlag in_loop = target()->ic_in_loop(); 669 InLoopFlag in_loop = target()->ic_in_loop();
596 MaybeObject* maybe_code = NULL; 670 MaybeObject* maybe_code = NULL;
597 Object* code; 671 Object* code;
598 if (state == UNINITIALIZED) { 672 if (state == UNINITIALIZED) {
599 // This is the first time we execute this inline cache. 673 // This is the first time we execute this inline cache.
600 // Set the target to the pre monomorphic stub to delay 674 // Set the target to the pre monomorphic stub to delay
601 // setting the monomorphic state. 675 // setting the monomorphic state.
602 maybe_code = StubCache::ComputeCallPreMonomorphic(argc, in_loop, kind_); 676 maybe_code = StubCache::ComputeCallPreMonomorphic(argc, in_loop, kind_);
(...skipping 14 matching lines...) Expand all
617 break; 691 break;
618 } 692 }
619 case CONSTANT_FUNCTION: { 693 case CONSTANT_FUNCTION: {
620 // Get the constant function and compute the code stub for this 694 // Get the constant function and compute the code stub for this
621 // call; used for rewriting to monomorphic state and making sure 695 // call; used for rewriting to monomorphic state and making sure
622 // that the code stub is in the stub cache. 696 // that the code stub is in the stub cache.
623 JSFunction* function = lookup->GetConstantFunction(); 697 JSFunction* function = lookup->GetConstantFunction();
624 maybe_code = StubCache::ComputeCallConstant(argc, 698 maybe_code = StubCache::ComputeCallConstant(argc,
625 in_loop, 699 in_loop,
626 kind_, 700 kind_,
701 extra_ic_state,
627 *name, 702 *name,
628 *object, 703 *object,
629 lookup->holder(), 704 lookup->holder(),
630 function); 705 function);
631 break; 706 break;
632 } 707 }
633 case NORMAL: { 708 case NORMAL: {
634 if (!object->IsJSObject()) return; 709 if (!object->IsJSObject()) return;
635 Handle<JSObject> receiver = Handle<JSObject>::cast(object); 710 Handle<JSObject> receiver = Handle<JSObject>::cast(object);
636 711
(...skipping 63 matching lines...) Expand 10 before | Expand all | Expand 10 after
700 TraceIC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC", 775 TraceIC(kind_ == Code::CALL_IC ? "CallIC" : "KeyedCallIC",
701 name, state, target(), in_loop ? " (in-loop)" : ""); 776 name, state, target(), in_loop ? " (in-loop)" : "");
702 #endif 777 #endif
703 } 778 }
704 779
705 780
706 MaybeObject* KeyedCallIC::LoadFunction(State state, 781 MaybeObject* KeyedCallIC::LoadFunction(State state,
707 Handle<Object> object, 782 Handle<Object> object,
708 Handle<Object> key) { 783 Handle<Object> key) {
709 if (key->IsSymbol()) { 784 if (key->IsSymbol()) {
710 return CallICBase::LoadFunction(state, object, Handle<String>::cast(key)); 785 return CallICBase::LoadFunction(state,
786 Code::kNoExtraICState,
787 object,
788 Handle<String>::cast(key));
711 } 789 }
712 790
713 if (object->IsUndefined() || object->IsNull()) { 791 if (object->IsUndefined() || object->IsNull()) {
714 return TypeError("non_object_property_call", object, key); 792 return TypeError("non_object_property_call", object, key);
715 } 793 }
716 794
717 if (object->IsString() || object->IsNumber() || object->IsBoolean()) { 795 if (object->IsString() || object->IsNumber() || object->IsBoolean()) {
718 ReceiverToObject(object); 796 ReceiverToObject(object);
719 } 797 }
720 798
(...skipping 913 matching lines...) Expand 10 before | Expand all | Expand 10 after
1634 return *function_handle; 1712 return *function_handle;
1635 } 1713 }
1636 1714
1637 1715
1638 // Used from ic-<arch>.cc. 1716 // Used from ic-<arch>.cc.
1639 MUST_USE_RESULT MaybeObject* CallIC_Miss(Arguments args) { 1717 MUST_USE_RESULT MaybeObject* CallIC_Miss(Arguments args) {
1640 NoHandleAllocation na; 1718 NoHandleAllocation na;
1641 ASSERT(args.length() == 2); 1719 ASSERT(args.length() == 2);
1642 CallIC ic; 1720 CallIC ic;
1643 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]); 1721 IC::State state = IC::StateFrom(ic.target(), args[0], args[1]);
1722 Code::ExtraICState extra_ic_state = ic.target()->extra_ic_state();
1723 MaybeObject* maybe_result = ic.LoadFunction(state,
1724 extra_ic_state,
1725 args.at<Object>(0),
1726 args.at<String>(1));
1644 Object* result; 1727 Object* result;
1645 { MaybeObject* maybe_result = 1728 if (!maybe_result->ToObject(&result)) return maybe_result;
1646 ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1));
1647 if (!maybe_result->ToObject(&result)) return maybe_result;
1648 }
1649 1729
1650 // The first time the inline cache is updated may be the first time the 1730 // The first time the inline cache is updated may be the first time the
1651 // function it references gets called. If the function was lazily compiled 1731 // function it references gets called. If the function was lazily compiled
1652 // then the first call will trigger a compilation. We check for this case 1732 // then the first call will trigger a compilation. We check for this case
1653 // and we do the compilation immediately, instead of waiting for the stub 1733 // and we do the compilation immediately, instead of waiting for the stub
1654 // currently attached to the JSFunction object to trigger compilation. We 1734 // currently attached to the JSFunction object to trigger compilation. We
1655 // do this in the case where we know that the inline cache is inside a loop, 1735 // do this in the case where we know that the inline cache is inside a loop,
1656 // because then we know that we want to optimize the function. 1736 // because then we know that we want to optimize the function.
1657 if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) { 1737 if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) {
1658 return result; 1738 return result;
(...skipping 505 matching lines...) Expand 10 before | Expand all | Expand 10 after
2164 #undef ADDR 2244 #undef ADDR
2165 }; 2245 };
2166 2246
2167 2247
2168 Address IC::AddressFromUtilityId(IC::UtilityId id) { 2248 Address IC::AddressFromUtilityId(IC::UtilityId id) {
2169 return IC_utilities[id]; 2249 return IC_utilities[id];
2170 } 2250 }
2171 2251
2172 2252
2173 } } // namespace v8::internal 2253 } } // namespace v8::internal
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698