diff options
Diffstat (limited to 'compiler/optimizing')
30 files changed, 643 insertions, 784 deletions
diff --git a/compiler/optimizing/builder.cc b/compiler/optimizing/builder.cc index a1d62760a1..05e1356ed8 100644 --- a/compiler/optimizing/builder.cc +++ b/compiler/optimizing/builder.cc @@ -32,6 +32,7 @@ #include "nodes.h" #include "primitive.h" #include "scoped_thread_state_change.h" +#include "ssa_builder.h" #include "thread.h" #include "utils/dex_cache_arrays_layout-inl.h" @@ -248,7 +249,7 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) // loop for synchronized blocks. if (block->HasThrowingInstructions()) { // Try to find a TryItem covering the block. - DCHECK_NE(block->GetDexPc(), kNoDexPc) << "Block must have a dec_pc to find its TryItem."; + DCHECK_NE(block->GetDexPc(), kNoDexPc) << "Block must have a dex_pc to find its TryItem."; const int32_t try_item_idx = DexFile::FindTryItem(code_item, block->GetDexPc()); if (try_item_idx != -1) { // Block throwing and in a TryItem. Store the try block information. @@ -322,7 +323,8 @@ void HGraphBuilder::InsertTryBoundaryBlocks(const DexFile::CodeItem& code_item) } } -bool HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { +GraphAnalysisResult HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item, + StackHandleScopeCollection* handles) { DCHECK(graph_->GetBlocks().empty()); const uint16_t* code_ptr = code_item.insns_; @@ -349,12 +351,12 @@ bool HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { // start a new block, and create these blocks. if (!ComputeBranchTargets(code_ptr, code_end, &number_of_branches)) { MaybeRecordStat(MethodCompilationStat::kNotCompiledBranchOutsideMethodCode); - return false; + return kAnalysisInvalidBytecode; } // Note that the compiler driver is null when unit testing. if ((compiler_driver_ != nullptr) && SkipCompilation(code_item, number_of_branches)) { - return false; + return kAnalysisInvalidBytecode; } // Find locations where we want to generate extra stackmaps for native debugging. @@ -385,7 +387,7 @@ bool HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { } } if (!AnalyzeDexInstruction(instruction, dex_pc)) { - return false; + return kAnalysisInvalidBytecode; } dex_pc += instruction.SizeInCodeUnits(); code_ptr += instruction.SizeInCodeUnits(); @@ -404,7 +406,13 @@ bool HGraphBuilder::BuildGraph(const DexFile::CodeItem& code_item) { // non-exceptional edges to have been created. InsertTryBoundaryBlocks(code_item); - return true; + GraphAnalysisResult result = graph_->BuildDominatorTree(); + if (result != kAnalysisSuccess) { + return result; + } + + graph_->InitializeInexactObjectRTI(handles); + return SsaBuilder(graph_, handles).BuildSsa(); } void HGraphBuilder::MaybeUpdateCurrentBlock(size_t dex_pc) { diff --git a/compiler/optimizing/builder.h b/compiler/optimizing/builder.h index 93e17d6422..e3dd0e8216 100644 --- a/compiler/optimizing/builder.h +++ b/compiler/optimizing/builder.h @@ -80,7 +80,8 @@ class HGraphBuilder : public ValueObject { null_dex_cache_(), dex_cache_(null_dex_cache_) {} - bool BuildGraph(const DexFile::CodeItem& code); + GraphAnalysisResult BuildGraph(const DexFile::CodeItem& code, + StackHandleScopeCollection* handles); static constexpr const char* kBuilderPassName = "builder"; diff --git a/compiler/optimizing/codegen_test.cc b/compiler/optimizing/codegen_test.cc index 4f37c37e39..6be79fa75c 100644 --- a/compiler/optimizing/codegen_test.cc +++ b/compiler/optimizing/codegen_test.cc @@ -206,10 +206,13 @@ static void RunCode(CodeGenerator* codegen, std::function<void(HGraph*)> hook_before_codegen, bool has_result, Expected expected) { - ASSERT_TRUE(graph->IsInSsaForm()); - - SSAChecker graph_checker(graph); + GraphChecker graph_checker(graph); graph_checker.Run(); + if (!graph_checker.IsValid()) { + for (auto error : graph_checker.GetErrors()) { + std::cout << error << std::endl; + } + } ASSERT_TRUE(graph_checker.IsValid()); SsaLivenessAnalysis liveness(graph, codegen); @@ -292,14 +295,9 @@ static void TestCode(const uint16_t* data, for (InstructionSet target_isa : GetTargetISAs()) { ArenaPool pool; ArenaAllocator arena(&pool); - HGraph* graph = CreateGraph(&arena); - HGraphBuilder builder(graph); - const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); - bool graph_built = builder.BuildGraph(*item); - ASSERT_TRUE(graph_built); + HGraph* graph = CreateCFG(&arena, data); // Remove suspend checks, they cannot be executed in this context. RemoveSuspendChecks(graph); - TransformToSsa(graph); RunCode(target_isa, graph, [](HGraph*) {}, has_result, expected); } } @@ -310,14 +308,9 @@ static void TestCodeLong(const uint16_t* data, for (InstructionSet target_isa : GetTargetISAs()) { ArenaPool pool; ArenaAllocator arena(&pool); - HGraph* graph = CreateGraph(&arena); - HGraphBuilder builder(graph, Primitive::kPrimLong); - const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); - bool graph_built = builder.BuildGraph(*item); - ASSERT_TRUE(graph_built); + HGraph* graph = CreateCFG(&arena, data, Primitive::kPrimLong); // Remove suspend checks, they cannot be executed in this context. RemoveSuspendChecks(graph); - TransformToSsa(graph); RunCode(target_isa, graph, [](HGraph*) {}, has_result, expected); } } @@ -640,6 +633,7 @@ TEST_F(CodegenTest, NonMaterializedCondition) { ArenaAllocator allocator(&pool); HGraph* graph = CreateGraph(&allocator); + HBasicBlock* entry = new (&allocator) HBasicBlock(graph); graph->AddBlock(entry); graph->SetEntryBlock(entry); @@ -672,7 +666,7 @@ TEST_F(CodegenTest, NonMaterializedCondition) { else_block->AddInstruction(new (&allocator) HReturn(constant1)); ASSERT_FALSE(equal->IsEmittedAtUseSite()); - TransformToSsa(graph); + graph->BuildDominatorTree(); PrepareForRegisterAllocation(graph).Run(); ASSERT_TRUE(equal->IsEmittedAtUseSite()); @@ -723,7 +717,7 @@ TEST_F(CodegenTest, MaterializedCondition1) { HReturn ret(&cmp_lt); code_block->AddInstruction(&ret); - TransformToSsa(graph); + graph->BuildDominatorTree(); auto hook_before_codegen = [](HGraph* graph_in) { HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0]; HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena()); @@ -791,7 +785,7 @@ TEST_F(CodegenTest, MaterializedCondition2) { HReturn ret_ge(cst_ge); if_false_block->AddInstruction(&ret_ge); - TransformToSsa(graph); + graph->BuildDominatorTree(); auto hook_before_codegen = [](HGraph* graph_in) { HBasicBlock* block = graph_in->GetEntryBlock()->GetSuccessors()[0]; HParallelMove* move = new (graph_in->GetArena()) HParallelMove(graph_in->GetArena()); @@ -907,7 +901,7 @@ static void TestComparison(IfCondition condition, block->AddInstruction(comparison); block->AddInstruction(new (&allocator) HReturn(comparison)); - TransformToSsa(graph); + graph->BuildDominatorTree(); RunCode(target_isa, graph, [](HGraph*) {}, true, expected_result); } diff --git a/compiler/optimizing/constant_folding_test.cc b/compiler/optimizing/constant_folding_test.cc index a8f65bf516..9c69f8c75b 100644 --- a/compiler/optimizing/constant_folding_test.cc +++ b/compiler/optimizing/constant_folding_test.cc @@ -56,7 +56,6 @@ class ConstantFoldingTest : public CommonCompilerTest { const std::string& expected_after_dce, std::function<void(HGraph*)> check_after_cf) { ASSERT_NE(graph_, nullptr); - TransformToSsa(graph_); StringPrettyPrinter printer_before(graph_); printer_before.VisitInsertionOrder(); @@ -67,9 +66,9 @@ class ConstantFoldingTest : public CommonCompilerTest { X86InstructionSetFeatures::FromCppDefines()); x86::CodeGeneratorX86 codegenX86(graph_, *features_x86.get(), CompilerOptions()); HConstantFolding(graph_).Run(); - SSAChecker ssa_checker_cf(graph_); - ssa_checker_cf.Run(); - ASSERT_TRUE(ssa_checker_cf.IsValid()); + GraphChecker graph_checker_cf(graph_); + graph_checker_cf.Run(); + ASSERT_TRUE(graph_checker_cf.IsValid()); StringPrettyPrinter printer_after_cf(graph_); printer_after_cf.VisitInsertionOrder(); @@ -79,9 +78,9 @@ class ConstantFoldingTest : public CommonCompilerTest { check_after_cf(graph_); HDeadCodeElimination(graph_).Run(); - SSAChecker ssa_checker_dce(graph_); - ssa_checker_dce.Run(); - ASSERT_TRUE(ssa_checker_dce.IsValid()); + GraphChecker graph_checker_dce(graph_); + graph_checker_dce.Run(); + ASSERT_TRUE(graph_checker_dce.IsValid()); StringPrettyPrinter printer_after_dce(graph_); printer_after_dce.VisitInsertionOrder(); @@ -775,76 +774,87 @@ TEST_F(ConstantFoldingTest, UnsignedComparisonsWithZero) { HInstruction* zero = graph_->GetIntConstant(0); HInstruction* last; block->AddInstruction(last = new (&allocator_) HAbove(zero, parameter)); - block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0)); block->AddInstruction(last = new (&allocator_) HAbove(parameter, zero)); - block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0)); block->AddInstruction(last = new (&allocator_) HAboveOrEqual(zero, parameter)); - block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0)); block->AddInstruction(last = new (&allocator_) HAboveOrEqual(parameter, zero)); - block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0)); block->AddInstruction(last = new (&allocator_) HBelow(zero, parameter)); - block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0)); block->AddInstruction(last = new (&allocator_) HBelow(parameter, zero)); - block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0)); block->AddInstruction(last = new (&allocator_) HBelowOrEqual(zero, parameter)); - block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0)); block->AddInstruction(last = new (&allocator_) HBelowOrEqual(parameter, zero)); - block->AddInstruction(new (&allocator_) HDeoptimize(last, 0)); + block->AddInstruction(new (&allocator_) HSelect(last, parameter, parameter, 0)); entry_block->AddInstruction(new (&allocator_) HGoto()); block->AddInstruction(new (&allocator_) HReturn(zero)); exit_block->AddInstruction(new (&allocator_) HExit()); + graph_->BuildDominatorTree(); + const std::string expected_before = "BasicBlock 0, succ: 1\n" - " 0: ParameterValue [16, 14, 12, 10, 8, 6, 4, 2]\n" + " 0: ParameterValue [17, 17, 16, 15, 15, 14, 13, 13, 12, 11, 11, 10, 9, 9, " + "8, 7, 7, 6, 5, 5, 4, 3, 3, 2]\n" " 1: IntConstant [19, 16, 14, 12, 10, 8, 6, 4, 2]\n" " 18: Goto 1\n" "BasicBlock 1, pred: 0, succ: 2\n" " 2: Above(1, 0) [3]\n" - " 3: Deoptimize(2)\n" + " 3: Select(0, 0, 2)\n" " 4: Above(0, 1) [5]\n" - " 5: Deoptimize(4)\n" + " 5: Select(0, 0, 4)\n" " 6: AboveOrEqual(1, 0) [7]\n" - " 7: Deoptimize(6)\n" + " 7: Select(0, 0, 6)\n" " 8: AboveOrEqual(0, 1) [9]\n" - " 9: Deoptimize(8)\n" + " 9: Select(0, 0, 8)\n" " 10: Below(1, 0) [11]\n" - " 11: Deoptimize(10)\n" + " 11: Select(0, 0, 10)\n" " 12: Below(0, 1) [13]\n" - " 13: Deoptimize(12)\n" + " 13: Select(0, 0, 12)\n" " 14: BelowOrEqual(1, 0) [15]\n" - " 15: Deoptimize(14)\n" + " 15: Select(0, 0, 14)\n" " 16: BelowOrEqual(0, 1) [17]\n" - " 17: Deoptimize(16)\n" + " 17: Select(0, 0, 16)\n" " 19: Return(1)\n" "BasicBlock 2, pred: 1\n" " 20: Exit\n"; const std::string expected_after_cf = "BasicBlock 0, succ: 1\n" - " 0: ParameterValue [16, 10, 6, 4]\n" + " 0: ParameterValue [17, 17, 16, 15, 15, 13, 13, 11, 11, 10, 9, 9, 7, 7, 6, 5, 5, 4, 3, 3]\n" " 1: IntConstant [13, 3, 19, 16, 10, 6, 4]\n" " 21: IntConstant [15, 9]\n" " 18: Goto 1\n" "BasicBlock 1, pred: 0, succ: 2\n" - " 3: Deoptimize(1)\n" + " 3: Select(0, 0, 1)\n" " 4: Above(0, 1) [5]\n" - " 5: Deoptimize(4)\n" + " 5: Select(0, 0, 4)\n" " 6: AboveOrEqual(1, 0) [7]\n" - " 7: Deoptimize(6)\n" - " 9: Deoptimize(21)\n" + " 7: Select(0, 0, 6)\n" + " 9: Select(0, 0, 21)\n" " 10: Below(1, 0) [11]\n" - " 11: Deoptimize(10)\n" - " 13: Deoptimize(1)\n" - " 15: Deoptimize(21)\n" + " 11: Select(0, 0, 10)\n" + " 13: Select(0, 0, 1)\n" + " 15: Select(0, 0, 21)\n" " 16: BelowOrEqual(0, 1) [17]\n" - " 17: Deoptimize(16)\n" + " 17: Select(0, 0, 16)\n" " 19: Return(1)\n" "BasicBlock 2, pred: 1\n" " 20: Exit\n"; - const std::string expected_after_dce = expected_after_cf; + const std::string expected_after_dce = + "BasicBlock 0, succ: 1\n" + " 0: ParameterValue\n" + " 1: IntConstant [19]\n" + " 18: Goto 1\n" + "BasicBlock 1, pred: 0, succ: 2\n" + " 19: Return(1)\n" + "BasicBlock 2, pred: 1\n" + " 20: Exit\n"; auto check_after_cf = [](HGraph* graph) { CHECK(graph != nullptr); diff --git a/compiler/optimizing/dead_code_elimination_test.cc b/compiler/optimizing/dead_code_elimination_test.cc index f0f98efadb..930795b4f6 100644 --- a/compiler/optimizing/dead_code_elimination_test.cc +++ b/compiler/optimizing/dead_code_elimination_test.cc @@ -36,8 +36,6 @@ static void TestCode(const uint16_t* data, HGraph* graph = CreateCFG(&allocator, data); ASSERT_NE(graph, nullptr); - TransformToSsa(graph); - StringPrettyPrinter printer_before(graph); printer_before.VisitInsertionOrder(); std::string actual_before = printer_before.str(); @@ -47,9 +45,9 @@ static void TestCode(const uint16_t* data, X86InstructionSetFeatures::FromCppDefines()); x86::CodeGeneratorX86 codegenX86(graph, *features_x86.get(), CompilerOptions()); HDeadCodeElimination(graph).Run(); - SSAChecker ssa_checker(graph); - ssa_checker.Run(); - ASSERT_TRUE(ssa_checker.IsValid()); + GraphChecker graph_checker(graph); + graph_checker.Run(); + ASSERT_TRUE(graph_checker.IsValid()); StringPrettyPrinter printer_after(graph); printer_after.VisitInsertionOrder(); diff --git a/compiler/optimizing/dominator_test.cc b/compiler/optimizing/dominator_test.cc index feb8b2092a..50c677adf5 100644 --- a/compiler/optimizing/dominator_test.cc +++ b/compiler/optimizing/dominator_test.cc @@ -24,15 +24,12 @@ namespace art { +class OptimizerTest : public CommonCompilerTest {}; + static void TestCode(const uint16_t* data, const uint32_t* blocks, size_t blocks_length) { ArenaPool pool; ArenaAllocator allocator(&pool); - HGraph* graph = CreateGraph(&allocator); - HGraphBuilder builder(graph); - const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); - bool graph_built = builder.BuildGraph(*item); - ASSERT_TRUE(graph_built); - graph->BuildDominatorTree(); + HGraph* graph = CreateCFG(&allocator, data); ASSERT_EQ(graph->GetBlocks().size(), blocks_length); for (size_t i = 0, e = blocks_length; i < e; ++i) { if (blocks[i] == kInvalidBlockId) { @@ -50,7 +47,7 @@ static void TestCode(const uint16_t* data, const uint32_t* blocks, size_t blocks } } -TEST(OptimizerTest, ReturnVoid) { +TEST_F(OptimizerTest, ReturnVoid) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::RETURN_VOID); // Block number 1 @@ -63,7 +60,7 @@ TEST(OptimizerTest, ReturnVoid) { TestCode(data, dominators, sizeof(dominators) / sizeof(int)); } -TEST(OptimizerTest, CFG1) { +TEST_F(OptimizerTest, CFG1) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x100, // Block number 1 Instruction::RETURN_VOID); // Block number 2 @@ -78,7 +75,7 @@ TEST(OptimizerTest, CFG1) { TestCode(data, dominators, sizeof(dominators) / sizeof(int)); } -TEST(OptimizerTest, CFG2) { +TEST_F(OptimizerTest, CFG2) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x100, // Block number 1 Instruction::GOTO | 0x100, // Block number 2 @@ -95,7 +92,7 @@ TEST(OptimizerTest, CFG2) { TestCode(data, dominators, sizeof(dominators) / sizeof(int)); } -TEST(OptimizerTest, CFG3) { +TEST_F(OptimizerTest, CFG3) { const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x200, // Block number 1 Instruction::RETURN_VOID, // Block number 2 @@ -126,7 +123,7 @@ TEST(OptimizerTest, CFG3) { TestCode(data3, dominators, sizeof(dominators) / sizeof(int)); } -TEST(OptimizerTest, CFG4) { +TEST_F(OptimizerTest, CFG4) { const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( Instruction::NOP, Instruction::GOTO | 0xFF00); @@ -146,7 +143,7 @@ TEST(OptimizerTest, CFG4) { TestCode(data2, dominators, sizeof(dominators) / sizeof(int)); } -TEST(OptimizerTest, CFG5) { +TEST_F(OptimizerTest, CFG5) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::RETURN_VOID, // Block number 1 Instruction::GOTO | 0x100, // Dead block @@ -163,7 +160,7 @@ TEST(OptimizerTest, CFG5) { TestCode(data, dominators, sizeof(dominators) / sizeof(int)); } -TEST(OptimizerTest, CFG6) { +TEST_F(OptimizerTest, CFG6) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, @@ -182,7 +179,7 @@ TEST(OptimizerTest, CFG6) { TestCode(data, dominators, sizeof(dominators) / sizeof(int)); } -TEST(OptimizerTest, CFG7) { +TEST_F(OptimizerTest, CFG7) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, // Block number 1 @@ -202,7 +199,7 @@ TEST(OptimizerTest, CFG7) { TestCode(data, dominators, sizeof(dominators) / sizeof(int)); } -TEST(OptimizerTest, CFG8) { +TEST_F(OptimizerTest, CFG8) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, // Block number 1 @@ -223,7 +220,7 @@ TEST(OptimizerTest, CFG8) { TestCode(data, dominators, sizeof(dominators) / sizeof(int)); } -TEST(OptimizerTest, CFG9) { +TEST_F(OptimizerTest, CFG9) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, // Block number 1 @@ -244,7 +241,7 @@ TEST(OptimizerTest, CFG9) { TestCode(data, dominators, sizeof(dominators) / sizeof(int)); } -TEST(OptimizerTest, CFG10) { +TEST_F(OptimizerTest, CFG10) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 6, // Block number 1 diff --git a/compiler/optimizing/find_loops_test.cc b/compiler/optimizing/find_loops_test.cc index 4770fa2eca..04789d9a2d 100644 --- a/compiler/optimizing/find_loops_test.cc +++ b/compiler/optimizing/find_loops_test.cc @@ -27,16 +27,9 @@ namespace art { -static HGraph* TestCode(const uint16_t* data, ArenaAllocator* allocator) { - HGraph* graph = CreateGraph(allocator); - HGraphBuilder builder(graph); - const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); - builder.BuildGraph(*item); - graph->BuildDominatorTree(); - return graph; -} +class FindLoopsTest : public CommonCompilerTest {}; -TEST(FindLoopsTest, CFG1) { +TEST_F(FindLoopsTest, CFG1) { // Constant is not used. const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, @@ -44,26 +37,26 @@ TEST(FindLoopsTest, CFG1) { ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); for (HBasicBlock* block : graph->GetBlocks()) { ASSERT_EQ(block->GetLoopInformation(), nullptr); } } -TEST(FindLoopsTest, CFG2) { +TEST_F(FindLoopsTest, CFG2) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::RETURN); ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); for (HBasicBlock* block : graph->GetBlocks()) { ASSERT_EQ(block->GetLoopInformation(), nullptr); } } -TEST(FindLoopsTest, CFG3) { +TEST_F(FindLoopsTest, CFG3) { const uint16_t data[] = TWO_REGISTERS_CODE_ITEM( Instruction::CONST_4 | 3 << 12 | 0, Instruction::CONST_4 | 4 << 12 | 1 << 8, @@ -73,13 +66,13 @@ TEST(FindLoopsTest, CFG3) { ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); for (HBasicBlock* block : graph->GetBlocks()) { ASSERT_EQ(block->GetLoopInformation(), nullptr); } } -TEST(FindLoopsTest, CFG4) { +TEST_F(FindLoopsTest, CFG4) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 4, @@ -90,13 +83,13 @@ TEST(FindLoopsTest, CFG4) { ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); for (HBasicBlock* block : graph->GetBlocks()) { ASSERT_EQ(block->GetLoopInformation(), nullptr); } } -TEST(FindLoopsTest, CFG5) { +TEST_F(FindLoopsTest, CFG5) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, @@ -105,7 +98,7 @@ TEST(FindLoopsTest, CFG5) { ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); for (HBasicBlock* block : graph->GetBlocks()) { ASSERT_EQ(block->GetLoopInformation(), nullptr); } @@ -137,7 +130,7 @@ static void TestBlock(HGraph* graph, } } -TEST(FindLoopsTest, Loop1) { +TEST_F(FindLoopsTest, Loop1) { // Simple loop with one preheader and one back edge. // var a = 0; // while (a == a) { @@ -151,7 +144,7 @@ TEST(FindLoopsTest, Loop1) { ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // pre header @@ -162,7 +155,7 @@ TEST(FindLoopsTest, Loop1) { TestBlock(graph, 5, false, kInvalidBlockId); // exit block } -TEST(FindLoopsTest, Loop2) { +TEST_F(FindLoopsTest, Loop2) { // Make sure we support a preheader of a loop not being the first predecessor // in the predecessor list of the header. // var a = 0; @@ -179,7 +172,7 @@ TEST(FindLoopsTest, Loop2) { ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // goto block @@ -191,7 +184,7 @@ TEST(FindLoopsTest, Loop2) { TestBlock(graph, 6, false, kInvalidBlockId); // exit block } -TEST(FindLoopsTest, Loop3) { +TEST_F(FindLoopsTest, Loop3) { // Make sure we create a preheader of a loop when a header originally has two // incoming blocks and one back edge. const uint16_t data[] = ONE_REGISTER_CODE_ITEM( @@ -204,7 +197,7 @@ TEST(FindLoopsTest, Loop3) { ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // goto block @@ -218,7 +211,7 @@ TEST(FindLoopsTest, Loop3) { TestBlock(graph, 8, false, kInvalidBlockId); // synthesized pre header } -TEST(FindLoopsTest, Loop4) { +TEST_F(FindLoopsTest, Loop4) { // Test loop with originally two back edges. const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, @@ -230,7 +223,7 @@ TEST(FindLoopsTest, Loop4) { ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // pre header @@ -244,7 +237,7 @@ TEST(FindLoopsTest, Loop4) { } -TEST(FindLoopsTest, Loop5) { +TEST_F(FindLoopsTest, Loop5) { // Test loop with two exit edges. const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, @@ -256,7 +249,7 @@ TEST(FindLoopsTest, Loop5) { ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // pre header @@ -270,7 +263,7 @@ TEST(FindLoopsTest, Loop5) { TestBlock(graph, 8, false, kInvalidBlockId); // synthesized block at the loop exit } -TEST(FindLoopsTest, InnerLoop) { +TEST_F(FindLoopsTest, InnerLoop) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 6, @@ -281,7 +274,7 @@ TEST(FindLoopsTest, InnerLoop) { ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // pre header of outer loop @@ -301,7 +294,7 @@ TEST(FindLoopsTest, InnerLoop) { *graph->GetBlocks()[3]->GetLoopInformation())); } -TEST(FindLoopsTest, TwoLoops) { +TEST_F(FindLoopsTest, TwoLoops) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, @@ -312,7 +305,7 @@ TEST(FindLoopsTest, TwoLoops) { ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // pre header of first loop @@ -331,7 +324,7 @@ TEST(FindLoopsTest, TwoLoops) { *graph->GetBlocks()[4]->GetLoopInformation())); } -TEST(FindLoopsTest, NonNaturalLoop) { +TEST_F(FindLoopsTest, NonNaturalLoop) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, @@ -342,14 +335,14 @@ TEST(FindLoopsTest, NonNaturalLoop) { ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); ASSERT_TRUE(graph->GetBlocks()[3]->IsLoopHeader()); HLoopInformation* info = graph->GetBlocks()[3]->GetLoopInformation(); ASSERT_EQ(1u, info->NumberOfBackEdges()); ASSERT_FALSE(info->GetHeader()->Dominates(info->GetBackEdges()[0])); } -TEST(FindLoopsTest, DoWhileLoop) { +TEST_F(FindLoopsTest, DoWhileLoop) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::GOTO | 0x0100, @@ -358,7 +351,7 @@ TEST(FindLoopsTest, DoWhileLoop) { ArenaPool arena; ArenaAllocator allocator(&arena); - HGraph* graph = TestCode(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); TestBlock(graph, 0, false, kInvalidBlockId); // entry block TestBlock(graph, 1, false, kInvalidBlockId); // pre header of first loop diff --git a/compiler/optimizing/graph_checker.cc b/compiler/optimizing/graph_checker.cc index 962e77dfc9..e6e9177841 100644 --- a/compiler/optimizing/graph_checker.cc +++ b/compiler/optimizing/graph_checker.cc @@ -149,6 +149,103 @@ void GraphChecker::VisitBasicBlock(HBasicBlock* block) { } current->Accept(this); } + + // Ensure that catch blocks are not normal successors, and normal blocks are + // never exceptional successors. + for (HBasicBlock* successor : block->GetNormalSuccessors()) { + if (successor->IsCatchBlock()) { + AddError(StringPrintf("Catch block %d is a normal successor of block %d.", + successor->GetBlockId(), + block->GetBlockId())); + } + } + for (HBasicBlock* successor : block->GetExceptionalSuccessors()) { + if (!successor->IsCatchBlock()) { + AddError(StringPrintf("Normal block %d is an exceptional successor of block %d.", + successor->GetBlockId(), + block->GetBlockId())); + } + } + + // Ensure dominated blocks have `block` as the dominator. + for (HBasicBlock* dominated : block->GetDominatedBlocks()) { + if (dominated->GetDominator() != block) { + AddError(StringPrintf("Block %d should be the dominator of %d.", + block->GetBlockId(), + dominated->GetBlockId())); + } + } + + // Ensure there is no critical edge (i.e., an edge connecting a + // block with multiple successors to a block with multiple + // predecessors). Exceptional edges are synthesized and hence + // not accounted for. + if (block->GetSuccessors().size() > 1) { + for (HBasicBlock* successor : block->GetNormalSuccessors()) { + if (successor->IsExitBlock() && + block->IsSingleTryBoundary() && + block->GetPredecessors().size() == 1u && + block->GetSinglePredecessor()->GetLastInstruction()->IsThrow()) { + // Allowed critical edge Throw->TryBoundary->Exit. + } else if (successor->GetPredecessors().size() > 1) { + AddError(StringPrintf("Critical edge between blocks %d and %d.", + block->GetBlockId(), + successor->GetBlockId())); + } + } + } + + // Ensure try membership information is consistent. + if (block->IsCatchBlock()) { + if (block->IsTryBlock()) { + const HTryBoundary& try_entry = block->GetTryCatchInformation()->GetTryEntry(); + AddError(StringPrintf("Catch blocks should not be try blocks but catch block %d " + "has try entry %s:%d.", + block->GetBlockId(), + try_entry.DebugName(), + try_entry.GetId())); + } + + if (block->IsLoopHeader()) { + AddError(StringPrintf("Catch blocks should not be loop headers but catch block %d is.", + block->GetBlockId())); + } + } else { + for (HBasicBlock* predecessor : block->GetPredecessors()) { + const HTryBoundary* incoming_try_entry = predecessor->ComputeTryEntryOfSuccessors(); + if (block->IsTryBlock()) { + const HTryBoundary& stored_try_entry = block->GetTryCatchInformation()->GetTryEntry(); + if (incoming_try_entry == nullptr) { + AddError(StringPrintf("Block %d has try entry %s:%d but no try entry follows " + "from predecessor %d.", + block->GetBlockId(), + stored_try_entry.DebugName(), + stored_try_entry.GetId(), + predecessor->GetBlockId())); + } else if (!incoming_try_entry->HasSameExceptionHandlersAs(stored_try_entry)) { + AddError(StringPrintf("Block %d has try entry %s:%d which is not consistent " + "with %s:%d that follows from predecessor %d.", + block->GetBlockId(), + stored_try_entry.DebugName(), + stored_try_entry.GetId(), + incoming_try_entry->DebugName(), + incoming_try_entry->GetId(), + predecessor->GetBlockId())); + } + } else if (incoming_try_entry != nullptr) { + AddError(StringPrintf("Block %d is not a try block but try entry %s:%d follows " + "from predecessor %d.", + block->GetBlockId(), + incoming_try_entry->DebugName(), + incoming_try_entry->GetId(), + predecessor->GetBlockId())); + } + } + } + + if (block->IsLoopHeader()) { + HandleLoop(block); + } } void GraphChecker::VisitBoundsCheck(HBoundsCheck* check) { @@ -168,7 +265,7 @@ void GraphChecker::VisitTryBoundary(HTryBoundary* try_boundary) { // Ensure that all exception handlers are catch blocks. // Note that a normal-flow successor may be a catch block before CFG - // simplification. We only test normal-flow successors in SsaChecker. + // simplification. We only test normal-flow successors in GraphChecker. for (HBasicBlock* handler : handlers) { if (!handler->IsCatchBlock()) { AddError(StringPrintf("Block %d with %s:%d has exceptional successor %d which " @@ -303,6 +400,88 @@ void GraphChecker::VisitInstruction(HInstruction* instruction) { input->GetId())); } } + + // Ensure an instruction dominates all its uses. + for (HUseIterator<HInstruction*> use_it(instruction->GetUses()); + !use_it.Done(); use_it.Advance()) { + HInstruction* use = use_it.Current()->GetUser(); + if (!use->IsPhi() && !instruction->StrictlyDominates(use)) { + AddError(StringPrintf("Instruction %s:%d in block %d does not dominate " + "use %s:%d in block %d.", + instruction->DebugName(), + instruction->GetId(), + current_block_->GetBlockId(), + use->DebugName(), + use->GetId(), + use->GetBlock()->GetBlockId())); + } + } + + if (instruction->NeedsEnvironment() && !instruction->HasEnvironment()) { + AddError(StringPrintf("Instruction %s:%d in block %d requires an environment " + "but does not have one.", + instruction->DebugName(), + instruction->GetId(), + current_block_->GetBlockId())); + } + + // Ensure an instruction having an environment is dominated by the + // instructions contained in the environment. + for (HEnvironment* environment = instruction->GetEnvironment(); + environment != nullptr; + environment = environment->GetParent()) { + for (size_t i = 0, e = environment->Size(); i < e; ++i) { + HInstruction* env_instruction = environment->GetInstructionAt(i); + if (env_instruction != nullptr + && !env_instruction->StrictlyDominates(instruction)) { + AddError(StringPrintf("Instruction %d in environment of instruction %d " + "from block %d does not dominate instruction %d.", + env_instruction->GetId(), + instruction->GetId(), + current_block_->GetBlockId(), + instruction->GetId())); + } + } + } + + // Ensure that reference type instructions have reference type info. + if (instruction->GetType() == Primitive::kPrimNot) { + ScopedObjectAccess soa(Thread::Current()); + if (!instruction->GetReferenceTypeInfo().IsValid()) { + AddError(StringPrintf("Reference type instruction %s:%d does not have " + "valid reference type information.", + instruction->DebugName(), + instruction->GetId())); + } + } + + if (instruction->CanThrowIntoCatchBlock()) { + // Find the top-level environment. This corresponds to the environment of + // the catch block since we do not inline methods with try/catch. + HEnvironment* environment = instruction->GetEnvironment(); + while (environment->GetParent() != nullptr) { + environment = environment->GetParent(); + } + + // Find all catch blocks and test that `instruction` has an environment + // value for each one. + const HTryBoundary& entry = instruction->GetBlock()->GetTryCatchInformation()->GetTryEntry(); + for (HBasicBlock* catch_block : entry.GetExceptionHandlers()) { + for (HInstructionIterator phi_it(catch_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) { + HPhi* catch_phi = phi_it.Current()->AsPhi(); + if (environment->GetInstructionAt(catch_phi->GetRegNumber()) == nullptr) { + AddError(StringPrintf("Instruction %s:%d throws into catch block %d " + "with catch phi %d for vreg %d but its " + "corresponding environment slot is empty.", + instruction->DebugName(), + instruction->GetId(), + catch_block->GetBlockId(), + catch_phi->GetId(), + catch_phi->GetRegNumber())); + } + } + } + } } void GraphChecker::VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) { @@ -371,108 +550,7 @@ void GraphChecker::VisitInstanceOf(HInstanceOf* instruction) { } } -void SSAChecker::VisitBasicBlock(HBasicBlock* block) { - super_type::VisitBasicBlock(block); - - // Ensure that catch blocks are not normal successors, and normal blocks are - // never exceptional successors. - for (HBasicBlock* successor : block->GetNormalSuccessors()) { - if (successor->IsCatchBlock()) { - AddError(StringPrintf("Catch block %d is a normal successor of block %d.", - successor->GetBlockId(), - block->GetBlockId())); - } - } - for (HBasicBlock* successor : block->GetExceptionalSuccessors()) { - if (!successor->IsCatchBlock()) { - AddError(StringPrintf("Normal block %d is an exceptional successor of block %d.", - successor->GetBlockId(), - block->GetBlockId())); - } - } - - // Ensure dominated blocks have `block` as the dominator. - for (HBasicBlock* dominated : block->GetDominatedBlocks()) { - if (dominated->GetDominator() != block) { - AddError(StringPrintf("Block %d should be the dominator of %d.", - block->GetBlockId(), - dominated->GetBlockId())); - } - } - - // Ensure there is no critical edge (i.e., an edge connecting a - // block with multiple successors to a block with multiple - // predecessors). Exceptional edges are synthesized and hence - // not accounted for. - if (block->GetSuccessors().size() > 1) { - for (HBasicBlock* successor : block->GetNormalSuccessors()) { - if (successor->IsExitBlock() && - block->IsSingleTryBoundary() && - block->GetPredecessors().size() == 1u && - block->GetSinglePredecessor()->GetLastInstruction()->IsThrow()) { - // Allowed critical edge Throw->TryBoundary->Exit. - } else if (successor->GetPredecessors().size() > 1) { - AddError(StringPrintf("Critical edge between blocks %d and %d.", - block->GetBlockId(), - successor->GetBlockId())); - } - } - } - - // Ensure try membership information is consistent. - if (block->IsCatchBlock()) { - if (block->IsTryBlock()) { - const HTryBoundary& try_entry = block->GetTryCatchInformation()->GetTryEntry(); - AddError(StringPrintf("Catch blocks should not be try blocks but catch block %d " - "has try entry %s:%d.", - block->GetBlockId(), - try_entry.DebugName(), - try_entry.GetId())); - } - - if (block->IsLoopHeader()) { - AddError(StringPrintf("Catch blocks should not be loop headers but catch block %d is.", - block->GetBlockId())); - } - } else { - for (HBasicBlock* predecessor : block->GetPredecessors()) { - const HTryBoundary* incoming_try_entry = predecessor->ComputeTryEntryOfSuccessors(); - if (block->IsTryBlock()) { - const HTryBoundary& stored_try_entry = block->GetTryCatchInformation()->GetTryEntry(); - if (incoming_try_entry == nullptr) { - AddError(StringPrintf("Block %d has try entry %s:%d but no try entry follows " - "from predecessor %d.", - block->GetBlockId(), - stored_try_entry.DebugName(), - stored_try_entry.GetId(), - predecessor->GetBlockId())); - } else if (!incoming_try_entry->HasSameExceptionHandlersAs(stored_try_entry)) { - AddError(StringPrintf("Block %d has try entry %s:%d which is not consistent " - "with %s:%d that follows from predecessor %d.", - block->GetBlockId(), - stored_try_entry.DebugName(), - stored_try_entry.GetId(), - incoming_try_entry->DebugName(), - incoming_try_entry->GetId(), - predecessor->GetBlockId())); - } - } else if (incoming_try_entry != nullptr) { - AddError(StringPrintf("Block %d is not a try block but try entry %s:%d follows " - "from predecessor %d.", - block->GetBlockId(), - incoming_try_entry->DebugName(), - incoming_try_entry->GetId(), - predecessor->GetBlockId())); - } - } - } - - if (block->IsLoopHeader()) { - CheckLoop(block); - } -} - -void SSAChecker::CheckLoop(HBasicBlock* loop_header) { +void GraphChecker::HandleLoop(HBasicBlock* loop_header) { int id = loop_header->GetBlockId(); HLoopInformation* loop_information = loop_header->GetLoopInformation(); @@ -582,92 +660,6 @@ void SSAChecker::CheckLoop(HBasicBlock* loop_header) { } } -void SSAChecker::VisitInstruction(HInstruction* instruction) { - super_type::VisitInstruction(instruction); - - // Ensure an instruction dominates all its uses. - for (HUseIterator<HInstruction*> use_it(instruction->GetUses()); - !use_it.Done(); use_it.Advance()) { - HInstruction* use = use_it.Current()->GetUser(); - if (!use->IsPhi() && !instruction->StrictlyDominates(use)) { - AddError(StringPrintf("Instruction %s:%d in block %d does not dominate " - "use %s:%d in block %d.", - instruction->DebugName(), - instruction->GetId(), - current_block_->GetBlockId(), - use->DebugName(), - use->GetId(), - use->GetBlock()->GetBlockId())); - } - } - - if (instruction->NeedsEnvironment() && !instruction->HasEnvironment()) { - AddError(StringPrintf("Instruction %s:%d in block %d requires an environment " - "but does not have one.", - instruction->DebugName(), - instruction->GetId(), - current_block_->GetBlockId())); - } - - // Ensure an instruction having an environment is dominated by the - // instructions contained in the environment. - for (HEnvironment* environment = instruction->GetEnvironment(); - environment != nullptr; - environment = environment->GetParent()) { - for (size_t i = 0, e = environment->Size(); i < e; ++i) { - HInstruction* env_instruction = environment->GetInstructionAt(i); - if (env_instruction != nullptr - && !env_instruction->StrictlyDominates(instruction)) { - AddError(StringPrintf("Instruction %d in environment of instruction %d " - "from block %d does not dominate instruction %d.", - env_instruction->GetId(), - instruction->GetId(), - current_block_->GetBlockId(), - instruction->GetId())); - } - } - } - - // Ensure that reference type instructions have reference type info. - if (instruction->GetType() == Primitive::kPrimNot) { - ScopedObjectAccess soa(Thread::Current()); - if (!instruction->GetReferenceTypeInfo().IsValid()) { - AddError(StringPrintf("Reference type instruction %s:%d does not have " - "valid reference type information.", - instruction->DebugName(), - instruction->GetId())); - } - } - - if (instruction->CanThrowIntoCatchBlock()) { - // Find the top-level environment. This corresponds to the environment of - // the catch block since we do not inline methods with try/catch. - HEnvironment* environment = instruction->GetEnvironment(); - while (environment->GetParent() != nullptr) { - environment = environment->GetParent(); - } - - // Find all catch blocks and test that `instruction` has an environment - // value for each one. - const HTryBoundary& entry = instruction->GetBlock()->GetTryCatchInformation()->GetTryEntry(); - for (HBasicBlock* catch_block : entry.GetExceptionHandlers()) { - for (HInstructionIterator phi_it(catch_block->GetPhis()); !phi_it.Done(); phi_it.Advance()) { - HPhi* catch_phi = phi_it.Current()->AsPhi(); - if (environment->GetInstructionAt(catch_phi->GetRegNumber()) == nullptr) { - AddError(StringPrintf("Instruction %s:%d throws into catch block %d " - "with catch phi %d for vreg %d but its " - "corresponding environment slot is empty.", - instruction->DebugName(), - instruction->GetId(), - catch_block->GetBlockId(), - catch_phi->GetId(), - catch_phi->GetRegNumber())); - } - } - } - } -} - static Primitive::Type PrimitiveKind(Primitive::Type type) { switch (type) { case Primitive::kPrimBoolean: @@ -710,7 +702,7 @@ static bool IsConstantEquivalent(HInstruction* insn1, HInstruction* insn2, BitVe } } -void SSAChecker::VisitPhi(HPhi* phi) { +void GraphChecker::VisitPhi(HPhi* phi) { VisitInstruction(phi); // Ensure the first input of a phi is not itself. @@ -846,7 +838,7 @@ void SSAChecker::VisitPhi(HPhi* phi) { } } -void SSAChecker::HandleBooleanInput(HInstruction* instruction, size_t input_index) { +void GraphChecker::HandleBooleanInput(HInstruction* instruction, size_t input_index) { HInstruction* input = instruction->InputAt(input_index); if (input->IsIntConstant()) { int32_t value = input->AsIntConstant()->GetValue(); @@ -876,7 +868,7 @@ void SSAChecker::HandleBooleanInput(HInstruction* instruction, size_t input_inde } } -void SSAChecker::VisitPackedSwitch(HPackedSwitch* instruction) { +void GraphChecker::VisitPackedSwitch(HPackedSwitch* instruction) { VisitInstruction(instruction); // Check that the number of block successors matches the switch count plus // one for the default block. @@ -892,22 +884,22 @@ void SSAChecker::VisitPackedSwitch(HPackedSwitch* instruction) { } } -void SSAChecker::VisitIf(HIf* instruction) { +void GraphChecker::VisitIf(HIf* instruction) { VisitInstruction(instruction); HandleBooleanInput(instruction, 0); } -void SSAChecker::VisitSelect(HSelect* instruction) { +void GraphChecker::VisitSelect(HSelect* instruction) { VisitInstruction(instruction); HandleBooleanInput(instruction, 2); } -void SSAChecker::VisitBooleanNot(HBooleanNot* instruction) { +void GraphChecker::VisitBooleanNot(HBooleanNot* instruction) { VisitInstruction(instruction); HandleBooleanInput(instruction, 0); } -void SSAChecker::VisitCondition(HCondition* op) { +void GraphChecker::VisitCondition(HCondition* op) { VisitInstruction(op); if (op->GetType() != Primitive::kPrimBoolean) { AddError(StringPrintf( @@ -937,7 +929,7 @@ void SSAChecker::VisitCondition(HCondition* op) { } } -void SSAChecker::VisitBinaryOperation(HBinaryOperation* op) { +void GraphChecker::VisitBinaryOperation(HBinaryOperation* op) { VisitInstruction(op); if (op->IsUShr() || op->IsShr() || op->IsShl() || op->IsRor()) { if (PrimitiveKind(op->InputAt(1)->GetType()) != Primitive::kPrimInt) { @@ -979,7 +971,7 @@ void SSAChecker::VisitBinaryOperation(HBinaryOperation* op) { } } -void SSAChecker::VisitConstant(HConstant* instruction) { +void GraphChecker::VisitConstant(HConstant* instruction) { HBasicBlock* block = instruction->GetBlock(); if (!block->IsEntryBlock()) { AddError(StringPrintf( @@ -990,7 +982,7 @@ void SSAChecker::VisitConstant(HConstant* instruction) { } } -void SSAChecker::VisitBoundType(HBoundType* instruction) { +void GraphChecker::VisitBoundType(HBoundType* instruction) { VisitInstruction(instruction); ScopedObjectAccess soa(Thread::Current()); diff --git a/compiler/optimizing/graph_checker.h b/compiler/optimizing/graph_checker.h index 8724cde5dd..52252cd3d4 100644 --- a/compiler/optimizing/graph_checker.h +++ b/compiler/optimizing/graph_checker.h @@ -32,34 +32,38 @@ class GraphChecker : public HGraphDelegateVisitor { dump_prefix_(dump_prefix), seen_ids_(graph->GetArena(), graph->GetCurrentInstructionId(), false) {} - // Check the whole graph (in insertion order). - virtual void Run() { VisitInsertionOrder(); } + // Check the whole graph (in reverse post-order). + void Run() { + // VisitReversePostOrder is used instead of VisitInsertionOrder, + // as the latter might visit dead blocks removed by the dominator + // computation. + VisitReversePostOrder(); + } - // Check `block`. void VisitBasicBlock(HBasicBlock* block) OVERRIDE; - // Check `instruction`. void VisitInstruction(HInstruction* instruction) OVERRIDE; + void VisitPhi(HPhi* phi) OVERRIDE; - // Perform control-flow graph checks on instruction. - void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE; - - // Check that the HasBoundsChecks() flag is set for bounds checks. + void VisitBinaryOperation(HBinaryOperation* op) OVERRIDE; + void VisitBooleanNot(HBooleanNot* instruction) OVERRIDE; + void VisitBoundType(HBoundType* instruction) OVERRIDE; void VisitBoundsCheck(HBoundsCheck* check) OVERRIDE; - - // Check successors of blocks ending in TryBoundary. - void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE; - - // Check that LoadException is the first instruction in a catch block. - void VisitLoadException(HLoadException* load) OVERRIDE; - - // Check that HCheckCast and HInstanceOf have HLoadClass as second input. void VisitCheckCast(HCheckCast* check) OVERRIDE; + void VisitCondition(HCondition* op) OVERRIDE; + void VisitConstant(HConstant* instruction) OVERRIDE; + void VisitIf(HIf* instruction) OVERRIDE; void VisitInstanceOf(HInstanceOf* check) OVERRIDE; - - // Check that the Return and ReturnVoid jump to the exit block. + void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE; + void VisitLoadException(HLoadException* load) OVERRIDE; + void VisitPackedSwitch(HPackedSwitch* instruction) OVERRIDE; void VisitReturn(HReturn* ret) OVERRIDE; void VisitReturnVoid(HReturnVoid* ret) OVERRIDE; + void VisitSelect(HSelect* instruction) OVERRIDE; + void VisitTryBoundary(HTryBoundary* try_boundary) OVERRIDE; + + void HandleLoop(HBasicBlock* loop_header); + void HandleBooleanInput(HInstruction* instruction, size_t input_index); // Was the last visit of the graph valid? bool IsValid() const { @@ -97,46 +101,6 @@ class GraphChecker : public HGraphDelegateVisitor { DISALLOW_COPY_AND_ASSIGN(GraphChecker); }; - -// An SSA graph visitor performing various checks. -class SSAChecker : public GraphChecker { - public: - typedef GraphChecker super_type; - - explicit SSAChecker(HGraph* graph) - : GraphChecker(graph, "art::SSAChecker: ") {} - - // Check the whole graph (in reverse post-order). - void Run() OVERRIDE { - // VisitReversePostOrder is used instead of VisitInsertionOrder, - // as the latter might visit dead blocks removed by the dominator - // computation. - VisitReversePostOrder(); - } - - // Perform SSA form checks on `block`. - void VisitBasicBlock(HBasicBlock* block) OVERRIDE; - // Loop-related checks from block `loop_header`. - void CheckLoop(HBasicBlock* loop_header); - - // Perform SSA form checks on instructions. - void VisitInstruction(HInstruction* instruction) OVERRIDE; - void VisitPhi(HPhi* phi) OVERRIDE; - void VisitBinaryOperation(HBinaryOperation* op) OVERRIDE; - void VisitCondition(HCondition* op) OVERRIDE; - void VisitIf(HIf* instruction) OVERRIDE; - void VisitPackedSwitch(HPackedSwitch* instruction) OVERRIDE; - void VisitSelect(HSelect* instruction) OVERRIDE; - void VisitBooleanNot(HBooleanNot* instruction) OVERRIDE; - void VisitConstant(HConstant* instruction) OVERRIDE; - void VisitBoundType(HBoundType* instruction) OVERRIDE; - - void HandleBooleanInput(HInstruction* instruction, size_t input_index); - - private: - DISALLOW_COPY_AND_ASSIGN(SSAChecker); -}; - } // namespace art #endif // ART_COMPILER_OPTIMIZING_GRAPH_CHECKER_H_ diff --git a/compiler/optimizing/graph_checker_test.cc b/compiler/optimizing/graph_checker_test.cc index d10df4ce3f..659141fbf4 100644 --- a/compiler/optimizing/graph_checker_test.cc +++ b/compiler/optimizing/graph_checker_test.cc @@ -52,28 +52,16 @@ static void TestCode(const uint16_t* data) { ASSERT_TRUE(graph_checker.IsValid()); } -static void TestCodeSSA(const uint16_t* data) { - ArenaPool pool; - ArenaAllocator allocator(&pool); - HGraph* graph = CreateCFG(&allocator, data); - ASSERT_NE(graph, nullptr); - - TransformToSsa(graph); +class GraphCheckerTest : public CommonCompilerTest {}; - SSAChecker ssa_checker(graph); - ssa_checker.Run(); - ASSERT_TRUE(ssa_checker.IsValid()); -} - - -TEST(GraphChecker, ReturnVoid) { +TEST_F(GraphCheckerTest, ReturnVoid) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::RETURN_VOID); TestCode(data); } -TEST(GraphChecker, CFG1) { +TEST_F(GraphCheckerTest, CFG1) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x100, Instruction::RETURN_VOID); @@ -81,7 +69,7 @@ TEST(GraphChecker, CFG1) { TestCode(data); } -TEST(GraphChecker, CFG2) { +TEST_F(GraphCheckerTest, CFG2) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, @@ -91,7 +79,7 @@ TEST(GraphChecker, CFG2) { TestCode(data); } -TEST(GraphChecker, CFG3) { +TEST_F(GraphCheckerTest, CFG3) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, @@ -103,7 +91,7 @@ TEST(GraphChecker, CFG3) { // Test case with an invalid graph containing inconsistent // predecessor/successor arcs in CFG. -TEST(GraphChecker, InconsistentPredecessorsAndSuccessors) { +TEST_F(GraphCheckerTest, InconsistentPredecessorsAndSuccessors) { ArenaPool pool; ArenaAllocator allocator(&pool); @@ -121,7 +109,7 @@ TEST(GraphChecker, InconsistentPredecessorsAndSuccessors) { // Test case with an invalid graph containing a non-branch last // instruction in a block. -TEST(GraphChecker, BlockEndingWithNonBranchInstruction) { +TEST_F(GraphCheckerTest, BlockEndingWithNonBranchInstruction) { ArenaPool pool; ArenaAllocator allocator(&pool); @@ -141,9 +129,7 @@ TEST(GraphChecker, BlockEndingWithNonBranchInstruction) { ASSERT_FALSE(graph_checker.IsValid()); } -class SSACheckerTest : public CommonCompilerTest {}; - -TEST_F(SSACheckerTest, SSAPhi) { +TEST_F(GraphCheckerTest, SSAPhi) { // This code creates one Phi function during the conversion to SSA form. const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, @@ -151,7 +137,7 @@ TEST_F(SSACheckerTest, SSAPhi) { Instruction::CONST_4 | 4 << 12 | 0, Instruction::RETURN | 0 << 8); - TestCodeSSA(data); + TestCode(data); } } // namespace art diff --git a/compiler/optimizing/graph_visualizer.cc b/compiler/optimizing/graph_visualizer.cc index 63b8fd05c0..4cf0eb1565 100644 --- a/compiler/optimizing/graph_visualizer.cc +++ b/compiler/optimizing/graph_visualizer.cc @@ -22,6 +22,7 @@ #include <sstream> #include "bounds_check_elimination.h" +#include "builder.h" #include "code_generator.h" #include "dead_code_elimination.h" #include "disassembler.h" @@ -31,7 +32,6 @@ #include "optimization.h" #include "reference_type_propagation.h" #include "register_allocator.h" -#include "ssa_builder.h" #include "ssa_liveness_analysis.h" #include "utils/assembler.h" @@ -510,7 +510,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { || IsPass(HDeadCodeElimination::kInitialDeadCodeEliminationPassName) || IsPass(BoundsCheckElimination::kBoundsCheckEliminationPassName) || IsPass(RegisterAllocator::kRegisterAllocatorPassName) - || IsPass(SsaBuilder::kSsaBuilderPassName)) { + || IsPass(HGraphBuilder::kBuilderPassName)) { HLoopInformation* info = instruction->GetBlock()->GetLoopInformation(); if (info == nullptr) { StartAttributeStream("loop") << "none"; @@ -527,7 +527,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { } } - if ((IsPass(SsaBuilder::kSsaBuilderPassName) + if ((IsPass(HGraphBuilder::kBuilderPassName) || IsPass(HInliner::kInlinerPassName)) && (instruction->GetType() == Primitive::kPrimNot)) { ReferenceTypeInfo info = instruction->IsLoadClass() @@ -547,7 +547,7 @@ class HGraphVisualizerPrinter : public HGraphDelegateVisitor { // doesn't run or doesn't inline anything, the NullConstant remains untyped. // So we should check NullConstants for validity only after reference type propagation. DCHECK(graph_in_bad_state_ || - (!is_after_pass_ && IsPass(SsaBuilder::kSsaBuilderPassName))) + (!is_after_pass_ && IsPass(HGraphBuilder::kBuilderPassName))) << instruction->DebugName() << instruction->GetId() << " has invalid rti " << (is_after_pass_ ? "after" : "before") << " pass " << pass_name_; } diff --git a/compiler/optimizing/gvn_test.cc b/compiler/optimizing/gvn_test.cc index 1f4eaf3cfd..56dc08826b 100644 --- a/compiler/optimizing/gvn_test.cc +++ b/compiler/optimizing/gvn_test.cc @@ -100,7 +100,7 @@ TEST_F(GVNTest, LocalFieldElimination) { ASSERT_EQ(different_offset->GetBlock(), block); ASSERT_EQ(use_after_kill->GetBlock(), block); - TransformToSsa(graph); + graph->BuildDominatorTree(); SideEffectsAnalysis side_effects(graph); side_effects.Run(); GVNOptimization(graph, side_effects).Run(); @@ -182,7 +182,7 @@ TEST_F(GVNTest, GlobalFieldElimination) { 0)); join->AddInstruction(new (&allocator) HExit()); - TransformToSsa(graph); + graph->BuildDominatorTree(); SideEffectsAnalysis side_effects(graph); side_effects.Run(); GVNOptimization(graph, side_effects).Run(); @@ -288,7 +288,7 @@ TEST_F(GVNTest, LoopFieldElimination) { ASSERT_EQ(field_get_in_loop_body->GetBlock(), loop_body); ASSERT_EQ(field_get_in_exit->GetBlock(), exit); - TransformToSsa(graph); + graph->BuildDominatorTree(); { SideEffectsAnalysis side_effects(graph); side_effects.Run(); @@ -364,7 +364,7 @@ TEST_F(GVNTest, LoopSideEffects) { inner_loop_exit->AddInstruction(new (&allocator) HGoto()); outer_loop_exit->AddInstruction(new (&allocator) HExit()); - TransformToSsa(graph); + graph->BuildDominatorTree(); ASSERT_TRUE(inner_loop_header->GetLoopInformation()->IsIn( *outer_loop_header->GetLoopInformation())); diff --git a/compiler/optimizing/induction_var_analysis_test.cc b/compiler/optimizing/induction_var_analysis_test.cc index 29a1845658..fd197bc7a6 100644 --- a/compiler/optimizing/induction_var_analysis_test.cc +++ b/compiler/optimizing/induction_var_analysis_test.cc @@ -86,39 +86,28 @@ class InductionVarAnalysisTest : public CommonCompilerTest { constant1_ = graph_->GetIntConstant(1); constant100_ = graph_->GetIntConstant(100); float_constant0_ = graph_->GetFloatConstant(0.0f); - induc_ = new (&allocator_) HLocal(n); - entry_->AddInstruction(induc_); - entry_->AddInstruction(new (&allocator_) HStoreLocal(induc_, constant0_)); - tmp_ = new (&allocator_) HLocal(n + 1); - entry_->AddInstruction(tmp_); - entry_->AddInstruction(new (&allocator_) HStoreLocal(tmp_, constant100_)); - dum_ = new (&allocator_) HLocal(n + 2); - entry_->AddInstruction(dum_); return_->AddInstruction(new (&allocator_) HReturnVoid()); exit_->AddInstruction(new (&allocator_) HExit()); // Provide loop instructions. for (int d = 0; d < n; d++) { - basic_[d] = new (&allocator_) HLocal(d); - entry_->AddInstruction(basic_[d]); - loop_preheader_[d]->AddInstruction(new (&allocator_) HStoreLocal(basic_[d], constant0_)); + basic_[d] = new (&allocator_) HPhi(&allocator_, d, 0, Primitive::kPrimInt); loop_preheader_[d]->AddInstruction(new (&allocator_) HGoto()); - HInstruction* load = new (&allocator_) HLoadLocal(basic_[d], Primitive::kPrimInt); - loop_header_[d]->AddInstruction(load); - HInstruction* compare = new (&allocator_) HLessThan(load, constant100_); + loop_header_[d]->AddPhi(basic_[d]); + HInstruction* compare = new (&allocator_) HLessThan(basic_[d], constant100_); loop_header_[d]->AddInstruction(compare); loop_header_[d]->AddInstruction(new (&allocator_) HIf(compare)); - load = new (&allocator_) HLoadLocal(basic_[d], Primitive::kPrimInt); - loop_body_[d]->AddInstruction(load); - increment_[d] = new (&allocator_) HAdd(Primitive::kPrimInt, load, constant1_); + increment_[d] = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[d], constant1_); loop_body_[d]->AddInstruction(increment_[d]); - loop_body_[d]->AddInstruction(new (&allocator_) HStoreLocal(basic_[d], increment_[d])); loop_body_[d]->AddInstruction(new (&allocator_) HGoto()); + + basic_[d]->AddInput(constant0_); + basic_[d]->AddInput(increment_[d]); } } // Builds if-statement at depth d. - void BuildIf(int d, HBasicBlock** ifT, HBasicBlock **ifF) { + HPhi* BuildIf(int d, HBasicBlock** ifT, HBasicBlock **ifF) { HBasicBlock* cond = new (&allocator_) HBasicBlock(graph_); HBasicBlock* ifTrue = new (&allocator_) HBasicBlock(graph_); HBasicBlock* ifFalse = new (&allocator_) HBasicBlock(graph_); @@ -134,6 +123,10 @@ class InductionVarAnalysisTest : public CommonCompilerTest { cond->AddInstruction(new (&allocator_) HIf(parameter_)); *ifT = ifTrue; *ifF = ifFalse; + + HPhi* select_phi = new (&allocator_) HPhi(&allocator_, -1, 0, Primitive::kPrimInt); + loop_body_[d]->AddPhi(select_phi); + return select_phi; } // Inserts instruction right before increment at depth d. @@ -142,25 +135,20 @@ class InductionVarAnalysisTest : public CommonCompilerTest { return instruction; } - // Inserts local load at depth d. - HInstruction* InsertLocalLoad(HLocal* local, int d) { - return InsertInstruction(new (&allocator_) HLoadLocal(local, Primitive::kPrimInt), d); - } - - // Inserts local store at depth d. - HInstruction* InsertLocalStore(HLocal* local, HInstruction* rhs, int d) { - return InsertInstruction(new (&allocator_) HStoreLocal(local, rhs), d); + // Inserts a phi to loop header at depth d and returns it. + HPhi* InsertLoopPhi(int vreg, int d) { + HPhi* phi = new (&allocator_) HPhi(&allocator_, vreg, 0, Primitive::kPrimInt); + loop_header_[d]->AddPhi(phi); + return phi; } - // Inserts an array store with given local as subscript at depth d to + // Inserts an array store with given `subscript` at depth d to // enable tests to inspect the computed induction at that point easily. - HInstruction* InsertArrayStore(HLocal* subscript, int d) { - HInstruction* load = InsertInstruction( - new (&allocator_) HLoadLocal(subscript, Primitive::kPrimInt), d); + HInstruction* InsertArrayStore(HInstruction* subscript, int d) { // ArraySet is given a float value in order to avoid SsaBuilder typing // it from the array's non-existent reference type info. return InsertInstruction(new (&allocator_) HArraySet( - parameter_, load, float_constant0_, Primitive::kPrimFloat, 0), d); + parameter_, subscript, float_constant0_, Primitive::kPrimFloat, 0), d); } // Returns induction information of instruction in loop at depth d. @@ -171,7 +159,7 @@ class InductionVarAnalysisTest : public CommonCompilerTest { // Performs InductionVarAnalysis (after proper set up). void PerformInductionVarAnalysis() { - TransformToSsa(graph_); + graph_->BuildDominatorTree(); iva_ = new (&allocator_) HInductionVarAnalysis(graph_); iva_->Run(); } @@ -191,16 +179,13 @@ class InductionVarAnalysisTest : public CommonCompilerTest { HInstruction* constant1_; HInstruction* constant100_; HInstruction* float_constant0_; - HLocal* induc_; // "vreg_n", the "k" - HLocal* tmp_; // "vreg_n+1" - HLocal* dum_; // "vreg_n+2" // Loop specifics. HBasicBlock* loop_preheader_[10]; HBasicBlock* loop_header_[10]; HBasicBlock* loop_body_[10]; HInstruction* increment_[10]; - HLocal* basic_[10]; // "vreg_d", the "i_d" + HPhi* basic_[10]; // "vreg_d", the "i_d" }; // @@ -216,7 +201,7 @@ TEST_F(InductionVarAnalysisTest, ProperLoopSetup) { // .. // } BuildLoopNest(10); - TransformToSsa(graph_); + graph_->BuildDominatorTree(); ASSERT_EQ(entry_->GetLoopInformation(), nullptr); for (int d = 0; d < 1; d++) { ASSERT_EQ(loop_preheader_[d]->GetLoopInformation(), @@ -258,20 +243,15 @@ TEST_F(InductionVarAnalysisTest, FindDerivedInduction) { // } BuildLoopNest(1); HInstruction *add = InsertInstruction( - new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0); - InsertLocalStore(induc_, add, 0); + new (&allocator_) HAdd(Primitive::kPrimInt, constant100_, basic_[0]), 0); HInstruction *sub = InsertInstruction( - new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0); - InsertLocalStore(induc_, sub, 0); + new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0); HInstruction *mul = InsertInstruction( - new (&allocator_) HMul(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0); - InsertLocalStore(induc_, mul, 0); + new (&allocator_) HMul(Primitive::kPrimInt, constant100_, basic_[0]), 0); HInstruction *shl = InsertInstruction( - new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0), constant1_), 0); - InsertLocalStore(induc_, shl, 0); + new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0); HInstruction *neg = InsertInstruction( - new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0)), 0); - InsertLocalStore(induc_, neg, 0); + new (&allocator_) HNeg(Primitive::kPrimInt, basic_[0]), 0); PerformInductionVarAnalysis(); EXPECT_STREQ("((1) * i + (100))", GetInductionInfo(add, 0).c_str()); @@ -291,14 +271,16 @@ TEST_F(InductionVarAnalysisTest, FindChainInduction) { // a[k] = 0; // } BuildLoopNest(1); + HPhi* k = InsertLoopPhi(0, 0); + k->AddInput(constant0_); + HInstruction *add = InsertInstruction( - new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0); - InsertLocalStore(induc_, add, 0); - HInstruction* store1 = InsertArrayStore(induc_, 0); + new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0); + HInstruction* store1 = InsertArrayStore(add, 0); HInstruction *sub = InsertInstruction( - new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0); - InsertLocalStore(induc_, sub, 0); - HInstruction* store2 = InsertArrayStore(induc_, 0); + new (&allocator_) HSub(Primitive::kPrimInt, add, constant1_), 0); + HInstruction* store2 = InsertArrayStore(sub, 0); + k->AddInput(sub); PerformInductionVarAnalysis(); EXPECT_STREQ("(((100) - (1)) * i + (100))", @@ -316,23 +298,24 @@ TEST_F(InductionVarAnalysisTest, FindTwoWayBasicInduction) { // a[k] = 0; // } BuildLoopNest(1); + HPhi* k_header = InsertLoopPhi(0, 0); + k_header->AddInput(constant0_); + HBasicBlock* ifTrue; HBasicBlock* ifFalse; - BuildIf(0, &ifTrue, &ifFalse); + HPhi* k_body = BuildIf(0, &ifTrue, &ifFalse); + // True-branch. - HInstruction* load1 = new (&allocator_) HLoadLocal(induc_, Primitive::kPrimInt); - ifTrue->AddInstruction(load1); - HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, load1, constant1_); + HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_); ifTrue->AddInstruction(inc1); - ifTrue->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc1)); + k_body->AddInput(inc1); // False-branch. - HInstruction* load2 = new (&allocator_) HLoadLocal(induc_, Primitive::kPrimInt); - ifFalse->AddInstruction(load2); - HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, load2, constant1_); + HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, k_header, constant1_); ifFalse->AddInstruction(inc2); - ifFalse->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc2)); + k_body->AddInput(inc2); // Merge over a phi. - HInstruction* store = InsertArrayStore(induc_, 0); + HInstruction* store = InsertArrayStore(k_body, 0); + k_header->AddInput(k_body); PerformInductionVarAnalysis(); EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str()); @@ -348,21 +331,18 @@ TEST_F(InductionVarAnalysisTest, FindTwoWayDerivedInduction) { BuildLoopNest(1); HBasicBlock* ifTrue; HBasicBlock* ifFalse; - BuildIf(0, &ifTrue, &ifFalse); + HPhi* k = BuildIf(0, &ifTrue, &ifFalse); + // True-branch. - HInstruction* load1 = new (&allocator_) HLoadLocal(basic_[0], Primitive::kPrimInt); - ifTrue->AddInstruction(load1); - HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, load1, constant1_); + HInstruction* inc1 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_); ifTrue->AddInstruction(inc1); - ifTrue->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc1)); + k->AddInput(inc1); // False-branch. - HInstruction* load2 = new (&allocator_) HLoadLocal(basic_[0], Primitive::kPrimInt); - ifFalse->AddInstruction(load2); - HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, load2, constant1_); + HInstruction* inc2 = new (&allocator_) HAdd(Primitive::kPrimInt, basic_[0], constant1_); ifFalse->AddInstruction(inc2); - ifFalse->AddInstruction(new (&allocator_) HStoreLocal(induc_, inc2)); + k->AddInput(inc2); // Merge over a phi. - HInstruction* store = InsertArrayStore(induc_, 0); + HInstruction* store = InsertArrayStore(k, 0); PerformInductionVarAnalysis(); EXPECT_STREQ("((1) * i + (1))", GetInductionInfo(store->InputAt(1), 0).c_str()); @@ -376,10 +356,13 @@ TEST_F(InductionVarAnalysisTest, FindFirstOrderWrapAroundInduction) { // k = 100 - i; // } BuildLoopNest(1); - HInstruction* store = InsertArrayStore(induc_, 0); + HPhi* k = InsertLoopPhi(0, 0); + k->AddInput(constant0_); + + HInstruction* store = InsertArrayStore(k, 0); HInstruction *sub = InsertInstruction( - new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0); - InsertLocalStore(induc_, sub, 0); + new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0]), 0); + k->AddInput(sub); PerformInductionVarAnalysis(); EXPECT_STREQ("wrap((0), (( - (1)) * i + (100)))", @@ -396,11 +379,16 @@ TEST_F(InductionVarAnalysisTest, FindSecondOrderWrapAroundInduction) { // t = 100 - i; // } BuildLoopNest(1); - HInstruction* store = InsertArrayStore(induc_, 0); - InsertLocalStore(induc_, InsertLocalLoad(tmp_, 0), 0); + HPhi* k = InsertLoopPhi(0, 0); + k->AddInput(constant0_); + HPhi* t = InsertLoopPhi(1, 0); + t->AddInput(constant100_); + + HInstruction* store = InsertArrayStore(k, 0); + k->AddInput(t); HInstruction *sub = InsertInstruction( - new (&allocator_) HSub(Primitive::kPrimInt, constant100_, InsertLocalLoad(basic_[0], 0)), 0); - InsertLocalStore(tmp_, sub, 0); + new (&allocator_) HSub(Primitive::kPrimInt, constant100_, basic_[0], 0), 0); + t->AddInput(sub); PerformInductionVarAnalysis(); EXPECT_STREQ("wrap((0), wrap((100), (( - (1)) * i + (100))))", @@ -419,26 +407,21 @@ TEST_F(InductionVarAnalysisTest, FindWrapAroundDerivedInduction) { // k = i << 1; // } BuildLoopNest(1); + HPhi* k = InsertLoopPhi(0, 0); + k->AddInput(constant0_); + HInstruction *add = InsertInstruction( - new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0); - InsertLocalStore(tmp_, add, 0); + new (&allocator_) HAdd(Primitive::kPrimInt, k, constant100_), 0); HInstruction *sub = InsertInstruction( - new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0); - InsertLocalStore(tmp_, sub, 0); + new (&allocator_) HSub(Primitive::kPrimInt, k, constant100_), 0); HInstruction *mul = InsertInstruction( - new (&allocator_) HMul(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0); - InsertLocalStore(tmp_, mul, 0); + new (&allocator_) HMul(Primitive::kPrimInt, k, constant100_), 0); HInstruction *shl = InsertInstruction( - new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0); - InsertLocalStore(tmp_, shl, 0); + new (&allocator_) HShl(Primitive::kPrimInt, k, constant1_), 0); HInstruction *neg = InsertInstruction( - new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(induc_, 0)), 0); - InsertLocalStore(tmp_, neg, 0); - InsertLocalStore( - induc_, - InsertInstruction( - new (&allocator_) - HShl(Primitive::kPrimInt, InsertLocalLoad(basic_[0], 0), constant1_), 0), 0); + new (&allocator_) HNeg(Primitive::kPrimInt, k), 0); + k->AddInput( + InsertInstruction(new (&allocator_) HShl(Primitive::kPrimInt, basic_[0], constant1_), 0)); PerformInductionVarAnalysis(); EXPECT_STREQ("wrap((100), ((2) * i + (100)))", GetInductionInfo(add, 0).c_str()); @@ -461,11 +444,15 @@ TEST_F(InductionVarAnalysisTest, FindPeriodicInduction) { // k = d; // } BuildLoopNest(1); - HInstruction* store1 = InsertArrayStore(induc_, 0); - HInstruction* store2 = InsertArrayStore(tmp_, 0); - InsertLocalStore(dum_, InsertLocalLoad(tmp_, 0), 0); - InsertLocalStore(tmp_, InsertLocalLoad(induc_, 0), 0); - InsertLocalStore(induc_, InsertLocalLoad(dum_, 0), 0); + HPhi* k = InsertLoopPhi(0, 0); + k->AddInput(constant0_); + HPhi* t = InsertLoopPhi(1, 0); + t->AddInput(constant100_); + + HInstruction* store1 = InsertArrayStore(k, 0); + HInstruction* store2 = InsertArrayStore(t, 0); + k->AddInput(t); + t->AddInput(k); PerformInductionVarAnalysis(); EXPECT_STREQ("periodic((0), (100))", GetInductionInfo(store1->InputAt(1), 0).c_str()); @@ -480,10 +467,13 @@ TEST_F(InductionVarAnalysisTest, FindIdiomaticPeriodicInduction) { // k = 1 - k; // } BuildLoopNest(1); - HInstruction* store = InsertArrayStore(induc_, 0); + HPhi* k = InsertLoopPhi(0, 0); + k->AddInput(constant0_); + + HInstruction* store = InsertArrayStore(k, 0); HInstruction *sub = InsertInstruction( - new (&allocator_) HSub(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 0)), 0); - InsertLocalStore(induc_, sub, 0); + new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k), 0); + k->AddInput(sub); PerformInductionVarAnalysis(); EXPECT_STREQ("periodic((0), (1))", GetInductionInfo(store->InputAt(1), 0).c_str()); @@ -502,26 +492,24 @@ TEST_F(InductionVarAnalysisTest, FindDerivedPeriodicInduction) { // t = - k; // } BuildLoopNest(1); - InsertLocalStore( - induc_, - InsertInstruction(new (&allocator_) - HSub(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 0)), 0), 0); + HPhi* k_header = InsertLoopPhi(0, 0); + k_header->AddInput(constant0_); + + HInstruction* k_body = InsertInstruction( + new (&allocator_) HSub(Primitive::kPrimInt, constant1_, k_header), 0); + k_header->AddInput(k_body); + // Derived expressions. HInstruction *add = InsertInstruction( - new (&allocator_) HAdd(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0); - InsertLocalStore(tmp_, add, 0); + new (&allocator_) HAdd(Primitive::kPrimInt, k_body, constant100_), 0); HInstruction *sub = InsertInstruction( - new (&allocator_) HSub(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0); - InsertLocalStore(tmp_, sub, 0); + new (&allocator_) HSub(Primitive::kPrimInt, k_body, constant100_), 0); HInstruction *mul = InsertInstruction( - new (&allocator_) HMul(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant100_), 0); - InsertLocalStore(tmp_, mul, 0); + new (&allocator_) HMul(Primitive::kPrimInt, k_body, constant100_), 0); HInstruction *shl = InsertInstruction( - new (&allocator_) HShl(Primitive::kPrimInt, InsertLocalLoad(induc_, 0), constant1_), 0); - InsertLocalStore(tmp_, shl, 0); + new (&allocator_) HShl(Primitive::kPrimInt, k_body, constant1_), 0); HInstruction *neg = InsertInstruction( - new (&allocator_) HNeg(Primitive::kPrimInt, InsertLocalLoad(induc_, 0)), 0); - InsertLocalStore(tmp_, neg, 0); + new (&allocator_) HNeg(Primitive::kPrimInt, k_body), 0); PerformInductionVarAnalysis(); EXPECT_STREQ("periodic(((1) + (100)), (100))", GetInductionInfo(add, 0).c_str()); @@ -543,10 +531,20 @@ TEST_F(InductionVarAnalysisTest, FindDeepLoopInduction) { // .. // } BuildLoopNest(10); + + HPhi* k[10]; + for (int d = 0; d < 10; d++) { + k[d] = InsertLoopPhi(0, d); + } + HInstruction *inc = InsertInstruction( - new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, InsertLocalLoad(induc_, 9)), 9); - InsertLocalStore(induc_, inc, 9); - HInstruction* store = InsertArrayStore(induc_, 9); + new (&allocator_) HAdd(Primitive::kPrimInt, constant1_, k[9]), 9); + HInstruction* store = InsertArrayStore(inc, 9); + + for (int d = 0; d < 10; d++) { + k[d]->AddInput((d != 0) ? k[d - 1] : constant0_ ); + k[d]->AddInput((d != 9) ? k[d + 1] : inc); + } PerformInductionVarAnalysis(); // Avoid exact phi number, since that depends on the SSA building phase. diff --git a/compiler/optimizing/induction_var_range_test.cc b/compiler/optimizing/induction_var_range_test.cc index eda9c01a01..55a654e301 100644 --- a/compiler/optimizing/induction_var_range_test.cc +++ b/compiler/optimizing/induction_var_range_test.cc @@ -86,25 +86,20 @@ class InductionVarRangeTest : public CommonCompilerTest { loop_body->AddSuccessor(loop_header); return_block->AddSuccessor(exit_block_); // Instructions. - HLocal* induc = new (&allocator_) HLocal(0); - entry_block_->AddInstruction(induc); - loop_preheader_->AddInstruction( - new (&allocator_) HStoreLocal(induc, graph_->GetIntConstant(lower))); // i = l loop_preheader_->AddInstruction(new (&allocator_) HGoto()); - HInstruction* load = new (&allocator_) HLoadLocal(induc, Primitive::kPrimInt); - loop_header->AddInstruction(load); + HPhi* phi = new (&allocator_) HPhi(&allocator_, 0, 0, Primitive::kPrimInt); + loop_header->AddPhi(phi); + phi->AddInput(graph_->GetIntConstant(lower)); // i = l if (stride > 0) { - condition_ = new (&allocator_) HLessThan(load, upper); // i < u + condition_ = new (&allocator_) HLessThan(phi, upper); // i < u } else { - condition_ = new (&allocator_) HGreaterThan(load, upper); // i > u + condition_ = new (&allocator_) HGreaterThan(phi, upper); // i > u } loop_header->AddInstruction(condition_); loop_header->AddInstruction(new (&allocator_) HIf(condition_)); - load = new (&allocator_) HLoadLocal(induc, Primitive::kPrimInt); - loop_body->AddInstruction(load); - increment_ = new (&allocator_) HAdd(Primitive::kPrimInt, load, graph_->GetIntConstant(stride)); - loop_body->AddInstruction(increment_); - loop_body->AddInstruction(new (&allocator_) HStoreLocal(induc, increment_)); // i += s + increment_ = new (&allocator_) HAdd(Primitive::kPrimInt, phi, graph_->GetIntConstant(stride)); + loop_body->AddInstruction(increment_); // i += s + phi->AddInput(increment_); loop_body->AddInstruction(new (&allocator_) HGoto()); return_block->AddInstruction(new (&allocator_) HReturnVoid()); exit_block_->AddInstruction(new (&allocator_) HExit()); @@ -112,7 +107,7 @@ class InductionVarRangeTest : public CommonCompilerTest { /** Constructs SSA and performs induction variable analysis. */ void PerformInductionVarAnalysis() { - TransformToSsa(graph_); + graph_->BuildDominatorTree(); iva_->Run(); } diff --git a/compiler/optimizing/inliner.cc b/compiler/optimizing/inliner.cc index 3740c405c1..68e96fba74 100644 --- a/compiler/optimizing/inliner.cc +++ b/compiler/optimizing/inliner.cc @@ -829,7 +829,7 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, resolved_method->GetQuickenedInfo(), dex_cache); - if (!builder.BuildGraph(*code_item)) { + if (builder.BuildGraph(*code_item, handles_) != kAnalysisSuccess) { VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file) << " could not be built, so cannot be inlined"; return false; @@ -842,12 +842,6 @@ bool HInliner::TryBuildAndInline(ArtMethod* resolved_method, return false; } - if (callee_graph->TryBuildingSsa(handles_) != kAnalysisSuccess) { - VLOG(compiler) << "Method " << PrettyMethod(method_index, callee_dex_file) - << " could not be transformed to SSA"; - return false; - } - size_t parameter_index = 0; for (HInstructionIterator instructions(callee_graph->GetEntryBlock()->GetInstructions()); !instructions.Done(); diff --git a/compiler/optimizing/licm_test.cc b/compiler/optimizing/licm_test.cc index 2b63ec8971..9fb32f4001 100644 --- a/compiler/optimizing/licm_test.cc +++ b/compiler/optimizing/licm_test.cc @@ -76,7 +76,7 @@ class LICMTest : public CommonCompilerTest { // Performs LICM optimizations (after proper set up). void PerformLICM() { - TransformToSsa(graph_); + graph_->BuildDominatorTree(); SideEffectsAnalysis side_effects(graph_); side_effects.Run(); LICM(graph_, side_effects).Run(); diff --git a/compiler/optimizing/linearize_test.cc b/compiler/optimizing/linearize_test.cc index ed275b1544..13e14c53b5 100644 --- a/compiler/optimizing/linearize_test.cc +++ b/compiler/optimizing/linearize_test.cc @@ -39,14 +39,7 @@ template <size_t number_of_blocks> static void TestCode(const uint16_t* data, const uint32_t (&expected_order)[number_of_blocks]) { ArenaPool pool; ArenaAllocator allocator(&pool); - HGraph* graph = CreateGraph(&allocator); - HGraphBuilder builder(graph); - const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); - bool graph_built = builder.BuildGraph(*item); - ASSERT_TRUE(graph_built); - - TransformToSsa(graph); - + HGraph* graph = CreateCFG(&allocator, data); std::unique_ptr<const X86InstructionSetFeatures> features_x86( X86InstructionSetFeatures::FromCppDefines()); x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); diff --git a/compiler/optimizing/live_ranges_test.cc b/compiler/optimizing/live_ranges_test.cc index 991f8f70ea..3202493c3a 100644 --- a/compiler/optimizing/live_ranges_test.cc +++ b/compiler/optimizing/live_ranges_test.cc @@ -32,14 +32,10 @@ namespace art { class LiveRangesTest : public CommonCompilerTest {}; static HGraph* BuildGraph(const uint16_t* data, ArenaAllocator* allocator) { - HGraph* graph = CreateGraph(allocator); - HGraphBuilder builder(graph); - const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); - builder.BuildGraph(*item); + HGraph* graph = CreateCFG(allocator, data); // Suspend checks implementation may change in the future, and this test relies // on how instructions are ordered. RemoveSuspendChecks(graph); - TransformToSsa(graph); // `Inline` conditions into ifs. PrepareForRegisterAllocation(graph).Run(); return graph; @@ -303,13 +299,12 @@ TEST_F(LiveRangesTest, Loop2) { * 12: equal * 14: if +++++ * | \ + - * | 18: suspend - * | 20: add - * | 22: goto + * | 18: add + * | 20: goto * | - * 26: return + * 24: return * | - * 30: exit + * 28: exit * * We want to make sure the phi at 10 has a lifetime hole after the add at 20. */ @@ -345,18 +340,18 @@ TEST_F(LiveRangesTest, Loop2) { interval = phi->GetLiveInterval(); range = interval->GetFirstRange(); ASSERT_EQ(10u, range->GetStart()); - ASSERT_EQ(21u, range->GetEnd()); + ASSERT_EQ(19u, range->GetEnd()); range = range->GetNext(); ASSERT_TRUE(range != nullptr); - ASSERT_EQ(24u, range->GetStart()); - ASSERT_EQ(26u, range->GetEnd()); + ASSERT_EQ(22u, range->GetStart()); + ASSERT_EQ(24u, range->GetEnd()); // Test for the add instruction. HAdd* add = liveness.GetInstructionFromSsaIndex(2)->AsAdd(); interval = add->GetLiveInterval(); range = interval->GetFirstRange(); - ASSERT_EQ(20u, range->GetStart()); - ASSERT_EQ(24u, range->GetEnd()); + ASSERT_EQ(18u, range->GetStart()); + ASSERT_EQ(22u, range->GetEnd()); ASSERT_TRUE(range->GetNext() == nullptr); } diff --git a/compiler/optimizing/liveness_test.cc b/compiler/optimizing/liveness_test.cc index 7736eedae1..92a987cb1d 100644 --- a/compiler/optimizing/liveness_test.cc +++ b/compiler/optimizing/liveness_test.cc @@ -46,12 +46,7 @@ static void DumpBitVector(BitVector* vector, static void TestCode(const uint16_t* data, const char* expected) { ArenaPool pool; ArenaAllocator allocator(&pool); - HGraph* graph = CreateGraph(&allocator); - HGraphBuilder builder(graph); - const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); - bool graph_built = builder.BuildGraph(*item); - ASSERT_TRUE(graph_built); - TransformToSsa(graph); + HGraph* graph = CreateCFG(&allocator, data); // `Inline` conditions into ifs. PrepareForRegisterAllocation(graph).Run(); std::unique_ptr<const X86InstructionSetFeatures> features_x86( diff --git a/compiler/optimizing/nodes.cc b/compiler/optimizing/nodes.cc index 453571451c..ca66f631a6 100644 --- a/compiler/optimizing/nodes.cc +++ b/compiler/optimizing/nodes.cc @@ -27,6 +27,15 @@ namespace art { +void HGraph::InitializeInexactObjectRTI(StackHandleScopeCollection* handles) { + ScopedObjectAccess soa(Thread::Current()); + // Create the inexact Object reference type and store it in the HGraph. + ClassLinker* linker = Runtime::Current()->GetClassLinker(); + inexact_object_rti_ = ReferenceTypeInfo::Create( + handles->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangObject)), + /* is_exact */ false); +} + void HGraph::AddBlock(HBasicBlock* block) { block->SetBlockId(blocks_.size()); blocks_.push_back(block); @@ -236,29 +245,6 @@ void HGraph::ComputeDominanceInformation() { } } -GraphAnalysisResult HGraph::TryBuildingSsa(StackHandleScopeCollection* handles) { - GraphAnalysisResult result = BuildDominatorTree(); - if (result != kAnalysisSuccess) { - return result; - } - - // Create the inexact Object reference type and store it in the HGraph. - ScopedObjectAccess soa(Thread::Current()); - ClassLinker* linker = Runtime::Current()->GetClassLinker(); - inexact_object_rti_ = ReferenceTypeInfo::Create( - handles->NewHandle(linker->GetClassRoot(ClassLinker::kJavaLangObject)), - /* is_exact */ false); - - // Tranforms graph to SSA form. - result = SsaBuilder(this, handles).BuildSsa(); - if (result != kAnalysisSuccess) { - return result; - } - - in_ssa_form_ = true; - return kAnalysisSuccess; -} - HBasicBlock* HGraph::SplitEdge(HBasicBlock* block, HBasicBlock* successor) { HBasicBlock* new_block = new (arena_) HBasicBlock(this, successor->GetDexPc()); AddBlock(new_block); @@ -1592,7 +1578,7 @@ void HBasicBlock::DisconnectAndDelete() { loop_info->Remove(this); if (loop_info->IsBackEdge(*this)) { // If this was the last back edge of the loop, we deliberately leave the - // loop in an inconsistent state and will fail SSAChecker unless the + // loop in an inconsistent state and will fail GraphChecker unless the // entire loop is removed during the pass. loop_info->RemoveBackEdge(this); } @@ -1631,7 +1617,7 @@ void HBasicBlock::DisconnectAndDelete() { } else if (num_pred_successors == 0u) { // The predecessor has no remaining successors and therefore must be dead. // We deliberately leave it without a control-flow instruction so that the - // SSAChecker fails unless it is not removed during the pass too. + // GraphChecker fails unless it is not removed during the pass too. predecessor->RemoveInstruction(last_instruction); } else { // There are multiple successors left. The removed block might be a successor diff --git a/compiler/optimizing/nodes.h b/compiler/optimizing/nodes.h index 2697af33eb..1c9b09f1e6 100644 --- a/compiler/optimizing/nodes.h +++ b/compiler/optimizing/nodes.h @@ -98,6 +98,7 @@ enum IfCondition { }; enum GraphAnalysisResult { + kAnalysisInvalidBytecode, kAnalysisFailThrowCatchLoop, kAnalysisFailAmbiguousArrayOp, kAnalysisSuccess, @@ -308,10 +309,14 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { blocks_.reserve(kDefaultNumberOfBlocks); } + // Acquires and stores RTI of inexact Object to be used when creating HNullConstant. + void InitializeInexactObjectRTI(StackHandleScopeCollection* handles); + ArenaAllocator* GetArena() const { return arena_; } const ArenaVector<HBasicBlock*>& GetBlocks() const { return blocks_; } bool IsInSsaForm() const { return in_ssa_form_; } + void SetInSsaForm() { in_ssa_form_ = true; } HBasicBlock* GetEntryBlock() const { return entry_block_; } HBasicBlock* GetExitBlock() const { return exit_block_; } @@ -322,11 +327,6 @@ class HGraph : public ArenaObject<kArenaAllocGraph> { void AddBlock(HBasicBlock* block); - // Try building the SSA form of this graph, with dominance computation and - // loop recognition. Returns a code specifying that it was successful or the - // reason for failure. - GraphAnalysisResult TryBuildingSsa(StackHandleScopeCollection* handles); - void ComputeDominanceInformation(); void ClearDominanceInformation(); void ClearLoopInformation(); diff --git a/compiler/optimizing/optimizing_compiler.cc b/compiler/optimizing/optimizing_compiler.cc index dcd8e7d216..12b748b7b6 100644 --- a/compiler/optimizing/optimizing_compiler.cc +++ b/compiler/optimizing/optimizing_compiler.cc @@ -186,18 +186,10 @@ class PassObserver : public ValueObject { // Validate the HGraph if running in debug mode. if (kIsDebugBuild) { if (!graph_in_bad_state_) { - if (graph_->IsInSsaForm()) { - SSAChecker checker(graph_); - checker.Run(); - if (!checker.IsValid()) { - LOG(FATAL) << "Error after " << pass_name << ": " << Dumpable<SSAChecker>(checker); - } - } else { - GraphChecker checker(graph_); - checker.Run(); - if (!checker.IsValid()) { - LOG(FATAL) << "Error after " << pass_name << ": " << Dumpable<GraphChecker>(checker); - } + GraphChecker checker(graph_); + checker.Run(); + if (!checker.IsValid()) { + LOG(FATAL) << "Error after " << pass_name << ": " << Dumpable<GraphChecker>(checker); } } } @@ -665,6 +657,7 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, && compiler_driver->RequiresConstructorBarrier(Thread::Current(), dex_compilation_unit.GetDexFile(), dex_compilation_unit.GetClassDefIndex()); + HGraph* graph = new (arena) HGraph( arena, dex_file, @@ -675,6 +668,21 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, compiler_driver->GetCompilerOptions().GetDebuggable(), osr); + const uint8_t* interpreter_metadata = nullptr; + { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScope<1> hs(soa.Self()); + Handle<mirror::ClassLoader> loader(hs.NewHandle( + soa.Decode<mirror::ClassLoader*>(class_loader))); + ArtMethod* art_method = compiler_driver->ResolveMethod( + soa, dex_cache, loader, &dex_compilation_unit, method_idx, invoke_type); + // We may not get a method, for example if its class is erroneous. + if (art_method != nullptr) { + graph->SetArtMethod(art_method); + interpreter_metadata = art_method->GetQuickenedInfo(); + } + } + std::unique_ptr<CodeGenerator> codegen( CodeGenerator::Create(graph, instruction_set, @@ -692,74 +700,55 @@ CodeGenerator* OptimizingCompiler::TryCompile(ArenaAllocator* arena, visualizer_output_.get(), compiler_driver); - const uint8_t* interpreter_metadata = nullptr; - { - ScopedObjectAccess soa(Thread::Current()); - StackHandleScope<1> hs(soa.Self()); - Handle<mirror::ClassLoader> loader(hs.NewHandle( - soa.Decode<mirror::ClassLoader*>(class_loader))); - ArtMethod* art_method = compiler_driver->ResolveMethod( - soa, dex_cache, loader, &dex_compilation_unit, method_idx, invoke_type); - // We may not get a method, for example if its class is erroneous. - if (art_method != nullptr) { - graph->SetArtMethod(art_method); - interpreter_metadata = art_method->GetQuickenedInfo(); - } - } - HGraphBuilder builder(graph, - &dex_compilation_unit, - &dex_compilation_unit, - &dex_file, - compiler_driver, - compilation_stats_.get(), - interpreter_metadata, - dex_cache); - VLOG(compiler) << "Building " << pass_observer.GetMethodName(); { - PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); - if (!builder.BuildGraph(*code_item)) { - pass_observer.SetGraphInBadState(); - return nullptr; + ScopedObjectAccess soa(Thread::Current()); + StackHandleScopeCollection handles(soa.Self()); + // Do not hold `mutator_lock_` between optimizations. + ScopedThreadSuspension sts(soa.Self(), kNative); + + { + PassScope scope(HGraphBuilder::kBuilderPassName, &pass_observer); + HGraphBuilder builder(graph, + &dex_compilation_unit, + &dex_compilation_unit, + &dex_file, + compiler_driver, + compilation_stats_.get(), + interpreter_metadata, + dex_cache); + GraphAnalysisResult result = builder.BuildGraph(*code_item, &handles); + if (result != kAnalysisSuccess) { + switch (result) { + case kAnalysisInvalidBytecode: + break; + case kAnalysisFailThrowCatchLoop: + MaybeRecordStat(MethodCompilationStat::kNotCompiledThrowCatchLoop); + break; + case kAnalysisFailAmbiguousArrayOp: + MaybeRecordStat(MethodCompilationStat::kNotCompiledAmbiguousArrayOp); + break; + case kAnalysisSuccess: + UNREACHABLE(); + } + pass_observer.SetGraphInBadState(); + return nullptr; + } } - } - VLOG(compiler) << "Optimizing " << pass_observer.GetMethodName(); + RunOptimizations(graph, + codegen.get(), + compiler_driver, + compilation_stats_.get(), + dex_compilation_unit, + &pass_observer, + &handles); - ScopedObjectAccess soa(Thread::Current()); - StackHandleScopeCollection handles(soa.Self()); - ScopedThreadSuspension sts(soa.Self(), kNative); - - { - PassScope scope(SsaBuilder::kSsaBuilderPassName, &pass_observer); - GraphAnalysisResult result = graph->TryBuildingSsa(&handles); - if (result != kAnalysisSuccess) { - switch (result) { - case kAnalysisFailThrowCatchLoop: - MaybeRecordStat(MethodCompilationStat::kNotCompiledThrowCatchLoop); - break; - case kAnalysisFailAmbiguousArrayOp: - MaybeRecordStat(MethodCompilationStat::kNotCompiledAmbiguousArrayOp); - break; - case kAnalysisSuccess: - UNREACHABLE(); - } - pass_observer.SetGraphInBadState(); - return nullptr; - } + codegen->Compile(code_allocator); + pass_observer.DumpDisassembly(); } - RunOptimizations(graph, - codegen.get(), - compiler_driver, - compilation_stats_.get(), - dex_compilation_unit, - &pass_observer, - &handles); - codegen->Compile(code_allocator); - pass_observer.DumpDisassembly(); - if (kArenaAllocatorCountAllocations) { if (arena->BytesAllocated() > 4 * MB) { MemStats mem_stats(arena->GetMemStats()); diff --git a/compiler/optimizing/optimizing_unit_test.h b/compiler/optimizing/optimizing_unit_test.h index 5a910433b4..0c7648edc2 100644 --- a/compiler/optimizing/optimizing_unit_test.h +++ b/compiler/optimizing/optimizing_unit_test.h @@ -64,10 +64,12 @@ LiveInterval* BuildInterval(const size_t ranges[][2], void RemoveSuspendChecks(HGraph* graph) { for (HBasicBlock* block : graph->GetBlocks()) { - for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { - HInstruction* current = it.Current(); - if (current->IsSuspendCheck()) { - current->GetBlock()->RemoveInstruction(current); + if (block != nullptr) { + for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { + HInstruction* current = it.Current(); + if (current->IsSuspendCheck()) { + current->GetBlock()->RemoveInstruction(current); + } } } } @@ -83,12 +85,17 @@ inline HGraph* CreateGraph(ArenaAllocator* allocator) { inline HGraph* CreateCFG(ArenaAllocator* allocator, const uint16_t* data, Primitive::Type return_type = Primitive::kPrimInt) { - HGraph* graph = CreateGraph(allocator); - HGraphBuilder builder(graph, return_type); const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); - bool graph_built = builder.BuildGraph(*item); - return graph_built ? graph : nullptr; + HGraph* graph = CreateGraph(allocator); + + { + ScopedObjectAccess soa(Thread::Current()); + StackHandleScopeCollection handles(soa.Self()); + HGraphBuilder builder(graph, return_type); + bool graph_built = (builder.BuildGraph(*item, &handles) == kAnalysisSuccess); + return graph_built ? graph : nullptr; + } } // Naive string diff data type. @@ -114,12 +121,6 @@ inline bool IsRemoved(HInstruction* instruction) { return instruction->GetBlock() == nullptr; } -inline void TransformToSsa(HGraph* graph) { - ScopedObjectAccess soa(Thread::Current()); - StackHandleScopeCollection handles(soa.Self()); - EXPECT_EQ(graph->TryBuildingSsa(&handles), kAnalysisSuccess); -} - } // namespace art #endif // ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_ diff --git a/compiler/optimizing/pretty_printer_test.cc b/compiler/optimizing/pretty_printer_test.cc index c56100dfa1..2de0c1be72 100644 --- a/compiler/optimizing/pretty_printer_test.cc +++ b/compiler/optimizing/pretty_printer_test.cc @@ -30,17 +30,15 @@ namespace art { static void TestCode(const uint16_t* data, const char* expected) { ArenaPool pool; ArenaAllocator allocator(&pool); - HGraph* graph = CreateGraph(&allocator); - HGraphBuilder builder(graph); - const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); - bool graph_built = builder.BuildGraph(*item); - ASSERT_TRUE(graph_built); + HGraph* graph = CreateCFG(&allocator, data); StringPrettyPrinter printer(graph); printer.VisitInsertionOrder(); ASSERT_STREQ(expected, printer.str().c_str()); } -TEST(PrettyPrinterTest, ReturnVoid) { +class PrettyPrinterTest : public CommonCompilerTest {}; + +TEST_F(PrettyPrinterTest, ReturnVoid) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::RETURN_VOID); @@ -56,7 +54,7 @@ TEST(PrettyPrinterTest, ReturnVoid) { TestCode(data, expected); } -TEST(PrettyPrinterTest, CFG1) { +TEST_F(PrettyPrinterTest, CFG1) { const char* expected = "BasicBlock 0, succ: 1\n" " 3: SuspendCheck\n" @@ -76,7 +74,7 @@ TEST(PrettyPrinterTest, CFG1) { TestCode(data, expected); } -TEST(PrettyPrinterTest, CFG2) { +TEST_F(PrettyPrinterTest, CFG2) { const char* expected = "BasicBlock 0, succ: 1\n" " 4: SuspendCheck\n" @@ -98,7 +96,7 @@ TEST(PrettyPrinterTest, CFG2) { TestCode(data, expected); } -TEST(PrettyPrinterTest, CFG3) { +TEST_F(PrettyPrinterTest, CFG3) { const char* expected = "BasicBlock 0, succ: 1\n" " 4: SuspendCheck\n" @@ -134,16 +132,16 @@ TEST(PrettyPrinterTest, CFG3) { TestCode(data3, expected); } -TEST(PrettyPrinterTest, CFG4) { +TEST_F(PrettyPrinterTest, CFG4) { const char* expected = - "BasicBlock 0, succ: 1\n" + "BasicBlock 0, succ: 3\n" " 3: SuspendCheck\n" - " 4: Goto 1\n" - "BasicBlock 1, pred: 0, 1, succ: 1\n" + " 4: Goto 3\n" + "BasicBlock 1, pred: 3, 1, succ: 1\n" " 0: SuspendCheck\n" " 1: Goto 1\n" - "BasicBlock 2\n" - " 2: Exit\n"; + "BasicBlock 3, pred: 0, succ: 1\n" + " 5: Goto 1\n"; const uint16_t data1[] = ZERO_REGISTER_CODE_ITEM( Instruction::NOP, @@ -157,15 +155,13 @@ TEST(PrettyPrinterTest, CFG4) { TestCode(data2, expected); } -TEST(PrettyPrinterTest, CFG5) { +TEST_F(PrettyPrinterTest, CFG5) { const char* expected = "BasicBlock 0, succ: 1\n" " 3: SuspendCheck\n" " 4: Goto 1\n" - "BasicBlock 1, pred: 0, 2, succ: 3\n" + "BasicBlock 1, pred: 0, succ: 3\n" " 0: ReturnVoid\n" - "BasicBlock 2, succ: 1\n" - " 1: Goto 1\n" "BasicBlock 3, pred: 1\n" " 2: Exit\n"; @@ -177,25 +173,23 @@ TEST(PrettyPrinterTest, CFG5) { TestCode(data, expected); } -TEST(PrettyPrinterTest, CFG6) { +TEST_F(PrettyPrinterTest, CFG6) { const char* expected = "BasicBlock 0, succ: 1\n" - " 0: Local [4, 3, 2]\n" - " 1: IntConstant [2]\n" + " 1: IntConstant [5, 5]\n" " 10: SuspendCheck\n" " 11: Goto 1\n" - "BasicBlock 1, pred: 0, succ: 3, 2\n" - " 2: StoreLocal(0, 1)\n" - " 3: LoadLocal(0) [5]\n" - " 4: LoadLocal(0) [5]\n" - " 5: Equal(3, 4) [6]\n" + "BasicBlock 1, pred: 0, succ: 5, 2\n" + " 5: Equal(1, 1) [6]\n" " 6: If(5)\n" "BasicBlock 2, pred: 1, succ: 3\n" " 7: Goto 3\n" - "BasicBlock 3, pred: 1, 2, succ: 4\n" + "BasicBlock 3, pred: 5, 2, succ: 4\n" " 8: ReturnVoid\n" "BasicBlock 4, pred: 3\n" - " 9: Exit\n"; + " 9: Exit\n" + "BasicBlock 5, pred: 1, succ: 3\n" + " 12: Goto 3\n"; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, @@ -206,26 +200,24 @@ TEST(PrettyPrinterTest, CFG6) { TestCode(data, expected); } -TEST(PrettyPrinterTest, CFG7) { +TEST_F(PrettyPrinterTest, CFG7) { const char* expected = "BasicBlock 0, succ: 1\n" - " 0: Local [4, 3, 2]\n" - " 1: IntConstant [2]\n" + " 1: IntConstant [5, 5]\n" " 11: SuspendCheck\n" " 12: Goto 1\n" - "BasicBlock 1, pred: 0, succ: 3, 2\n" - " 2: StoreLocal(0, 1)\n" - " 3: LoadLocal(0) [5]\n" - " 4: LoadLocal(0) [5]\n" - " 5: Equal(3, 4) [6]\n" + "BasicBlock 1, pred: 0, succ: 5, 6\n" + " 5: Equal(1, 1) [6]\n" " 6: If(5)\n" - "BasicBlock 2, pred: 1, 3, succ: 3\n" + "BasicBlock 2, pred: 6, 3, succ: 3\n" " 7: Goto 3\n" - "BasicBlock 3, pred: 1, 2, succ: 2\n" + "BasicBlock 3, pred: 5, 2, succ: 2\n" " 8: SuspendCheck\n" " 9: Goto 2\n" - "BasicBlock 4\n" - " 10: Exit\n"; + "BasicBlock 5, pred: 1, succ: 3\n" + " 13: Goto 3\n" + "BasicBlock 6, pred: 1, succ: 2\n" + " 14: Goto 2\n"; const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, @@ -236,15 +228,13 @@ TEST(PrettyPrinterTest, CFG7) { TestCode(data, expected); } -TEST(PrettyPrinterTest, IntConstant) { +TEST_F(PrettyPrinterTest, IntConstant) { const char* expected = "BasicBlock 0, succ: 1\n" - " 0: Local [2]\n" - " 1: IntConstant [2]\n" + " 1: IntConstant\n" " 5: SuspendCheck\n" " 6: Goto 1\n" "BasicBlock 1, pred: 0, succ: 2\n" - " 2: StoreLocal(0, 1)\n" " 3: ReturnVoid\n" "BasicBlock 2, pred: 1\n" " 4: Exit\n"; diff --git a/compiler/optimizing/register_allocator_test.cc b/compiler/optimizing/register_allocator_test.cc index 572faa841e..a9de7c3e59 100644 --- a/compiler/optimizing/register_allocator_test.cc +++ b/compiler/optimizing/register_allocator_test.cc @@ -38,11 +38,7 @@ class RegisterAllocatorTest : public CommonCompilerTest {}; static bool Check(const uint16_t* data) { ArenaPool pool; ArenaAllocator allocator(&pool); - HGraph* graph = CreateGraph(&allocator); - HGraphBuilder builder(graph); - const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); - builder.BuildGraph(*item); - TransformToSsa(graph); + HGraph* graph = CreateCFG(&allocator, data); std::unique_ptr<const X86InstructionSetFeatures> features_x86( X86InstructionSetFeatures::FromCppDefines()); x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); @@ -254,15 +250,6 @@ TEST_F(RegisterAllocatorTest, Loop2) { ASSERT_TRUE(Check(data)); } -static HGraph* BuildSSAGraph(const uint16_t* data, ArenaAllocator* allocator) { - HGraph* graph = CreateGraph(allocator); - HGraphBuilder builder(graph); - const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); - builder.BuildGraph(*item); - TransformToSsa(graph); - return graph; -} - TEST_F(RegisterAllocatorTest, Loop3) { /* * Test the following snippet: @@ -302,7 +289,7 @@ TEST_F(RegisterAllocatorTest, Loop3) { ArenaPool pool; ArenaAllocator allocator(&pool); - HGraph* graph = BuildSSAGraph(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); std::unique_ptr<const X86InstructionSetFeatures> features_x86( X86InstructionSetFeatures::FromCppDefines()); x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); @@ -336,7 +323,7 @@ TEST_F(RegisterAllocatorTest, FirstRegisterUse) { ArenaPool pool; ArenaAllocator allocator(&pool); - HGraph* graph = BuildSSAGraph(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); std::unique_ptr<const X86InstructionSetFeatures> features_x86( X86InstructionSetFeatures::FromCppDefines()); x86::CodeGeneratorX86 codegen(graph, *features_x86.get(), CompilerOptions()); @@ -390,7 +377,7 @@ TEST_F(RegisterAllocatorTest, DeadPhi) { ArenaPool pool; ArenaAllocator allocator(&pool); - HGraph* graph = BuildSSAGraph(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); SsaDeadPhiElimination(graph).Run(); std::unique_ptr<const X86InstructionSetFeatures> features_x86( X86InstructionSetFeatures::FromCppDefines()); @@ -414,7 +401,7 @@ TEST_F(RegisterAllocatorTest, FreeUntil) { ArenaPool pool; ArenaAllocator allocator(&pool); - HGraph* graph = BuildSSAGraph(data, &allocator); + HGraph* graph = CreateCFG(&allocator, data); SsaDeadPhiElimination(graph).Run(); std::unique_ptr<const X86InstructionSetFeatures> features_x86( X86InstructionSetFeatures::FromCppDefines()); diff --git a/compiler/optimizing/ssa_builder.cc b/compiler/optimizing/ssa_builder.cc index 2d0a399290..308a25414a 100644 --- a/compiler/optimizing/ssa_builder.cc +++ b/compiler/optimizing/ssa_builder.cc @@ -451,6 +451,8 @@ void SsaBuilder::RemoveRedundantUninitializedStrings() { } GraphAnalysisResult SsaBuilder::BuildSsa() { + DCHECK(!GetGraph()->IsInSsaForm()); + // 1) Visit in reverse post order. We need to have all predecessors of a block // visited (with the exception of loops) in order to create the right environment // for that block. For loops, we create phis whose inputs will be set in 2). @@ -533,6 +535,7 @@ GraphAnalysisResult SsaBuilder::BuildSsa() { } } + GetGraph()->SetInSsaForm(); return kAnalysisSuccess; } diff --git a/compiler/optimizing/ssa_builder.h b/compiler/optimizing/ssa_builder.h index 4cba41f389..2dae9c2de0 100644 --- a/compiler/optimizing/ssa_builder.h +++ b/compiler/optimizing/ssa_builder.h @@ -79,8 +79,6 @@ class SsaBuilder : public HGraphVisitor { void VisitArraySet(HArraySet* aset) OVERRIDE; void VisitInvokeStaticOrDirect(HInvokeStaticOrDirect* invoke) OVERRIDE; - static constexpr const char* kSsaBuilderPassName = "ssa_builder"; - private: void SetLoopHeaderPhiInputs(); void FixEnvironmentPhis(); diff --git a/compiler/optimizing/ssa_liveness_analysis.cc b/compiler/optimizing/ssa_liveness_analysis.cc index 1dd35080a6..83e9dacb1a 100644 --- a/compiler/optimizing/ssa_liveness_analysis.cc +++ b/compiler/optimizing/ssa_liveness_analysis.cc @@ -226,7 +226,7 @@ void SsaLivenessAnalysis::ComputeLiveRanges() { // The only instructions which may not be recorded in the environments // are constants created by the SSA builder as typed equivalents of // untyped constants from the bytecode, or phis with only such constants - // as inputs (verified by SSAChecker). Their raw binary value must + // as inputs (verified by GraphChecker). Their raw binary value must // therefore be the same and we only need to keep alive one. } else { size_t phi_input_index = successor->GetPredecessorIndexOf(block); diff --git a/compiler/optimizing/ssa_test.cc b/compiler/optimizing/ssa_test.cc index d2885a8fd7..a6880921c5 100644 --- a/compiler/optimizing/ssa_test.cc +++ b/compiler/optimizing/ssa_test.cc @@ -79,13 +79,7 @@ static void ReNumberInstructions(HGraph* graph) { static void TestCode(const uint16_t* data, const char* expected) { ArenaPool pool; ArenaAllocator allocator(&pool); - HGraph* graph = CreateGraph(&allocator); - HGraphBuilder builder(graph); - const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); - bool graph_built = builder.BuildGraph(*item); - ASSERT_TRUE(graph_built); - - TransformToSsa(graph); + HGraph* graph = CreateCFG(&allocator, data); // Suspend checks implementation may change in the future, and this test relies // on how instructions are ordered. RemoveSuspendChecks(graph); diff --git a/compiler/optimizing/suspend_check_test.cc b/compiler/optimizing/suspend_check_test.cc index b6c704c1b1..15cd4e8a08 100644 --- a/compiler/optimizing/suspend_check_test.cc +++ b/compiler/optimizing/suspend_check_test.cc @@ -18,6 +18,7 @@ #include "dex_instruction.h" #include "nodes.h" #include "optimizing_unit_test.h" +#include "pretty_printer.h" #include "gtest/gtest.h" @@ -30,20 +31,17 @@ namespace art { static void TestCode(const uint16_t* data) { ArenaPool pool; ArenaAllocator allocator(&pool); - HGraph* graph = CreateGraph(&allocator); - HGraphBuilder builder(graph); - const DexFile::CodeItem* item = reinterpret_cast<const DexFile::CodeItem*>(data); - bool graph_built = builder.BuildGraph(*item); - ASSERT_TRUE(graph_built); - - HBasicBlock* first_block = graph->GetEntryBlock()->GetSuccessors()[0]; - HInstruction* first_instruction = first_block->GetFirstInstruction(); - // Account for some tests having a store local as first instruction. - ASSERT_TRUE(first_instruction->IsSuspendCheck() - || first_instruction->GetNext()->IsSuspendCheck()); + HGraph* graph = CreateCFG(&allocator, data); + HBasicBlock* first_block = graph->GetEntryBlock()->GetSingleSuccessor(); + HBasicBlock* loop_header = first_block->GetSingleSuccessor(); + ASSERT_TRUE(loop_header->IsLoopHeader()); + ASSERT_EQ(loop_header->GetLoopInformation()->GetPreHeader(), first_block); + ASSERT_TRUE(loop_header->GetFirstInstruction()->IsSuspendCheck()); } -TEST(CodegenTest, CFG1) { +class SuspendCheckTest : public CommonCompilerTest {}; + +TEST_F(SuspendCheckTest, CFG1) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::NOP, Instruction::GOTO | 0xFF00); @@ -51,14 +49,14 @@ TEST(CodegenTest, CFG1) { TestCode(data); } -TEST(CodegenTest, CFG2) { +TEST_F(SuspendCheckTest, CFG2) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO_32, 0, 0); TestCode(data); } -TEST(CodegenTest, CFG3) { +TEST_F(SuspendCheckTest, CFG3) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 0xFFFF, @@ -67,7 +65,7 @@ TEST(CodegenTest, CFG3) { TestCode(data); } -TEST(CodegenTest, CFG4) { +TEST_F(SuspendCheckTest, CFG4) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_NE, 0xFFFF, @@ -76,7 +74,7 @@ TEST(CodegenTest, CFG4) { TestCode(data); } -TEST(CodegenTest, CFG5) { +TEST_F(SuspendCheckTest, CFG5) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQZ, 0xFFFF, @@ -85,7 +83,7 @@ TEST(CodegenTest, CFG5) { TestCode(data); } -TEST(CodegenTest, CFG6) { +TEST_F(SuspendCheckTest, CFG6) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_NEZ, 0xFFFF, |