| 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 |