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(LocalScope* lookup_scope, |
| 1453 const intptr_t old_ctx_level, |
| 1454 JoinEntryInstr* target) { |
| 1455 // Building a jump consists of the following actions: |
| 1456 // * Record the current continuation result in a temporary. |
| 1457 // * Restore the old context. |
| 1458 // * Overwrite the old context's continuation result with the temporary. |
| 1459 // * Append a Goto to the target's join. |
| 1460 LocalVariable* old_ctx = lookup_scope->LookupVariable( |
| 1461 Symbols::AwaitContextVar(), false); |
| 1462 LocalVariable* continuation_result = lookup_scope->LookupVariable( |
| 1463 Symbols::AsyncOperationParam(), false); |
| 1464 ASSERT((continuation_result != NULL) && continuation_result->is_captured()); |
| 1465 ASSERT((old_ctx != NULL) && old_ctx->is_captured()); |
| 1466 // Before restoring the continuation context we need to temporary save the |
| 1467 // current continuation result. |
| 1468 Value* continuation_result_value = Bind(BuildLoadLocal(*continuation_result)); |
| 1469 Do(BuildStoreExprTemp(continuation_result_value)); |
| 1470 |
| 1471 // Restore the saved continuation context. |
| 1472 BuildRestoreContext(*old_ctx); |
| 1473 |
| 1474 // Pass over the continuation result. |
| 1475 Value* saved_continuation_result = Bind(BuildLoadExprTemp()); |
| 1476 // FlowGraphBuilder is at top context level, but the await target has possibly |
| 1477 // been recorded in a nested context (old_ctx_level). We need to unroll |
| 1478 // manually here. |
| 1479 LocalVariable* tmp_var = EnterTempLocalScope(saved_continuation_result); |
| 1480 intptr_t delta = old_ctx_level - |
| 1481 continuation_result->owner()->context_level(); |
| 1482 ASSERT(delta >= 0); |
| 1483 Value* context = Bind(new(I) CurrentContextInstr()); |
| 1484 while (delta-- > 0) { |
| 1485 context = Bind(new(I) LoadFieldInstr( |
| 1486 context, Context::parent_offset(), Type::ZoneHandle(I, Type::null()), |
| 1487 Scanner::kNoSourcePos)); |
| 1488 } |
| 1489 Value* tmp_val = Bind(new(I) LoadLocalInstr(*tmp_var)); |
| 1490 StoreInstanceFieldInstr* store = new(I) StoreInstanceFieldInstr( |
| 1491 Context::variable_offset(continuation_result->index()), |
| 1492 context, |
| 1493 tmp_val, |
| 1494 kEmitStoreBarrier, |
| 1495 Scanner::kNoSourcePos); |
| 1496 Do(store); |
| 1497 Do(ExitTempLocalScope(tmp_var)); |
| 1498 |
| 1499 // Goto saved join. |
| 1500 Goto(target); |
| 1501 } |
| 1502 |
| 1503 |
1447 // Used for type casts and to test assignments. | 1504 // Used for type casts and to test assignments. |
1448 Value* EffectGraphVisitor::BuildAssignableValue(intptr_t token_pos, | 1505 Value* EffectGraphVisitor::BuildAssignableValue(intptr_t token_pos, |
1449 Value* value, | 1506 Value* value, |
1450 const AbstractType& dst_type, | 1507 const AbstractType& dst_type, |
1451 const String& dst_name) { | 1508 const String& dst_name) { |
1452 if (CanSkipTypeCheck(token_pos, value, dst_type, dst_name)) { | 1509 if (CanSkipTypeCheck(token_pos, value, dst_type, dst_name)) { |
1453 return value; | 1510 return value; |
1454 } | 1511 } |
1455 return Bind(BuildAssertAssignable(token_pos, value, dst_type, dst_name)); | 1512 return Bind(BuildAssertAssignable(token_pos, value, dst_type, dst_name)); |
1456 } | 1513 } |
(...skipping 656 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
2113 UNREACHABLE(); | 2170 UNREACHABLE(); |
2114 } | 2171 } |
2115 | 2172 |
2116 | 2173 |
2117 void EffectGraphVisitor::VisitAwaitNode(AwaitNode* node) { | 2174 void EffectGraphVisitor::VisitAwaitNode(AwaitNode* node) { |
2118 // Await nodes are temporary during parsing. | 2175 // Await nodes are temporary during parsing. |
2119 UNREACHABLE(); | 2176 UNREACHABLE(); |
2120 } | 2177 } |
2121 | 2178 |
2122 | 2179 |
| 2180 void EffectGraphVisitor::VisitAwaitMarkerNode(AwaitMarkerNode* node) { |
| 2181 if (node->marker_type() == AwaitMarkerNode::kNewContinuationState) { |
| 2182 // We need to create a new await state which involves: |
| 2183 // * Increase the jump counter. Sanity check against the list of targets. |
| 2184 // * Save the current context for resuming. |
| 2185 ASSERT(node->scope() != NULL); |
| 2186 LocalVariable* jump_var = node->scope()->LookupVariable( |
| 2187 Symbols::AwaitJumpVar(), false); |
| 2188 LocalVariable* ctx_var = node->scope()->LookupVariable( |
| 2189 Symbols::AwaitContextVar(), false); |
| 2190 ASSERT((jump_var != NULL) && jump_var->is_captured()); |
| 2191 ASSERT((ctx_var != NULL) && ctx_var->is_captured()); |
| 2192 const intptr_t jump_cnt = owner()->next_await_counter(); |
| 2193 ASSERT(jump_cnt >= 0); |
| 2194 // Sanity check that we always add a JoinEntryInstr before adding a new |
| 2195 // state. |
| 2196 ASSERT(jump_cnt == owner()->await_joins()->length()); |
| 2197 // Store the counter in :await_jump_var. |
| 2198 Value* jump_val = Bind(new (I) ConstantInstr( |
| 2199 Smi::ZoneHandle(I, Smi::New(jump_cnt)))); |
| 2200 Do(BuildStoreLocal(*jump_var, jump_val)); |
| 2201 // Save the current context for resuming. |
| 2202 BuildSaveContext(*ctx_var); |
| 2203 owner()->await_levels()->Add(owner()->context_level()); |
| 2204 return; |
| 2205 } |
| 2206 if (node->marker_type() == AwaitMarkerNode::kTargetForContinuation) { |
| 2207 // We need to create a new await target which involves: |
| 2208 // * Append a join that is also added to the list that will later result in |
| 2209 // a preamble. |
| 2210 JoinEntryInstr* const join = new(I) JoinEntryInstr( |
| 2211 owner()->AllocateBlockId(), owner()->try_index()); |
| 2212 owner()->await_joins()->Add(join); |
| 2213 Goto(join); |
| 2214 exit_ = join; |
| 2215 return; |
| 2216 } |
| 2217 UNREACHABLE(); |
| 2218 } |
| 2219 |
| 2220 |
2123 intptr_t EffectGraphVisitor::GetCurrentTempLocalIndex() const { | 2221 intptr_t EffectGraphVisitor::GetCurrentTempLocalIndex() const { |
2124 return kFirstLocalSlotFromFp | 2222 return kFirstLocalSlotFromFp |
2125 - owner()->num_stack_locals() | 2223 - owner()->num_stack_locals() |
2126 - owner()->num_copied_params() | 2224 - owner()->num_copied_params() |
2127 - owner()->args_pushed() | 2225 - owner()->args_pushed() |
2128 - owner()->temp_count() + 1; | 2226 - owner()->temp_count() + 1; |
2129 } | 2227 } |
2130 | 2228 |
2131 | 2229 |
2132 LocalVariable* EffectGraphVisitor::EnterTempLocalScope(Value* value) { | 2230 LocalVariable* EffectGraphVisitor::EnterTempLocalScope(Value* value) { |
(...skipping 1498 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
3631 parameter_value = Bind(assert_assignable); | 3729 parameter_value = Bind(assert_assignable); |
3632 // Store the type checked argument back to its corresponding local | 3730 // Store the type checked argument back to its corresponding local |
3633 // variable so that ssa renaming detects the dependency and makes use | 3731 // variable so that ssa renaming detects the dependency and makes use |
3634 // of the checked type in type propagation. | 3732 // of the checked type in type propagation. |
3635 Do(BuildStoreLocal(parameter, parameter_value)); | 3733 Do(BuildStoreLocal(parameter, parameter_value)); |
3636 } | 3734 } |
3637 pos++; | 3735 pos++; |
3638 } | 3736 } |
3639 } | 3737 } |
3640 | 3738 |
| 3739 // Continuation part: |
| 3740 // If this node sequence is the body of an async closure leave room for a |
| 3741 // preamble. The preamble is generated after visiting the body. |
| 3742 GotoInstr* preamble_start = NULL; |
| 3743 if ((node == owner()->parsed_function()->node_sequence()) && |
| 3744 (owner()->parsed_function()->function().is_async_closure())) { |
| 3745 JoinEntryInstr* preamble_end = new(I) JoinEntryInstr( |
| 3746 owner()->AllocateBlockId(), owner()->try_index()); |
| 3747 ASSERT(exit() != NULL); |
| 3748 exit()->Goto(preamble_end); |
| 3749 ASSERT(exit()->next()->IsGoto()); |
| 3750 preamble_start = exit()->next()->AsGoto(); |
| 3751 ASSERT(preamble_start->IsGoto()); |
| 3752 exit_ = preamble_end; |
| 3753 } |
| 3754 |
3641 intptr_t i = 0; | 3755 intptr_t i = 0; |
3642 while (is_open() && (i < node->length())) { | 3756 while (is_open() && (i < node->length())) { |
3643 EffectGraphVisitor for_effect(owner()); | 3757 EffectGraphVisitor for_effect(owner()); |
3644 node->NodeAt(i++)->Visit(&for_effect); | 3758 node->NodeAt(i++)->Visit(&for_effect); |
3645 Append(for_effect); | 3759 Append(for_effect); |
3646 if (!is_open()) { | 3760 if (!is_open()) { |
3647 // E.g., because of a JumpNode. | 3761 // E.g., because of a JumpNode. |
3648 break; | 3762 break; |
3649 } | 3763 } |
3650 } | 3764 } |
3651 | 3765 |
| 3766 // Continuation part: |
| 3767 // After generating the CFG for the body we can create the preamble because we |
| 3768 // know exactly how many continuation states we need. |
| 3769 if ((node == owner()->parsed_function()->node_sequence()) && |
| 3770 (owner()->parsed_function()->function().is_async_closure())) { |
| 3771 ASSERT(preamble_start != NULL); |
| 3772 // We are at the top level. Fetch the corresponding scope. |
| 3773 LocalScope* top_scope = node->scope(); |
| 3774 LocalVariable* jump_var = top_scope->LookupVariable( |
| 3775 Symbols::AwaitJumpVar(), false); |
| 3776 ASSERT(jump_var != NULL && jump_var->is_captured()); |
| 3777 |
| 3778 Instruction* saved_entry = entry_; |
| 3779 Instruction* saved_exit = exit_; |
| 3780 entry_ = NULL; |
| 3781 exit_ = NULL; |
| 3782 |
| 3783 LoadLocalNode* load_jump_cnt = new(I) LoadLocalNode( |
| 3784 Scanner::kNoSourcePos, jump_var); |
| 3785 ComparisonNode* check_jump_cnt; |
| 3786 const intptr_t num_await_states = owner()->await_joins()->length(); |
| 3787 for (intptr_t i = 0; i < num_await_states; i++) { |
| 3788 check_jump_cnt = new(I) ComparisonNode( |
| 3789 Scanner::kNoSourcePos, |
| 3790 Token::kEQ, |
| 3791 load_jump_cnt, |
| 3792 new(I) LiteralNode( |
| 3793 Scanner::kNoSourcePos, Smi::ZoneHandle(I, Smi::New(i)))); |
| 3794 TestGraphVisitor for_test(owner(), Scanner::kNoSourcePos); |
| 3795 check_jump_cnt->Visit(&for_test); |
| 3796 EffectGraphVisitor for_true(owner()); |
| 3797 EffectGraphVisitor for_false(owner()); |
| 3798 |
| 3799 for_true.BuildAwaitJump(top_scope, |
| 3800 (*owner()->await_levels())[i], |
| 3801 (*owner()->await_joins())[i]); |
| 3802 Join(for_test, for_true, for_false); |
| 3803 |
| 3804 if (i == 0) { |
| 3805 // Manually link up the preamble start. |
| 3806 preamble_start->previous()->set_next(for_test.entry()); |
| 3807 for_test.entry()->set_previous(preamble_start->previous()); |
| 3808 } |
| 3809 if (i == (num_await_states - 1)) { |
| 3810 // Link up preamble end. |
| 3811 if (exit_ == NULL) { |
| 3812 exit_ = preamble_start; |
| 3813 } else { |
| 3814 exit_->LinkTo(preamble_start); |
| 3815 } |
| 3816 } |
| 3817 } |
| 3818 entry_ = saved_entry; |
| 3819 exit_ = saved_exit; |
| 3820 } |
| 3821 |
3652 if (is_open()) { | 3822 if (is_open()) { |
3653 if (MustSaveRestoreContext(node)) { | 3823 if (MustSaveRestoreContext(node)) { |
3654 BuildRestoreContext( | 3824 BuildRestoreContext( |
3655 *owner()->parsed_function()->saved_entry_context_var()); | 3825 *owner()->parsed_function()->saved_entry_context_var()); |
3656 } else if (num_context_variables > 0) { | 3826 } else if (num_context_variables > 0) { |
3657 UnchainContexts(1); | 3827 UnchainContexts(1); |
3658 } | 3828 } |
3659 } | 3829 } |
3660 | 3830 |
3661 // If this node sequence is labeled, a break out of the sequence will have | 3831 // 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... |
4017 Report::MessageF(Report::kBailout, | 4187 Report::MessageF(Report::kBailout, |
4018 Script::Handle(function.script()), | 4188 Script::Handle(function.script()), |
4019 function.token_pos(), | 4189 function.token_pos(), |
4020 "FlowGraphBuilder Bailout: %s %s", | 4190 "FlowGraphBuilder Bailout: %s %s", |
4021 String::Handle(function.name()).ToCString(), | 4191 String::Handle(function.name()).ToCString(), |
4022 reason); | 4192 reason); |
4023 UNREACHABLE(); | 4193 UNREACHABLE(); |
4024 } | 4194 } |
4025 | 4195 |
4026 } // namespace dart | 4196 } // namespace dart |
OLD | NEW |