OLD | NEW |
---|---|
1 // Copyright (c) 2012, the Dart project authors. Please see the AUTHORS file | 1 // Copyright (c) 2012, 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/flow_graph_builder.h" | 5 #include "vm/flow_graph_builder.h" |
6 | 6 |
7 #include "lib/invocation_mirror.h" | 7 #include "lib/invocation_mirror.h" |
8 #include "vm/ast_printer.h" | 8 #include "vm/ast_printer.h" |
9 #include "vm/bit_vector.h" | 9 #include "vm/bit_vector.h" |
10 #include "vm/class_finalizer.h" | 10 #include "vm/class_finalizer.h" |
(...skipping 242 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
253 guarded_fields_(new(I) ZoneGrowableArray<const Field*>()), | 253 guarded_fields_(new(I) ZoneGrowableArray<const Field*>()), |
254 last_used_block_id_(0), // 0 is used for the graph entry. | 254 last_used_block_id_(0), // 0 is used for the graph entry. |
255 try_index_(CatchClauseNode::kInvalidTryIndex), | 255 try_index_(CatchClauseNode::kInvalidTryIndex), |
256 catch_try_index_(CatchClauseNode::kInvalidTryIndex), | 256 catch_try_index_(CatchClauseNode::kInvalidTryIndex), |
257 loop_depth_(0), | 257 loop_depth_(0), |
258 graph_entry_(NULL), | 258 graph_entry_(NULL), |
259 temp_count_(0), | 259 temp_count_(0), |
260 args_pushed_(0), | 260 args_pushed_(0), |
261 nesting_stack_(NULL), | 261 nesting_stack_(NULL), |
262 osr_id_(osr_id), | 262 osr_id_(osr_id), |
263 is_optimizing_(is_optimizing) { } | 263 is_optimizing_(is_optimizing), |
264 jump_cnt_(0), | |
265 await_joins_(new(I) ZoneGrowableArray<JoinEntryInstr*>()), | |
266 await_levels_(new(I) ZoneGrowableArray<intptr_t>()) { } | |
264 | 267 |
265 | 268 |
266 void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) { | 269 void FlowGraphBuilder::AddCatchEntry(CatchBlockEntryInstr* entry) { |
267 graph_entry_->AddCatchEntry(entry); | 270 graph_entry_->AddCatchEntry(entry); |
268 } | 271 } |
269 | 272 |
270 | 273 |
271 void InlineExitCollector::PrepareGraphs(FlowGraph* callee_graph) { | 274 void InlineExitCollector::PrepareGraphs(FlowGraph* callee_graph) { |
272 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); | 275 ASSERT(callee_graph->graph_entry()->SuccessorCount() == 1); |
273 ASSERT(callee_graph->max_block_id() > caller_graph_->max_block_id()); | 276 ASSERT(callee_graph->max_block_id() > caller_graph_->max_block_id()); |
(...skipping 757 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1031 if (!is_implicit_dynamic_getter && !function.IsConstructor()) { | 1034 if (!is_implicit_dynamic_getter && !function.IsConstructor()) { |
1032 const AbstractType& dst_type = | 1035 const AbstractType& dst_type = |
1033 AbstractType::ZoneHandle(I, function.result_type()); | 1036 AbstractType::ZoneHandle(I, function.result_type()); |
1034 return_value = BuildAssignableValue(node->value()->token_pos(), | 1037 return_value = BuildAssignableValue(node->value()->token_pos(), |
1035 return_value, | 1038 return_value, |
1036 dst_type, | 1039 dst_type, |
1037 Symbols::FunctionResult()); | 1040 Symbols::FunctionResult()); |
1038 } | 1041 } |
1039 } | 1042 } |
1040 | 1043 |
1041 intptr_t current_context_level = owner()->context_level(); | |
1042 ASSERT(current_context_level >= 0); | |
1043 if (owner()->parsed_function()->saved_entry_context_var() != NULL) { | |
1044 // CTX on entry was saved, but not linked as context parent. | |
1045 BuildRestoreContext(*owner()->parsed_function()->saved_entry_context_var()); | |
1046 } else { | |
1047 UnchainContexts(current_context_level); | |
1048 } | |
1049 | |
1050 // Async functions contain two types of return statements: | 1044 // Async functions contain two types of return statements: |
1051 // 1) Returns that should complete the completer once all finally blocks have | 1045 // 1) Returns that should complete the completer once all finally blocks have |
1052 // been inlined (call: :async_completer.complete(return_value)). These | 1046 // been inlined (call: :async_completer.complete(return_value)). These |
1053 // returns end up returning null in the end. | 1047 // returns end up returning null in the end. |
1054 // 2) "Continuation" returns that should not complete the completer but return | 1048 // 2) "Continuation" returns that should not complete the completer but return |
1055 // the value. | 1049 // the value. |
1056 // | 1050 // |
1057 // We distinguish those kinds of nodes via is_regular_return(). | 1051 // We distinguish those kinds of nodes via is_regular_return(). |
1058 // | 1052 // |
1059 if (function.is_async_closure() && node->is_regular_return()) { | 1053 if (function.is_async_closure() && |
1054 (node->return_type() == ReturnNode::kRegular)) { | |
1060 // Temporary store the computed return value. | 1055 // Temporary store the computed return value. |
1061 Do(BuildStoreExprTemp(return_value)); | 1056 Do(BuildStoreExprTemp(return_value)); |
1062 | 1057 |
1063 LocalVariable* rcv_var = node->scope()->LookupVariable( | 1058 LocalVariable* rcv_var = node->scope()->LookupVariable( |
1064 Symbols::AsyncCompleter(), false); | 1059 Symbols::AsyncCompleter(), false); |
1065 ASSERT(rcv_var != NULL && rcv_var->is_captured()); | 1060 ASSERT(rcv_var != NULL && rcv_var->is_captured()); |
1066 ZoneGrowableArray<PushArgumentInstr*>* arguments = | 1061 ZoneGrowableArray<PushArgumentInstr*>* arguments = |
1067 new(I) ZoneGrowableArray<PushArgumentInstr*>(2); | 1062 new(I) ZoneGrowableArray<PushArgumentInstr*>(2); |
1068 Value* rcv_value = Bind(BuildLoadLocal(*rcv_var)); | 1063 Value* rcv_value = Bind(BuildLoadLocal(*rcv_var)); |
1069 arguments->Add(PushArgument(rcv_value)); | 1064 arguments->Add(PushArgument(rcv_value)); |
1070 Value* returned_value = Bind(BuildLoadExprTemp()); | 1065 Value* returned_value = Bind(BuildLoadExprTemp()); |
1071 arguments->Add(PushArgument(returned_value)); | 1066 arguments->Add(PushArgument(returned_value)); |
1072 InstanceCallInstr* call = new(I) InstanceCallInstr( | 1067 InstanceCallInstr* call = new(I) InstanceCallInstr( |
1073 Scanner::kNoSourcePos, | 1068 Scanner::kNoSourcePos, |
1074 Symbols::CompleterComplete(), | 1069 Symbols::CompleterComplete(), |
1075 Token::kILLEGAL, | 1070 Token::kILLEGAL, |
1076 arguments, | 1071 arguments, |
1077 Object::null_array(), | 1072 Object::null_array(), |
1078 1, | 1073 1, |
1079 owner()->ic_data_array()); | 1074 owner()->ic_data_array()); |
1080 Do(call); | 1075 Do(call); |
1081 | 1076 |
1082 // Rebind the return value for the actual return call to be null. | 1077 // Rebind the return value for the actual return call to be null. |
1083 return_value = BuildNullValue(); | 1078 return_value = BuildNullValue(); |
1084 } | 1079 } |
1085 | 1080 |
1081 intptr_t current_context_level = owner()->context_level(); | |
1082 ASSERT(current_context_level >= 0); | |
1083 if (owner()->parsed_function()->saved_entry_context_var() != NULL) { | |
1084 // CTX on entry was saved, but not linked as context parent. | |
1085 BuildRestoreContext(*owner()->parsed_function()->saved_entry_context_var()); | |
1086 } else { | |
1087 UnchainContexts(current_context_level); | |
1088 } | |
1089 | |
1090 | |
1086 AddReturnExit(node->token_pos(), return_value); | 1091 AddReturnExit(node->token_pos(), return_value); |
1087 } | 1092 } |
1088 | 1093 |
1089 | 1094 |
1090 // <Expression> ::= Literal { literal: Instance } | 1095 // <Expression> ::= Literal { literal: Instance } |
1091 void EffectGraphVisitor::VisitLiteralNode(LiteralNode* node) { | 1096 void EffectGraphVisitor::VisitLiteralNode(LiteralNode* node) { |
1092 ReturnDefinition(new(I) ConstantInstr(node->literal())); | 1097 ReturnDefinition(new(I) ConstantInstr(node->literal())); |
1093 } | 1098 } |
1094 | 1099 |
1095 | 1100 |
(...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1437 } | 1442 } |
1438 return new(I) AssertAssignableInstr(token_pos, | 1443 return new(I) AssertAssignableInstr(token_pos, |
1439 value, | 1444 value, |
1440 instantiator, | 1445 instantiator, |
1441 instantiator_type_arguments, | 1446 instantiator_type_arguments, |
1442 dst_type, | 1447 dst_type, |
1443 dst_name); | 1448 dst_name); |
1444 } | 1449 } |
1445 | 1450 |
1446 | 1451 |
1452 void EffectGraphVisitor::BuildAwaitJump( | |
1453 const LocalVariable& continuation_result, | |
1454 const LocalVariable& old_ctx, | |
1455 const intptr_t old_ctx_level, | |
1456 JoinEntryInstr* target) { | |
1457 // Building a jump consists of the following actions: | |
1458 // * Record the current continuation result in a temporary. | |
1459 // * Restore the old context. | |
1460 // * Overwrite the old context's continuation result with the temporary. | |
1461 // * Append a Goto to the target's join. | |
1462 ASSERT(continuation_result.is_captured()); | |
1463 ASSERT(old_ctx.is_captured()); | |
1464 // Before resoring the continuation context we need to temporary save the | |
1465 // current continuation result. | |
1466 Value* continuation_result_value = Bind(BuildLoadLocal(continuation_result)); | |
1467 Do(BuildStoreExprTemp(continuation_result_value)); | |
1468 | |
1469 // Restore the saved continuation context. | |
1470 BuildRestoreContext(old_ctx); | |
1471 | |
1472 // Pass over the continuation result. | |
1473 Value* saved_continuation_result = Bind(BuildLoadExprTemp()); | |
1474 // FlowGraphBuilder is at top context level, but the await target has possibly | |
1475 // been recorded in a nested context (old_ctx_level). We need to unroll | |
1476 // manually here. | |
1477 LocalVariable* tmp_var = EnterTempLocalScope(saved_continuation_result); | |
1478 intptr_t delta = old_ctx_level - continuation_result.owner()->context_level(); | |
1479 ASSERT(delta >= 0); | |
1480 Value* context = Bind(new(I) CurrentContextInstr()); | |
1481 while (delta-- > 0) { | |
1482 context = Bind(new(I) LoadFieldInstr( | |
1483 context, Context::parent_offset(), Type::ZoneHandle(I, Type::null()), | |
1484 Scanner::kNoSourcePos)); | |
1485 } | |
1486 Value* tmp_val = Bind(new(I) LoadLocalInstr(*tmp_var)); | |
1487 StoreInstanceFieldInstr* store = new(I) StoreInstanceFieldInstr( | |
1488 Context::variable_offset(continuation_result.index()), | |
1489 context, | |
1490 tmp_val, | |
1491 kEmitStoreBarrier, | |
1492 Scanner::kNoSourcePos); | |
1493 Do(store); | |
1494 Do(ExitTempLocalScope(tmp_var)); | |
1495 | |
1496 // Goto saved join. | |
1497 Goto(target); | |
1498 } | |
1499 | |
1500 | |
1447 // Used for type casts and to test assignments. | 1501 // Used for type casts and to test assignments. |
1448 Value* EffectGraphVisitor::BuildAssignableValue(intptr_t token_pos, | 1502 Value* EffectGraphVisitor::BuildAssignableValue(intptr_t token_pos, |
1449 Value* value, | 1503 Value* value, |
1450 const AbstractType& dst_type, | 1504 const AbstractType& dst_type, |
1451 const String& dst_name) { | 1505 const String& dst_name) { |
1452 if (CanSkipTypeCheck(token_pos, value, dst_type, dst_name)) { | 1506 if (CanSkipTypeCheck(token_pos, value, dst_type, dst_name)) { |
1453 return value; | 1507 return value; |
1454 } | 1508 } |
1455 return Bind(BuildAssertAssignable(token_pos, value, dst_type, dst_name)); | 1509 return Bind(BuildAssertAssignable(token_pos, value, dst_type, dst_name)); |
1456 } | 1510 } |
(...skipping 656 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2113 UNREACHABLE(); | 2167 UNREACHABLE(); |
2114 } | 2168 } |
2115 | 2169 |
2116 | 2170 |
2117 void EffectGraphVisitor::VisitAwaitNode(AwaitNode* node) { | 2171 void EffectGraphVisitor::VisitAwaitNode(AwaitNode* node) { |
2118 // Await nodes are temporary during parsing. | 2172 // Await nodes are temporary during parsing. |
2119 UNREACHABLE(); | 2173 UNREACHABLE(); |
2120 } | 2174 } |
2121 | 2175 |
2122 | 2176 |
2177 void EffectGraphVisitor::VisitAwaitMarkerNode(AwaitMarkerNode* node) { | |
2178 if (node->type() == AwaitMarkerNode::kNewContinuationState) { | |
2179 // We need to create a new await state which involves: | |
2180 // * Increase the jump counter. Sanity check against the list of targets. | |
2181 // * Save the current context for resuming. | |
2182 ASSERT(node->scope() != NULL); | |
2183 LocalVariable* jump_var = node->scope()->LookupVariable( | |
2184 Symbols::AwaitJumpVar(), false); | |
2185 LocalVariable* ctx_var = node->scope()->LookupVariable( | |
2186 Symbols::AwaitContextVar(), false); | |
2187 ASSERT(jump_var != NULL && jump_var->is_captured()); | |
2188 ASSERT(ctx_var != NULL && ctx_var->is_captured()); | |
2189 const intptr_t jump_cnt = owner()->next_await_counter(); | |
2190 ASSERT(jump_cnt >= 0); | |
2191 // Sanity check that we always add a JoinEntryInstr before adding a new | |
2192 // state. | |
2193 ASSERT(jump_cnt == owner()->await_joins()->length()); | |
2194 // Store the counter in :await_jump_var. | |
2195 Value* jump_val = Bind(new (I) ConstantInstr( | |
2196 Smi::ZoneHandle(I, Smi::New(jump_cnt)))); | |
2197 Do(BuildStoreLocal(*jump_var, jump_val)); | |
2198 // Save the current context for resuming. | |
2199 BuildSaveContext(*ctx_var); | |
2200 owner()->await_levels()->Add(owner()->context_level()); | |
2201 return; | |
2202 } | |
2203 if (node->type() == AwaitMarkerNode::kTargetForContinuation) { | |
2204 // We need to create a new await target which involves: | |
2205 // * Append a join that is also added to the list that will later result in | |
2206 // a preamble. | |
2207 JoinEntryInstr* const join = new(I) JoinEntryInstr( | |
2208 owner()->AllocateBlockId(), owner()->try_index()); | |
2209 owner()->await_joins()->Add(join); | |
2210 Goto(join); | |
2211 exit_ = join; | |
2212 return; | |
2213 } | |
2214 UNREACHABLE(); | |
2215 } | |
2216 | |
2217 | |
2123 intptr_t EffectGraphVisitor::GetCurrentTempLocalIndex() const { | 2218 intptr_t EffectGraphVisitor::GetCurrentTempLocalIndex() const { |
2124 return kFirstLocalSlotFromFp | 2219 return kFirstLocalSlotFromFp |
2125 - owner()->num_stack_locals() | 2220 - owner()->num_stack_locals() |
2126 - owner()->num_copied_params() | 2221 - owner()->num_copied_params() |
2127 - owner()->args_pushed() | 2222 - owner()->args_pushed() |
2128 - owner()->temp_count() + 1; | 2223 - owner()->temp_count() + 1; |
2129 } | 2224 } |
2130 | 2225 |
2131 | 2226 |
2132 LocalVariable* EffectGraphVisitor::EnterTempLocalScope(Value* value) { | 2227 LocalVariable* EffectGraphVisitor::EnterTempLocalScope(Value* value) { |
(...skipping 1486 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3619 parameter_value = Bind(assert_assignable); | 3714 parameter_value = Bind(assert_assignable); |
3620 // Store the type checked argument back to its corresponding local | 3715 // Store the type checked argument back to its corresponding local |
3621 // variable so that ssa renaming detects the dependency and makes use | 3716 // variable so that ssa renaming detects the dependency and makes use |
3622 // of the checked type in type propagation. | 3717 // of the checked type in type propagation. |
3623 Do(BuildStoreLocal(parameter, parameter_value)); | 3718 Do(BuildStoreLocal(parameter, parameter_value)); |
3624 } | 3719 } |
3625 pos++; | 3720 pos++; |
3626 } | 3721 } |
3627 } | 3722 } |
3628 | 3723 |
3724 // Continuation part: | |
3725 // If this node sequence is the body of an async closure leave room for a | |
3726 // preamble. The preamble is generated after visiting the body. | |
3727 GotoInstr* preamble_start = NULL; | |
3728 if ((node == owner()->parsed_function()->node_sequence()) && | |
3729 (owner()->parsed_function()->function().is_async_closure())) { | |
3730 JoinEntryInstr* preamble_end = new(I) JoinEntryInstr( | |
3731 owner()->AllocateBlockId(), owner()->try_index()); | |
3732 ASSERT(exit() != NULL); | |
3733 exit()->Goto(preamble_end); | |
3734 ASSERT(exit()->next()->IsGoto()); | |
3735 preamble_start = exit()->next()->AsGoto(); | |
3736 ASSERT(preamble_start->IsGoto()); | |
3737 exit_ = preamble_end; | |
3738 } | |
3739 | |
3629 intptr_t i = 0; | 3740 intptr_t i = 0; |
3630 while (is_open() && (i < node->length())) { | 3741 while (is_open() && (i < node->length())) { |
3631 EffectGraphVisitor for_effect(owner()); | 3742 EffectGraphVisitor for_effect(owner()); |
3632 node->NodeAt(i++)->Visit(&for_effect); | 3743 node->NodeAt(i++)->Visit(&for_effect); |
3633 Append(for_effect); | 3744 Append(for_effect); |
3634 if (!is_open()) { | 3745 if (!is_open()) { |
3635 // E.g., because of a JumpNode. | 3746 // E.g., because of a JumpNode. |
3636 break; | 3747 break; |
3637 } | 3748 } |
3638 } | 3749 } |
3639 | 3750 |
3751 // Continuation part: | |
3752 // After generating the CFG for the body we can create the preamble because we | |
3753 // know exactly how many continuation states we need. | |
3754 if ((node == owner()->parsed_function()->node_sequence()) && | |
3755 (owner()->parsed_function()->function().is_async_closure())) { | |
3756 ASSERT(preamble_start != NULL); | |
3757 // We are at the top level. Fetch the corresponding scope. | |
3758 LocalScope* top_scope = node->scope(); | |
3759 LocalVariable* jump_var = top_scope->LookupVariable( | |
3760 Symbols::AwaitJumpVar(), false); | |
3761 LocalVariable* ctx_var = top_scope->LookupVariable( | |
3762 Symbols::AwaitContextVar(), false); | |
3763 ASSERT(jump_var != NULL && jump_var->is_captured()); | |
3764 ASSERT(ctx_var != NULL && ctx_var->is_captured()); | |
srdjan
2014/08/26 15:54:33
Add parantheses above
| |
3765 | |
3766 Instruction* saved_entry = entry_; | |
3767 Instruction* saved_exit = exit_; | |
3768 entry_ = NULL; | |
3769 exit_ = NULL; | |
3770 | |
3771 LoadLocalNode* load_jump_cnt = new(I) LoadLocalNode( | |
3772 Scanner::kNoSourcePos, jump_var); | |
3773 ComparisonNode* check_jump_cnt; | |
3774 const intptr_t num_await_states = owner()->await_joins()->length(); | |
3775 for (intptr_t i = 0; i < num_await_states; i++) { | |
3776 check_jump_cnt = new(I) ComparisonNode( | |
3777 Scanner::kNoSourcePos, | |
3778 Token::kEQ, | |
3779 load_jump_cnt, | |
3780 new(I) LiteralNode( | |
3781 Scanner::kNoSourcePos, Smi::ZoneHandle(I, Smi::New(i)))); | |
3782 TestGraphVisitor for_test(owner(), Scanner::kNoSourcePos); | |
3783 check_jump_cnt->Visit(&for_test); | |
3784 EffectGraphVisitor for_true(owner()); | |
3785 EffectGraphVisitor for_false(owner()); | |
3786 | |
3787 LocalVariable* continuation_result_var = | |
3788 top_scope->LookupVariable(Symbols::AsyncOperationParam(), false); | |
3789 ASSERT(continuation_result_var != NULL); | |
3790 for_true.BuildAwaitJump(*continuation_result_var, | |
3791 *ctx_var, | |
3792 (*owner()->await_levels())[i], | |
3793 (*owner()->await_joins())[i]); | |
3794 Join(for_test, for_true, for_false); | |
3795 | |
3796 if (i == 0) { | |
3797 // Manually link up the preamble start. | |
3798 preamble_start->previous()->set_next(for_test.entry()); | |
3799 for_test.entry()->set_previous(preamble_start->previous()); | |
3800 } | |
3801 if (i == (num_await_states - 1)) { | |
3802 // Link up preamble end. | |
3803 if (exit_ == NULL) { | |
3804 exit_ = preamble_start; | |
3805 } else { | |
3806 exit_->LinkTo(preamble_start); | |
3807 } | |
3808 } | |
3809 } | |
3810 entry_ = saved_entry; | |
3811 exit_ = saved_exit; | |
3812 } | |
3813 | |
3640 if (is_open()) { | 3814 if (is_open()) { |
3641 if (MustSaveRestoreContext(node)) { | 3815 if (MustSaveRestoreContext(node)) { |
3642 BuildRestoreContext( | 3816 BuildRestoreContext( |
3643 *owner()->parsed_function()->saved_entry_context_var()); | 3817 *owner()->parsed_function()->saved_entry_context_var()); |
3644 } else if (num_context_variables > 0) { | 3818 } else if (num_context_variables > 0) { |
3645 UnchainContexts(1); | 3819 UnchainContexts(1); |
3646 } | 3820 } |
3647 } | 3821 } |
3648 | 3822 |
3649 // If this node sequence is labeled, a break out of the sequence will have | 3823 // If this node sequence is labeled, a break out of the sequence will have |
(...skipping 355 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
4005 Report::MessageF(Report::kBailout, | 4179 Report::MessageF(Report::kBailout, |
4006 Script::Handle(function.script()), | 4180 Script::Handle(function.script()), |
4007 function.token_pos(), | 4181 function.token_pos(), |
4008 "FlowGraphBuilder Bailout: %s %s", | 4182 "FlowGraphBuilder Bailout: %s %s", |
4009 String::Handle(function.name()).ToCString(), | 4183 String::Handle(function.name()).ToCString(), |
4010 reason); | 4184 reason); |
4011 UNREACHABLE(); | 4185 UNREACHABLE(); |
4012 } | 4186 } |
4013 | 4187 |
4014 } // namespace dart | 4188 } // namespace dart |
OLD | NEW |