Chromium Code Reviews| 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 "vm/flags.h" | 7 #include "vm/flags.h" |
| 8 #include "vm/intermediate_language.h" | 8 #include "vm/intermediate_language.h" |
| 9 #include "vm/longjump.h" | |
| 9 #include "vm/os.h" | 10 #include "vm/os.h" |
| 10 #include "vm/parser.h" | 11 #include "vm/parser.h" |
| 11 | 12 |
| 12 namespace dart { | 13 namespace dart { |
| 13 | 14 |
| 14 DEFINE_FLAG(bool, trace_bailout, false, "Print bailout from graph builder."); | |
| 15 DEFINE_FLAG(bool, print_flow_graph, false, "Print the IR flow graph."); | 15 DEFINE_FLAG(bool, print_flow_graph, false, "Print the IR flow graph."); |
| 16 DECLARE_FLAG(bool, enable_type_checks); | 16 DECLARE_FLAG(bool, enable_type_checks); |
| 17 | 17 |
| 18 void EffectGraphVisitor::Append(const EffectGraphVisitor& other_fragment) { | 18 void EffectGraphVisitor::Append(const EffectGraphVisitor& other_fragment) { |
| 19 ASSERT(is_open()); | 19 ASSERT(is_open()); |
| 20 if (other_fragment.is_empty()) return; | 20 if (other_fragment.is_empty()) return; |
| 21 if (is_empty()) { | 21 if (is_empty()) { |
| 22 entry_ = other_fragment.entry(); | 22 entry_ = other_fragment.entry(); |
| 23 exit_ = other_fragment.exit(); | 23 exit_ = other_fragment.exit(); |
| 24 } else { | 24 } else { |
| 25 exit()->set_successor(other_fragment.entry()); | 25 exit()->SetSuccessor(other_fragment.entry()); |
| 26 exit_ = other_fragment.exit(); | 26 exit_ = other_fragment.exit(); |
| 27 } | 27 } |
| 28 } | 28 } |
| 29 | 29 |
| 30 | 30 |
| 31 void EffectGraphVisitor::AddInstruction(Instruction* instruction) { | 31 void EffectGraphVisitor::AddInstruction(Instruction* instruction) { |
| 32 ASSERT(is_open()); | 32 ASSERT(is_open()); |
| 33 if (is_empty()) { | 33 if (is_empty()) { |
| 34 entry_ = exit_ = instruction; | 34 entry_ = exit_ = instruction; |
| 35 } else { | 35 } else { |
| 36 exit()->set_successor(instruction); | 36 exit()->SetSuccessor(instruction); |
| 37 exit_ = instruction; | 37 exit_ = instruction; |
| 38 } | 38 } |
| 39 } | 39 } |
| 40 | 40 |
| 41 | 41 |
| 42 void EffectGraphVisitor::Join(const TestGraphVisitor& test_fragment, | 42 void EffectGraphVisitor::Join(const TestGraphVisitor& test_fragment, |
| 43 const EffectGraphVisitor& true_fragment, | 43 const EffectGraphVisitor& true_fragment, |
| 44 const EffectGraphVisitor& false_fragment) { | 44 const EffectGraphVisitor& false_fragment) { |
| 45 // We have: a test graph fragment with zero, one, or two available exits; | 45 // We have: a test graph fragment with zero, one, or two available exits; |
| 46 // and a pair of effect graph fragments with zero or one available exits. | 46 // and a pair of effect graph fragments with zero or one available exits. |
| 47 // We want to append the branch and (if necessary) a join node to this | 47 // We want to append the branch and (if necessary) a join node to this |
| 48 // graph fragment. | 48 // graph fragment. |
| 49 ASSERT(is_open()); | 49 ASSERT(is_open()); |
| 50 | 50 |
| 51 // 1. Connect the test to this graph. | 51 // 1. Connect the test to this graph. |
| 52 Append(test_fragment); | 52 Append(test_fragment); |
| 53 | 53 |
| 54 // 2. Connect the true and false bodies to the test if they are reachable, | 54 // 2. Connect the true and false bodies to the test if they are reachable, |
| 55 // and if so record their exits (if any). | 55 // and if so record their exits (if any). |
| 56 if (test_fragment.can_be_true()) { | 56 if (test_fragment.can_be_true()) { |
| 57 Instruction* true_exit = NULL; | 57 Instruction* true_exit = NULL; |
| 58 Instruction* false_exit = NULL; | 58 Instruction* false_exit = NULL; |
| 59 TargetEntryInstr* true_entry = new TargetEntryInstr(); | 59 TargetEntryInstr* true_entry = new TargetEntryInstr(); |
| 60 *test_fragment.true_successor_address() = true_entry; | 60 *test_fragment.true_successor_address() = true_entry; |
| 61 true_entry->set_successor(true_fragment.entry()); | 61 true_entry->SetSuccessor(true_fragment.entry()); |
| 62 true_exit = true_fragment.is_empty() ? true_entry : true_fragment.exit(); | 62 true_exit = true_fragment.is_empty() ? true_entry : true_fragment.exit(); |
| 63 | 63 |
| 64 TargetEntryInstr* false_entry = new TargetEntryInstr(); | 64 TargetEntryInstr* false_entry = new TargetEntryInstr(); |
| 65 *test_fragment.false_successor_address() = false_entry; | 65 *test_fragment.false_successor_address() = false_entry; |
| 66 false_entry->set_successor(false_fragment.entry()); | 66 false_entry->SetSuccessor(false_fragment.entry()); |
| 67 false_exit = | 67 false_exit = |
| 68 false_fragment.is_empty() ? false_entry : false_fragment.exit(); | 68 false_fragment.is_empty() ? false_entry : false_fragment.exit(); |
| 69 | 69 |
| 70 exit_ = new JoinEntryInstr(); | 70 exit_ = new JoinEntryInstr(); |
| 71 true_exit->set_successor(exit_); | 71 true_exit->SetSuccessor(exit_); |
| 72 false_exit->set_successor(exit_); | 72 false_exit->SetSuccessor(exit_); |
| 73 } | 73 } |
| 74 } | 74 } |
| 75 | 75 |
| 76 | 76 |
| 77 void EffectGraphVisitor::TieLoop(const TestGraphVisitor& test_fragment, | 77 void EffectGraphVisitor::TieLoop(const TestGraphVisitor& test_fragment, |
| 78 const EffectGraphVisitor& body_fragment) { | 78 const EffectGraphVisitor& body_fragment) { |
| 79 // We have: a test graph fragment with zero, one, or two available exits; | 79 // We have: a test graph fragment with zero, one, or two available exits; |
| 80 // and an effect graph fragment with zero or one available exits. We want | 80 // and an effect graph fragment with zero or one available exits. We want |
| 81 // to append the 'while loop' consisting of the test graph fragment as | 81 // to append the 'while loop' consisting of the test graph fragment as |
| 82 // condition and the effect graph fragment as body. | 82 // condition and the effect graph fragment as body. |
| 83 ASSERT(is_open()); | 83 ASSERT(is_open()); |
| 84 | 84 |
| 85 // 1. Connect the body to the test if it is reachable, and if so record | 85 // 1. Connect the body to the test if it is reachable, and if so record |
| 86 // its exit (if any). | 86 // its exit (if any). |
| 87 Instruction* body_exit = NULL; | 87 Instruction* body_exit = NULL; |
| 88 if (test_fragment.can_be_true()) { | 88 if (test_fragment.can_be_true()) { |
| 89 TargetEntryInstr* body_entry = new TargetEntryInstr(); | 89 TargetEntryInstr* body_entry = new TargetEntryInstr(); |
| 90 *test_fragment.true_successor_address() = body_entry; | 90 *test_fragment.true_successor_address() = body_entry; |
| 91 body_entry->set_successor(body_fragment.entry()); | 91 body_entry->SetSuccessor(body_fragment.entry()); |
| 92 body_exit = body_fragment.is_empty() ? body_entry : body_fragment.exit(); | 92 body_exit = body_fragment.is_empty() ? body_entry : body_fragment.exit(); |
| 93 } | 93 } |
| 94 | 94 |
| 95 // 2. Connect the test to this graph, including the body if reachable and | 95 // 2. Connect the test to this graph, including the body if reachable and |
| 96 // using a fresh join node if the body is reachable and has an open exit. | 96 // using a fresh join node if the body is reachable and has an open exit. |
| 97 if (body_exit == NULL) { | 97 if (body_exit == NULL) { |
| 98 Append(test_fragment); | 98 Append(test_fragment); |
| 99 } else { | 99 } else { |
| 100 JoinEntryInstr* join = new JoinEntryInstr(); | 100 JoinEntryInstr* join = new JoinEntryInstr(); |
| 101 AddInstruction(join); | 101 AddInstruction(join); |
| 102 join->set_successor(test_fragment.entry()); | 102 join->SetSuccessor(test_fragment.entry()); |
| 103 body_exit->set_successor(join); | 103 body_exit->SetSuccessor(join); |
| 104 } | 104 } |
| 105 | 105 |
| 106 // 3. Set the exit to the graph to be empty or a fresh target node | 106 // 3. Set the exit to the graph to be empty or a fresh target node |
| 107 // depending on whether the false branch of the test is reachable. | 107 // depending on whether the false branch of the test is reachable. |
| 108 if (test_fragment.can_be_false()) { | 108 if (test_fragment.can_be_false()) { |
| 109 exit_ = *test_fragment.false_successor_address() = new TargetEntryInstr(); | 109 exit_ = *test_fragment.false_successor_address() = new TargetEntryInstr(); |
| 110 } else { | 110 } else { |
| 111 exit_ = NULL; | 111 exit_ = NULL; |
| 112 } | 112 } |
| 113 } | 113 } |
| 114 | 114 |
| 115 | 115 |
| 116 void TestGraphVisitor::BranchOnValue(Value* value) { | 116 void TestGraphVisitor::BranchOnValue(Value* value) { |
| 117 BranchInstr* branch = new BranchInstr(value); | 117 BranchInstr* branch = new BranchInstr(value); |
| 118 AddInstruction(branch); | 118 AddInstruction(branch); |
| 119 CloseFragment(); | 119 CloseFragment(); |
| 120 true_successor_address_ = branch->true_successor_address(); | 120 true_successor_address_ = branch->true_successor_address(); |
| 121 false_successor_address_ = branch->false_successor_address(); | 121 false_successor_address_ = branch->false_successor_address(); |
| 122 } | 122 } |
| 123 | 123 |
| 124 | 124 |
| 125 void EffectGraphVisitor::Bailout(const char* reason) { | 125 void EffectGraphVisitor::Bailout(const char* reason) { |
| 126 if (FLAG_trace_bailout) { | |
| 127 OS::Print("Flow Graph Bailout: %s\n", reason); | |
| 128 } | |
| 129 owner()->Bailout(reason); | 126 owner()->Bailout(reason); |
| 130 } | 127 } |
| 131 | 128 |
| 132 | 129 |
| 133 // 'bailout' is a statement (without a semicolon), typically a return. | 130 // 'bailout' is a statement (without a semicolon), typically a return. |
| 134 #define CHECK_ALIVE(bailout) \ | 131 #define CHECK_ALIVE(bailout) \ |
| 135 do { \ | 132 do { \ |
| 136 if (owner()->HasBailedOut() || !is_open()) { \ | 133 if (!is_open()) { \ |
| 137 bailout; \ | 134 bailout; \ |
| 138 } \ | 135 } \ |
| 139 } while (false) | 136 } while (false) |
| 140 | 137 |
| 141 | 138 |
| 142 // <Statement> ::= Return { value: <Expression> | 139 // <Statement> ::= Return { value: <Expression> |
| 143 // inlined_finally_list: <InlinedFinally>* } | 140 // inlined_finally_list: <InlinedFinally>* } |
| 144 void EffectGraphVisitor::VisitReturnNode(ReturnNode* node) { | 141 void EffectGraphVisitor::VisitReturnNode(ReturnNode* node) { |
| 145 ValueGraphVisitor for_value(owner(), temp_index()); | 142 ValueGraphVisitor for_value(owner(), temp_index()); |
| 146 node->value()->Visit(&for_value); | 143 node->value()->Visit(&for_value); |
| (...skipping 83 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 230 CHECK_ALIVE(return); | 227 CHECK_ALIVE(return); |
| 231 BranchOnValueOf(assert); | 228 BranchOnValueOf(assert); |
| 232 } | 229 } |
| 233 | 230 |
| 234 | 231 |
| 235 // <Expression> :: BinaryOp { kind: Token::Kind | 232 // <Expression> :: BinaryOp { kind: Token::Kind |
| 236 // left: <Expression> | 233 // left: <Expression> |
| 237 // right: <Expression> } | 234 // right: <Expression> } |
| 238 InstanceCallComp* EffectGraphVisitor::TranslateBinaryOp( | 235 InstanceCallComp* EffectGraphVisitor::TranslateBinaryOp( |
| 239 const BinaryOpNode& node) { | 236 const BinaryOpNode& node) { |
| 237 // Operators "&&" and "||" cannot be overloaded therefore do not call | |
| 238 // operator. | |
| 240 if ((node.kind() == Token::kAND) || (node.kind() == Token::kOR)) { | 239 if ((node.kind() == Token::kAND) || (node.kind() == Token::kOR)) { |
| 241 Bailout("EffectGraphVisitor::VisitBinaryOpNode"); | 240 Bailout("EffectGraphVisitor::VisitBinaryOpNode AND/OR"); |
| 242 return NULL; | |
| 243 } | 241 } |
| 244 ValueGraphVisitor for_left_value(owner(), temp_index()); | 242 ValueGraphVisitor for_left_value(owner(), temp_index()); |
| 245 node.left()->Visit(&for_left_value); | 243 node.left()->Visit(&for_left_value); |
| 246 Append(for_left_value); | 244 Append(for_left_value); |
| 247 CHECK_ALIVE(return NULL); | 245 CHECK_ALIVE(return NULL); |
| 248 ValueGraphVisitor for_right_value(owner(), for_left_value.temp_index()); | 246 ValueGraphVisitor for_right_value(owner(), for_left_value.temp_index()); |
| 249 node.right()->Visit(&for_right_value); | 247 node.right()->Visit(&for_right_value); |
| 250 Append(for_right_value); | 248 Append(for_right_value); |
| 251 CHECK_ALIVE(return NULL); | 249 CHECK_ALIVE(return NULL); |
| 252 ZoneGrowableArray<Value*>* arguments = new ZoneGrowableArray<Value*>(2); | 250 ZoneGrowableArray<Value*>* arguments = new ZoneGrowableArray<Value*>(2); |
| (...skipping 68 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 321 ReturnValueOf(call); | 319 ReturnValueOf(call); |
| 322 } | 320 } |
| 323 | 321 |
| 324 void TestGraphVisitor::VisitComparisonNode(ComparisonNode* node) { | 322 void TestGraphVisitor::VisitComparisonNode(ComparisonNode* node) { |
| 325 InstanceCallComp* call = TranslateComparison(*node); | 323 InstanceCallComp* call = TranslateComparison(*node); |
| 326 CHECK_ALIVE(return); | 324 CHECK_ALIVE(return); |
| 327 BranchOnValueOf(call); | 325 BranchOnValueOf(call); |
| 328 } | 326 } |
| 329 | 327 |
| 330 | 328 |
| 329 | |
| 330 InstanceCallComp* EffectGraphVisitor::TranslateUnaryOp( | |
| 331 const UnaryOpNode& node) { | |
| 332 // "!" cannot be overloaded, therefore do not call operator. | |
| 333 if (node.kind() == Token::kNOT) { | |
| 334 Bailout("EffectGraphVisitor::VisitUnaryOpNode NOT"); | |
| 335 } | |
| 336 ValueGraphVisitor for_value(owner(), temp_index()); | |
| 337 node.operand()->Visit(&for_value); | |
| 338 Append(for_value); | |
| 339 ZoneGrowableArray<Value*>* argument = new ZoneGrowableArray<Value*>(1); | |
| 340 argument->Add(for_value.value()); | |
| 341 return new InstanceCallComp(node.Name(), argument); | |
|
Kevin Millikin (Google)
2012/02/23 09:31:43
We might want to make the InstanceCallComp (and ot
srdjan
2012/02/23 16:06:12
I do not think that cost of allocating ZoneGrowabl
| |
| 342 } | |
| 343 | |
| 344 | |
| 331 void EffectGraphVisitor::VisitUnaryOpNode(UnaryOpNode* node) { | 345 void EffectGraphVisitor::VisitUnaryOpNode(UnaryOpNode* node) { |
| 332 Bailout("EffectGraphVisitor::VisitUnaryOpNode"); | 346 InstanceCallComp* call = TranslateUnaryOp(*node); |
| 347 DoComputation(call); | |
| 333 } | 348 } |
| 334 void ValueGraphVisitor::VisitUnaryOpNode(UnaryOpNode* node) { | 349 void ValueGraphVisitor::VisitUnaryOpNode(UnaryOpNode* node) { |
| 335 Bailout("ValueGraphVisitor::VisitUnaryOpNode"); | 350 InstanceCallComp* call = TranslateUnaryOp(*node); |
| 351 ReturnValueOf(call); | |
| 336 } | 352 } |
| 337 void TestGraphVisitor::VisitUnaryOpNode(UnaryOpNode* node) { | 353 void TestGraphVisitor::VisitUnaryOpNode(UnaryOpNode* node) { |
| 338 Bailout("TestGraphVisitor::VisitUnaryOpNode"); | 354 InstanceCallComp* call = TranslateUnaryOp(*node); |
| 355 BranchOnValueOf(call); | |
| 339 } | 356 } |
| 340 | 357 |
| 341 | 358 |
| 342 void EffectGraphVisitor::VisitIncrOpLocalNode(IncrOpLocalNode* node) { | 359 void EffectGraphVisitor::VisitIncrOpLocalNode(IncrOpLocalNode* node) { |
| 343 Bailout("EffectGraphVisitor::VisitIncrOpLocalNode"); | 360 Bailout("EffectGraphVisitor::VisitIncrOpLocalNode"); |
| 344 } | 361 } |
| 345 void ValueGraphVisitor::VisitIncrOpLocalNode(IncrOpLocalNode* node) { | 362 void ValueGraphVisitor::VisitIncrOpLocalNode(IncrOpLocalNode* node) { |
| 346 Bailout("ValueGraphVisitor::VisitIncrOpLocalNode"); | 363 Bailout("ValueGraphVisitor::VisitIncrOpLocalNode"); |
| 347 } | 364 } |
| 348 void TestGraphVisitor::VisitIncrOpLocalNode(IncrOpLocalNode* node) { | 365 void TestGraphVisitor::VisitIncrOpLocalNode(IncrOpLocalNode* node) { |
| (...skipping 500 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 849 Bailout("EffectGraphVisitor::VisitInlinedFinallyNode"); | 866 Bailout("EffectGraphVisitor::VisitInlinedFinallyNode"); |
| 850 } | 867 } |
| 851 void ValueGraphVisitor::VisitInlinedFinallyNode(InlinedFinallyNode* node) { | 868 void ValueGraphVisitor::VisitInlinedFinallyNode(InlinedFinallyNode* node) { |
| 852 Bailout("ValueGraphVisitor::VisitInlinedFinallyNode"); | 869 Bailout("ValueGraphVisitor::VisitInlinedFinallyNode"); |
| 853 } | 870 } |
| 854 void TestGraphVisitor::VisitInlinedFinallyNode(InlinedFinallyNode* node) { | 871 void TestGraphVisitor::VisitInlinedFinallyNode(InlinedFinallyNode* node) { |
| 855 Bailout("TestGraphVisitor::VisitInlinedFinallyNode"); | 872 Bailout("TestGraphVisitor::VisitInlinedFinallyNode"); |
| 856 } | 873 } |
| 857 | 874 |
| 858 | 875 |
| 859 void FlowGraphBuilder::TraceBailout() const { | |
| 860 if (FLAG_trace_bailout && HasBailedOut()) { | |
| 861 OS::Print("Failed: %s in %s\n", | |
| 862 bailout_reason_, | |
| 863 parsed_function().function().ToFullyQualifiedCString()); | |
| 864 } | |
| 865 } | |
| 866 | |
| 867 | |
| 868 void FlowGraphBuilder::PrintGraph() const { | 876 void FlowGraphBuilder::PrintGraph() const { |
| 869 if (!FLAG_print_flow_graph || HasBailedOut()) return; | |
| 870 | |
| 871 OS::Print("==== %s\n", | 877 OS::Print("==== %s\n", |
| 872 parsed_function().function().ToFullyQualifiedCString()); | 878 parsed_function().function().ToFullyQualifiedCString()); |
| 873 | 879 |
| 874 for (intptr_t i = postorder_.length() - 1; i >= 0; --i) { | 880 for (intptr_t i = postorder_block_entries_.length() - 1; i >= 0; --i) { |
| 875 OS::Print("%8d: ", postorder_.length() - i); | 881 // Print the block entry. |
| 876 postorder_[i]->Print(i, postorder_); | 882 Instruction* current = postorder_block_entries_[i]->Print(); |
| 883 // And all the successors until an exit, branch, or a block entry. | |
| 884 while (current != NULL && !current->IsBlockEntry()) { | |
| 885 OS::Print("\n"); | |
| 886 current = current->Print(); | |
| 887 } | |
| 888 if (current != NULL && current->IsBlockEntry()) { | |
| 889 OS::Print(" goto %d", current->GetBlockNumber()); | |
| 890 } | |
| 877 OS::Print("\n"); | 891 OS::Print("\n"); |
| 878 } | 892 } |
| 879 OS::Print("\n"); | |
| 880 } | 893 } |
| 881 | 894 |
| 882 | 895 |
| 883 void FlowGraphBuilder::BuildGraph() { | 896 void FlowGraphBuilder::BuildGraph() { |
| 884 EffectGraphVisitor for_effect(this, 0); | 897 EffectGraphVisitor for_effect(this, 0); |
| 898 for_effect.AddInstruction(new TargetEntryInstr()); | |
| 885 parsed_function().node_sequence()->Visit(&for_effect); | 899 parsed_function().node_sequence()->Visit(&for_effect); |
| 886 TraceBailout(); | 900 if (for_effect.entry() != NULL) { |
| 887 if (!HasBailedOut() && (for_effect.entry() != NULL)) { | 901 // Accumulate basic block entries via postorder traversal. |
| 888 for_effect.entry()->Postorder(&postorder_); | 902 for_effect.entry()->Postorder(&postorder_block_entries_); |
| 903 // Number the blocks in reverse postorder starting with 0. | |
| 904 intptr_t index = postorder_block_entries_.length() - 1; | |
| 905 intptr_t number = 0; | |
| 906 while (index >= 0) { | |
| 907 postorder_block_entries_[index--]->SetBlockNumber(number++); | |
| 908 } | |
| 889 } | 909 } |
| 890 PrintGraph(); | 910 if (FLAG_print_flow_graph) { |
| 911 PrintGraph(); | |
| 912 } | |
| 891 } | 913 } |
| 892 | 914 |
| 915 | |
| 916 void FlowGraphBuilder::Bailout(const char* reason) { | |
| 917 const char* kFormat = "FlowGraphBuilder Bailout: %s"; | |
| 918 intptr_t len = OS::SNPrint(NULL, 0, kFormat, reason) + 1; | |
| 919 char* chars = reinterpret_cast<char*>( | |
| 920 Isolate::Current()->current_zone()->Allocate(len)); | |
| 921 OS::SNPrint(chars, len, kFormat, reason); | |
| 922 const Error& error = Error::Handle( | |
| 923 LanguageError::New(String::Handle(String::New(chars)))); | |
| 924 Isolate::Current()->long_jump_base()->Jump(1, error); | |
| 925 } | |
| 926 | |
| 927 | |
| 893 } // namespace dart | 928 } // namespace dart |
| OLD | NEW |