1 /* 2 * Copyright (c) 2021-2024 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 "macros.h" 20 #include "optimizer/ir/ir_constructor.h" 21 #include "gtest/gtest.h" 22 #include "mem/arena_allocator.h" 23 #include "mem/pool_manager.h" 24 #include <numeric> 25 #include <unordered_map> 26 #include "compiler.h" 27 #include "compiler_logger.h" 28 #include "graph_comparator.h" 29 #include "include/runtime.h" 30 #include "libpandafile/file_item_container.h" 31 32 namespace ark::compiler { 33 struct RuntimeInterfaceMock : public compiler::RuntimeInterface { 34 // Only one exact CALLEE may exist in fake runtime GetMethodByIdRuntimeInterfaceMock35 MethodPtr GetMethodById(MethodPtr /* unused */, MethodId id) const override 36 { 37 savedCalleeId_ = id; 38 return reinterpret_cast<MethodPtr>(CALLEE); 39 } 40 GetMethodIdRuntimeInterfaceMock41 MethodId GetMethodId(MethodPtr method) const override 42 { 43 return (reinterpret_cast<uintptr_t>(method) == CALLEE) ? savedCalleeId_ : 0; 44 } 45 GetUniqMethodIdRuntimeInterfaceMock46 uint64_t GetUniqMethodId(MethodPtr method) const override 47 { 48 return (reinterpret_cast<uintptr_t>(method) == CALLEE) ? savedCalleeId_ : 0; 49 } 50 GetBinaryFileForMethodRuntimeInterfaceMock51 BinaryFilePtr GetBinaryFileForMethod(MethodPtr /* unused */) const override 52 { 53 static constexpr uintptr_t FAKE_FILE = 0xdeadf; 54 return reinterpret_cast<void *>(FAKE_FILE); 55 } 56 GetMethodReturnTypeRuntimeInterfaceMock57 DataType::Type GetMethodReturnType(MethodPtr /* unused */) const override 58 { 59 return returnType_; 60 } 61 GetMethodReturnTypeRuntimeInterfaceMock62 DataType::Type GetMethodReturnType(MethodPtr /* unused */, MethodId /* unused */) const override 63 { 64 return returnType_; 65 } 66 GetMethodTotalArgumentTypeRuntimeInterfaceMock67 DataType::Type GetMethodTotalArgumentType(MethodPtr /* unused */, size_t index) const override 68 { 69 if (argTypes_ == nullptr || index >= argTypes_->size()) { 70 return DataType::NO_TYPE; 71 } 72 return argTypes_->at(index); 73 } 74 GetMethodTotalArgumentsCountRuntimeInterfaceMock75 size_t GetMethodTotalArgumentsCount(MethodPtr /* unused */) const override 76 { 77 if (argTypes_ == nullptr) { 78 return argsCount_; 79 } 80 return argTypes_->size(); 81 } 82 GetMethodArgumentsCountRuntimeInterfaceMock83 size_t GetMethodArgumentsCount(MethodPtr /* unused */) const override 84 { 85 return argsCount_; 86 } 87 GetMethodRegistersCountRuntimeInterfaceMock88 size_t GetMethodRegistersCount(MethodPtr /* unused */) const override 89 { 90 return vregsCount_; 91 } 92 GetFieldTypeRuntimeInterfaceMock93 DataType::Type GetFieldType(PandaRuntimeInterface::FieldPtr field) const override 94 { 95 return fieldTypes_ == nullptr ? DataType::NO_TYPE : (*fieldTypes_)[field]; 96 } 97 GetArrayComponentTypeRuntimeInterfaceMock98 DataType::Type GetArrayComponentType(PandaRuntimeInterface::ClassPtr klass) const override 99 { 100 return classTypes_ == nullptr ? DataType::NO_TYPE : (*classTypes_)[klass]; 101 } 102 103 private: 104 static constexpr uintptr_t METHOD = 0xdead; 105 static constexpr uintptr_t CALLEE = 0xdeadc; 106 107 size_t argsCount_ {0}; 108 size_t vregsCount_ {0}; 109 mutable unsigned savedCalleeId_ {0}; 110 DataType::Type returnType_ {DataType::NO_TYPE}; 111 ArenaVector<DataType::Type> *argTypes_ {nullptr}; 112 ArenaUnorderedMap<PandaRuntimeInterface::FieldPtr, DataType::Type> *fieldTypes_ {nullptr}; 113 ArenaUnorderedMap<PandaRuntimeInterface::ClassPtr, DataType::Type> *classTypes_ {nullptr}; 114 115 friend class GraphTest; 116 friend class GraphCreator; 117 friend class InstGenerator; 118 }; 119 120 class CommonTest : public ::testing::Test { 121 public: CommonTest()122 CommonTest() 123 { 124 #if defined(PANDA_TARGET_ARM64) || defined(PANDA_TARGET_32) 125 // We have issue with QEMU - so reduce memory heap 126 ark::mem::MemConfig::Initialize(32_MB, 64_MB, 200_MB, 32_MB, 0, 0); // NOLINT(readability-magic-numbers) 127 #else 128 ark::mem::MemConfig::Initialize(32_MB, 64_MB, 256_MB, 32_MB, 0, 0); // NOLINT(readability-magic-numbers) 129 #endif 130 PoolManager::Initialize(); 131 allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER); 132 objectAllocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_OBJECT); 133 localAllocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER); 134 builder_ = new IrConstructor(); 135 } 136 ~CommonTest() override; 137 138 NO_COPY_SEMANTIC(CommonTest); 139 NO_MOVE_SEMANTIC(CommonTest); 140 GetAllocator()141 ArenaAllocator *GetAllocator() const 142 { 143 return allocator_; 144 } 145 GetObjectAllocator()146 ArenaAllocator *GetObjectAllocator() const 147 { 148 return objectAllocator_; 149 } 150 GetLocalAllocator()151 ArenaAllocator *GetLocalAllocator() const 152 { 153 return localAllocator_; 154 } 155 GetArch()156 Arch GetArch() const 157 { 158 return arch_; 159 } 160 161 Graph *CreateEmptyGraph(bool isOsr = false) const 162 { 163 return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, isOsr); 164 } 165 CreateEmptyGraph(Arch arch)166 Graph *CreateEmptyGraph(Arch arch) const 167 { 168 return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch, false); 169 } 170 CreateOsrGraph()171 Graph *CreateOsrGraph() const 172 { 173 return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, true); 174 } 175 176 Graph *CreateGraphStartEndBlocks(bool isDynamic = false) const 177 { 178 auto graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, isDynamic, false); 179 graph->CreateStartBlock(); 180 graph->CreateEndBlock(); 181 return graph; 182 } CreateDynEmptyGraph()183 Graph *CreateDynEmptyGraph() const 184 { 185 return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, true, false); 186 } CreateEmptyBytecodeGraph()187 Graph *CreateEmptyBytecodeGraph() const 188 { 189 return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), Arch::NONE, false, true); 190 } CreateEmptyFastpathGraph(Arch arch)191 Graph *CreateEmptyFastpathGraph(Arch arch) const 192 { 193 auto graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch, false); 194 graph->SetMode(GraphMode::FastPath()); 195 return graph; 196 } 197 CreateEmptyBlock(Graph * graph)198 BasicBlock *CreateEmptyBlock(Graph *graph) const 199 { 200 auto block = graph->GetAllocator()->New<BasicBlock>(graph); 201 graph->AddBlock(block); 202 return block; 203 } 204 GetBlocksById(Graph * graph,std::vector<size_t> && ids)205 ArenaVector<BasicBlock *> GetBlocksById(Graph *graph, std::vector<size_t> &&ids) const 206 { 207 ArenaVector<BasicBlock *> blocks(graph->GetAllocator()->Adapter()); 208 for (auto id : ids) { 209 blocks.push_back(&BB(id)); 210 } 211 return blocks; 212 } 213 CheckInputs(Inst & inst,std::initializer_list<size_t> list)214 bool CheckInputs(Inst &inst, std::initializer_list<size_t> list) const 215 { 216 if (inst.GetInputs().Size() != list.size()) { 217 return false; 218 } 219 auto it2 = list.begin(); 220 for (auto it = inst.GetInputs().begin(); it != inst.GetInputs().end(); ++it, ++it2) { 221 if (it->GetInst() != &INS(*it2)) { 222 return false; 223 } 224 } 225 return true; 226 } 227 CheckUsers(Inst & inst,std::initializer_list<int> list)228 bool CheckUsers(Inst &inst, std::initializer_list<int> list) const 229 { 230 std::unordered_map<int, size_t> usersMap; 231 for (auto l : list) { 232 ++usersMap[l]; 233 } 234 for (auto &user : inst.GetUsers()) { 235 EXPECT_EQ(user.GetInst()->GetInput(user.GetIndex()).GetInst(), &inst); 236 if (usersMap[user.GetInst()->GetId()]-- == 0) { 237 return false; 238 } 239 } 240 auto rest = std::accumulate(usersMap.begin(), usersMap.end(), 0, [](int a, auto &x) { return a + x.second; }); 241 EXPECT_EQ(rest, 0); 242 return rest == 0; 243 } 244 245 protected: 246 IrConstructor *builder_; // NOLINT(misc-non-private-member-variables-in-classes) 247 248 private: 249 ArenaAllocator *allocator_; 250 ArenaAllocator *objectAllocator_; 251 ArenaAllocator *localAllocator_; 252 #ifdef PANDA_TARGET_ARM32 253 Arch arch_ {Arch::AARCH32}; 254 #else 255 Arch arch_ {Arch::AARCH64}; 256 #endif 257 }; 258 259 class GraphTest : public CommonTest { 260 public: GraphTest()261 GraphTest() : graph_(CreateEmptyGraph()) 262 { 263 graph_->SetRuntime(&runtime_); 264 runtime_.fieldTypes_ = 265 graph_->GetAllocator()->New<ArenaUnorderedMap<PandaRuntimeInterface::FieldPtr, DataType::Type>>( 266 graph_->GetAllocator()->Adapter()); 267 268 runtime_.classTypes_ = 269 graph_->GetAllocator()->New<ArenaUnorderedMap<PandaRuntimeInterface::ClassPtr, DataType::Type>>( 270 graph_->GetAllocator()->Adapter()); 271 } 272 ~GraphTest() override = default; 273 274 NO_MOVE_SEMANTIC(GraphTest); 275 NO_COPY_SEMANTIC(GraphTest); 276 GetGraph()277 Graph *GetGraph() const 278 { 279 return graph_; 280 } 281 SetGraphArch(Arch arch)282 void SetGraphArch(Arch arch) 283 { 284 graph_ = CreateEmptyGraph(arch); 285 graph_->SetRuntime(&runtime_); 286 } 287 ResetGraph()288 void ResetGraph() 289 { 290 graph_ = CreateEmptyGraph(); 291 graph_->SetRuntime(&runtime_); 292 } 293 SetNumVirtRegs(size_t num)294 void SetNumVirtRegs(size_t num) 295 { 296 runtime_.vregsCount_ = num; 297 graph_->SetVRegsCount(std::max(graph_->GetVRegsCount(), runtime_.vregsCount_ + runtime_.argsCount_ + 1)); 298 } 299 SetNumArgs(size_t num)300 void SetNumArgs(size_t num) 301 { 302 runtime_.argsCount_ = num; 303 graph_->SetVRegsCount(std::max(graph_->GetVRegsCount(), runtime_.vregsCount_ + runtime_.argsCount_ + 1)); 304 } 305 RegisterFieldType(PandaRuntimeInterface::FieldPtr field,DataType::Type type)306 void RegisterFieldType(PandaRuntimeInterface::FieldPtr field, DataType::Type type) 307 { 308 (*runtime_.fieldTypes_)[field] = type; 309 } 310 RegisterClassType(PandaRuntimeInterface::ClassPtr klass,DataType::Type type)311 void RegisterClassType(PandaRuntimeInterface::ClassPtr klass, DataType::Type type) 312 { 313 (*runtime_.classTypes_)[klass] = type; 314 } 315 316 protected: 317 RuntimeInterfaceMock runtime_; // NOLINT(misc-non-private-member-variables-in-classes) 318 Graph *graph_ {nullptr}; // NOLINT(misc-non-private-member-variables-in-classes) 319 }; 320 321 // NOLINTNEXTLINE(fuchsia-multiple-inheritance) 322 class PandaRuntimeTest : public ::testing::Test, public PandaRuntimeInterface { 323 public: 324 PandaRuntimeTest(); 325 ~PandaRuntimeTest() override; 326 NO_MOVE_SEMANTIC(PandaRuntimeTest); 327 NO_COPY_SEMANTIC(PandaRuntimeTest); 328 329 static void Initialize(int argc, char **argv); 330 CreateGraph()331 Graph *CreateGraph() 332 { 333 auto graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_); 334 graph->SetRuntime(this); 335 return graph; 336 } 337 CreateGraphOsr()338 Graph *CreateGraphOsr() 339 { 340 Graph *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, true); 341 graph->SetRuntime(this); 342 return graph; 343 } 344 345 // this method is needed to create a graph with a working dump CreateGraphWithDefaultRuntime()346 Graph *CreateGraphWithDefaultRuntime() 347 { 348 auto *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_); 349 graph->SetRuntime(GetDefaultRuntime()); 350 return graph; 351 } 352 CreateGraphDynWithDefaultRuntime()353 Graph *CreateGraphDynWithDefaultRuntime() 354 { 355 auto *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_); 356 graph->SetRuntime(GetDefaultRuntime()); 357 graph->SetDynamicMethod(); 358 #ifndef NDEBUG 359 graph->SetDynUnitTestFlag(); 360 #endif 361 return graph; 362 } 363 CreateGraphDynStubWithDefaultRuntime()364 Graph *CreateGraphDynStubWithDefaultRuntime() 365 { 366 auto *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_); 367 graph->SetRuntime(GetDefaultRuntime()); 368 graph->SetDynamicMethod(); 369 graph->SetDynamicStub(); 370 #ifndef NDEBUG 371 graph->SetDynUnitTestFlag(); 372 #endif 373 return graph; 374 } 375 376 // this method is needed to create a graph with a working dump CreateGraphOsrWithDefaultRuntime()377 Graph *CreateGraphOsrWithDefaultRuntime() 378 { 379 auto *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, true); 380 graph->SetRuntime(GetDefaultRuntime()); 381 return graph; 382 } 383 GetAllocator()384 ArenaAllocator *GetAllocator() 385 { 386 return allocator_; 387 } 388 GetLocalAllocator()389 ArenaAllocator *GetLocalAllocator() 390 { 391 return localAllocator_; 392 } 393 GetGraph()394 virtual Graph *GetGraph() 395 { 396 return graph_; 397 } 398 GetClassLinker()399 auto GetClassLinker() 400 { 401 return ark::Runtime::GetCurrent()->GetClassLinker(); 402 } 403 404 void EnableLogs(Logger::Level level = Logger::Level::DEBUG) 405 { 406 Logger::EnableComponent(Logger::Component::COMPILER); 407 Logger::SetLevel(level); 408 } GetExecPath()409 const char *GetExecPath() const 410 { 411 return execPath_; 412 } 413 static RuntimeInterface *GetDefaultRuntime(); 414 415 protected: 416 IrConstructor *builder_; // NOLINT(misc-non-private-member-variables-in-classes) 417 418 private: 419 Graph *graph_ {nullptr}; 420 ArenaAllocator *allocator_ {nullptr}; 421 ArenaAllocator *localAllocator_ {nullptr}; 422 static inline const char *execPath_ {nullptr}; 423 Arch arch_ {RUNTIME_ARCH}; 424 }; 425 426 // NOLINTNEXTLINE(fuchsia-multia-inheritance,cppcoreguidelines-special-member-functions) 427 class AsmTest : public PandaRuntimeTest { 428 public: 429 std::unique_ptr<const panda_file::File> ParseToFile(const char *source, const char *fileName = "test.pb"); 430 bool Parse(const char *source, const char *fileName = "test.pb"); 431 Graph *BuildGraph(const char *methodName, Graph *graph = nullptr); 432 void CleanUp(Graph *graph); 433 434 AsmTest() = default; 435 ~AsmTest() override = default; 436 NO_MOVE_SEMANTIC(AsmTest); 437 NO_COPY_SEMANTIC(AsmTest); 438 439 template <bool WITH_CLEANUP = false> 440 bool ParseToGraph(const char *source, const char *methodName, Graph *graph = nullptr) 441 { 442 if (!Parse(source)) { 443 return false; 444 } 445 if (graph == nullptr) { 446 graph = GetGraph(); 447 } 448 if (BuildGraph(methodName, graph) == nullptr) { 449 return false; 450 } 451 if constexpr (WITH_CLEANUP) { 452 CleanUp(graph); 453 } 454 return true; 455 } 456 }; 457 458 struct TmpFile { TmpFileTmpFile459 explicit TmpFile(const char *fileName) : fileName_(fileName) {} ~TmpFileTmpFile460 ~TmpFile() 461 { 462 ASSERT(fileName_ != nullptr); 463 std::remove(fileName_); 464 } 465 466 NO_MOVE_SEMANTIC(TmpFile); 467 NO_COPY_SEMANTIC(TmpFile); 468 GetFileNameTmpFile469 const char *GetFileName() const 470 { 471 return fileName_; 472 } 473 474 private: 475 const char *fileName_ {nullptr}; 476 }; 477 // NOLINTBEGIN(cppcoreguidelines-macro-usage) 478 #define TEST_GRAPH(ns, testName, ...) /* CC-OFF(G.PRE.06) solid logic */ \ 479 namespace ns { \ 480 class testName { \ 481 public: \ 482 template <typename... Args> \ 483 static void Create(IrConstructor *builder, Args &&...args) \ 484 { \ 485 testName(builder, std::forward<Args>(args)...); \ 486 } \ 487 \ 488 private: \ 489 template <typename... Args> \ 490 explicit testName(IrConstructor *builder, Args &&...args) : builder_(builder) \ 491 { \ 492 BuildGraph(std::forward<Args>(args)...); \ 493 } \ 494 void BuildGraph(__VA_ARGS__); \ 495 \ 496 auto GetBuilder() \ 497 { \ 498 /* CC-OFFNXT(G.PRE.05) function gen */ \ 499 return builder_; \ 500 } \ 501 \ 502 private: \ 503 IrConstructor *builder_ {}; \ 504 }; \ 505 } \ 506 void ns::testName::BuildGraph(__VA_ARGS__) 507 508 #define CREATE(...) Create(builder_, __VA_ARGS__) 509 #define SRC_GRAPH(testName, ...) TEST_GRAPH(src_graph, testName, __VA_ARGS__) 510 #define OUT_GRAPH(testName, ...) TEST_GRAPH(out_graph, testName, __VA_ARGS__) 511 // NOLINTEND(cppcoreguidelines-macro-usage) 512 513 } // namespace ark::compiler 514 515 #endif // COMPILER_TESTS_UNIT_TEST_H 516