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
30namespace panda::compiler {
31struct RuntimeInterfaceMock : public compiler::RuntimeInterface {
32    DataType::Type GetMethodReturnType(MethodPtr /* unsuded */) const override
33    {
34        return return_type;
35    }
36
37    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
45    size_t GetMethodTotalArgumentsCount(MethodPtr /* unused */) const override
46    {
47        if (arg_types == nullptr) {
48            return args_count;
49        }
50        return arg_types->size();
51    }
52    size_t GetMethodArgumentsCount(MethodPtr /* unused */) const override
53    {
54        return args_count;
55    }
56
57    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
68class CommonTest : public ::testing::Test {
69public:
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
86    ArenaAllocator *GetAllocator() const
87    {
88        return allocator_;
89    }
90
91    ArenaAllocator *GetObjectAllocator() const
92    {
93        return object_allocator_;
94    }
95
96    ArenaAllocator *GetLocalAllocator() const
97    {
98        return local_allocator_;
99    }
100
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
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    }
123    Graph *CreateDynEmptyGraph() const
124    {
125        return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, true, false);
126    }
127    Graph *CreateEmptyBytecodeGraph() const
128    {
129        return GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), Arch::NONE, false, true);
130    }
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
138    BasicBlock *CreateEmptyBlock(Graph *graph) const
139    {
140        auto block = graph->GetAllocator()->New<BasicBlock>(graph);
141        graph->AddBlock(block);
142        return block;
143    }
144
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
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
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
183protected:
184    IrConstructor *builder_;
185
186private:
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
197class GraphTest : public CommonTest {
198public:
199    GraphTest() : graph_(CreateEmptyGraph())
200    {
201        graph_->SetRuntime(&runtime_);
202    }
203    ~GraphTest() override {}
204
205    Graph *GetGraph() const
206    {
207        return graph_;
208    }
209
210    void ResetGraph()
211    {
212        graph_ = CreateEmptyGraph();
213        graph_->SetRuntime(&runtime_);
214    }
215
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
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
228protected:
229    RuntimeInterfaceMock runtime_;
230    Graph *graph_ {nullptr};
231};
232
233class PandaRuntimeTest : public ::testing::Test, public PandaRuntimeInterface {
234public:
235    PandaRuntimeTest();
236
237    ~PandaRuntimeTest() override;
238
239    static void Initialize(int argc, char **argv);
240
241    Graph *CreateGraph()
242    {
243        auto graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_);
244        graph->SetRuntime(this);
245        return graph;
246    }
247
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
256    Graph *CreateGraphWithDefaultRuntime()
257    {
258        auto *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_);
259        graph->SetRuntime(GetDefaultRuntime());
260        return graph;
261    }
262
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
272    Graph *CreateGraphOsrWithDefaultRuntime()
273    {
274        auto *graph = GetAllocator()->New<Graph>(GetAllocator(), GetLocalAllocator(), arch_, true);
275        graph->SetRuntime(GetDefaultRuntime());
276        return graph;
277    }
278
279    ArenaAllocator *GetAllocator()
280    {
281        return allocator_;
282    }
283
284    ArenaAllocator *GetLocalAllocator()
285    {
286        return local_allocator_;
287    }
288
289    virtual Graph *GetGraph()
290    {
291        return graph_;
292    }
293
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
305    const char *GetExecPath() const
306    {
307        return exec_path_;
308    }
309
310    static RuntimeInterface *GetDefaultRuntime();
311
312protected:
313    IrConstructor *builder_;
314
315private:
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
323class AsmTest : public PandaRuntimeTest {
324public:
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
351struct TmpFile {
352    explicit TmpFile(const char *file_name) : file_name_(file_name) {}
353    ~TmpFile()
354    {
355        ASSERT(file_name_ != nullptr);
356        remove(file_name_);
357    }
358    const char *GetFileName() const
359    {
360        return file_name_;
361    }
362
363private:
364    const char *file_name_ {nullptr};
365};
366}  // namespace panda::compiler
367
368#endif  // COMPILER_TESTS_UNIT_TEST_H
369