OLD | NEW |
---|---|
1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2011, the Dart project authors. Please see the AUTHORS file |
2 // for details. All rights reserved. Use of this source code is governed by a | 2 // for details. All rights reserved. Use of this source code is governed by a |
3 // BSD-style license that can be found in the LICENSE file. | 3 // BSD-style license that can be found in the LICENSE file. |
4 | 4 |
5 #include "vm/code_generator.h" | 5 #include "vm/code_generator.h" |
6 | 6 |
7 #include "vm/code_index_table.h" | 7 #include "vm/code_index_table.h" |
8 #include "vm/code_patcher.h" | 8 #include "vm/code_patcher.h" |
9 #include "vm/compiler.h" | 9 #include "vm/compiler.h" |
10 #include "vm/dart_api_impl.h" | |
10 #include "vm/dart_entry.h" | 11 #include "vm/dart_entry.h" |
11 #include "vm/exceptions.h" | 12 #include "vm/exceptions.h" |
12 #include "vm/ic_data.h" | 13 #include "vm/ic_data.h" |
13 #include "vm/ic_stubs.h" | |
14 #include "vm/object_store.h" | 14 #include "vm/object_store.h" |
15 #include "vm/resolver.h" | 15 #include "vm/resolver.h" |
16 #include "vm/runtime_entry.h" | 16 #include "vm/runtime_entry.h" |
17 #include "vm/stack_frame.h" | 17 #include "vm/stack_frame.h" |
18 #include "vm/verifier.h" | 18 #include "vm/verifier.h" |
19 | 19 |
20 namespace dart { | 20 namespace dart { |
21 | 21 |
22 DEFINE_FLAG(bool, inline_cache, true, "enable inline caches"); | 22 DEFINE_FLAG(bool, inline_cache, true, "enable inline caches"); |
23 DEFINE_FLAG(bool, trace_deopt, false, "Trace deoptimization"); | 23 DEFINE_FLAG(bool, trace_deopt, false, "Trace deoptimization"); |
(...skipping 390 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
414 Exceptions::ReThrow(excp, stack); | 414 Exceptions::ReThrow(excp, stack); |
415 } | 415 } |
416 } | 416 } |
417 | 417 |
418 | 418 |
419 // Resolves an instance function and compiles it if necessary. | 419 // Resolves an instance function and compiles it if necessary. |
420 // Arg0: receiver object. | 420 // Arg0: receiver object. |
421 // Returns: RawCode object or NULL (method not found or not compileable). | 421 // Returns: RawCode object or NULL (method not found or not compileable). |
422 // This is called by the megamorphic stub when instance call does not need to be | 422 // This is called by the megamorphic stub when instance call does not need to be |
423 // patched. | 423 // patched. |
424 // Used by megamorphic lookup/no-such-method-handling. | |
424 DEFINE_RUNTIME_ENTRY(ResolveCompileInstanceFunction, 1) { | 425 DEFINE_RUNTIME_ENTRY(ResolveCompileInstanceFunction, 1) { |
425 ASSERT(arguments.Count() == | 426 ASSERT(arguments.Count() == |
426 kResolveCompileInstanceFunctionRuntimeEntry.argument_count()); | 427 kResolveCompileInstanceFunctionRuntimeEntry.argument_count()); |
427 const Instance& receiver = Instance::CheckedHandle(arguments.At(0)); | 428 const Instance& receiver = Instance::CheckedHandle(arguments.At(0)); |
428 const Code& code = Code::Handle(ResolveCompileInstanceCallTarget(receiver)); | 429 const Code& code = Code::Handle(ResolveCompileInstanceCallTarget(receiver)); |
429 arguments.SetReturn(Code::Handle(code.raw())); | 430 arguments.SetReturn(Code::Handle(code.raw())); |
430 } | 431 } |
431 | 432 |
432 | 433 |
433 // Resolve instance call and patch it to jump to IC stub or megamorphic stub. | 434 // Handles inline cache misses bu updating the IC data array of the call |
regis
2011/10/25 18:06:27
bu -> by
srdjan
2011/10/25 18:28:50
Done.
| |
434 // After patching the caller's instance call instruction, that call will | 435 // site. |
435 // be reexecuted and ran through the created IC stub. The null receivers | 436 // Arg0: Receiver object. |
436 // have special handling, i.e., they lead to megamorphic lookup that implements | 437 // Returns: target function with compiled code or 0. |
437 // the appropriate null behavior. | 438 // Modifies the instance call to hold the updated IC data array. |
438 // Arg0: receiver object. | 439 DEFINE_RUNTIME_ENTRY(InlineCacheMissHandler, 1) { |
439 DEFINE_RUNTIME_ENTRY(ResolvePatchInstanceCall, 1) { | |
440 ASSERT(arguments.Count() == | 440 ASSERT(arguments.Count() == |
441 kResolvePatchInstanceCallRuntimeEntry.argument_count()); | 441 kInlineCacheMissHandlerRuntimeEntry.argument_count()); |
442 const Instance& receiver = Instance::CheckedHandle(arguments.At(0)); | 442 const Instance& receiver = Instance::CheckedHandle(arguments.At(0)); |
443 const Code& code = Code::Handle(ResolveCompileInstanceCallTarget(receiver)); | 443 const Code& target_code = |
444 Code::Handle(ResolveCompileInstanceCallTarget(receiver)); | |
445 if (target_code.IsNull()) { | |
446 // Let the megamorphic stub handle special cases: NoSuchMethod, | |
447 // closure calls. | |
448 arguments.SetReturn(target_code); | |
449 return; | |
450 } | |
451 const Function& target_function = | |
452 Function::Handle(target_code.function()); | |
453 ASSERT(!target_function.IsNull()); | |
454 if (receiver.IsNull()) { | |
455 // Null dispatch is slow (e.g., (null).toCString()). The only | |
456 // fast execution with null receiver is the "==" operator. | |
457 // Special handling so that we do not pollute the inline cache with null | |
458 // classes. | |
459 arguments.SetReturn(target_function); | |
460 return; | |
461 } | |
444 DartFrameIterator iterator; | 462 DartFrameIterator iterator; |
445 DartFrame* caller_frame = iterator.NextFrame(); | 463 DartFrame* caller_frame = iterator.NextFrame(); |
446 String& function_name = String::Handle(); | 464 ICData ic_data(Array::Handle( |
447 if ((!receiver.IsNull() && code.IsNull()) || !FLAG_inline_cache) { | 465 CodePatcher::GetInstanceCallIcDataAt(caller_frame->pc()))); |
448 // We did not find a method; it means either that we need to invoke | 466 GrowableArray<const Class*> classes; |
449 // noSuchMethod or that we have encountered a situation with implicit | 467 classes.Add(&Class::ZoneHandle(receiver.clazz())); |
450 // closures. All these cases are handled by the megamorphic lookup stub. | 468 ic_data.AddCheck(classes, target_function); |
451 CodePatcher::PatchInstanceCallAt( | 469 CodePatcher::SetInstanceCallIcDataAt(caller_frame->pc(), |
452 caller_frame->pc(), StubCode::MegamorphicLookupEntryPoint()); | 470 Array::ZoneHandle(ic_data.data())); |
453 if (FLAG_trace_ic) { | 471 arguments.SetReturn(target_function); |
454 OS::Print("IC: cannot find function at 0x%x -> megamorphic lookup.\n", | |
455 caller_frame->pc()); | |
456 } | |
457 if (FLAG_trace_patching) { | |
458 OS::Print("ResolvePatchInstanceCall: patching 0x%x to megamorphic\n", | |
459 caller_frame->pc()); | |
460 } | |
461 } else { | |
462 int num_arguments = -1; | |
463 int num_named_arguments = -1; | |
464 uword caller_target = 0; | |
465 CodePatcher::GetInstanceCallAt(caller_frame->pc(), | |
466 &function_name, | |
467 &num_arguments, | |
468 &num_named_arguments, | |
469 &caller_target); | |
470 // If caller_target is not in CallInstanceFunction stub (resolve call) | |
471 // then it must be pointing to an IC stub. | |
472 const Class& receiver_class = Class::ZoneHandle(receiver.clazz()); | |
473 const bool ic_miss = | |
474 !StubCode::InCallInstanceFunctionStubCode(caller_target); | |
475 GrowableArray<const Class*> classes; | |
476 GrowableArray<const Function*> targets; | |
477 if (ic_miss) { | |
478 bool is_ic = | |
479 ICStubs::RecognizeICStub(caller_target, &classes, &targets); | |
480 ASSERT(is_ic); | |
481 ASSERT(classes.length() == targets.length()); | |
482 // The returned classes array can be empty if the first patch occured | |
483 // with a null class. 'receiver_class' should not exists. | |
484 ASSERT(ICStubs::IndexOfClass(classes, receiver_class) < 0); | |
485 ASSERT(!code.IsNull()); | |
486 ASSERT(!receiver_class.IsNullClass()); | |
487 const Function& function = Function::ZoneHandle(code.function()); | |
488 targets.Add(&function); | |
489 classes.Add(&receiver_class); | |
490 } else { | |
491 // First patch of instance call. | |
492 // Do not add classes for null receiver. For first IC patch it means that | |
493 // the IC will always miss and jump to megamorphic lookup (null handling). | |
494 if (!receiver_class.IsNullClass()) { | |
495 ASSERT(!code.IsNull()); | |
496 const Function& function = Function::ZoneHandle(code.function()); | |
497 targets.Add(&function); | |
498 classes.Add(&receiver_class); | |
499 } | |
500 } | |
501 const Code& ic_code = Code::Handle(ICStubs::GetICStub(classes, targets)); | |
502 if (FLAG_trace_ic) { | |
503 CodeIndexTable* ci_table = Isolate::Current()->code_index_table(); | |
504 ASSERT(ci_table != NULL); | |
505 const Function& caller = | |
506 Function::Handle(ci_table->LookupFunction(caller_frame->pc())); | |
507 const char* patch_kind = ic_miss ? "miss" : "patch"; | |
508 OS::Print("IC %s at 0x%x '%s' (receiver:'%s' function:'%s')", | |
509 patch_kind, | |
510 caller_frame->pc(), | |
511 String::Handle(caller.name()).ToCString(), | |
512 receiver.ToCString(), | |
513 function_name.ToCString()); | |
514 OS::Print(" patched to 0x%x\n", ic_code.EntryPoint()); | |
515 if (ic_miss) { | |
516 for (int i = 0; i < classes.length(); i++) { | |
517 OS::Print(" IC Miss on %s\n", classes[i]->ToCString()); | |
518 } | |
519 } | |
520 } | |
521 CodePatcher::PatchInstanceCallAt(caller_frame->pc(), ic_code.EntryPoint()); | |
522 if (FLAG_trace_patching) { | |
523 OS::Print("ResolvePatchInstanceCall: patching 0x%x to ic 0x%x\n", | |
524 caller_frame->pc(), ic_code.EntryPoint()); | |
525 } | |
526 } | |
527 } | 472 } |
528 | 473 |
529 | 474 |
530 static RawFunction* LookupDynamicFunction(const Class& in_cls, | 475 static RawFunction* LookupDynamicFunction(const Class& in_cls, |
531 const String& name) { | 476 const String& name) { |
532 Class& cls = Class::Handle(); | 477 Class& cls = Class::Handle(); |
533 // For lookups treat null as an instance of class Object. | 478 // For lookups treat null as an instance of class Object. |
534 if (in_cls.IsNullClass()) { | 479 if (in_cls.IsNullClass()) { |
535 cls = Isolate::Current()->object_store()->object_class(); | 480 cls = Isolate::Current()->object_store()->object_class(); |
536 } else { | 481 } else { |
(...skipping 253 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
790 uword old_stack_limit = Isolate::Current()->stack_limit(); | 735 uword old_stack_limit = Isolate::Current()->stack_limit(); |
791 Isolate::Current()->AdjustStackLimitForException(); | 736 Isolate::Current()->AdjustStackLimitForException(); |
792 // Recursive stack overflow check. | 737 // Recursive stack overflow check. |
793 ASSERT(old_stack_limit != Isolate::Current()->stack_limit()); | 738 ASSERT(old_stack_limit != Isolate::Current()->stack_limit()); |
794 GrowableArray<const Object*> args; | 739 GrowableArray<const Object*> args; |
795 Exceptions::ThrowByType(Exceptions::kStackOverflow, args); | 740 Exceptions::ThrowByType(Exceptions::kStackOverflow, args); |
796 Isolate::Current()->ResetStackLimitAfterException(); | 741 Isolate::Current()->ResetStackLimitAfterException(); |
797 } | 742 } |
798 | 743 |
799 | 744 |
800 static void DisableOldCode(const Function& function, | |
801 const Code& old_code, | |
802 const Code& new_code) { | |
803 const Array& class_ic_stubs = Array::Handle(old_code.class_ic_stubs()); | |
804 if (function.IsClosureFunction()) { | |
805 // Nothing to do, code may not have inline caches. | |
806 ASSERT(class_ic_stubs.Length() == 0); | |
807 return; | |
808 } | |
809 if (function.is_static() || function.IsConstructor()) { | |
810 ASSERT(class_ic_stubs.Length() == 0); | |
811 return; | |
812 } | |
813 Code& ic_stub = Code::Handle(); | |
814 for (int i = 0; i < class_ic_stubs.Length(); i += 2) { | |
815 // i: array of classes, i + 1: ic stub code. | |
816 ic_stub ^= class_ic_stubs.At(i + 1); | |
817 ICStubs::PatchTargets(ic_stub.EntryPoint(), | |
818 old_code.EntryPoint(), | |
819 new_code.EntryPoint()); | |
820 } | |
821 new_code.set_class_ic_stubs(class_ic_stubs); | |
822 old_code.set_class_ic_stubs(Array::Handle(Array::Empty())); | |
823 } | |
824 | |
825 | |
826 // Only unoptimized code has invocation counter threshold checking. | 745 // Only unoptimized code has invocation counter threshold checking. |
827 // Once the invocation counter threshold is reached any entry into the | 746 // Once the invocation counter threshold is reached any entry into the |
828 // unoptimized code is redirected to this function. | 747 // unoptimized code is redirected to this function. |
829 DEFINE_RUNTIME_ENTRY(OptimizeInvokedFunction, 1) { | 748 DEFINE_RUNTIME_ENTRY(OptimizeInvokedFunction, 1) { |
830 ASSERT(arguments.Count() == | 749 ASSERT(arguments.Count() == |
831 kOptimizeInvokedFunctionRuntimeEntry.argument_count()); | 750 kOptimizeInvokedFunctionRuntimeEntry.argument_count()); |
832 const Function& function = Function::CheckedHandle(arguments.At(0)); | 751 const Function& function = Function::CheckedHandle(arguments.At(0)); |
833 if (function.is_optimizable()) { | 752 if (function.is_optimizable()) { |
834 ASSERT(!Code::Handle(function.code()).is_optimized()); | 753 ASSERT(!Code::Handle(function.code()).is_optimized()); |
835 const Code& unoptimized_code = Code::Handle(function.code()); | 754 const Code& unoptimized_code = Code::Handle(function.code()); |
836 // Compilation patches the entry of unoptimized code. | 755 // Compilation patches the entry of unoptimized code. |
837 Compiler::CompileOptimizedFunction(function); | 756 Compiler::CompileOptimizedFunction(function); |
838 const Code& optimized_code = Code::Handle(function.code()); | 757 const Code& optimized_code = Code::Handle(function.code()); |
839 ASSERT(!optimized_code.IsNull()); | 758 ASSERT(!optimized_code.IsNull()); |
840 ASSERT(!unoptimized_code.IsNull()); | 759 ASSERT(!unoptimized_code.IsNull()); |
841 DisableOldCode(function, unoptimized_code, optimized_code); | |
842 } else { | 760 } else { |
843 // TODO(5442338): Abort as this should not happen. | 761 // TODO(5442338): Abort as this should not happen. |
844 function.set_invocation_counter(0); | 762 function.set_invocation_counter(0); |
845 } | 763 } |
846 } | 764 } |
847 | 765 |
848 | 766 |
849 // The caller must be a static call in a Dart frame, or an entry frame. | 767 // The caller must be a static call in a Dart frame, or an entry frame. |
850 // Patch static call to point to 'new_entry_point'. | 768 // Patch static call to point to 'new_entry_point'. |
851 DEFINE_RUNTIME_ENTRY(FixCallersTarget, 1) { | 769 DEFINE_RUNTIME_ENTRY(FixCallersTarget, 1) { |
(...skipping 71 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
923 function.set_invocation_counter(0); | 841 function.set_invocation_counter(0); |
924 function.set_deoptimization_counter(function.deoptimization_counter() + 1); | 842 function.set_deoptimization_counter(function.deoptimization_counter() + 1); |
925 | 843 |
926 // We have to skip the following otherwise the compiler will complain | 844 // We have to skip the following otherwise the compiler will complain |
927 // when it attempts to install unoptimized code into a function that | 845 // when it attempts to install unoptimized code into a function that |
928 // was already deoptimized. | 846 // was already deoptimized. |
929 if (Code::Handle(function.code()).is_optimized()) { | 847 if (Code::Handle(function.code()).is_optimized()) { |
930 // Get unoptimized code. Compilation restores (reenables) the entry of | 848 // Get unoptimized code. Compilation restores (reenables) the entry of |
931 // unoptimized code. | 849 // unoptimized code. |
932 Compiler::CompileFunction(function); | 850 Compiler::CompileFunction(function); |
933 | |
934 DisableOldCode(function, optimized_code, unoptimized_code); | |
935 } | 851 } |
936 // TODO(srdjan): Handle better complex cases, e.g. when an older optimized | 852 // TODO(srdjan): Handle better complex cases, e.g. when an older optimized |
937 // code is alive on frame and gets deoptimized after the function was | 853 // code is alive on frame and gets deoptimized after the function was |
938 // optimized a second time. | 854 // optimized a second time. |
939 if (FLAG_trace_deopt) { | 855 if (FLAG_trace_deopt) { |
940 OS::Print("After patching ->0x%x:\n", continue_at_pc); | 856 OS::Print("After patching ->0x%x:\n", continue_at_pc); |
941 } | 857 } |
942 } | 858 } |
943 | 859 |
944 | 860 |
(...skipping 70 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1015 } | 931 } |
1016 } | 932 } |
1017 } | 933 } |
1018 // The cache is null terminated, therefore the loop above should never | 934 // The cache is null terminated, therefore the loop above should never |
1019 // terminate by itself. | 935 // terminate by itself. |
1020 UNREACHABLE(); | 936 UNREACHABLE(); |
1021 return Code::null(); | 937 return Code::null(); |
1022 } | 938 } |
1023 | 939 |
1024 } // namespace dart | 940 } // namespace dart |
OLD | NEW |