summaryrefslogtreecommitdiff
path: root/compiler/optimizing
diff options
context:
space:
mode:
author David Brazdil <dbrazdil@google.com> 2016-02-02 16:28:56 +0000
committer David Brazdil <dbrazdil@google.com> 2016-02-15 10:21:07 +0000
commitbadd826664896d4a9628a5a89b78016894aa414b (patch)
treea30e8b3e62126ae1e1df1152ac643cfc5f2b074a /compiler/optimizing
parent47a2a45a6673ddf3322115ff5058763f82a9368f (diff)
ART: Run SsaBuilder from HGraphBuilder
First step towards merging the two passes, which will later result in HGraphBuilder directly producing SSA form. This CL mostly just updates tests broken by not being able to inspect the pre-SSA form. Using HLocals outside the HGraphBuilder is now deprecated. Bug: 27150508 Change-Id: I00fb6050580f409dcc5aa5b5aa3a536d6e8d759e
Diffstat (limited to 'compiler/optimizing')
-rw-r--r--compiler/optimizing/builder.cc20
-rw-r--r--compiler/optimizing/builder.h3
-rw-r--r--compiler/optimizing/codegen_test.cc32
-rw-r--r--compiler/optimizing/constant_folding_test.cc78
-rw-r--r--compiler/optimizing/dead_code_elimination_test.cc8
-rw-r--r--compiler/optimizing/dominator_test.cc31
-rw-r--r--compiler/optimizing/find_loops_test.cc65
-rw-r--r--compiler/optimizing/graph_checker.cc390
-rw-r--r--compiler/optimizing/graph_checker.h80
-rw-r--r--compiler/optimizing/graph_checker_test.cc32
-rw-r--r--compiler/optimizing/graph_visualizer.cc8
-rw-r--r--compiler/optimizing/gvn_test.cc8
-rw-r--r--compiler/optimizing/induction_var_analysis_test.cc240
-rw-r--r--compiler/optimizing/induction_var_range_test.cc23
-rw-r--r--compiler/optimizing/inliner.cc8
-rw-r--r--compiler/optimizing/licm_test.cc2
-rw-r--r--compiler/optimizing/linearize_test.cc9
-rw-r--r--compiler/optimizing/live_ranges_test.cc25
-rw-r--r--compiler/optimizing/liveness_test.cc7
-rw-r--r--compiler/optimizing/nodes.cc36
-rw-r--r--compiler/optimizing/nodes.h10
-rw-r--r--compiler/optimizing/optimizing_compiler.cc133
-rw-r--r--compiler/optimizing/optimizing_unit_test.h29
-rw-r--r--compiler/optimizing/pretty_printer_test.cc80
-rw-r--r--compiler/optimizing/register_allocator_test.cc23
-rw-r--r--compiler/optimizing/ssa_builder.cc3
-rw-r--r--compiler/optimizing/ssa_builder.h2
-rw-r--r--compiler/optimizing/ssa_liveness_analysis.cc2
-rw-r--r--compiler/optimizing/ssa_test.cc8
-rw-r--r--compiler/optimizing/suspend_check_test.cc32
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,