Chromium Code Reviews| OLD | NEW |
|---|---|
| 1 // Copyright 2012 the V8 project authors. All rights reserved. | 1 // Copyright 2012 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 5644 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 5655 AddInstruction(HCheckMaps::NewWithTransitions(receiver, receiver_map)); | 5655 AddInstruction(HCheckMaps::NewWithTransitions(receiver, receiver_map)); |
| 5656 } | 5656 } |
| 5657 if (!expr->holder().is_null()) { | 5657 if (!expr->holder().is_null()) { |
| 5658 AddInstruction(new(zone()) HCheckPrototypeMaps( | 5658 AddInstruction(new(zone()) HCheckPrototypeMaps( |
| 5659 Handle<JSObject>(JSObject::cast(receiver_map->prototype())), | 5659 Handle<JSObject>(JSObject::cast(receiver_map->prototype())), |
| 5660 expr->holder())); | 5660 expr->holder())); |
| 5661 } | 5661 } |
| 5662 } | 5662 } |
| 5663 | 5663 |
| 5664 | 5664 |
| 5665 class FunctionSorter { | |
| 5666 public: | |
| 5667 FunctionSorter() : index_(0), ticks_(0), ast_length_(0), src_length_(0) { } | |
| 5668 FunctionSorter(int index, int ticks, int ast_length, int src_length) | |
| 5669 : index_(index), | |
| 5670 ticks_(ticks), | |
| 5671 ast_length_(ast_length), | |
| 5672 src_length_(src_length) { } | |
| 5673 | |
| 5674 int index() const { return index_; } | |
| 5675 int ticks() const { return ticks_; } | |
| 5676 int ast_length() const { return ast_length_; } | |
| 5677 int src_length() const { return src_length_; } | |
| 5678 | |
| 5679 private: | |
| 5680 int index_; | |
| 5681 int ticks_; | |
| 5682 int ast_length_; | |
| 5683 int src_length_; | |
| 5684 }; | |
| 5685 | |
| 5686 | |
| 5687 static int CompareHotness(void const* a, void const* b) { | |
| 5688 FunctionSorter const* function1 = reinterpret_cast<FunctionSorter const*>(a); | |
| 5689 FunctionSorter const* function2 = reinterpret_cast<FunctionSorter const*>(b); | |
| 5690 int diff = function1->ticks() - function2->ticks(); | |
| 5691 if (diff != 0) return -diff; | |
| 5692 diff = function1->ast_length() - function2->ast_length(); | |
| 5693 if (diff != 0) return diff; | |
| 5694 return function1->src_length() - function2->src_length(); | |
| 5695 } | |
| 5696 | |
| 5697 | |
| 5665 void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, | 5698 void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, |
| 5666 HValue* receiver, | 5699 HValue* receiver, |
| 5667 SmallMapList* types, | 5700 SmallMapList* types, |
| 5668 Handle<String> name) { | 5701 Handle<String> name) { |
| 5669 // TODO(ager): We should recognize when the prototype chains for different | 5702 // TODO(ager): We should recognize when the prototype chains for different |
| 5670 // maps are identical. In that case we can avoid repeatedly generating the | 5703 // maps are identical. In that case we can avoid repeatedly generating the |
| 5671 // same prototype map checks. | 5704 // same prototype map checks. |
| 5672 int argument_count = expr->arguments()->length() + 1; // Includes receiver. | 5705 int argument_count = expr->arguments()->length() + 1; // Includes receiver. |
| 5673 int count = 0; | |
| 5674 HBasicBlock* join = NULL; | 5706 HBasicBlock* join = NULL; |
| 5675 for (int i = 0; i < types->length() && count < kMaxCallPolymorphism; ++i) { | 5707 FunctionSorter order[kMaxCallPolymorphism]; |
| 5708 int ordered_functions = 0; | |
| 5709 for (int i = 0; | |
| 5710 i < types->length() && ordered_functions < kMaxCallPolymorphism; | |
| 5711 ++i) { | |
| 5676 Handle<Map> map = types->at(i); | 5712 Handle<Map> map = types->at(i); |
| 5677 if (expr->ComputeTarget(map, name)) { | 5713 if (expr->ComputeTarget(map, name)) { |
| 5678 if (count == 0) { | 5714 order[ordered_functions++] = |
| 5679 // Only needed once. | 5715 FunctionSorter(i, |
| 5680 AddInstruction(new(zone()) HCheckNonSmi(receiver)); | 5716 expr->target()->shared()->code()->profiler_ticks(), |
| 5681 join = graph()->CreateBasicBlock(); | 5717 InliningAstSize(expr->target()), |
|
Michael Starzinger
2012/05/02 08:10:35
Could we just use "expr->target()->shared()->ast_n
Erik Corry
2012/05/08 12:13:38
The reason I don't do this is that we already have
| |
| 5682 } | 5718 expr->target()->shared()->SourceSize()); |
| 5683 ++count; | 5719 } |
| 5684 HBasicBlock* if_true = graph()->CreateBasicBlock(); | 5720 } |
| 5685 HBasicBlock* if_false = graph()->CreateBasicBlock(); | |
| 5686 HCompareMap* compare = | |
| 5687 new(zone()) HCompareMap(receiver, map, if_true, if_false); | |
| 5688 current_block()->Finish(compare); | |
| 5689 | 5721 |
| 5690 set_current_block(if_true); | 5722 qsort(reinterpret_cast<void*>(&order[0]), |
| 5691 AddCheckConstantFunction(expr, receiver, map, false); | 5723 ordered_functions, |
| 5692 if (FLAG_trace_inlining && FLAG_polymorphic_inlining) { | 5724 sizeof(order[0]), |
| 5693 PrintF("Trying to inline the polymorphic call to %s\n", | 5725 &CompareHotness); |
| 5694 *name->ToCString()); | |
| 5695 } | |
| 5696 if (FLAG_polymorphic_inlining && TryInlineCall(expr)) { | |
| 5697 // Trying to inline will signal that we should bailout from the | |
| 5698 // entire compilation by setting stack overflow on the visitor. | |
| 5699 if (HasStackOverflow()) return; | |
| 5700 } else { | |
| 5701 HCallConstantFunction* call = | |
| 5702 new(zone()) HCallConstantFunction(expr->target(), argument_count); | |
| 5703 call->set_position(expr->position()); | |
| 5704 PreProcessCall(call); | |
| 5705 AddInstruction(call); | |
| 5706 if (!ast_context()->IsEffect()) Push(call); | |
| 5707 } | |
| 5708 | 5726 |
| 5709 if (current_block() != NULL) current_block()->Goto(join); | 5727 for (int fn = 0; fn < ordered_functions; ++fn) { |
| 5710 set_current_block(if_false); | 5728 int i = order[fn].index(); |
| 5729 Handle<Map> map = types->at(i); | |
| 5730 if (fn == 0) { | |
| 5731 // Only needed once. | |
| 5732 AddInstruction(new(zone()) HCheckNonSmi(receiver)); | |
| 5733 join = graph()->CreateBasicBlock(); | |
| 5711 } | 5734 } |
| 5735 HBasicBlock* if_true = graph()->CreateBasicBlock(); | |
| 5736 HBasicBlock* if_false = graph()->CreateBasicBlock(); | |
| 5737 HCompareMap* compare = | |
| 5738 new(zone()) HCompareMap(receiver, map, if_true, if_false); | |
| 5739 current_block()->Finish(compare); | |
| 5740 | |
| 5741 set_current_block(if_true); | |
| 5742 expr->ComputeTarget(map, name); | |
| 5743 AddCheckConstantFunction(expr, receiver, map, false); | |
| 5744 if (FLAG_trace_inlining && FLAG_polymorphic_inlining) { | |
| 5745 Handle<JSFunction> caller = info()->closure(); | |
| 5746 SmartArrayPointer<char> caller_name = | |
| 5747 caller->shared()->DebugName()->ToCString(); | |
| 5748 PrintF("Trying to inline the polymorphic call to %s from %s\n", | |
| 5749 *name->ToCString(), | |
| 5750 *caller_name); | |
| 5751 } | |
| 5752 if (FLAG_polymorphic_inlining && TryInlineCall(expr)) { | |
| 5753 // Trying to inline will signal that we should bailout from the | |
| 5754 // entire compilation by setting stack overflow on the visitor. | |
| 5755 if (HasStackOverflow()) return; | |
| 5756 } else { | |
| 5757 HCallConstantFunction* call = | |
| 5758 new(zone()) HCallConstantFunction(expr->target(), argument_count); | |
| 5759 call->set_position(expr->position()); | |
| 5760 PreProcessCall(call); | |
| 5761 AddInstruction(call); | |
| 5762 if (!ast_context()->IsEffect()) Push(call); | |
| 5763 } | |
| 5764 | |
| 5765 if (current_block() != NULL) current_block()->Goto(join); | |
| 5766 set_current_block(if_false); | |
| 5712 } | 5767 } |
| 5713 | 5768 |
| 5714 // Finish up. Unconditionally deoptimize if we've handled all the maps we | 5769 // Finish up. Unconditionally deoptimize if we've handled all the maps we |
| 5715 // know about and do not want to handle ones we've never seen. Otherwise | 5770 // know about and do not want to handle ones we've never seen. Otherwise |
| 5716 // use a generic IC. | 5771 // use a generic IC. |
| 5717 if (count == types->length() && FLAG_deoptimize_uncommon_cases) { | 5772 if (ordered_functions == types->length() && FLAG_deoptimize_uncommon_cases) { |
| 5718 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses); | 5773 current_block()->FinishExitWithDeoptimization(HDeoptimize::kNoUses); |
| 5719 } else { | 5774 } else { |
| 5720 HValue* context = environment()->LookupContext(); | 5775 HValue* context = environment()->LookupContext(); |
| 5721 HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count); | 5776 HCallNamed* call = new(zone()) HCallNamed(context, name, argument_count); |
| 5722 call->set_position(expr->position()); | 5777 call->set_position(expr->position()); |
| 5723 PreProcessCall(call); | 5778 PreProcessCall(call); |
| 5724 | 5779 |
| 5725 if (join != NULL) { | 5780 if (join != NULL) { |
| 5726 AddInstruction(call); | 5781 AddInstruction(call); |
| 5727 if (!ast_context()->IsEffect()) Push(call); | 5782 if (!ast_context()->IsEffect()) Push(call); |
| (...skipping 28 matching lines...) Expand all Loading... | |
| 5756 if (reason == NULL) { | 5811 if (reason == NULL) { |
| 5757 PrintF("Inlined %s called from %s.\n", *target_name, *caller_name); | 5812 PrintF("Inlined %s called from %s.\n", *target_name, *caller_name); |
| 5758 } else { | 5813 } else { |
| 5759 PrintF("Did not inline %s called from %s (%s).\n", | 5814 PrintF("Did not inline %s called from %s (%s).\n", |
| 5760 *target_name, *caller_name, reason); | 5815 *target_name, *caller_name, reason); |
| 5761 } | 5816 } |
| 5762 } | 5817 } |
| 5763 } | 5818 } |
| 5764 | 5819 |
| 5765 | 5820 |
| 5766 bool HGraphBuilder::TryInline(CallKind call_kind, | 5821 static const int kNotInlinable = 1000000000; |
| 5767 Handle<JSFunction> target, | 5822 |
| 5768 ZoneList<Expression*>* arguments, | 5823 |
| 5769 HValue* receiver, | 5824 int HGraphBuilder::InliningAstSize(Handle<JSFunction> target) { |
| 5770 int ast_id, | 5825 if (!FLAG_use_inlining) return kNotInlinable; |
| 5771 int return_id, | |
| 5772 ReturnHandlingFlag return_handling) { | |
| 5773 if (!FLAG_use_inlining) return false; | |
| 5774 | 5826 |
| 5775 // Precondition: call is monomorphic and we have found a target with the | 5827 // Precondition: call is monomorphic and we have found a target with the |
| 5776 // appropriate arity. | 5828 // appropriate arity. |
| 5777 Handle<JSFunction> caller = info()->closure(); | 5829 Handle<JSFunction> caller = info()->closure(); |
| 5778 Handle<SharedFunctionInfo> target_shared(target->shared()); | 5830 Handle<SharedFunctionInfo> target_shared(target->shared()); |
| 5779 | 5831 |
| 5780 // Do a quick check on source code length to avoid parsing large | 5832 // Do a quick check on source code length to avoid parsing large |
| 5781 // inlining candidates. | 5833 // inlining candidates. |
| 5782 if (target_shared->SourceSize() > | 5834 if (target_shared->SourceSize() > |
| 5783 Min(FLAG_max_inlined_source_size, kUnlimitedMaxInlinedSourceSize)) { | 5835 Min(FLAG_max_inlined_source_size, kUnlimitedMaxInlinedSourceSize)) { |
| 5784 TraceInline(target, caller, "target text too big"); | 5836 TraceInline(target, caller, "target text too big"); |
| 5785 return false; | 5837 return kNotInlinable; |
| 5786 } | 5838 } |
| 5787 | 5839 |
| 5788 // Target must be inlineable. | 5840 // Target must be inlineable. |
| 5789 if (!target->IsInlineable()) { | 5841 if (!target->IsInlineable()) { |
| 5790 TraceInline(target, caller, "target not inlineable"); | 5842 TraceInline(target, caller, "target not inlineable"); |
| 5791 return false; | 5843 return kNotInlinable; |
| 5792 } | 5844 } |
| 5793 if (target_shared->dont_inline() || target_shared->dont_optimize()) { | 5845 if (target_shared->dont_inline() || target_shared->dont_optimize()) { |
| 5794 TraceInline(target, caller, "target contains unsupported syntax [early]"); | 5846 TraceInline(target, caller, "target contains unsupported syntax [early]"); |
| 5795 return false; | 5847 return kNotInlinable; |
| 5796 } | 5848 } |
| 5797 | 5849 |
| 5798 int nodes_added = target_shared->ast_node_count(); | 5850 int nodes_added = target_shared->ast_node_count(); |
| 5851 return nodes_added; | |
| 5852 } | |
| 5853 | |
| 5854 | |
| 5855 bool HGraphBuilder::TryInline(CallKind call_kind, | |
| 5856 Handle<JSFunction> target, | |
| 5857 ZoneList<Expression*>* arguments, | |
| 5858 HValue* receiver, | |
| 5859 int ast_id, | |
| 5860 int return_id, | |
| 5861 ReturnHandlingFlag return_handling) { | |
| 5862 int nodes_added = InliningAstSize(target); | |
| 5863 if (nodes_added == kNotInlinable) return false; | |
| 5864 | |
| 5865 Handle<JSFunction> caller = info()->closure(); | |
| 5866 | |
| 5799 if (nodes_added > Min(FLAG_max_inlined_nodes, kUnlimitedMaxInlinedNodes)) { | 5867 if (nodes_added > Min(FLAG_max_inlined_nodes, kUnlimitedMaxInlinedNodes)) { |
| 5800 TraceInline(target, caller, "target AST is too large [early]"); | 5868 TraceInline(target, caller, "target AST is too large [early]"); |
| 5801 return false; | 5869 return false; |
| 5802 } | 5870 } |
| 5803 | 5871 |
| 5872 Handle<SharedFunctionInfo> target_shared(target->shared()); | |
| 5873 | |
| 5804 #if !defined(V8_TARGET_ARCH_IA32) | 5874 #if !defined(V8_TARGET_ARCH_IA32) |
| 5805 // Target must be able to use caller's context. | 5875 // Target must be able to use caller's context. |
| 5806 CompilationInfo* outer_info = info(); | 5876 CompilationInfo* outer_info = info(); |
| 5807 if (target->context() != outer_info->closure()->context() || | 5877 if (target->context() != outer_info->closure()->context() || |
| 5808 outer_info->scope()->contains_with() || | 5878 outer_info->scope()->contains_with() || |
| 5809 outer_info->scope()->num_heap_slots() > 0) { | 5879 outer_info->scope()->num_heap_slots() > 0) { |
| 5810 TraceInline(target, caller, "target requires context change"); | 5880 TraceInline(target, caller, "target requires context change"); |
| 5811 return false; | 5881 return false; |
| 5812 } | 5882 } |
| 5813 #endif | 5883 #endif |
| (...skipping 2976 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 8790 } | 8860 } |
| 8791 } | 8861 } |
| 8792 | 8862 |
| 8793 #ifdef DEBUG | 8863 #ifdef DEBUG |
| 8794 if (graph_ != NULL) graph_->Verify(false); // No full verify. | 8864 if (graph_ != NULL) graph_->Verify(false); // No full verify. |
| 8795 if (allocator_ != NULL) allocator_->Verify(); | 8865 if (allocator_ != NULL) allocator_->Verify(); |
| 8796 #endif | 8866 #endif |
| 8797 } | 8867 } |
| 8798 | 8868 |
| 8799 } } // namespace v8::internal | 8869 } } // namespace v8::internal |
| OLD | NEW |