/* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include "base/arena_allocator.h" #include "base/stringprintf.h" #include "builder.h" #include "nodes.h" #include "optimizing_unit_test.h" #include "pretty_printer.h" #include "gtest/gtest.h" namespace art { static HBasicBlock* createIfBlock(HGraph* graph, ArenaAllocator* allocator) { HBasicBlock* if_block = new (allocator) HBasicBlock(graph); graph->AddBlock(if_block); HInstruction* instr = graph->GetIntConstant(4); HInstruction* equal = new (allocator) HEqual(instr, instr); if_block->AddInstruction(equal); instr = new (allocator) HIf(equal); if_block->AddInstruction(instr); return if_block; } static HBasicBlock* createGotoBlock(HGraph* graph, ArenaAllocator* allocator) { HBasicBlock* block = new (allocator) HBasicBlock(graph); graph->AddBlock(block); HInstruction* got = new (allocator) HGoto(); block->AddInstruction(got); return block; } static HBasicBlock* createEntryBlock(HGraph* graph, ArenaAllocator* allocator) { HBasicBlock* block = createGotoBlock(graph, allocator); graph->SetEntryBlock(block); return block; } static HBasicBlock* createReturnBlock(HGraph* graph, ArenaAllocator* allocator) { HBasicBlock* block = new (allocator) HBasicBlock(graph); graph->AddBlock(block); HInstruction* return_instr = new (allocator) HReturnVoid(); block->AddInstruction(return_instr); return block; } static HBasicBlock* createExitBlock(HGraph* graph, ArenaAllocator* allocator) { HBasicBlock* block = new (allocator) HBasicBlock(graph); graph->AddBlock(block); HInstruction* exit_instr = new (allocator) HExit(); block->AddInstruction(exit_instr); return block; } // Test that the successors of an if block stay consistent after a SimplifyCFG. // This test sets the false block to be the return block. TEST(GraphTest, IfSuccessorSimpleJoinBlock1) { ArenaPool pool; ArenaAllocator allocator(&pool); HGraph* graph = CreateGraph(&allocator); HBasicBlock* entry_block = createEntryBlock(graph, &allocator); HBasicBlock* if_block = createIfBlock(graph, &allocator); HBasicBlock* if_true = createGotoBlock(graph, &allocator); HBasicBlock* return_block = createReturnBlock(graph, &allocator); HBasicBlock* exit_block = createExitBlock(graph, &allocator); entry_block->AddSuccessor(if_block); if_block->AddSuccessor(if_true); if_true->AddSuccessor(return_block); if_block->AddSuccessor(return_block); return_block->AddSuccessor(exit_block); ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor(), if_true); ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor(), return_block); graph->SimplifyCFG(); // Ensure we still have the same if true block. ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor(), if_true); // Ensure the critical edge has been removed. HBasicBlock* false_block = if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor(); ASSERT_NE(false_block, return_block); // Ensure the new block branches to the join block. ASSERT_EQ(false_block->GetSuccessors()[0], return_block); } // Test that the successors of an if block stay consistent after a SimplifyCFG. // This test sets the true block to be the return block. TEST(GraphTest, IfSuccessorSimpleJoinBlock2) { ArenaPool pool; ArenaAllocator allocator(&pool); HGraph* graph = CreateGraph(&allocator); HBasicBlock* entry_block = createEntryBlock(graph, &allocator); HBasicBlock* if_block = createIfBlock(graph, &allocator); HBasicBlock* if_false = createGotoBlock(graph, &allocator); HBasicBlock* return_block = createReturnBlock(graph, &allocator); HBasicBlock* exit_block = createExitBlock(graph, &allocator); entry_block->AddSuccessor(if_block); if_block->AddSuccessor(return_block); if_false->AddSuccessor(return_block); if_block->AddSuccessor(if_false); return_block->AddSuccessor(exit_block); ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor(), return_block); ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor(), if_false); graph->SimplifyCFG(); // Ensure we still have the same if true block. ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor(), if_false); // Ensure the critical edge has been removed. HBasicBlock* true_block = if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor(); ASSERT_NE(true_block, return_block); // Ensure the new block branches to the join block. ASSERT_EQ(true_block->GetSuccessors()[0], return_block); } // Test that the successors of an if block stay consistent after a SimplifyCFG. // This test sets the true block to be the loop header. TEST(GraphTest, IfSuccessorMultipleBackEdges1) { ArenaPool pool; ArenaAllocator allocator(&pool); HGraph* graph = CreateGraph(&allocator); HBasicBlock* entry_block = createEntryBlock(graph, &allocator); HBasicBlock* if_block = createIfBlock(graph, &allocator); HBasicBlock* return_block = createReturnBlock(graph, &allocator); HBasicBlock* exit_block = createExitBlock(graph, &allocator); entry_block->AddSuccessor(if_block); if_block->AddSuccessor(if_block); if_block->AddSuccessor(return_block); return_block->AddSuccessor(exit_block); ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor(), if_block); ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor(), return_block); graph->BuildDominatorTree(); // Ensure we still have the same if false block. ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor(), return_block); // Ensure there is only one back edge. ASSERT_EQ(if_block->GetPredecessors().size(), 2u); ASSERT_EQ(if_block->GetPredecessors()[0], entry_block->GetSingleSuccessor()); ASSERT_NE(if_block->GetPredecessors()[1], if_block); // Ensure the new block is the back edge. ASSERT_EQ(if_block->GetPredecessors()[1], if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor()); } // Test that the successors of an if block stay consistent after a SimplifyCFG. // This test sets the false block to be the loop header. TEST(GraphTest, IfSuccessorMultipleBackEdges2) { ArenaPool pool; ArenaAllocator allocator(&pool); HGraph* graph = CreateGraph(&allocator); HBasicBlock* entry_block = createEntryBlock(graph, &allocator); HBasicBlock* if_block = createIfBlock(graph, &allocator); HBasicBlock* return_block = createReturnBlock(graph, &allocator); HBasicBlock* exit_block = createExitBlock(graph, &allocator); entry_block->AddSuccessor(if_block); if_block->AddSuccessor(return_block); if_block->AddSuccessor(if_block); return_block->AddSuccessor(exit_block); ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor(), return_block); ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor(), if_block); graph->BuildDominatorTree(); // Ensure we still have the same if true block. ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor(), return_block); // Ensure there is only one back edge. ASSERT_EQ(if_block->GetPredecessors().size(), 2u); ASSERT_EQ(if_block->GetPredecessors()[0], entry_block->GetSingleSuccessor()); ASSERT_NE(if_block->GetPredecessors()[1], if_block); // Ensure the new block is the back edge. ASSERT_EQ(if_block->GetPredecessors()[1], if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor()); } // Test that the successors of an if block stay consistent after a SimplifyCFG. // This test sets the true block to be a loop header with multiple pre headers. TEST(GraphTest, IfSuccessorMultiplePreHeaders1) { ArenaPool pool; ArenaAllocator allocator(&pool); HGraph* graph = CreateGraph(&allocator); HBasicBlock* entry_block = createEntryBlock(graph, &allocator); HBasicBlock* first_if_block = createIfBlock(graph, &allocator); HBasicBlock* if_block = createIfBlock(graph, &allocator); HBasicBlock* loop_block = createGotoBlock(graph, &allocator); HBasicBlock* return_block = createReturnBlock(graph, &allocator); entry_block->AddSuccessor(first_if_block); first_if_block->AddSuccessor(if_block); first_if_block->AddSuccessor(loop_block); loop_block->AddSuccessor(loop_block); if_block->AddSuccessor(loop_block); if_block->AddSuccessor(return_block); ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor(), loop_block); ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor(), return_block); graph->BuildDominatorTree(); HIf* if_instr = if_block->GetLastInstruction()->AsIf(); // Ensure we still have the same if false block. ASSERT_EQ(if_instr->IfFalseSuccessor(), return_block); // Ensure there is only one pre header.. ASSERT_EQ(loop_block->GetPredecessors().size(), 2u); // Ensure the new block is the successor of the true block. ASSERT_EQ(if_instr->IfTrueSuccessor()->GetSuccessors().size(), 1u); ASSERT_EQ(if_instr->IfTrueSuccessor()->GetSuccessors()[0], loop_block->GetLoopInformation()->GetPreHeader()); } // Test that the successors of an if block stay consistent after a SimplifyCFG. // This test sets the false block to be a loop header with multiple pre headers. TEST(GraphTest, IfSuccessorMultiplePreHeaders2) { ArenaPool pool; ArenaAllocator allocator(&pool); HGraph* graph = CreateGraph(&allocator); HBasicBlock* entry_block = createEntryBlock(graph, &allocator); HBasicBlock* first_if_block = createIfBlock(graph, &allocator); HBasicBlock* if_block = createIfBlock(graph, &allocator); HBasicBlock* loop_block = createGotoBlock(graph, &allocator); HBasicBlock* return_block = createReturnBlock(graph, &allocator); entry_block->AddSuccessor(first_if_block); first_if_block->AddSuccessor(if_block); first_if_block->AddSuccessor(loop_block); loop_block->AddSuccessor(loop_block); if_block->AddSuccessor(return_block); if_block->AddSuccessor(loop_block); ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfTrueSuccessor(), return_block); ASSERT_EQ(if_block->GetLastInstruction()->AsIf()->IfFalseSuccessor(), loop_block); graph->BuildDominatorTree(); HIf* if_instr = if_block->GetLastInstruction()->AsIf(); // Ensure we still have the same if true block. ASSERT_EQ(if_instr->IfTrueSuccessor(), return_block); // Ensure there is only one pre header.. ASSERT_EQ(loop_block->GetPredecessors().size(), 2u); // Ensure the new block is the successor of the false block. ASSERT_EQ(if_instr->IfFalseSuccessor()->GetSuccessors().size(), 1u); ASSERT_EQ(if_instr->IfFalseSuccessor()->GetSuccessors()[0], loop_block->GetLoopInformation()->GetPreHeader()); } TEST(GraphTest, InsertInstructionBefore) { ArenaPool pool; ArenaAllocator allocator(&pool); HGraph* graph = CreateGraph(&allocator); HBasicBlock* block = createGotoBlock(graph, &allocator); HInstruction* got = block->GetLastInstruction(); ASSERT_TRUE(got->IsControlFlow()); // Test at the beginning of the block. HInstruction* first_instruction = new (&allocator) HIntConstant(4); block->InsertInstructionBefore(first_instruction, got); ASSERT_NE(first_instruction->GetId(), -1); ASSERT_EQ(first_instruction->GetBlock(), block); ASSERT_EQ(block->GetFirstInstruction(), first_instruction); ASSERT_EQ(block->GetLastInstruction(), got); ASSERT_EQ(first_instruction->GetNext(), got); ASSERT_EQ(first_instruction->GetPrevious(), nullptr); ASSERT_EQ(got->GetNext(), nullptr); ASSERT_EQ(got->GetPrevious(), first_instruction); // Test in the middle of the block. HInstruction* second_instruction = new (&allocator) HIntConstant(4); block->InsertInstructionBefore(second_instruction, got); ASSERT_NE(second_instruction->GetId(), -1); ASSERT_EQ(second_instruction->GetBlock(), block); ASSERT_EQ(block->GetFirstInstruction(), first_instruction); ASSERT_EQ(block->GetLastInstruction(), got); ASSERT_EQ(first_instruction->GetNext(), second_instruction); ASSERT_EQ(first_instruction->GetPrevious(), nullptr); ASSERT_EQ(second_instruction->GetNext(), got); ASSERT_EQ(second_instruction->GetPrevious(), first_instruction); ASSERT_EQ(got->GetNext(), nullptr); ASSERT_EQ(got->GetPrevious(), second_instruction); } } // namespace art