1 /* 2 * Copyright (c) 2021-2023 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 panda::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 98 private: 99 static constexpr uintptr_t METHOD = 0xdead; 100 static constexpr uintptr_t CALLEE = 0xdeadc; 101 102 size_t argsCount_ {0}; 103 size_t vregsCount_ {0}; 104 mutable unsigned savedCalleeId_ {0}; 105 DataType::Type returnType_ {DataType::NO_TYPE}; 106 ArenaVector<DataType::Type> *argTypes_ {nullptr}; 107 ArenaUnorderedMap<PandaRuntimeInterface::FieldPtr, DataType::Type> *fieldTypes_ {nullptr}; 108 109 friend class GraphTest; 110 friend class GraphCreator; 111 friend class InstGenerator; 112 }; 113 114 class CommonTest : public ::testing::Test { 115 public: CommonTest()116 CommonTest() 117 { 118 #if defined(PANDA_TARGET_ARM64) || defined(PANDA_TARGET_32) 119 // We have issue with QEMU - so reduce memory heap 120 panda::mem::MemConfig::Initialize(32_MB, 64_MB, 200_MB, 32_MB, 0, 0); // NOLINT(readability-magic-numbers) 121 #else 122 panda::mem::MemConfig::Initialize(32_MB, 64_MB, 256_MB, 32_MB, 0, 0); // NOLINT(readability-magic-numbers) 123 #endif 124 PoolManager::Initialize(); 125 allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER); 126 objectAllocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_OBJECT); 127 localAllocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER); 128 builder_ = new IrConstructor(); 129 } 130 ~CommonTest() override; 131 132 NO_COPY_SEMANTIC(CommonTest); 133 NO_MOVE_SEMANTIC(CommonTest); 134 GetAllocator()135 ArenaAllocator *GetAllocator() const 136 { 137 return allocator_; 138 } 139 GetObjectAllocator()140 ArenaAllocator *GetObjectAllocator() const 141 { 142 return objectAllocator_; 143 } 144 GetLocalAllocator()145 ArenaAllocator *GetLocalAllocator() const 146 { 147 return localAllocator_; 148 } 149 GetArch()150 Arch GetArch() const 151 { 152 return arch_; 153 } 154 155 Graph *CreateEmptyGraph(bool isOsr = false) const 156 { 157 return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, isOsr); 158 } 159 CreateEmptyGraph(Arch arch)160 Graph *CreateEmptyGraph(Arch arch) const 161 { 162 return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch, false); 163 } 164 165 Graph *CreateGraphStartEndBlocks(bool isDynamic = false) const 166 { 167 auto graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, isDynamic, false); 168 graph->CreateStartBlock(); 169 graph->CreateEndBlock(); 170 return graph; 171 } CreateDynEmptyGraph()172 Graph *CreateDynEmptyGraph() const 173 { 174 return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, true, false); 175 } CreateEmptyBytecodeGraph()176 Graph *CreateEmptyBytecodeGraph() const 177 { 178 return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), Arch::NONE, false, true); 179 } CreateEmptyFastpathGraph(Arch arch)180 Graph *CreateEmptyFastpathGraph(Arch arch) const 181 { 182 auto graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch, false); 183 graph->SetMode(GraphMode::FastPath()); 184 return graph; 185 } 186 CreateEmptyBlock(Graph * graph)187 BasicBlock *CreateEmptyBlock(Graph *graph) const 188 { 189 auto block = graph->GetAllocator()->New<BasicBlock>(graph); 190 graph->AddBlock(block); 191 return block; 192 } 193 GetBlocksById(Graph * graph,std::vector<size_t> && ids)194 ArenaVector<BasicBlock *> GetBlocksById(Graph *graph, std::vector<size_t> &&ids) const 195 { 196 ArenaVector<BasicBlock *> blocks(graph->GetAllocator()->Adapter()); 197 for (auto id : ids) { 198 blocks.push_back(&BB(id)); 199 } 200 return blocks; 201 } 202 CheckInputs(Inst & inst,std::initializer_list<size_t> list)203 bool CheckInputs(Inst &inst, std::initializer_list<size_t> list) const 204 { 205 if (inst.GetInputs().Size() != list.size()) { 206 return false; 207 } 208 auto it2 = list.begin(); 209 for (auto it = inst.GetInputs().begin(); it != inst.GetInputs().end(); ++it, ++it2) { 210 if (it->GetInst() != &INS(*it2)) { 211 return false; 212 } 213 } 214 return true; 215 } 216 CheckUsers(Inst & inst,std::initializer_list<int> list)217 bool CheckUsers(Inst &inst, std::initializer_list<int> list) const 218 { 219 std::unordered_map<int, size_t> usersMap; 220 for (auto l : list) { 221 ++usersMap[l]; 222 } 223 for (auto &user : inst.GetUsers()) { 224 EXPECT_EQ(user.GetInst()->GetInput(user.GetIndex()).GetInst(), &inst); 225 if (usersMap[user.GetInst()->GetId()]-- == 0) { 226 return false; 227 } 228 } 229 auto rest = std::accumulate(usersMap.begin(), usersMap.end(), 0, [](int a, auto &x) { return a + x.second; }); 230 EXPECT_EQ(rest, 0); 231 return rest == 0; 232 } 233 234 protected: 235 IrConstructor *builder_; // NOLINT(misc-non-private-member-variables-in-classes) 236 237 private: 238 ArenaAllocator *allocator_; 239 ArenaAllocator *objectAllocator_; 240 ArenaAllocator *localAllocator_; 241 #ifdef PANDA_TARGET_ARM32 242 Arch arch_ {Arch::AARCH32}; 243 #else 244 Arch arch_ {Arch::AARCH64}; 245 #endif 246 }; 247 248 class GraphTest : public CommonTest { 249 public: GraphTest()250 GraphTest() : graph_(CreateEmptyGraph()) 251 { 252 graph_->SetRuntime(&runtime_); 253 runtime_.fieldTypes_ = 254 graph_->GetAllocator()->New<ArenaUnorderedMap<PandaRuntimeInterface::FieldPtr, DataType::Type>>( 255 graph_->GetAllocator()->Adapter()); 256 } 257 ~GraphTest() override = default; 258 259 NO_MOVE_SEMANTIC(GraphTest); 260 NO_COPY_SEMANTIC(GraphTest); 261 GetGraph()262 Graph *GetGraph() const 263 { 264 return graph_; 265 } 266 SetGraphArch(Arch arch)267 void SetGraphArch(Arch arch) 268 { 269 graph_ = CreateEmptyGraph(arch); 270 graph_->SetRuntime(&runtime_); 271 } 272 ResetGraph()273 void ResetGraph() 274 { 275 graph_ = CreateEmptyGraph(); 276 graph_->SetRuntime(&runtime_); 277 } 278 SetNumVirtRegs(size_t num)279 void SetNumVirtRegs(size_t num) 280 { 281 runtime_.vregsCount_ = num; 282 graph_->SetVRegsCount(std::max(graph_->GetVRegsCount(), runtime_.vregsCount_ + runtime_.argsCount_ + 1)); 283 } 284 SetNumArgs(size_t num)285 void SetNumArgs(size_t num) 286 { 287 runtime_.argsCount_ = num; 288 graph_->SetVRegsCount(std::max(graph_->GetVRegsCount(), runtime_.vregsCount_ + runtime_.argsCount_ + 1)); 289 } 290 RegisterFieldType(PandaRuntimeInterface::FieldPtr field,DataType::Type type)291 void RegisterFieldType(PandaRuntimeInterface::FieldPtr field, DataType::Type type) 292 { 293 (*runtime_.fieldTypes_)[field] = type; 294 } 295 296 protected: 297 RuntimeInterfaceMock runtime_; // NOLINT(misc-non-private-member-variables-in-classes) 298 Graph *graph_ {nullptr}; // NOLINT(misc-non-private-member-variables-in-classes) 299 }; 300 301 // NOLINTNEXTLINE(fuchsia-multiple-inheritance) 302 class PandaRuntimeTest : public ::testing::Test, public PandaRuntimeInterface { 303 public: 304 PandaRuntimeTest(); 305 ~PandaRuntimeTest() override; 306 307 NO_MOVE_SEMANTIC(PandaRuntimeTest); 308 NO_COPY_SEMANTIC(PandaRuntimeTest); 309 310 static void Initialize(int argc, char **argv); 311 CreateGraph()312 Graph *CreateGraph() 313 { 314 auto graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_); 315 graph->SetRuntime(this); 316 return graph; 317 } 318 CreateGraphOsr()319 Graph *CreateGraphOsr() 320 { 321 Graph *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, true); 322 graph->SetRuntime(this); 323 return graph; 324 } 325 326 // this method is needed to create a graph with a working dump CreateGraphWithDefaultRuntime()327 Graph *CreateGraphWithDefaultRuntime() 328 { 329 auto *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_); 330 graph->SetRuntime(GetDefaultRuntime()); 331 return graph; 332 } 333 CreateGraphDynWithDefaultRuntime()334 Graph *CreateGraphDynWithDefaultRuntime() 335 { 336 auto *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_); 337 graph->SetRuntime(GetDefaultRuntime()); 338 graph->SetDynamicMethod(); 339 #ifndef NDEBUG 340 graph->SetDynUnitTestFlag(); 341 #endif 342 return graph; 343 } 344 CreateGraphDynStubWithDefaultRuntime()345 Graph *CreateGraphDynStubWithDefaultRuntime() 346 { 347 auto *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_); 348 graph->SetRuntime(GetDefaultRuntime()); 349 graph->SetDynamicMethod(); 350 graph->SetDynamicStub(); 351 #ifndef NDEBUG 352 graph->SetDynUnitTestFlag(); 353 #endif 354 return graph; 355 } 356 357 // this method is needed to create a graph with a working dump CreateGraphOsrWithDefaultRuntime()358 Graph *CreateGraphOsrWithDefaultRuntime() 359 { 360 auto *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, true); 361 graph->SetRuntime(GetDefaultRuntime()); 362 return graph; 363 } 364 GetAllocator()365 ArenaAllocator *GetAllocator() 366 { 367 return allocator_; 368 } 369 GetLocalAllocator()370 ArenaAllocator *GetLocalAllocator() 371 { 372 return localAllocator_; 373 } 374 GetGraph()375 virtual Graph *GetGraph() 376 { 377 return graph_; 378 } 379 GetClassLinker()380 auto GetClassLinker() 381 { 382 return panda::Runtime::GetCurrent()->GetClassLinker(); 383 } 384 385 void EnableLogs(Logger::Level level = Logger::Level::DEBUG) 386 { 387 Logger::EnableComponent(Logger::Component::COMPILER); 388 Logger::SetLevel(level); 389 } 390 GetExecPath()391 const char *GetExecPath() const 392 { 393 return execPath_; 394 } 395 396 static RuntimeInterface *GetDefaultRuntime(); 397 398 protected: 399 IrConstructor *builder_; // NOLINT(misc-non-private-member-variables-in-classes) 400 401 private: 402 Graph *graph_ {nullptr}; 403 ArenaAllocator *allocator_ {nullptr}; 404 ArenaAllocator *localAllocator_ {nullptr}; 405 static inline const char *execPath_ {nullptr}; 406 Arch arch_ {RUNTIME_ARCH}; 407 }; 408 409 // NOLINTNEXTLINE(fuchsia-multia-inheritance,cppcoreguidelines-special-member-functions) 410 class AsmTest : public PandaRuntimeTest { 411 public: 412 std::unique_ptr<const panda_file::File> ParseToFile(const char *source, const char *fileName = "test.pb"); 413 bool Parse(const char *source, const char *fileName = "test.pb"); 414 Graph *BuildGraph(const char *methodName, Graph *graph = nullptr); 415 void CleanUp(Graph *graph); 416 417 AsmTest() = default; 418 ~AsmTest() override = default; 419 NO_MOVE_SEMANTIC(AsmTest); 420 NO_COPY_SEMANTIC(AsmTest); 421 422 template <bool WITH_CLEANUP = false> 423 bool ParseToGraph(const char *source, const char *methodName, Graph *graph = nullptr) 424 { 425 if (!Parse(source)) { 426 return false; 427 } 428 if (graph == nullptr) { 429 graph = GetGraph(); 430 } 431 if (BuildGraph(methodName, graph) == nullptr) { 432 return false; 433 } 434 if constexpr (WITH_CLEANUP) { 435 CleanUp(graph); 436 } 437 return true; 438 } 439 }; 440 441 struct TmpFile { TmpFileTmpFile442 explicit TmpFile(const char *fileName) : fileName_(fileName) {} ~TmpFileTmpFile443 ~TmpFile() 444 { 445 ASSERT(fileName_ != nullptr); 446 remove(fileName_); 447 } 448 449 NO_MOVE_SEMANTIC(TmpFile); 450 NO_COPY_SEMANTIC(TmpFile); 451 GetFileNameTmpFile452 const char *GetFileName() const 453 { 454 return fileName_; 455 } 456 457 private: 458 const char *fileName_ {nullptr}; 459 }; 460 } // namespace panda::compiler 461 462 #endif // COMPILER_TESTS_UNIT_TEST_H 463