1 /** 2 * Copyright (c) 2021-2022 Huawei Device Co., Ltd. 3 * Licensed under the Apache License, Version 2.0 (the "License"); 4 * you may not use this file except in compliance with the License. 5 * You may obtain a copy of the License at 6 * 7 * http://www.apache.org/licenses/LICENSE-2.0 8 * 9 * Unless required by applicable law or agreed to in writing, software 10 * distributed under the License is distributed on an "AS IS" BASIS, 11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 * See the License for the specific language governing permissions and 13 * limitations under the License. 14 */ 15 16 #ifndef COMPILER_TESTS_UNIT_TEST_H 17 #define COMPILER_TESTS_UNIT_TEST_H 18 19 #include <numeric> 20 #include <unordered_map> 21 #include "gtest/gtest.h" 22 #include "optimizer/ir/ir_constructor.h" 23 #include "mem/arena_allocator.h" 24 #include "mem/pool_manager.h" 25 #include "compiler.h" 26 #include "compiler_logger.h" 27 #include "graph_comparator.h" 28 #include "include/runtime.h" 29 30 namespace panda::compiler { 31 struct RuntimeInterfaceMock : public compiler::RuntimeInterface { GetMethodReturnTypeRuntimeInterfaceMock32 DataType::Type GetMethodReturnType(MethodPtr /* unsuded */) const override 33 { 34 return return_type; 35 } 36 GetMethodTotalArgumentTypeRuntimeInterfaceMock37 DataType::Type GetMethodTotalArgumentType(MethodPtr /* unused */, size_t index) const override 38 { 39 if (arg_types == nullptr || index >= arg_types->size()) { 40 return DataType::NO_TYPE; 41 } 42 return arg_types->at(index); 43 } 44 GetMethodTotalArgumentsCountRuntimeInterfaceMock45 size_t GetMethodTotalArgumentsCount(MethodPtr /* unused */) const override 46 { 47 if (arg_types == nullptr) { 48 return args_count; 49 } 50 return arg_types->size(); 51 } GetMethodArgumentsCountRuntimeInterfaceMock52 size_t GetMethodArgumentsCount(MethodPtr /* unused */) const override 53 { 54 return args_count; 55 } 56 GetMethodRegistersCountRuntimeInterfaceMock57 size_t GetMethodRegistersCount(MethodPtr /* unused */) const override 58 { 59 return vregs_count; 60 } 61 62 size_t args_count {0}; 63 size_t vregs_count {0}; 64 DataType::Type return_type {DataType::NO_TYPE}; 65 ArenaVector<DataType::Type> *arg_types {nullptr}; 66 }; 67 68 class CommonTest : public ::testing::Test { 69 public: CommonTest()70 CommonTest() 71 { 72 #if defined(PANDA_TARGET_ARM64) || defined(PANDA_TARGET_32) 73 // We have issue with QEMU - so reduce memory heap 74 panda::mem::MemConfig::Initialize(32_MB, 64_MB, 200_MB, 32_MB); 75 #else 76 panda::mem::MemConfig::Initialize(32_MB, 64_MB, 256_MB, 32_MB); 77 #endif 78 PoolManager::Initialize(); 79 allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER); 80 object_allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_OBJECT); 81 local_allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER); 82 builder_ = new IrConstructor(); 83 } 84 ~CommonTest() override; 85 GetAllocator()86 ArenaAllocator *GetAllocator() const 87 { 88 return allocator_; 89 } 90 GetObjectAllocator()91 ArenaAllocator *GetObjectAllocator() const 92 { 93 return object_allocator_; 94 } 95 GetLocalAllocator()96 ArenaAllocator *GetLocalAllocator() const 97 { 98 return local_allocator_; 99 } 100 GetArch()101 Arch GetArch() const 102 { 103 return arch_; 104 } 105 106 Graph *CreateEmptyGraph(bool is_osr = false) const 107 { 108 return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, is_osr); 109 } 110 CreateEmptyGraph(Arch arch)111 Graph *CreateEmptyGraph(Arch arch) const 112 { 113 return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch, false); 114 } 115 116 Graph *CreateGraphStartEndBlocks(bool is_dynamic = false) const 117 { 118 auto graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, is_dynamic, false); 119 graph->CreateStartBlock(); 120 graph->CreateEndBlock(); 121 return graph; 122 } CreateDynEmptyGraph()123 Graph *CreateDynEmptyGraph() const 124 { 125 return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, true, false); 126 } CreateEmptyBytecodeGraph()127 Graph *CreateEmptyBytecodeGraph() const 128 { 129 return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), Arch::NONE, false, true); 130 } CreateEmptyFastpathGraph(Arch arch)131 Graph *CreateEmptyFastpathGraph(Arch arch) const 132 { 133 auto graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch, false); 134 graph->SetMode(GraphMode::FastPath()); 135 return graph; 136 } 137 CreateEmptyBlock(Graph * graph)138 BasicBlock *CreateEmptyBlock(Graph *graph) const 139 { 140 auto block = graph->GetAllocator()->New<BasicBlock>(graph); 141 graph->AddBlock(block); 142 return block; 143 } 144 GetBlocksById(Graph * graph,std::vector<size_t> && ids)145 ArenaVector<BasicBlock *> GetBlocksById(Graph *graph, std::vector<size_t> &&ids) const 146 { 147 ArenaVector<BasicBlock *> blocks(graph->GetAllocator()->Adapter()); 148 for (auto id : ids) { 149 blocks.push_back(&BB(id)); 150 } 151 return blocks; 152 } 153 CheckInputs(Inst & inst,std::initializer_list<size_t> list)154 bool CheckInputs(Inst &inst, std::initializer_list<size_t> list) const 155 { 156 if (inst.GetInputs().Size() != list.size()) { 157 return false; 158 } 159 auto it2 = list.begin(); 160 for (auto it = inst.GetInputs().begin(); it != inst.GetInputs().end(); ++it, ++it2) { 161 if (it->GetInst() != &INS(*it2)) { 162 return false; 163 } 164 } 165 return true; 166 } 167 CheckUsers(Inst & inst,std::initializer_list<int> list)168 bool CheckUsers(Inst &inst, std::initializer_list<int> list) const 169 { 170 std::unordered_map<int, size_t> users_map; 171 for (auto l : list) 172 ++users_map[l]; 173 for (auto &user : inst.GetUsers()) { 174 EXPECT_EQ(user.GetInst()->GetInput(user.GetIndex()).GetInst(), &inst); 175 if (users_map[user.GetInst()->GetId()]-- == 0) 176 return false; 177 } 178 auto rest = std::accumulate(users_map.begin(), users_map.end(), 0, [](int a, auto &x) { return a + x.second; }); 179 EXPECT_EQ(rest, 0); 180 return (rest == 0) ? true : false; 181 } 182 183 protected: 184 IrConstructor *builder_; 185 186 private: 187 ArenaAllocator *allocator_; 188 ArenaAllocator *object_allocator_; 189 ArenaAllocator *local_allocator_; 190 #ifdef PANDA_TARGET_ARM32 191 Arch arch_ {Arch::AARCH32}; 192 #else 193 Arch arch_ {Arch::AARCH64}; 194 #endif 195 }; 196 197 class GraphTest : public CommonTest { 198 public: GraphTest()199 GraphTest() : graph_(CreateEmptyGraph()) 200 { 201 graph_->SetRuntime(&runtime_); 202 } ~GraphTest()203 ~GraphTest() override {} 204 GetGraph()205 Graph *GetGraph() const 206 { 207 return graph_; 208 } 209 ResetGraph()210 void ResetGraph() 211 { 212 graph_ = CreateEmptyGraph(); 213 graph_->SetRuntime(&runtime_); 214 } 215 SetNumVirtRegs(size_t num)216 void SetNumVirtRegs(size_t num) 217 { 218 runtime_.vregs_count = num; 219 graph_->SetVRegsCount(std::max(graph_->GetVRegsCount(), runtime_.vregs_count + runtime_.args_count + 1)); 220 } 221 SetNumArgs(size_t num)222 void SetNumArgs(size_t num) 223 { 224 runtime_.args_count = num; 225 graph_->SetVRegsCount(std::max(graph_->GetVRegsCount(), runtime_.vregs_count + runtime_.args_count + 1)); 226 } 227 228 protected: 229 RuntimeInterfaceMock runtime_; 230 Graph *graph_ {nullptr}; 231 }; 232 233 class PandaRuntimeTest : public ::testing::Test, public PandaRuntimeInterface { 234 public: 235 PandaRuntimeTest(); 236 237 ~PandaRuntimeTest() override; 238 239 static void Initialize(int argc, char **argv); 240 CreateGraph()241 Graph *CreateGraph() 242 { 243 auto graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_); 244 graph->SetRuntime(this); 245 return graph; 246 } 247 CreateGraphOsr()248 Graph *CreateGraphOsr() 249 { 250 Graph *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, true); 251 graph->SetRuntime(this); 252 return graph; 253 } 254 255 // this method is needed to create a graph with a working dump CreateGraphWithDefaultRuntime()256 Graph *CreateGraphWithDefaultRuntime() 257 { 258 auto *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_); 259 graph->SetRuntime(GetDefaultRuntime()); 260 return graph; 261 } 262 CreateGraphDynWithDefaultRuntime()263 Graph *CreateGraphDynWithDefaultRuntime() 264 { 265 auto *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_); 266 graph->SetRuntime(GetDefaultRuntime()); 267 graph->SetDynamicMethod(); 268 return graph; 269 } 270 271 // this method is needed to create a graph with a working dump CreateGraphOsrWithDefaultRuntime()272 Graph *CreateGraphOsrWithDefaultRuntime() 273 { 274 auto *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, true); 275 graph->SetRuntime(GetDefaultRuntime()); 276 return graph; 277 } 278 GetAllocator()279 ArenaAllocator *GetAllocator() 280 { 281 return allocator_; 282 } 283 GetLocalAllocator()284 ArenaAllocator *GetLocalAllocator() 285 { 286 return local_allocator_; 287 } 288 GetGraph()289 virtual Graph *GetGraph() 290 { 291 return graph_; 292 } 293 GetClassLinker()294 auto GetClassLinker() 295 { 296 return panda::Runtime::GetCurrent()->GetClassLinker(); 297 } 298 299 void EnableLogs(Logger::Level level = Logger::Level::DEBUG) 300 { 301 Logger::EnableComponent(Logger::Component::COMPILER); 302 Logger::SetLevel(level); 303 } 304 GetExecPath()305 const char *GetExecPath() const 306 { 307 return exec_path_; 308 } 309 310 static RuntimeInterface *GetDefaultRuntime(); 311 312 protected: 313 IrConstructor *builder_; 314 315 private: 316 Graph *graph_ {nullptr}; 317 ArenaAllocator *allocator_ {nullptr}; 318 ArenaAllocator *local_allocator_ {nullptr}; 319 static inline const char *exec_path_ {nullptr}; 320 Arch arch_ {RUNTIME_ARCH}; 321 }; 322 323 class AsmTest : public PandaRuntimeTest { 324 public: 325 std::unique_ptr<const panda_file::File> ParseToFile(const char *source, const char *file_name = "test.pb"); 326 bool Parse(const char *source, const char *file_name = "test.pb"); 327 Graph *BuildGraph(const char *method_name, Graph *graph = nullptr); 328 void CleanUp(Graph *graph); 329 330 virtual ~AsmTest() = default; 331 332 template <bool with_cleanup = false> 333 bool ParseToGraph(const char *source, const char *method_name, Graph *graph = nullptr) 334 { 335 if (!Parse(source)) { 336 return false; 337 } 338 if (graph == nullptr) { 339 graph = GetGraph(); 340 } 341 if (BuildGraph(method_name, graph) == nullptr) { 342 return false; 343 } 344 if constexpr (with_cleanup) { 345 CleanUp(graph); 346 } 347 return true; 348 } 349 }; 350 351 struct TmpFile { TmpFileTmpFile352 explicit TmpFile(const char *file_name) : file_name_(file_name) {} ~TmpFileTmpFile353 ~TmpFile() 354 { 355 ASSERT(file_name_ != nullptr); 356 remove(file_name_); 357 } GetFileNameTmpFile358 const char *GetFileName() const 359 { 360 return file_name_; 361 } 362 363 private: 364 const char *file_name_ {nullptr}; 365 }; 366 } // namespace panda::compiler 367 368 #endif // COMPILER_TESTS_UNIT_TEST_H 369