OLD | NEW |
1 // Copyright 2011 the V8 project authors. All rights reserved. | 1 // Copyright 2011 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 132 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
143 if (end->FirstSuccessor() != NULL) { | 143 if (end->FirstSuccessor() != NULL) { |
144 end->FirstSuccessor()->RegisterPredecessor(this); | 144 end->FirstSuccessor()->RegisterPredecessor(this); |
145 if (end->SecondSuccessor() != NULL) { | 145 if (end->SecondSuccessor() != NULL) { |
146 end->SecondSuccessor()->RegisterPredecessor(this); | 146 end->SecondSuccessor()->RegisterPredecessor(this); |
147 } | 147 } |
148 } | 148 } |
149 } | 149 } |
150 | 150 |
151 | 151 |
152 void HBasicBlock::Goto(HBasicBlock* block, bool include_stack_check) { | 152 void HBasicBlock::Goto(HBasicBlock* block, bool include_stack_check) { |
| 153 if (block->IsInlineReturnTarget()) { |
| 154 AddInstruction(new HLeaveInlined); |
| 155 last_environment_ = last_environment()->outer(); |
| 156 } |
153 AddSimulate(AstNode::kNoNumber); | 157 AddSimulate(AstNode::kNoNumber); |
154 HGoto* instr = new HGoto(block); | 158 HGoto* instr = new HGoto(block); |
155 instr->set_include_stack_check(include_stack_check); | 159 instr->set_include_stack_check(include_stack_check); |
156 Finish(instr); | 160 Finish(instr); |
157 } | 161 } |
158 | 162 |
159 | 163 |
| 164 void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) { |
| 165 ASSERT(target->IsInlineReturnTarget()); |
| 166 ASSERT(return_value != NULL); |
| 167 AddInstruction(new HLeaveInlined); |
| 168 last_environment_ = last_environment()->outer(); |
| 169 last_environment()->Push(return_value); |
| 170 AddSimulate(AstNode::kNoNumber); |
| 171 HGoto* instr = new HGoto(target); |
| 172 Finish(instr); |
| 173 } |
| 174 |
| 175 |
160 void HBasicBlock::SetInitialEnvironment(HEnvironment* env) { | 176 void HBasicBlock::SetInitialEnvironment(HEnvironment* env) { |
161 ASSERT(!HasEnvironment()); | 177 ASSERT(!HasEnvironment()); |
162 ASSERT(first() == NULL); | 178 ASSERT(first() == NULL); |
163 UpdateEnvironment(env); | 179 UpdateEnvironment(env); |
164 } | 180 } |
165 | 181 |
166 | 182 |
167 void HBasicBlock::SetJoinId(int id) { | 183 void HBasicBlock::SetJoinId(int id) { |
168 int length = predecessors_.length(); | 184 int length = predecessors_.length(); |
169 ASSERT(length > 0); | 185 ASSERT(length > 0); |
(...skipping 1802 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
1972 // We expect the graph to be in edge-split form: there is no edge that | 1988 // We expect the graph to be in edge-split form: there is no edge that |
1973 // connects a branch node to a join node. We conservatively ensure that | 1989 // connects a branch node to a join node. We conservatively ensure that |
1974 // property by always adding an empty block on the outgoing edges of this | 1990 // property by always adding an empty block on the outgoing edges of this |
1975 // branch. | 1991 // branch. |
1976 HGraphBuilder* builder = owner(); | 1992 HGraphBuilder* builder = owner(); |
1977 HBasicBlock* empty_true = builder->graph()->CreateBasicBlock(); | 1993 HBasicBlock* empty_true = builder->graph()->CreateBasicBlock(); |
1978 HBasicBlock* empty_false = builder->graph()->CreateBasicBlock(); | 1994 HBasicBlock* empty_false = builder->graph()->CreateBasicBlock(); |
1979 HTest* test = new HTest(value, empty_true, empty_false); | 1995 HTest* test = new HTest(value, empty_true, empty_false); |
1980 builder->current_block()->Finish(test); | 1996 builder->current_block()->Finish(test); |
1981 | 1997 |
1982 HValue* const no_return_value = NULL; | 1998 empty_true->Goto(if_true(), false); |
1983 HBasicBlock* true_target = if_true(); | 1999 empty_false->Goto(if_false(), false); |
1984 if (true_target->IsInlineReturnTarget()) { | |
1985 empty_true->AddLeaveInlined(no_return_value, true_target); | |
1986 } else { | |
1987 empty_true->Goto(true_target); | |
1988 } | |
1989 | |
1990 HBasicBlock* false_target = if_false(); | |
1991 if (false_target->IsInlineReturnTarget()) { | |
1992 empty_false->AddLeaveInlined(no_return_value, false_target); | |
1993 } else { | |
1994 empty_false->Goto(false_target); | |
1995 } | |
1996 builder->set_current_block(NULL); | 2000 builder->set_current_block(NULL); |
1997 } | 2001 } |
1998 | 2002 |
1999 | 2003 |
2000 // HGraphBuilder infrastructure for bailing out and checking bailouts. | 2004 // HGraphBuilder infrastructure for bailing out and checking bailouts. |
2001 #define BAILOUT(reason) \ | 2005 #define BAILOUT(reason) \ |
2002 do { \ | 2006 do { \ |
2003 Bailout(reason); \ | 2007 Bailout(reason); \ |
2004 return; \ | 2008 return; \ |
2005 } while (false) | 2009 } while (false) |
(...skipping 295 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2301 } | 2305 } |
2302 | 2306 |
2303 | 2307 |
2304 HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) { | 2308 HBasicBlock* HGraphBuilder::CreateBasicBlock(HEnvironment* env) { |
2305 HBasicBlock* b = graph()->CreateBasicBlock(); | 2309 HBasicBlock* b = graph()->CreateBasicBlock(); |
2306 b->SetInitialEnvironment(env); | 2310 b->SetInitialEnvironment(env); |
2307 return b; | 2311 return b; |
2308 } | 2312 } |
2309 | 2313 |
2310 | 2314 |
2311 HSubgraph* HGraphBuilder::CreateInlinedSubgraph(HEnvironment* outer, | |
2312 Handle<JSFunction> target, | |
2313 FunctionLiteral* function) { | |
2314 HConstant* undefined = graph()->GetConstantUndefined(); | |
2315 HEnvironment* inner = | |
2316 outer->CopyForInlining(target, function, true, undefined); | |
2317 HSubgraph* subgraph = new HSubgraph(graph()); | |
2318 subgraph->Initialize(CreateBasicBlock(inner)); | |
2319 return subgraph; | |
2320 } | |
2321 | |
2322 | |
2323 HSubgraph* HGraphBuilder::CreateEmptySubgraph() { | 2315 HSubgraph* HGraphBuilder::CreateEmptySubgraph() { |
2324 HSubgraph* subgraph = new HSubgraph(graph()); | 2316 HSubgraph* subgraph = new HSubgraph(graph()); |
2325 subgraph->Initialize(graph()->CreateBasicBlock()); | 2317 subgraph->Initialize(graph()->CreateBasicBlock()); |
2326 return subgraph; | 2318 return subgraph; |
2327 } | 2319 } |
2328 | 2320 |
2329 | 2321 |
2330 HSubgraph* HGraphBuilder::CreateBranchSubgraph(HEnvironment* env) { | |
2331 HSubgraph* subgraph = new HSubgraph(graph()); | |
2332 HEnvironment* new_env = env->Copy(); | |
2333 subgraph->Initialize(CreateBasicBlock(new_env)); | |
2334 return subgraph; | |
2335 } | |
2336 | |
2337 | |
2338 HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() { | 2322 HBasicBlock* HGraphBuilder::CreateLoopHeaderBlock() { |
2339 HBasicBlock* header = graph()->CreateBasicBlock(); | 2323 HBasicBlock* header = graph()->CreateBasicBlock(); |
2340 HEnvironment* entry_env = environment()->CopyAsLoopHeader(header); | 2324 HEnvironment* entry_env = environment()->CopyAsLoopHeader(header); |
2341 header->SetInitialEnvironment(entry_env); | 2325 header->SetInitialEnvironment(entry_env); |
2342 header->AttachLoopInformation(); | 2326 header->AttachLoopInformation(); |
2343 return header; | 2327 return header; |
2344 } | 2328 } |
2345 | 2329 |
2346 | 2330 |
2347 void HGraphBuilder::VisitBlock(Block* stmt) { | 2331 void HGraphBuilder::VisitBlock(Block* stmt) { |
(...skipping 103 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2451 current_block()->FinishExit(new HReturn(result)); | 2435 current_block()->FinishExit(new HReturn(result)); |
2452 set_current_block(NULL); | 2436 set_current_block(NULL); |
2453 } else { | 2437 } else { |
2454 // Return from an inlined function, visit the subexpression in the | 2438 // Return from an inlined function, visit the subexpression in the |
2455 // expression context of the call. | 2439 // expression context of the call. |
2456 if (context->IsTest()) { | 2440 if (context->IsTest()) { |
2457 TestContext* test = TestContext::cast(context); | 2441 TestContext* test = TestContext::cast(context); |
2458 VisitForControl(stmt->expression(), | 2442 VisitForControl(stmt->expression(), |
2459 test->if_true(), | 2443 test->if_true(), |
2460 test->if_false()); | 2444 test->if_false()); |
| 2445 } else if (context->IsEffect()) { |
| 2446 VISIT_FOR_EFFECT(stmt->expression()); |
| 2447 current_block()->Goto(function_return(), false); |
2461 } else { | 2448 } else { |
2462 HValue* return_value = NULL; | 2449 ASSERT(context->IsValue()); |
2463 if (context->IsEffect()) { | 2450 VISIT_FOR_VALUE(stmt->expression()); |
2464 VISIT_FOR_EFFECT(stmt->expression()); | 2451 HValue* return_value = environment()->Pop(); |
2465 return_value = graph()->GetConstantUndefined(); | 2452 current_block()->AddLeaveInlined(return_value, function_return()); |
2466 } else { | |
2467 ASSERT(context->IsValue()); | |
2468 VISIT_FOR_VALUE(stmt->expression()); | |
2469 return_value = environment()->Pop(); | |
2470 } | |
2471 current_block()->AddLeaveInlined(return_value, | |
2472 function_return()); | |
2473 set_current_block(NULL); | |
2474 } | 2453 } |
| 2454 set_current_block(NULL); |
2475 } | 2455 } |
2476 } | 2456 } |
2477 | 2457 |
2478 | 2458 |
2479 void HGraphBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) { | 2459 void HGraphBuilder::VisitWithEnterStatement(WithEnterStatement* stmt) { |
2480 BAILOUT("WithEnterStatement"); | 2460 BAILOUT("WithEnterStatement"); |
2481 } | 2461 } |
2482 | 2462 |
2483 | 2463 |
2484 void HGraphBuilder::VisitWithExitStatement(WithExitStatement* stmt) { | 2464 void HGraphBuilder::VisitWithExitStatement(WithExitStatement* stmt) { |
(...skipping 557 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3042 } | 3022 } |
3043 ast_context()->ReturnValue(Pop()); | 3023 ast_context()->ReturnValue(Pop()); |
3044 } | 3024 } |
3045 | 3025 |
3046 | 3026 |
3047 void HGraphBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) { | 3027 void HGraphBuilder::VisitCatchExtensionObject(CatchExtensionObject* expr) { |
3048 BAILOUT("CatchExtensionObject"); | 3028 BAILOUT("CatchExtensionObject"); |
3049 } | 3029 } |
3050 | 3030 |
3051 | 3031 |
3052 HBasicBlock* HGraphBuilder::BuildTypeSwitch(HValue* receiver, | |
3053 ZoneMapList* maps, | |
3054 ZoneList<HSubgraph*>* body_graphs, | |
3055 HSubgraph* default_graph, | |
3056 int join_id) { | |
3057 ASSERT(maps->length() == body_graphs->length()); | |
3058 HBasicBlock* join_block = graph()->CreateBasicBlock(); | |
3059 AddInstruction(new HCheckNonSmi(receiver)); | |
3060 | |
3061 for (int i = 0; i < maps->length(); ++i) { | |
3062 // Build the branches, connect all the target subgraphs to the join | |
3063 // block. Use the default as a target of the last branch. | |
3064 HSubgraph* if_true = body_graphs->at(i); | |
3065 HSubgraph* if_false = (i == maps->length() - 1) | |
3066 ? default_graph | |
3067 : CreateBranchSubgraph(environment()); | |
3068 HCompareMap* compare = | |
3069 new HCompareMap(receiver, | |
3070 maps->at(i), | |
3071 if_true->entry_block(), | |
3072 if_false->entry_block()); | |
3073 current_block()->Finish(compare); | |
3074 | |
3075 if (if_true->exit_block() != NULL) { | |
3076 // In an effect context the value of the type switch is not needed. | |
3077 // There is no need to merge it at the join block only to discard it. | |
3078 if (ast_context()->IsEffect()) { | |
3079 if_true->exit_block()->last_environment()->Drop(1); | |
3080 } | |
3081 if_true->exit_block()->Goto(join_block); | |
3082 } | |
3083 | |
3084 set_current_block(if_false->exit_block()); | |
3085 } | |
3086 | |
3087 // Connect the default if necessary. | |
3088 if (current_block() != NULL) { | |
3089 if (ast_context()->IsEffect()) { | |
3090 environment()->Drop(1); | |
3091 } | |
3092 current_block()->Goto(join_block); | |
3093 } | |
3094 | |
3095 if (join_block->predecessors()->is_empty()) return NULL; | |
3096 join_block->SetJoinId(join_id); | |
3097 return join_block; | |
3098 } | |
3099 | |
3100 | |
3101 // Sets the lookup result and returns true if the store can be inlined. | 3032 // Sets the lookup result and returns true if the store can be inlined. |
3102 static bool ComputeStoredField(Handle<Map> type, | 3033 static bool ComputeStoredField(Handle<Map> type, |
3103 Handle<String> name, | 3034 Handle<String> name, |
3104 LookupResult* lookup) { | 3035 LookupResult* lookup) { |
3105 type->LookupInDescriptors(NULL, *name, lookup); | 3036 type->LookupInDescriptors(NULL, *name, lookup); |
3106 if (!lookup->IsPropertyOrTransition()) return false; | 3037 if (!lookup->IsPropertyOrTransition()) return false; |
3107 if (lookup->type() == FIELD) return true; | 3038 if (lookup->type() == FIELD) return true; |
3108 return (lookup->type() == MAP_TRANSITION) && | 3039 return (lookup->type() == MAP_TRANSITION) && |
3109 (type->unused_property_fields() > 0); | 3040 (type->unused_property_fields() > 0); |
3110 } | 3041 } |
(...skipping 767 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3878 Handle<JSObject>(JSObject::cast(receiver_map->prototype())), | 3809 Handle<JSObject>(JSObject::cast(receiver_map->prototype())), |
3879 expr->holder())); | 3810 expr->holder())); |
3880 } | 3811 } |
3881 } | 3812 } |
3882 | 3813 |
3883 | 3814 |
3884 void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, | 3815 void HGraphBuilder::HandlePolymorphicCallNamed(Call* expr, |
3885 HValue* receiver, | 3816 HValue* receiver, |
3886 ZoneMapList* types, | 3817 ZoneMapList* types, |
3887 Handle<String> name) { | 3818 Handle<String> name) { |
3888 int argument_count = expr->arguments()->length() + 1; // Plus receiver. | |
3889 int number_of_types = Min(types->length(), kMaxCallPolymorphism); | |
3890 ZoneMapList maps(number_of_types); | |
3891 ZoneList<HSubgraph*> subgraphs(number_of_types); | |
3892 bool needs_generic = (types->length() > kMaxCallPolymorphism); | |
3893 | |
3894 // Build subgraphs for each of the specific maps. | |
3895 // | |
3896 // TODO(ager): We should recognize when the prototype chains for different | 3819 // TODO(ager): We should recognize when the prototype chains for different |
3897 // maps are identical. In that case we can avoid repeatedly generating the | 3820 // maps are identical. In that case we can avoid repeatedly generating the |
3898 // same prototype map checks. | 3821 // same prototype map checks. |
3899 for (int i = 0; i < number_of_types; ++i) { | 3822 int argument_count = expr->arguments()->length() + 1; // Includes receiver. |
| 3823 int count = 0; |
| 3824 HBasicBlock* join = NULL; |
| 3825 for (int i = 0; i < types->length() && count < kMaxCallPolymorphism; ++i) { |
3900 Handle<Map> map = types->at(i); | 3826 Handle<Map> map = types->at(i); |
3901 if (expr->ComputeTarget(map, name)) { | 3827 if (expr->ComputeTarget(map, name)) { |
3902 HSubgraph* subgraph = CreateBranchSubgraph(environment()); | 3828 if (count == 0) { |
3903 SubgraphScope scope(this, subgraph); | 3829 AddInstruction(new HCheckNonSmi(receiver)); // Only needed once. |
| 3830 join = graph()->CreateBasicBlock(); |
| 3831 } |
| 3832 ++count; |
| 3833 HBasicBlock* if_true = graph()->CreateBasicBlock(); |
| 3834 HBasicBlock* if_false = graph()->CreateBasicBlock(); |
| 3835 HCompareMap* compare = new HCompareMap(receiver, map, if_true, if_false); |
| 3836 current_block()->Finish(compare); |
| 3837 |
| 3838 set_current_block(if_true); |
3904 AddCheckConstantFunction(expr, receiver, map, false); | 3839 AddCheckConstantFunction(expr, receiver, map, false); |
3905 if (FLAG_trace_inlining && FLAG_polymorphic_inlining) { | 3840 if (FLAG_trace_inlining && FLAG_polymorphic_inlining) { |
3906 PrintF("Trying to inline the polymorphic call to %s\n", | 3841 PrintF("Trying to inline the polymorphic call to %s\n", |
3907 *name->ToCString()); | 3842 *name->ToCString()); |
3908 } | 3843 } |
3909 if (!FLAG_polymorphic_inlining || !TryInline(expr)) { | 3844 if (!FLAG_polymorphic_inlining || !TryInline(expr)) { |
3910 // Check for bailout, as trying to inline might fail due to bailout | 3845 // Check for bailout, as trying to inline might fail due to bailout |
3911 // during hydrogen processing. | 3846 // during hydrogen processing. |
3912 CHECK_BAILOUT; | 3847 CHECK_BAILOUT; |
3913 HCallConstantFunction* call = | 3848 HCallConstantFunction* call = |
3914 new HCallConstantFunction(expr->target(), argument_count); | 3849 new HCallConstantFunction(expr->target(), argument_count); |
3915 call->set_position(expr->position()); | 3850 call->set_position(expr->position()); |
3916 PreProcessCall(call); | 3851 PreProcessCall(call); |
3917 PushAndAdd(call); | 3852 AddInstruction(call); |
| 3853 if (!ast_context()->IsEffect()) Push(call); |
3918 } | 3854 } |
3919 maps.Add(map); | 3855 |
3920 subgraphs.Add(subgraph); | 3856 if (current_block() != NULL) current_block()->Goto(join); |
3921 } else { | 3857 set_current_block(if_false); |
3922 needs_generic = true; | |
3923 } | 3858 } |
3924 } | 3859 } |
3925 | 3860 |
3926 // If we couldn't compute the target for any of the maps just perform an | 3861 // Finish up. Unconditionally deoptimize if we've handled all the maps we |
3927 // IC call. | 3862 // know about and do not want to handle ones we've never seen. Otherwise |
3928 if (maps.length() == 0) { | 3863 // use a generic IC. |
| 3864 if (count == types->length() && FLAG_deoptimize_uncommon_cases) { |
| 3865 current_block()->FinishExit(new HDeoptimize); |
| 3866 } else { |
3929 HContext* context = new HContext; | 3867 HContext* context = new HContext; |
3930 AddInstruction(context); | 3868 AddInstruction(context); |
3931 HCallNamed* call = new HCallNamed(context, name, argument_count); | 3869 HCallNamed* call = new HCallNamed(context, name, argument_count); |
3932 call->set_position(expr->position()); | 3870 call->set_position(expr->position()); |
3933 PreProcessCall(call); | 3871 PreProcessCall(call); |
3934 ast_context()->ReturnInstruction(call, expr->id()); | 3872 |
3935 } else { | 3873 if (join != NULL) { |
3936 // Build subgraph for generic call through IC. | 3874 AddInstruction(call); |
3937 HSubgraph* default_graph = CreateBranchSubgraph(environment()); | 3875 if (!ast_context()->IsEffect()) Push(call); |
3938 { SubgraphScope scope(this, default_graph); | 3876 current_block()->Goto(join); |
3939 if (!needs_generic && FLAG_deoptimize_uncommon_cases) { | 3877 } else { |
3940 default_graph->exit_block()->FinishExit(new HDeoptimize()); | 3878 ast_context()->ReturnInstruction(call, expr->id()); |
3941 default_graph->set_exit_block(NULL); | 3879 return; |
3942 } else { | |
3943 HContext* context = new HContext; | |
3944 AddInstruction(context); | |
3945 HCallNamed* call = new HCallNamed(context, name, argument_count); | |
3946 call->set_position(expr->position()); | |
3947 PreProcessCall(call); | |
3948 PushAndAdd(call); | |
3949 } | |
3950 } | 3880 } |
| 3881 } |
3951 | 3882 |
3952 HBasicBlock* new_exit_block = | 3883 // We assume that control flow is always live after an expression. So |
3953 BuildTypeSwitch(receiver, &maps, &subgraphs, default_graph, expr->id()); | 3884 // even without predecessors to the join block, we set it as the exit |
3954 set_current_block(new_exit_block); | 3885 // block and continue by adding instructions there. |
3955 // In an effect context, we did not materialized the value in the | 3886 ASSERT(join != NULL); |
3956 // predecessor environments so there's no need to handle it here. | 3887 set_current_block(join); |
3957 if (new_exit_block != NULL && !ast_context()->IsEffect()) { | 3888 if (join->HasPredecessor()) { |
3958 ast_context()->ReturnValue(Pop()); | 3889 join->SetJoinId(expr->id()); |
3959 } | 3890 if (!ast_context()->IsEffect()) ast_context()->ReturnValue(Pop()); |
3960 } | 3891 } |
3961 } | 3892 } |
3962 | 3893 |
3963 | 3894 |
3964 void HGraphBuilder::TraceInline(Handle<JSFunction> target, const char* reason) { | 3895 void HGraphBuilder::TraceInline(Handle<JSFunction> target, const char* reason) { |
3965 if (FLAG_trace_inlining) { | 3896 if (FLAG_trace_inlining) { |
3966 SmartPointer<char> callee = target->shared()->DebugName()->ToCString(); | 3897 SmartPointer<char> callee = target->shared()->DebugName()->ToCString(); |
3967 SmartPointer<char> caller = | 3898 SmartPointer<char> caller = |
3968 info()->function()->debug_name()->ToCString(); | 3899 info()->function()->debug_name()->ToCString(); |
3969 if (reason == NULL) { | 3900 if (reason == NULL) { |
(...skipping 126 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4096 | 4027 |
4097 // ---------------------------------------------------------------- | 4028 // ---------------------------------------------------------------- |
4098 // Save the pending call context and type feedback oracle. Set up new ones | 4029 // Save the pending call context and type feedback oracle. Set up new ones |
4099 // for the inlined function. | 4030 // for the inlined function. |
4100 ASSERT(target_shared->has_deoptimization_support()); | 4031 ASSERT(target_shared->has_deoptimization_support()); |
4101 TypeFeedbackOracle target_oracle( | 4032 TypeFeedbackOracle target_oracle( |
4102 Handle<Code>(target_shared->code()), | 4033 Handle<Code>(target_shared->code()), |
4103 Handle<Context>(target->context()->global_context())); | 4034 Handle<Context>(target->context()->global_context())); |
4104 FunctionState target_state(this, &target_info, &target_oracle); | 4035 FunctionState target_state(this, &target_info, &target_oracle); |
4105 | 4036 |
4106 HSubgraph* body = CreateInlinedSubgraph(env, target, function); | 4037 HConstant* undefined = graph()->GetConstantUndefined(); |
4107 body->exit_block()->AddInstruction(new HEnterInlined(target, function)); | 4038 HEnvironment* inner_env = |
4108 AddToSubgraph(body, function->body()); | 4039 environment()->CopyForInlining(target, function, true, undefined); |
| 4040 HBasicBlock* body_entry = CreateBasicBlock(inner_env); |
| 4041 current_block()->Goto(body_entry); |
| 4042 |
| 4043 body_entry->SetJoinId(expr->ReturnId()); |
| 4044 set_current_block(body_entry); |
| 4045 AddInstruction(new HEnterInlined(target, function)); |
| 4046 VisitStatements(function->body()); |
4109 if (HasStackOverflow()) { | 4047 if (HasStackOverflow()) { |
4110 // Bail out if the inline function did, as we cannot residualize a call | 4048 // Bail out if the inline function did, as we cannot residualize a call |
4111 // instead. | 4049 // instead. |
4112 TraceInline(target, "inline graph construction failed"); | 4050 TraceInline(target, "inline graph construction failed"); |
4113 return false; | 4051 return false; |
4114 } | 4052 } |
4115 | 4053 |
4116 // Update inlined nodes count. | 4054 // Update inlined nodes count. |
4117 inlined_count_ += nodes_added; | 4055 inlined_count_ += nodes_added; |
4118 | 4056 |
4119 TraceInline(target, NULL); | 4057 TraceInline(target, NULL); |
4120 | 4058 |
4121 if (body->exit_block() != NULL) { | 4059 if (current_block() != NULL) { |
4122 // Add a return of undefined if control can fall off the body. In a | 4060 // Add a return of undefined if control can fall off the body. In a |
4123 // test context, undefined is false. | 4061 // test context, undefined is false. |
4124 HValue* return_value = graph()->GetConstantUndefined(); | |
4125 if (inlined_test_context() == NULL) { | 4062 if (inlined_test_context() == NULL) { |
4126 ASSERT(function_return() != NULL); | 4063 ASSERT(function_return() != NULL); |
4127 body->exit_block()->AddLeaveInlined(return_value, function_return()); | 4064 ASSERT(call_context()->IsEffect() || call_context()->IsValue()); |
| 4065 if (call_context()->IsEffect()) { |
| 4066 current_block()->Goto(function_return(), false); |
| 4067 } else { |
| 4068 current_block()->AddLeaveInlined(undefined, function_return()); |
| 4069 } |
4128 } else { | 4070 } else { |
4129 // The graph builder assumes control can reach both branches of a | 4071 // The graph builder assumes control can reach both branches of a |
4130 // test, so we materialize the undefined value and test it rather than | 4072 // test, so we materialize the undefined value and test it rather than |
4131 // simply jumping to the false target. | 4073 // simply jumping to the false target. |
4132 // | 4074 // |
4133 // TODO(3168478): refactor to avoid this. | 4075 // TODO(3168478): refactor to avoid this. |
4134 HBasicBlock* empty_true = graph()->CreateBasicBlock(); | 4076 HBasicBlock* empty_true = graph()->CreateBasicBlock(); |
4135 HBasicBlock* empty_false = graph()->CreateBasicBlock(); | 4077 HBasicBlock* empty_false = graph()->CreateBasicBlock(); |
4136 HTest* test = new HTest(return_value, empty_true, empty_false); | 4078 HTest* test = new HTest(undefined, empty_true, empty_false); |
4137 body->exit_block()->Finish(test); | 4079 current_block()->Finish(test); |
4138 | 4080 |
4139 HValue* const no_return_value = NULL; | 4081 empty_true->Goto(inlined_test_context()->if_true(), false); |
4140 empty_true->AddLeaveInlined(no_return_value, | 4082 empty_false->Goto(inlined_test_context()->if_false(), false); |
4141 inlined_test_context()->if_true()); | |
4142 empty_false->AddLeaveInlined(no_return_value, | |
4143 inlined_test_context()->if_false()); | |
4144 } | 4083 } |
4145 body->set_exit_block(NULL); | |
4146 } | 4084 } |
4147 | 4085 |
4148 // Record the environment at the inlined function call. | |
4149 AddSimulate(expr->ReturnId()); | |
4150 | |
4151 // Jump to the function entry (without re-recording the environment). | |
4152 current_block()->Finish(new HGoto(body->entry_block())); | |
4153 | |
4154 // Fix up the function exits. | 4086 // Fix up the function exits. |
4155 if (inlined_test_context() != NULL) { | 4087 if (inlined_test_context() != NULL) { |
4156 HBasicBlock* if_true = inlined_test_context()->if_true(); | 4088 HBasicBlock* if_true = inlined_test_context()->if_true(); |
4157 HBasicBlock* if_false = inlined_test_context()->if_false(); | 4089 HBasicBlock* if_false = inlined_test_context()->if_false(); |
4158 if_true->SetJoinId(expr->id()); | 4090 if_true->SetJoinId(expr->id()); |
4159 if_false->SetJoinId(expr->id()); | 4091 if_false->SetJoinId(expr->id()); |
4160 ASSERT(ast_context() == inlined_test_context()); | 4092 ASSERT(ast_context() == inlined_test_context()); |
4161 // Pop the return test context from the expression context stack. | 4093 // Pop the return test context from the expression context stack. |
4162 ClearInlinedTestContext(); | 4094 ClearInlinedTestContext(); |
4163 | 4095 |
4164 // Forward to the real test context. | 4096 // Forward to the real test context. |
4165 HValue* const no_return_value = NULL; | |
4166 HBasicBlock* true_target = TestContext::cast(ast_context())->if_true(); | 4097 HBasicBlock* true_target = TestContext::cast(ast_context())->if_true(); |
4167 if (true_target->IsInlineReturnTarget()) { | |
4168 if_true->AddLeaveInlined(no_return_value, true_target); | |
4169 } else { | |
4170 if_true->Goto(true_target); | |
4171 } | |
4172 | |
4173 HBasicBlock* false_target = TestContext::cast(ast_context())->if_false(); | 4098 HBasicBlock* false_target = TestContext::cast(ast_context())->if_false(); |
4174 if (false_target->IsInlineReturnTarget()) { | 4099 if_true->Goto(true_target, false); |
4175 if_false->AddLeaveInlined(no_return_value, false_target); | 4100 if_false->Goto(false_target, false); |
4176 } else { | |
4177 if_false->Goto(false_target); | |
4178 } | |
4179 | 4101 |
4180 // TODO(kmillikin): Come up with a better way to handle this. It is too | 4102 // TODO(kmillikin): Come up with a better way to handle this. It is too |
4181 // subtle. NULL here indicates that the enclosing context has no control | 4103 // subtle. NULL here indicates that the enclosing context has no control |
4182 // flow to handle. | 4104 // flow to handle. |
4183 set_current_block(NULL); | 4105 set_current_block(NULL); |
4184 | 4106 |
4185 } else { | 4107 } else { |
4186 function_return()->SetJoinId(expr->id()); | 4108 function_return()->SetJoinId(expr->id()); |
4187 set_current_block(function_return()); | 4109 set_current_block(function_return()); |
4188 } | 4110 } |
4189 | 4111 |
4190 return true; | 4112 return true; |
4191 } | 4113 } |
4192 | 4114 |
4193 | 4115 |
4194 void HBasicBlock::AddLeaveInlined(HValue* return_value, HBasicBlock* target) { | |
4195 ASSERT(target->IsInlineReturnTarget()); | |
4196 AddInstruction(new HLeaveInlined); | |
4197 HEnvironment* outer = last_environment()->outer(); | |
4198 if (return_value != NULL) outer->Push(return_value); | |
4199 UpdateEnvironment(outer); | |
4200 Goto(target); | |
4201 } | |
4202 | |
4203 | |
4204 bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr, | 4116 bool HGraphBuilder::TryInlineBuiltinFunction(Call* expr, |
4205 HValue* receiver, | 4117 HValue* receiver, |
4206 Handle<Map> receiver_map, | 4118 Handle<Map> receiver_map, |
4207 CheckType check_type) { | 4119 CheckType check_type) { |
4208 ASSERT(check_type != RECEIVER_MAP_CHECK || !receiver_map.is_null()); | 4120 ASSERT(check_type != RECEIVER_MAP_CHECK || !receiver_map.is_null()); |
4209 // Try to inline calls like Math.* as operations in the calling function. | 4121 // Try to inline calls like Math.* as operations in the calling function. |
4210 if (!expr->target()->shared()->HasBuiltinFunctionId()) return false; | 4122 if (!expr->target()->shared()->HasBuiltinFunctionId()) return false; |
4211 BuiltinFunctionId id = expr->target()->shared()->builtin_function_id(); | 4123 BuiltinFunctionId id = expr->target()->shared()->builtin_function_id(); |
4212 int argument_count = expr->arguments()->length() + 1; // Plus receiver. | 4124 int argument_count = expr->arguments()->length() + 1; // Plus receiver. |
4213 switch (id) { | 4125 switch (id) { |
(...skipping 186 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4400 // When the target has a custom call IC generator, use the IC, | 4312 // When the target has a custom call IC generator, use the IC, |
4401 // because it is likely to generate better code. Also use the | 4313 // because it is likely to generate better code. Also use the |
4402 // IC when a primitive receiver check is required. | 4314 // IC when a primitive receiver check is required. |
4403 HContext* context = new HContext; | 4315 HContext* context = new HContext; |
4404 AddInstruction(context); | 4316 AddInstruction(context); |
4405 call = PreProcessCall(new HCallNamed(context, name, argument_count)); | 4317 call = PreProcessCall(new HCallNamed(context, name, argument_count)); |
4406 } else { | 4318 } else { |
4407 AddCheckConstantFunction(expr, receiver, receiver_map, true); | 4319 AddCheckConstantFunction(expr, receiver, receiver_map, true); |
4408 | 4320 |
4409 if (TryInline(expr)) { | 4321 if (TryInline(expr)) { |
4410 if (current_block() != NULL) { | |
4411 HValue* return_value = Pop(); | |
4412 // If we inlined a function in a test context then we need to emit | |
4413 // a simulate here to shadow the ones at the end of the | |
4414 // predecessor blocks. Those environments contain the return | |
4415 // value on top and do not correspond to any actual state of the | |
4416 // unoptimized code. | |
4417 if (ast_context()->IsEffect()) AddSimulate(expr->id()); | |
4418 ast_context()->ReturnValue(return_value); | |
4419 } | |
4420 return; | 4322 return; |
4421 } else { | 4323 } else { |
4422 // Check for bailout, as the TryInline call in the if condition above | 4324 // Check for bailout, as the TryInline call in the if condition above |
4423 // might return false due to bailout during hydrogen processing. | 4325 // might return false due to bailout during hydrogen processing. |
4424 CHECK_BAILOUT; | 4326 CHECK_BAILOUT; |
4425 call = PreProcessCall(new HCallConstantFunction(expr->target(), | 4327 call = PreProcessCall(new HCallConstantFunction(expr->target(), |
4426 argument_count)); | 4328 argument_count)); |
4427 } | 4329 } |
4428 } | 4330 } |
4429 } else if (types != NULL && types->length() > 1) { | 4331 } else if (types != NULL && types->length() > 1) { |
(...skipping 43 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
4473 // Replace the global object with the global receiver. | 4375 // Replace the global object with the global receiver. |
4474 HGlobalReceiver* global_receiver = new HGlobalReceiver(global_object); | 4376 HGlobalReceiver* global_receiver = new HGlobalReceiver(global_object); |
4475 // Index of the receiver from the top of the expression stack. | 4377 // Index of the receiver from the top of the expression stack. |
4476 const int receiver_index = argument_count - 1; | 4378 const int receiver_index = argument_count - 1; |
4477 AddInstruction(global_receiver); | 4379 AddInstruction(global_receiver); |
4478 ASSERT(environment()->ExpressionStackAt(receiver_index)-> | 4380 ASSERT(environment()->ExpressionStackAt(receiver_index)-> |
4479 IsGlobalObject()); | 4381 IsGlobalObject()); |
4480 environment()->SetExpressionStackAt(receiver_index, global_receiver); | 4382 environment()->SetExpressionStackAt(receiver_index, global_receiver); |
4481 | 4383 |
4482 if (TryInline(expr)) { | 4384 if (TryInline(expr)) { |
4483 if (current_block() != NULL) { | |
4484 HValue* return_value = Pop(); | |
4485 // If we inlined a function in a test context then we need to | |
4486 // emit a simulate here to shadow the ones at the end of the | |
4487 // predecessor blocks. Those environments contain the return | |
4488 // value on top and do not correspond to any actual state of the | |
4489 // unoptimized code. | |
4490 if (ast_context()->IsEffect()) AddSimulate(expr->id()); | |
4491 ast_context()->ReturnValue(return_value); | |
4492 } | |
4493 return; | 4385 return; |
4494 } | 4386 } |
4495 // Check for bailout, as trying to inline might fail due to bailout | 4387 // Check for bailout, as trying to inline might fail due to bailout |
4496 // during hydrogen processing. | 4388 // during hydrogen processing. |
4497 CHECK_BAILOUT; | 4389 CHECK_BAILOUT; |
4498 | 4390 |
4499 call = PreProcessCall(new HCallKnownGlobal(expr->target(), | 4391 call = PreProcessCall(new HCallKnownGlobal(expr->target(), |
4500 argument_count)); | 4392 argument_count)); |
4501 } else { | 4393 } else { |
4502 HContext* context = new HContext; | 4394 HContext* context = new HContext; |
(...skipping 1544 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
6047 } | 5939 } |
6048 } | 5940 } |
6049 | 5941 |
6050 #ifdef DEBUG | 5942 #ifdef DEBUG |
6051 if (graph_ != NULL) graph_->Verify(); | 5943 if (graph_ != NULL) graph_->Verify(); |
6052 if (allocator_ != NULL) allocator_->Verify(); | 5944 if (allocator_ != NULL) allocator_->Verify(); |
6053 #endif | 5945 #endif |
6054 } | 5946 } |
6055 | 5947 |
6056 } } // namespace v8::internal | 5948 } } // namespace v8::internal |
OLD | NEW |