• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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