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() && node->is_regular_return()) { |
(...skipping 16 matching lines...) Expand all Loading... | |
1076 arguments, | 1070 arguments, |
1077 Object::null_array(), | 1071 Object::null_array(), |
1078 1, | 1072 1, |
1079 owner()->ic_data_array()); | 1073 owner()->ic_data_array()); |
1080 Do(call); | 1074 Do(call); |
1081 | 1075 |
1082 // Rebind the return value for the actual return call to be null. | 1076 // Rebind the return value for the actual return call to be null. |
1083 return_value = BuildNullValue(); | 1077 return_value = BuildNullValue(); |
1084 } | 1078 } |
1085 | 1079 |
1080 intptr_t current_context_level = owner()->context_level(); | |
1081 ASSERT(current_context_level >= 0); | |
1082 if (owner()->parsed_function()->saved_entry_context_var() != NULL) { | |
1083 // CTX on entry was saved, but not linked as context parent. | |
1084 BuildRestoreContext(*owner()->parsed_function()->saved_entry_context_var()); | |
1085 } else { | |
1086 UnchainContexts(current_context_level); | |
1087 } | |
1088 | |
1089 | |
1086 AddReturnExit(node->token_pos(), return_value); | 1090 AddReturnExit(node->token_pos(), return_value); |
1087 } | 1091 } |
1088 | 1092 |
1089 | 1093 |
1090 // <Expression> ::= Literal { literal: Instance } | 1094 // <Expression> ::= Literal { literal: Instance } |
1091 void EffectGraphVisitor::VisitLiteralNode(LiteralNode* node) { | 1095 void EffectGraphVisitor::VisitLiteralNode(LiteralNode* node) { |
1092 ReturnDefinition(new(I) ConstantInstr(node->literal())); | 1096 ReturnDefinition(new(I) ConstantInstr(node->literal())); |
1093 } | 1097 } |
1094 | 1098 |
1095 | 1099 |
(...skipping 341 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
1437 } | 1441 } |
1438 return new(I) AssertAssignableInstr(token_pos, | 1442 return new(I) AssertAssignableInstr(token_pos, |
1439 value, | 1443 value, |
1440 instantiator, | 1444 instantiator, |
1441 instantiator_type_arguments, | 1445 instantiator_type_arguments, |
1442 dst_type, | 1446 dst_type, |
1443 dst_name); | 1447 dst_name); |
1444 } | 1448 } |
1445 | 1449 |
1446 | 1450 |
1451 void EffectGraphVisitor::BuildAwaitJump( | |
1452 const LocalVariable& continuation_result, | |
1453 const LocalVariable& old_ctx, | |
1454 const intptr_t old_ctx_level, | |
1455 JoinEntryInstr* target) { | |
1456 // Building a jump consists of the following actions: | |
1457 // * Record the current continuation result in a temporary. | |
1458 // * Restore the old context. | |
1459 // * Overwrite the old context's continuation result with the temporary. | |
1460 // * Append a Goto to the target's join. | |
1461 ASSERT(continuation_result.is_captured()); | |
1462 ASSERT(old_ctx.is_captured()); | |
1463 // Before resoring the continuation context we need to temporary save the | |
1464 // current continuation result. | |
1465 Value* continuation_result_value = Bind(BuildLoadLocal(continuation_result)); | |
1466 Do(BuildStoreExprTemp(continuation_result_value)); | |
1467 | |
1468 // Restore the saved continuation context. | |
1469 BuildRestoreContext(old_ctx); | |
1470 | |
1471 // Pass over the continuation result. | |
1472 Value* saved_continuation_result = Bind(BuildLoadExprTemp()); | |
1473 // FGB is at top context level, but the await target has possibly been | |
srdjan
2014/08/20 17:50:06
ditto for FGB
Michael Lippautz (Google)
2014/08/20 20:56:06
Done.
| |
1474 // recorded in a nested context (old_ctx_level). We need to unroll manually | |
1475 // here. | |
1476 LocalVariable* tmp_var = EnterTempLocalScope(saved_continuation_result); | |
1477 intptr_t delta = old_ctx_level - continuation_result.owner()->context_level(); | |
1478 ASSERT(delta >= 0); | |
1479 Value* context = Bind(new(I) CurrentContextInstr()); | |
1480 while (delta-- > 0) { | |
1481 context = Bind(new(I) LoadFieldInstr( | |
1482 context, Context::parent_offset(), Type::ZoneHandle(I, Type::null()), | |
1483 Scanner::kNoSourcePos)); | |
1484 } | |
1485 Value* tmp_val = Bind(new(I) LoadLocalInstr(*tmp_var)); | |
1486 StoreInstanceFieldInstr* store = new(I) StoreInstanceFieldInstr( | |
1487 Context::variable_offset(continuation_result.index()), | |
1488 context, | |
1489 tmp_val, | |
1490 kEmitStoreBarrier, | |
1491 Scanner::kNoSourcePos); | |
1492 Do(store); | |
1493 Do(ExitTempLocalScope(tmp_var)); | |
1494 | |
1495 // Goto saved join. | |
1496 Goto(target); | |
1497 } | |
1498 | |
1499 | |
1447 // Used for type casts and to test assignments. | 1500 // Used for type casts and to test assignments. |
1448 Value* EffectGraphVisitor::BuildAssignableValue(intptr_t token_pos, | 1501 Value* EffectGraphVisitor::BuildAssignableValue(intptr_t token_pos, |
1449 Value* value, | 1502 Value* value, |
1450 const AbstractType& dst_type, | 1503 const AbstractType& dst_type, |
1451 const String& dst_name) { | 1504 const String& dst_name) { |
1452 if (CanSkipTypeCheck(token_pos, value, dst_type, dst_name)) { | 1505 if (CanSkipTypeCheck(token_pos, value, dst_type, dst_name)) { |
1453 return value; | 1506 return value; |
1454 } | 1507 } |
1455 return Bind(BuildAssertAssignable(token_pos, value, dst_type, dst_name)); | 1508 return Bind(BuildAssertAssignable(token_pos, value, dst_type, dst_name)); |
1456 } | 1509 } |
(...skipping 656 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
2113 UNREACHABLE(); | 2166 UNREACHABLE(); |
2114 } | 2167 } |
2115 | 2168 |
2116 | 2169 |
2117 void EffectGraphVisitor::VisitAwaitNode(AwaitNode* node) { | 2170 void EffectGraphVisitor::VisitAwaitNode(AwaitNode* node) { |
2118 // Await nodes are temporary during parsing. | 2171 // Await nodes are temporary during parsing. |
2119 UNREACHABLE(); | 2172 UNREACHABLE(); |
2120 } | 2173 } |
2121 | 2174 |
2122 | 2175 |
2176 void EffectGraphVisitor::VisitAwaitMarkerNode(AwaitMarkerNode* node) { | |
2177 if (node->type() == AwaitMarkerNode::kNewContinuationState) { | |
2178 // We need to create a new await state which involves: | |
2179 // * Increase the jump counter. Sanity check against the list of targets. | |
2180 // * Save the current context for resuming. | |
2181 ASSERT(node->scope() != NULL); | |
2182 LocalVariable* jump_var = node->scope()->LookupVariable( | |
2183 Symbols::AwaitJumpVar(), false); | |
2184 LocalVariable* ctx_var = node->scope()->LookupVariable( | |
2185 Symbols::AwaitContextVar(), false); | |
2186 ASSERT(jump_var != NULL && jump_var->is_captured()); | |
2187 ASSERT(ctx_var != NULL && ctx_var->is_captured()); | |
2188 const int32_t jump_cnt = owner()->next_await_counter(); | |
2189 ASSERT(jump_cnt >= 0); | |
2190 // Sanity check that we always add a JoinEntryInstr before adding a new | |
2191 // state. | |
2192 ASSERT(jump_cnt == owner()->await_joins()->length()); | |
2193 // Store the counter in :await_jump_var. | |
2194 StoreLocalNode* store_jump_cnt = new(I) StoreLocalNode( | |
Florian Schneider
2014/08/21 11:09:29
Why do you create an AST node first, only to visit
Michael Lippautz (Google)
2014/08/21 16:39:14
Yes and no :)
I inlined the IL instructions, but
| |
2195 Scanner::kNoSourcePos, | |
2196 jump_var, | |
2197 new(I) LiteralNode( | |
2198 Scanner::kNoSourcePos, Smi::ZoneHandle(I, Smi::New(jump_cnt)))); | |
2199 EffectGraphVisitor for_effect(owner()); | |
2200 store_jump_cnt->Visit(&for_effect); | |
2201 Append(for_effect); | |
2202 // Save the current context for resuming. | |
2203 BuildSaveContext(*ctx_var); | |
2204 owner()->await_levels()->Add(owner()->context_level()); | |
2205 return; | |
2206 } else if (node->type() == AwaitMarkerNode::kTargetForContinuation) { | |
Florian Schneider
2014/08/21 11:09:29
No need for "else if" if you return inside the if-
Michael Lippautz (Google)
2014/08/21 16:39:14
Done.
| |
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 1486 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
3619 parameter_value = Bind(assert_assignable); | 3717 parameter_value = Bind(assert_assignable); |
3620 // Store the type checked argument back to its corresponding local | 3718 // Store the type checked argument back to its corresponding local |
3621 // variable so that ssa renaming detects the dependency and makes use | 3719 // variable so that ssa renaming detects the dependency and makes use |
3622 // of the checked type in type propagation. | 3720 // of the checked type in type propagation. |
3623 Do(BuildStoreLocal(parameter, parameter_value)); | 3721 Do(BuildStoreLocal(parameter, parameter_value)); |
3624 } | 3722 } |
3625 pos++; | 3723 pos++; |
3626 } | 3724 } |
3627 } | 3725 } |
3628 | 3726 |
3727 // Continuation part: | |
3728 // If this node sequence is the body of an async closure leave room for a | |
3729 // preamble. The preamble is generated after visiting the body. | |
3730 GotoInstr* preamble_start = NULL; | |
3731 if (node == owner()->parsed_function()->node_sequence() && | |
srdjan
2014/08/20 17:50:06
Add parentheses.
Michael Lippautz (Google)
2014/08/20 20:56:06
Done.
| |
3732 owner()->parsed_function()->function().is_async_closure()) { | |
3733 JoinEntryInstr* preamble_end = new(I) JoinEntryInstr( | |
3734 owner()->AllocateBlockId(), owner()->try_index()); | |
3735 ASSERT(exit() != NULL); | |
3736 exit()->Goto(preamble_end); | |
3737 ASSERT(exit()->next()->IsGoto()); | |
3738 preamble_start = exit()->next()->AsGoto(); | |
3739 ASSERT(preamble_start->IsGoto()); | |
3740 exit_ = preamble_end; | |
3741 } | |
3742 | |
3629 intptr_t i = 0; | 3743 intptr_t i = 0; |
3630 while (is_open() && (i < node->length())) { | 3744 while (is_open() && (i < node->length())) { |
3631 EffectGraphVisitor for_effect(owner()); | 3745 EffectGraphVisitor for_effect(owner()); |
3632 node->NodeAt(i++)->Visit(&for_effect); | 3746 node->NodeAt(i++)->Visit(&for_effect); |
3633 Append(for_effect); | 3747 Append(for_effect); |
3634 if (!is_open()) { | 3748 if (!is_open()) { |
3635 // E.g., because of a JumpNode. | 3749 // E.g., because of a JumpNode. |
3636 break; | 3750 break; |
3637 } | 3751 } |
3638 } | 3752 } |
3639 | 3753 |
3754 // Continuation part: | |
3755 // After generating the CFG for the body we can create the preamble because we | |
3756 // know exactly how many continuation states we need. | |
3757 if (node == owner()->parsed_function()->node_sequence() && | |
srdjan
2014/08/20 17:50:06
ditto
Michael Lippautz (Google)
2014/08/20 20:56:06
Done.
| |
3758 owner()->parsed_function()->function().is_async_closure()) { | |
3759 ASSERT(preamble_start != NULL); | |
3760 // We are at the top level. Fetch the corresponding scope. | |
3761 LocalScope* top_scope = node->scope(); | |
3762 LocalVariable* jump_var = top_scope->LookupVariable( | |
3763 Symbols::AwaitJumpVar(), false); | |
3764 LocalVariable* ctx_var = top_scope->LookupVariable( | |
3765 Symbols::AwaitContextVar(), false); | |
3766 ASSERT(jump_var != NULL && jump_var->is_captured()); | |
3767 ASSERT(ctx_var != NULL && ctx_var->is_captured()); | |
3768 | |
3769 Instruction* saved_entry = entry_; | |
3770 Instruction* saved_exit = exit_; | |
3771 entry_ = NULL; | |
3772 exit_ = NULL; | |
3773 | |
3774 LoadLocalNode* load_jump_cnt = new(I) LoadLocalNode( | |
3775 Scanner::kNoSourcePos, jump_var); | |
3776 ComparisonNode* check_jump_cnt; | |
3777 const int32_t num_await_states = owner()->await_joins()->length(); | |
3778 for (int32_t i = 0; i < num_await_states; i++) { | |
3779 check_jump_cnt = new(I) ComparisonNode( | |
3780 Scanner::kNoSourcePos, | |
3781 Token::kEQ, | |
3782 load_jump_cnt, | |
3783 new(I) LiteralNode( | |
3784 Scanner::kNoSourcePos, Smi::ZoneHandle(I, Smi::New(i)))); | |
3785 TestGraphVisitor for_test(owner(), Scanner::kNoSourcePos); | |
3786 check_jump_cnt->Visit(&for_test); | |
3787 EffectGraphVisitor for_true(owner()); | |
3788 EffectGraphVisitor for_false(owner()); | |
3789 | |
3790 LocalVariable* continuation_result_var = | |
3791 top_scope->LookupVariable(Symbols::AsyncOperationParam(), false); | |
3792 ASSERT(continuation_result_var != NULL); | |
3793 for_true.BuildAwaitJump(*continuation_result_var, | |
3794 *ctx_var, | |
3795 (*owner()->await_levels())[i], | |
3796 (*owner()->await_joins())[i]); | |
3797 Join(for_test, for_true, for_false); | |
3798 | |
3799 if (i == 0) { | |
3800 // Link up the preamble start. | |
3801 preamble_start->previous()->set_next(for_test.entry()); | |
3802 } | |
3803 if (i == (num_await_states - 1)) { | |
3804 // Link up preamble end. | |
3805 if (exit_ == NULL) { | |
3806 exit_ = preamble_start; | |
3807 } else { | |
3808 exit_->LinkTo(preamble_start); | |
3809 } | |
3810 } | |
3811 } | |
3812 entry_ = saved_entry; | |
3813 exit_ = saved_exit; | |
3814 } | |
3815 | |
3640 if (is_open()) { | 3816 if (is_open()) { |
3641 if (MustSaveRestoreContext(node)) { | 3817 if (MustSaveRestoreContext(node)) { |
3642 BuildRestoreContext( | 3818 BuildRestoreContext( |
3643 *owner()->parsed_function()->saved_entry_context_var()); | 3819 *owner()->parsed_function()->saved_entry_context_var()); |
3644 } else if (num_context_variables > 0) { | 3820 } else if (num_context_variables > 0) { |
3645 UnchainContexts(1); | 3821 UnchainContexts(1); |
3646 } | 3822 } |
3647 } | 3823 } |
3648 | 3824 |
3649 // If this node sequence is labeled, a break out of the sequence will have | 3825 // 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, | 4181 Report::MessageF(Report::kBailout, |
4006 Script::Handle(function.script()), | 4182 Script::Handle(function.script()), |
4007 function.token_pos(), | 4183 function.token_pos(), |
4008 "FlowGraphBuilder Bailout: %s %s", | 4184 "FlowGraphBuilder Bailout: %s %s", |
4009 String::Handle(function.name()).ToCString(), | 4185 String::Handle(function.name()).ToCString(), |
4010 reason); | 4186 reason); |
4011 UNREACHABLE(); | 4187 UNREACHABLE(); |
4012 } | 4188 } |
4013 | 4189 |
4014 } // namespace dart | 4190 } // namespace dart |
OLD | NEW |