Chromium Code Reviews| 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 |