OLD | NEW |
---|---|
1 // Copyright 2006-2008 the V8 project authors. All rights reserved. | 1 // Copyright 2006-2008 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 20 matching lines...) Expand all Loading... | |
31 #include "api.h" | 31 #include "api.h" |
32 #include "arguments.h" | 32 #include "arguments.h" |
33 #include "execution.h" | 33 #include "execution.h" |
34 #include "ic-inl.h" | 34 #include "ic-inl.h" |
35 #include "runtime.h" | 35 #include "runtime.h" |
36 #include "stub-cache.h" | 36 #include "stub-cache.h" |
37 | 37 |
38 namespace v8 { namespace internal { | 38 namespace v8 { namespace internal { |
39 | 39 |
40 #ifdef DEBUG | 40 #ifdef DEBUG |
41 static char TransitionMarkFromState(IC::State state) { | 41 static const char TransitionMarkFromState(IC::State state) { |
42 switch (state) { | 42 switch (state) { |
43 case UNINITIALIZED: return '0'; | 43 case UNINITIALIZED: return '0'; |
44 case UNINITIALIZED_IN_LOOP: return 'L'; | |
45 case PREMONOMORPHIC: return 'P'; | 44 case PREMONOMORPHIC: return 'P'; |
46 case MONOMORPHIC: return '1'; | 45 case MONOMORPHIC: return '1'; |
47 case MONOMORPHIC_PROTOTYPE_FAILURE: return '^'; | 46 case MONOMORPHIC_PROTOTYPE_FAILURE: return '^'; |
48 case MEGAMORPHIC: return 'N'; | 47 case MEGAMORPHIC: return 'N'; |
49 | 48 |
50 // We never see the debugger states here, because the state is | 49 // We never see the debugger states here, because the state is |
51 // computed from the original code - not the patched code. Let | 50 // computed from the original code - not the patched code. Let |
52 // these cases fall through to the unreachable code below. | 51 // these cases fall through to the unreachable code below. |
53 case DEBUG_BREAK: break; | 52 case DEBUG_BREAK: break; |
54 case DEBUG_PREPARE_STEP_IN: break; | 53 case DEBUG_PREPARE_STEP_IN: break; |
55 } | 54 } |
56 UNREACHABLE(); | 55 UNREACHABLE(); |
57 return 0; | 56 return 0; |
58 } | 57 } |
59 | 58 |
60 void IC::TraceIC(const char* type, | 59 void IC::TraceIC(const char* type, |
61 Handle<String> name, | 60 Handle<String> name, |
62 State old_state, | 61 State old_state, |
63 Code* new_target) { | 62 Code* new_target, |
63 const char* extra_info) { | |
64 if (FLAG_trace_ic) { | 64 if (FLAG_trace_ic) { |
65 State new_state = StateFrom(new_target, Heap::undefined_value()); | 65 State new_state = StateFrom(new_target, Heap::undefined_value()); |
66 PrintF("[%s (%c->%c) ", type, | 66 PrintF("[%s (%c->%c)%s", type, |
67 TransitionMarkFromState(old_state), | 67 TransitionMarkFromState(old_state), |
68 TransitionMarkFromState(new_state)); | 68 TransitionMarkFromState(new_state), |
69 extra_info); | |
69 name->Print(); | 70 name->Print(); |
70 PrintF("]\n"); | 71 PrintF("]\n"); |
71 } | 72 } |
72 } | 73 } |
73 #endif | 74 #endif |
74 | 75 |
75 | 76 |
76 IC::IC(FrameDepth depth) { | 77 IC::IC(FrameDepth depth) { |
77 // To improve the performance of the (much used) IC code, we unfold | 78 // To improve the performance of the (much used) IC code, we unfold |
78 // a few levels of the stack frame iteration code. This yields a | 79 // a few levels of the stack frame iteration code. This yields a |
(...skipping 140 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
219 case Code::STORE_IC: return StoreIC::Clear(address, target); | 220 case Code::STORE_IC: return StoreIC::Clear(address, target); |
220 case Code::KEYED_STORE_IC: return KeyedStoreIC::Clear(address, target); | 221 case Code::KEYED_STORE_IC: return KeyedStoreIC::Clear(address, target); |
221 case Code::CALL_IC: return CallIC::Clear(address, target); | 222 case Code::CALL_IC: return CallIC::Clear(address, target); |
222 default: UNREACHABLE(); | 223 default: UNREACHABLE(); |
223 } | 224 } |
224 } | 225 } |
225 | 226 |
226 | 227 |
227 void CallIC::Clear(Address address, Code* target) { | 228 void CallIC::Clear(Address address, Code* target) { |
228 State state = target->ic_state(); | 229 State state = target->ic_state(); |
229 if (state == UNINITIALIZED || state == UNINITIALIZED_IN_LOOP) return; | 230 InlineCacheInLoop in_loop = target->ic_in_loop(); |
230 Code* code = StubCache::FindCallInitialize(target->arguments_count()); | 231 if (state == UNINITIALIZED) return; |
232 Code* code = | |
233 StubCache::FindCallInitialize(target->arguments_count(), in_loop); | |
231 SetTargetAtAddress(address, code); | 234 SetTargetAtAddress(address, code); |
232 } | 235 } |
233 | 236 |
234 | 237 |
235 void KeyedLoadIC::Clear(Address address, Code* target) { | 238 void KeyedLoadIC::Clear(Address address, Code* target) { |
236 if (target->ic_state() == UNINITIALIZED) return; | 239 if (target->ic_state() == UNINITIALIZED) return; |
237 // Make sure to also clear the map used in inline fast cases. If we | 240 // Make sure to also clear the map used in inline fast cases. If we |
238 // do not clear these maps, cached code can keep objects alive | 241 // do not clear these maps, cached code can keep objects alive |
239 // through the embedded maps. | 242 // through the embedded maps. |
240 ClearInlinedVersion(address); | 243 ClearInlinedVersion(address); |
(...skipping 142 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
383 void CallIC::UpdateCaches(LookupResult* lookup, | 386 void CallIC::UpdateCaches(LookupResult* lookup, |
384 State state, | 387 State state, |
385 Handle<Object> object, | 388 Handle<Object> object, |
386 Handle<String> name) { | 389 Handle<String> name) { |
387 ASSERT(lookup->IsLoaded()); | 390 ASSERT(lookup->IsLoaded()); |
388 // Bail out if we didn't find a result. | 391 // Bail out if we didn't find a result. |
389 if (!lookup->IsValid() || !lookup->IsCacheable()) return; | 392 if (!lookup->IsValid() || !lookup->IsCacheable()) return; |
390 | 393 |
391 // Compute the number of arguments. | 394 // Compute the number of arguments. |
392 int argc = target()->arguments_count(); | 395 int argc = target()->arguments_count(); |
396 InlineCacheInLoop in_loop = target()->ic_in_loop(); | |
393 Object* code = NULL; | 397 Object* code = NULL; |
394 | 398 |
395 if (state == UNINITIALIZED) { | 399 if (state == UNINITIALIZED) { |
396 // This is the first time we execute this inline cache. | 400 // This is the first time we execute this inline cache. |
397 // Set the target to the pre monomorphic stub to delay | 401 // Set the target to the pre monomorphic stub to delay |
398 // setting the monomorphic state. | 402 // setting the monomorphic state. |
399 code = StubCache::ComputeCallPreMonomorphic(argc); | 403 code = StubCache::ComputeCallPreMonomorphic(argc, in_loop); |
400 } else if (state == MONOMORPHIC) { | 404 } else if (state == MONOMORPHIC) { |
401 code = StubCache::ComputeCallMegamorphic(argc); | 405 code = StubCache::ComputeCallMegamorphic(argc, in_loop); |
402 } else { | 406 } else { |
403 // Compute monomorphic stub. | 407 // Compute monomorphic stub. |
404 switch (lookup->type()) { | 408 switch (lookup->type()) { |
405 case FIELD: { | 409 case FIELD: { |
406 int index = lookup->GetFieldIndex(); | 410 int index = lookup->GetFieldIndex(); |
407 code = StubCache::ComputeCallField(argc, *name, *object, | 411 code = StubCache::ComputeCallField(argc, in_loop, *name, *object, |
408 lookup->holder(), index); | 412 lookup->holder(), index); |
409 break; | 413 break; |
410 } | 414 } |
411 case CONSTANT_FUNCTION: { | 415 case CONSTANT_FUNCTION: { |
412 // Get the constant function and compute the code stub for this | 416 // Get the constant function and compute the code stub for this |
413 // call; used for rewriting to monomorphic state and making sure | 417 // call; used for rewriting to monomorphic state and making sure |
414 // that the code stub is in the stub cache. | 418 // that the code stub is in the stub cache. |
415 JSFunction* function = lookup->GetConstantFunction(); | 419 JSFunction* function = lookup->GetConstantFunction(); |
416 code = StubCache::ComputeCallConstant(argc, *name, *object, | 420 code = StubCache::ComputeCallConstant(argc, in_loop, *name, *object, |
417 lookup->holder(), function); | 421 lookup->holder(), function); |
418 break; | 422 break; |
419 } | 423 } |
420 case NORMAL: { | 424 case NORMAL: { |
421 // There is only one shared stub for calling normalized | 425 // There is only one shared stub for calling normalized |
422 // properties. It does not traverse the prototype chain, so the | 426 // properties. It does not traverse the prototype chain, so the |
423 // property must be found in the receiver for the stub to be | 427 // property must be found in the receiver for the stub to be |
424 // applicable. | 428 // applicable. |
425 if (!object->IsJSObject()) return; | 429 if (!object->IsJSObject()) return; |
426 Handle<JSObject> receiver = Handle<JSObject>::cast(object); | 430 Handle<JSObject> receiver = Handle<JSObject>::cast(object); |
427 if (lookup->holder() != *receiver) return; | 431 if (lookup->holder() != *receiver) return; |
428 code = StubCache::ComputeCallNormal(argc, *name, *receiver); | 432 code = StubCache::ComputeCallNormal(argc, in_loop, *name, *receiver); |
429 break; | 433 break; |
430 } | 434 } |
431 case INTERCEPTOR: { | 435 case INTERCEPTOR: { |
432 code = StubCache::ComputeCallInterceptor(argc, *name, *object, | 436 code = StubCache::ComputeCallInterceptor(argc, *name, *object, |
433 lookup->holder()); | 437 lookup->holder()); |
434 break; | 438 break; |
435 } | 439 } |
436 default: | 440 default: |
437 return; | 441 return; |
438 } | 442 } |
439 } | 443 } |
440 | 444 |
441 // If we're unable to compute the stub (not enough memory left), we | 445 // If we're unable to compute the stub (not enough memory left), we |
442 // simply avoid updating the caches. | 446 // simply avoid updating the caches. |
443 if (code->IsFailure()) return; | 447 if (code->IsFailure()) return; |
444 | 448 |
445 // Patch the call site depending on the state of the cache. | 449 // Patch the call site depending on the state of the cache. |
446 if (state == UNINITIALIZED || state == UNINITIALIZED_IN_LOOP || | 450 if (state == UNINITIALIZED || |
447 state == PREMONOMORPHIC || state == MONOMORPHIC || | 451 state == PREMONOMORPHIC || state == MONOMORPHIC || |
Kevin Millikin (Chromium)
2009/05/25 11:00:42
One disjunct per line?
| |
448 state == MONOMORPHIC_PROTOTYPE_FAILURE) { | 452 state == MONOMORPHIC_PROTOTYPE_FAILURE) { |
449 set_target(Code::cast(code)); | 453 set_target(Code::cast(code)); |
450 } | 454 } |
451 | 455 |
452 #ifdef DEBUG | 456 #ifdef DEBUG |
453 TraceIC("CallIC", name, state, target()); | 457 TraceIC("CallIC", name, state, target(), in_loop ? " (in-loop)" : ""); |
454 #endif | 458 #endif |
455 } | 459 } |
456 | 460 |
457 | 461 |
458 Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) { | 462 Object* LoadIC::Load(State state, Handle<Object> object, Handle<String> name) { |
459 // If the object is undefined or null it's illegal to try to get any | 463 // If the object is undefined or null it's illegal to try to get any |
460 // of its properties; throw a TypeError in that case. | 464 // of its properties; throw a TypeError in that case. |
461 if (object->IsUndefined() || object->IsNull()) { | 465 if (object->IsUndefined() || object->IsNull()) { |
462 return TypeError("non_object_property_load", object, name); | 466 return TypeError("non_object_property_load", object, name); |
463 } | 467 } |
(...skipping 617 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1081 // | 1085 // |
1082 | 1086 |
1083 // Used from ic_<arch>.cc. | 1087 // Used from ic_<arch>.cc. |
1084 Object* CallIC_Miss(Arguments args) { | 1088 Object* CallIC_Miss(Arguments args) { |
1085 NoHandleAllocation na; | 1089 NoHandleAllocation na; |
1086 ASSERT(args.length() == 2); | 1090 ASSERT(args.length() == 2); |
1087 CallIC ic; | 1091 CallIC ic; |
1088 IC::State state = IC::StateFrom(ic.target(), args[0]); | 1092 IC::State state = IC::StateFrom(ic.target(), args[0]); |
1089 Object* result = | 1093 Object* result = |
1090 ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1)); | 1094 ic.LoadFunction(state, args.at<Object>(0), args.at<String>(1)); |
1091 if (state != UNINITIALIZED_IN_LOOP || !result->IsJSFunction()) | 1095 |
1096 // The first time the inline cache is updated may be the first time the | |
1097 // function it references gets called. If the function was lazily compiled | |
1098 // then the first call will trigger a compilation. We check for this case | |
1099 // and we do the compilation immediately, instead of waiting for the stub | |
1100 // currently attached to the JSFunction object to trigger compilation. We | |
1101 // do this in the case where we know that the inline cache is inside a loop, | |
1102 // because then we know that we want to optimize the function. | |
1103 if (!result->IsJSFunction() || JSFunction::cast(result)->is_compiled()) { | |
1092 return result; | 1104 return result; |
1105 } | |
1093 | 1106 |
1094 // Compile the function with the knowledge that it's called from | 1107 // Compile now with optimization. |
1095 // within a loop. This enables further optimization of the function. | |
1096 HandleScope scope; | 1108 HandleScope scope; |
1097 Handle<JSFunction> function = Handle<JSFunction>(JSFunction::cast(result)); | 1109 Handle<JSFunction> function = Handle<JSFunction>(JSFunction::cast(result)); |
1098 if (!function->is_compiled()) CompileLazyInLoop(function, CLEAR_EXCEPTION); | 1110 InlineCacheInLoop in_loop = ic.target()->ic_in_loop(); |
1111 if (in_loop) { | |
Kevin Millikin (Chromium)
2009/05/25 11:00:42
if (in_loop == IN_LOOP) ....
| |
1112 CompileLazyInLoop(function, CLEAR_EXCEPTION); | |
1113 } else { | |
1114 CompileLazy(function, CLEAR_EXCEPTION); | |
1115 } | |
1099 return *function; | 1116 return *function; |
1100 } | 1117 } |
1101 | 1118 |
1102 | 1119 |
1103 void CallIC::GenerateInitialize(MacroAssembler* masm, int argc) { | 1120 void CallIC::GenerateInitialize(MacroAssembler* masm, int argc) { |
1104 Generate(masm, argc, ExternalReference(IC_Utility(kCallIC_Miss))); | 1121 Generate(masm, argc, ExternalReference(IC_Utility(kCallIC_Miss))); |
1105 } | 1122 } |
1106 | 1123 |
1107 | 1124 |
1108 void CallIC::GeneratePreMonomorphic(MacroAssembler* masm, int argc) { | 1125 void CallIC::GeneratePreMonomorphic(MacroAssembler* masm, int argc) { |
(...skipping 129 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1238 #undef ADDR | 1255 #undef ADDR |
1239 }; | 1256 }; |
1240 | 1257 |
1241 | 1258 |
1242 Address IC::AddressFromUtilityId(IC::UtilityId id) { | 1259 Address IC::AddressFromUtilityId(IC::UtilityId id) { |
1243 return IC_utilities[id]; | 1260 return IC_utilities[id]; |
1244 } | 1261 } |
1245 | 1262 |
1246 | 1263 |
1247 } } // namespace v8::internal | 1264 } } // namespace v8::internal |
OLD | NEW |