1 /* 2 * Copyright (C) 2014 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_ 18 #define ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_ 19 20 #include <memory> 21 #include <vector> 22 23 #include "base/malloc_arena_pool.h" 24 #include "base/scoped_arena_allocator.h" 25 #include "builder.h" 26 #include "common_compiler_test.h" 27 #include "dex/code_item_accessors-inl.h" 28 #include "dex/dex_file.h" 29 #include "dex/dex_instruction.h" 30 #include "dex/standard_dex_file.h" 31 #include "driver/dex_compilation_unit.h" 32 #include "graph_checker.h" 33 #include "handle_scope-inl.h" 34 #include "mirror/class_loader.h" 35 #include "mirror/dex_cache.h" 36 #include "nodes.h" 37 #include "scoped_thread_state_change.h" 38 #include "ssa_builder.h" 39 #include "ssa_liveness_analysis.h" 40 41 #include "gtest/gtest.h" 42 43 namespace art { 44 45 #define NUM_INSTRUCTIONS(...) \ 46 (sizeof((uint16_t[]) {__VA_ARGS__}) /sizeof(uint16_t)) 47 48 #define N_REGISTERS_CODE_ITEM(NUM_REGS, ...) \ 49 { NUM_REGS, 0, 0, 0, 0, 0, NUM_INSTRUCTIONS(__VA_ARGS__), 0, __VA_ARGS__ } 50 51 #define ZERO_REGISTER_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(0, __VA_ARGS__) 52 #define ONE_REGISTER_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(1, __VA_ARGS__) 53 #define TWO_REGISTERS_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(2, __VA_ARGS__) 54 #define THREE_REGISTERS_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(3, __VA_ARGS__) 55 #define FOUR_REGISTERS_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(4, __VA_ARGS__) 56 #define FIVE_REGISTERS_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(5, __VA_ARGS__) 57 #define SIX_REGISTERS_CODE_ITEM(...) N_REGISTERS_CODE_ITEM(6, __VA_ARGS__) 58 59 LiveInterval* BuildInterval(const size_t ranges[][2], 60 size_t number_of_ranges, 61 ScopedArenaAllocator* allocator, 62 int reg = -1, 63 HInstruction* defined_by = nullptr) { 64 LiveInterval* interval = 65 LiveInterval::MakeInterval(allocator, DataType::Type::kInt32, defined_by); 66 if (defined_by != nullptr) { 67 defined_by->SetLiveInterval(interval); 68 } 69 for (size_t i = number_of_ranges; i > 0; --i) { 70 interval->AddRange(ranges[i - 1][0], ranges[i - 1][1]); 71 } 72 interval->SetRegister(reg); 73 return interval; 74 } 75 76 void RemoveSuspendChecks(HGraph* graph) { 77 for (HBasicBlock* block : graph->GetBlocks()) { 78 if (block != nullptr) { 79 if (block->GetLoopInformation() != nullptr) { 80 block->GetLoopInformation()->SetSuspendCheck(nullptr); 81 } 82 for (HInstructionIterator it(block->GetInstructions()); !it.Done(); it.Advance()) { 83 HInstruction* current = it.Current(); 84 if (current->IsSuspendCheck()) { 85 current->GetBlock()->RemoveInstruction(current); 86 } 87 } 88 } 89 } 90 } 91 92 class ArenaPoolAndAllocator { 93 public: 94 ArenaPoolAndAllocator() 95 : pool_(), allocator_(&pool_), arena_stack_(&pool_), scoped_allocator_(&arena_stack_) { } 96 97 ArenaAllocator* GetAllocator() { return &allocator_; } 98 ArenaStack* GetArenaStack() { return &arena_stack_; } 99 ScopedArenaAllocator* GetScopedAllocator() { return &scoped_allocator_; } 100 101 private: 102 MallocArenaPool pool_; 103 ArenaAllocator allocator_; 104 ArenaStack arena_stack_; 105 ScopedArenaAllocator scoped_allocator_; 106 }; 107 108 // Have a separate helper so the OptimizingCFITest can inherit it without causing 109 // multiple inheritance errors from having two gtest as a parent twice. 110 class OptimizingUnitTestHelper { 111 public: 112 OptimizingUnitTestHelper() : pool_and_allocator_(new ArenaPoolAndAllocator()) { } 113 114 ArenaAllocator* GetAllocator() { return pool_and_allocator_->GetAllocator(); } 115 ArenaStack* GetArenaStack() { return pool_and_allocator_->GetArenaStack(); } 116 ScopedArenaAllocator* GetScopedAllocator() { return pool_and_allocator_->GetScopedAllocator(); } 117 118 void ResetPoolAndAllocator() { 119 pool_and_allocator_.reset(new ArenaPoolAndAllocator()); 120 handles_.reset(); // When getting rid of the old HGraph, we can also reset handles_. 121 } 122 123 HGraph* CreateGraph() { 124 ArenaAllocator* const allocator = pool_and_allocator_->GetAllocator(); 125 126 // Reserve a big array of 0s so the dex file constructor can offsets from the header. 127 static constexpr size_t kDexDataSize = 4 * KB; 128 const uint8_t* dex_data = reinterpret_cast<uint8_t*>(allocator->Alloc(kDexDataSize)); 129 130 // Create the dex file based on the fake data. Call the constructor so that we can use virtual 131 // functions. Don't use the arena for the StandardDexFile otherwise the dex location leaks. 132 dex_files_.emplace_back(new StandardDexFile( 133 dex_data, 134 sizeof(StandardDexFile::Header), 135 "no_location", 136 /*location_checksum*/ 0, 137 /*oat_dex_file*/ nullptr, 138 /*container*/ nullptr)); 139 140 return new (allocator) HGraph( 141 allocator, 142 pool_and_allocator_->GetArenaStack(), 143 *dex_files_.back(), 144 /*method_idx*/-1, 145 kRuntimeISA); 146 } 147 148 // Create a control-flow graph from Dex instructions. 149 HGraph* CreateCFG(const std::vector<uint16_t>& data, 150 DataType::Type return_type = DataType::Type::kInt32) { 151 HGraph* graph = CreateGraph(); 152 153 // The code item data might not aligned to 4 bytes, copy it to ensure that. 154 const size_t code_item_size = data.size() * sizeof(data.front()); 155 void* aligned_data = GetAllocator()->Alloc(code_item_size); 156 memcpy(aligned_data, &data[0], code_item_size); 157 CHECK_ALIGNED(aligned_data, StandardDexFile::CodeItem::kAlignment); 158 const dex::CodeItem* code_item = reinterpret_cast<const dex::CodeItem*>(aligned_data); 159 160 { 161 ScopedObjectAccess soa(Thread::Current()); 162 if (handles_ == nullptr) { 163 handles_.reset(new VariableSizedHandleScope(soa.Self())); 164 } 165 const DexCompilationUnit* dex_compilation_unit = 166 new (graph->GetAllocator()) DexCompilationUnit( 167 handles_->NewHandle<mirror::ClassLoader>(nullptr), 168 /* class_linker= */ nullptr, 169 graph->GetDexFile(), 170 code_item, 171 /* class_def_index= */ DexFile::kDexNoIndex16, 172 /* method_idx= */ dex::kDexNoIndex, 173 /* access_flags= */ 0u, 174 /* verified_method= */ nullptr, 175 handles_->NewHandle<mirror::DexCache>(nullptr)); 176 CodeItemDebugInfoAccessor accessor(graph->GetDexFile(), code_item, /*dex_method_idx*/ 0u); 177 HGraphBuilder builder(graph, dex_compilation_unit, accessor, handles_.get(), return_type); 178 bool graph_built = (builder.BuildGraph() == kAnalysisSuccess); 179 return graph_built ? graph : nullptr; 180 } 181 } 182 183 private: 184 std::vector<std::unique_ptr<const StandardDexFile>> dex_files_; 185 std::unique_ptr<ArenaPoolAndAllocator> pool_and_allocator_; 186 std::unique_ptr<VariableSizedHandleScope> handles_; 187 }; 188 189 class OptimizingUnitTest : public CommonCompilerTest, public OptimizingUnitTestHelper {}; 190 191 // OptimizingUnitTest with some handy functions to ease the graph creation. 192 class ImprovedOptimizingUnitTest : public OptimizingUnitTest { 193 public: 194 ImprovedOptimizingUnitTest() : graph_(CreateGraph()), 195 entry_block_(nullptr), 196 return_block_(nullptr), 197 exit_block_(nullptr), 198 parameter_(nullptr) {} 199 200 virtual ~ImprovedOptimizingUnitTest() {} 201 202 void InitGraph() { 203 entry_block_ = new (GetAllocator()) HBasicBlock(graph_); 204 graph_->AddBlock(entry_block_); 205 graph_->SetEntryBlock(entry_block_); 206 207 return_block_ = new (GetAllocator()) HBasicBlock(graph_); 208 graph_->AddBlock(return_block_); 209 210 exit_block_ = new (GetAllocator()) HBasicBlock(graph_); 211 graph_->AddBlock(exit_block_); 212 graph_->SetExitBlock(exit_block_); 213 214 entry_block_->AddSuccessor(return_block_); 215 return_block_->AddSuccessor(exit_block_); 216 217 parameter_ = new (GetAllocator()) HParameterValue(graph_->GetDexFile(), 218 dex::TypeIndex(0), 219 0, 220 DataType::Type::kInt32); 221 entry_block_->AddInstruction(parameter_); 222 return_block_->AddInstruction(new (GetAllocator()) HReturnVoid()); 223 exit_block_->AddInstruction(new (GetAllocator()) HExit()); 224 } 225 226 bool CheckGraph() { 227 GraphChecker checker(graph_); 228 checker.Run(); 229 if (!checker.IsValid()) { 230 for (const std::string& error : checker.GetErrors()) { 231 std::cout << error << std::endl; 232 } 233 return false; 234 } 235 return true; 236 } 237 238 HEnvironment* ManuallyBuildEnvFor(HInstruction* instruction, 239 ArenaVector<HInstruction*>* current_locals) { 240 HEnvironment* environment = new (GetAllocator()) HEnvironment( 241 (GetAllocator()), 242 current_locals->size(), 243 graph_->GetArtMethod(), 244 instruction->GetDexPc(), 245 instruction); 246 247 environment->CopyFrom(ArrayRef<HInstruction* const>(*current_locals)); 248 instruction->SetRawEnvironment(environment); 249 return environment; 250 } 251 252 protected: 253 HGraph* graph_; 254 255 HBasicBlock* entry_block_; 256 HBasicBlock* return_block_; 257 HBasicBlock* exit_block_; 258 259 HInstruction* parameter_; 260 }; 261 262 // Naive string diff data type. 263 typedef std::list<std::pair<std::string, std::string>> diff_t; 264 265 // An alias for the empty string used to make it clear that a line is 266 // removed in a diff. 267 static const std::string removed = ""; // NOLINT [runtime/string] [4] 268 269 // Naive patch command: apply a diff to a string. 270 inline std::string Patch(const std::string& original, const diff_t& diff) { 271 std::string result = original; 272 for (const auto& p : diff) { 273 std::string::size_type pos = result.find(p.first); 274 DCHECK_NE(pos, std::string::npos) 275 << "Could not find: \"" << p.first << "\" in \"" << result << "\""; 276 result.replace(pos, p.first.size(), p.second); 277 } 278 return result; 279 } 280 281 // Returns if the instruction is removed from the graph. 282 inline bool IsRemoved(HInstruction* instruction) { 283 return instruction->GetBlock() == nullptr; 284 } 285 286 } // namespace art 287 288 #endif // ART_COMPILER_OPTIMIZING_OPTIMIZING_UNIT_TEST_H_ 289