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