/* * 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 "graph_checker.h" #include "optimizing_unit_test.h" namespace art { /** * Create a simple control-flow graph composed of two blocks: * * BasicBlock 0, succ: 1 * 0: ReturnVoid 1 * BasicBlock 1, pred: 0 * 1: Exit */ HGraph* CreateSimpleCFG(ArenaAllocator* allocator) { HGraph* graph = CreateGraph(allocator); HBasicBlock* entry_block = new (allocator) HBasicBlock(graph); entry_block->AddInstruction(new (allocator) HReturnVoid()); graph->AddBlock(entry_block); graph->SetEntryBlock(entry_block); HBasicBlock* exit_block = new (allocator) HBasicBlock(graph); exit_block->AddInstruction(new (allocator) HExit()); graph->AddBlock(exit_block); graph->SetExitBlock(exit_block); entry_block->AddSuccessor(exit_block); graph->BuildDominatorTree(); return graph; } static void TestCode(const uint16_t* data) { ArenaPool pool; ArenaAllocator allocator(&pool); HGraph* graph = CreateCFG(&allocator, data); ASSERT_NE(graph, nullptr); GraphChecker graph_checker(graph); graph_checker.Run(); ASSERT_TRUE(graph_checker.IsValid()); } class GraphCheckerTest : public CommonCompilerTest {}; TEST_F(GraphCheckerTest, ReturnVoid) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::RETURN_VOID); TestCode(data); } TEST_F(GraphCheckerTest, CFG1) { const uint16_t data[] = ZERO_REGISTER_CODE_ITEM( Instruction::GOTO | 0x100, Instruction::RETURN_VOID); TestCode(data); } TEST_F(GraphCheckerTest, CFG2) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x100, Instruction::RETURN_VOID); TestCode(data); } TEST_F(GraphCheckerTest, CFG3) { const uint16_t data[] = ONE_REGISTER_CODE_ITEM( Instruction::CONST_4 | 0 | 0, Instruction::IF_EQ, 3, Instruction::GOTO | 0x100, Instruction::GOTO | 0xFF00); TestCode(data); } // Test case with an invalid graph containing inconsistent // predecessor/successor arcs in CFG. TEST_F(GraphCheckerTest, InconsistentPredecessorsAndSuccessors) { ArenaPool pool; ArenaAllocator allocator(&pool); HGraph* graph = CreateSimpleCFG(&allocator); GraphChecker graph_checker(graph); graph_checker.Run(); ASSERT_TRUE(graph_checker.IsValid()); // Remove the entry block from the exit block's predecessors, to create an // inconsistent successor/predecessor relation. graph->GetExitBlock()->RemovePredecessor(graph->GetEntryBlock()); graph_checker.Run(); ASSERT_FALSE(graph_checker.IsValid()); } // Test case with an invalid graph containing a non-branch last // instruction in a block. TEST_F(GraphCheckerTest, BlockEndingWithNonBranchInstruction) { ArenaPool pool; ArenaAllocator allocator(&pool); HGraph* graph = CreateSimpleCFG(&allocator); GraphChecker graph_checker(graph); graph_checker.Run(); ASSERT_TRUE(graph_checker.IsValid()); // Remove the sole instruction of the exit block (composed of a // single Exit instruction) to make it invalid (i.e. not ending by a // branch instruction). HBasicBlock* exit_block = graph->GetExitBlock(); HInstruction* last_inst = exit_block->GetLastInstruction(); exit_block->RemoveInstruction(last_inst); graph_checker.Run(); ASSERT_FALSE(graph_checker.IsValid()); } 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, Instruction::IF_EQ, 3, Instruction::CONST_4 | 4 << 12 | 0, Instruction::RETURN | 0 << 8); TestCode(data); } } // namespace art