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 BYTECODE_OPTIMIZER_TESTS_COMMON_H
17#define BYTECODE_OPTIMIZER_TESTS_COMMON_H
18
19#include <gtest/gtest.h>
20#include <string>
21#include <string_view>
22
23#include "assembler/assembly-emitter.h"
24#include "assembler/assembly-parser.h"
25#include "assembler/assembly-program.h"
26#include "assembler/extensions/extensions.h"
27#include "canonicalization.h"
28#include "class_data_accessor-inl.h"
29#include "codegen.h"
30#include "compiler/compiler_options.h"
31#include "compiler/optimizer/analysis/rpo.h"
32#include "compiler/optimizer/ir/datatype.h"
33#include "compiler/optimizer/ir/inst.h"
34#include "compiler/optimizer/ir/ir_constructor.h"
35#include "compiler/optimizer/ir_builder/ir_builder.h"
36#include "compiler/optimizer/optimizations/cleanup.h"
37#include "compiler/optimizer/optimizations/lowering.h"
38#include "compiler/optimizer/optimizations/regalloc/reg_alloc_linear_scan.h"
39#include "file_items.h"
40#include "ir_interface.h"
41#include "libpandabase/utils/logger.h"
42#include "mem/arena_allocator.h"
43#include "mem/pool_manager.h"
44#include "method_data_accessor-inl.h"
45#include "optimize_bytecode.h"
46#include "reg_encoder.h"
47#include "runtime_adapter.h"
48
49namespace panda::bytecodeopt {
50
51using compiler::BasicBlock;
52using compiler::Graph;
53using compiler::Input;
54using compiler::Inst;
55using compiler::Opcode;
56
57struct RuntimeInterfaceMock : public compiler::RuntimeInterface {
58    size_t argument_count {0};
59    bool is_constructor {true};
60
61    explicit RuntimeInterfaceMock(size_t arg_count) : RuntimeInterfaceMock(arg_count, true) {}
62
63    RuntimeInterfaceMock(size_t arg_count, bool is_ctor) : argument_count(arg_count), is_constructor(is_ctor) {}
64
65    size_t GetMethodTotalArgumentsCount([[maybe_unused]] MethodPtr method) const override
66    {
67        return argument_count;
68    }
69
70    bool IsConstructor([[maybe_unused]] MethodPtr method, [[maybe_unused]] uint32_t class_id) override
71    {
72        return is_constructor;
73    }
74};
75
76class IrInterfaceTest : public BytecodeOptIrInterface {
77public:
78    explicit IrInterfaceTest(pandasm::Program *prog = nullptr,
79                             const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps = nullptr)
80        : BytecodeOptIrInterface(maps, prog)
81    {
82    }
83
84    std::string GetFieldIdByOffset([[maybe_unused]] uint32_t offset) const override
85    {
86        return "";
87    }
88
89    std::string GetTypeIdByOffset([[maybe_unused]] uint32_t offset) const override
90    {
91        return IsMapsSet() ? BytecodeOptIrInterface::GetTypeIdByOffset(offset) : "";
92    }
93
94    std::string GetMethodIdByOffset([[maybe_unused]] uint32_t offset) const override
95    {
96        return "";
97    }
98
99    std::string GetStringIdByOffset([[maybe_unused]] uint32_t offset) const override
100    {
101        return "";
102    }
103};
104
105namespace test {
106
107extern std::string glob_argv0;
108
109}  // namespace test
110
111class CommonTest : public ::testing::Test {
112public:
113    CommonTest()
114    {
115        compiler::options.SetCompilerUseSafepoint(false);
116        compiler::options.SetCompilerSupportInitObjectInst(true);
117
118        mem::MemConfig::Initialize(128_MB, 64_MB, 64_MB, 32_MB);
119        PoolManager::Initialize();
120        allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_INTERNAL);
121        local_allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_INTERNAL);
122        builder_ = new compiler::IrConstructor();
123
124        Logger::InitializeStdLogging(Logger::Level::ERROR,
125                                     panda::Logger::ComponentMask().set(Logger::Component::BYTECODE_OPTIMIZER));
126    }
127    virtual ~CommonTest()
128    {
129        delete allocator_;
130        delete local_allocator_;
131        delete builder_;
132        PoolManager::Finalize();
133        mem::MemConfig::Finalize();
134
135        Logger::Destroy();
136    }
137    ArenaAllocator *GetAllocator()
138    {
139        return allocator_;
140    }
141    ArenaAllocator *GetLocalAllocator()
142    {
143        return local_allocator_;
144    }
145
146    compiler::Graph *CreateEmptyGraph(bool isDynamic = false)
147    {
148        auto *graph =
149            GetAllocator()->New<compiler::Graph>(GetAllocator(), GetLocalAllocator(), Arch::NONE, isDynamic, true);
150        return graph;
151    }
152
153    compiler::Graph *GetGraph()
154    {
155        return graph_;
156    }
157
158    void SetGraph(compiler::Graph *graph)
159    {
160        graph_ = graph;
161    }
162
163    bool FuncHasInst(pandasm::Function *func, pandasm::Opcode opcode)
164    {
165        for (const auto &inst : func->ins) {
166            if (inst.opcode == opcode) {
167                return true;
168            }
169        }
170        return false;
171    }
172
173protected:
174    compiler::IrConstructor *builder_;
175
176private:
177    ArenaAllocator *allocator_;
178    ArenaAllocator *local_allocator_;
179    compiler::Graph *graph_ {nullptr};
180};
181
182class AsmTest : public CommonTest {
183public:
184    bool ParseToGraph(const std::string &source, const std::string &func_name, const char *file_name = "test.pb")
185    {
186        panda::pandasm::Parser parser;
187        auto res = parser.Parse(source, file_name);
188        if (parser.ShowError().err != pandasm::Error::ErrorType::ERR_NONE) {
189            std::cerr << "Parse failed: " << parser.ShowError().message << std::endl
190                      << parser.ShowError().whole_line << std::endl;
191            ADD_FAILURE();
192            return false;
193        }
194        auto &prog = res.Value();
195        return ParseToGraph(&prog, func_name);
196    }
197
198    bool ParseToGraph(pandasm::Program *prog, const std::string &func_name)
199    {
200        pfile_ = pandasm::AsmEmitter::Emit(*prog, &maps_);
201        ir_interface_ = std::make_unique<bytecodeopt::BytecodeOptIrInterface>(&maps_, prog);
202
203        if (pfile_ == nullptr) {
204            ADD_FAILURE();
205            return false;
206        }
207
208        auto ptr_file = pfile_.get();
209        if (ptr_file == nullptr) {
210            ADD_FAILURE();
211            return false;
212        }
213
214        compiler::Graph *temp_graph = nullptr;
215
216        for (uint32_t id : ptr_file->GetClasses()) {
217            panda_file::File::EntityId record_id {id};
218
219            if (ptr_file->IsExternal(record_id)) {
220                continue;
221            }
222
223            panda_file::ClassDataAccessor cda {*ptr_file, record_id};
224            cda.EnumerateMethods([&temp_graph, ptr_file, func_name, this](panda_file::MethodDataAccessor &mda) {
225                auto name_id = mda.GetNameId();
226                auto str = ptr_file->GetStringData(name_id).data;
227                bool is_equal = (std::string(func_name) == std::string(reinterpret_cast<const char *>(str)));
228                auto method_ptr =
229                    reinterpret_cast<compiler::RuntimeInterface::MethodPtr>(mda.GetMethodId().GetOffset());
230
231                if (!mda.IsExternal() && !mda.IsAbstract() && !mda.IsNative() && is_equal) {
232                    auto adapter = allocator_.New<BytecodeOptimizerRuntimeAdapter>(mda.GetPandaFile());
233                    temp_graph = allocator_.New<compiler::Graph>(&allocator_, &local_allocator_, Arch::NONE, method_ptr,
234                                                                 adapter, false, nullptr, false, true);
235                    ASSERT_NE(temp_graph, nullptr);
236                    ASSERT_TRUE(temp_graph->RunPass<compiler::IrBuilder>());
237                }
238            });
239        }
240
241        if (temp_graph != nullptr) {
242            SetGraph(temp_graph);
243            return true;
244        }
245        return false;
246    }
247
248    bytecodeopt::BytecodeOptIrInterface *GetIrInterface()
249    {
250        return ir_interface_.get();
251    }
252
253    pandasm::AsmEmitter::PandaFileToPandaAsmMaps *GetMaps()
254    {
255        return &maps_;
256    }
257
258    auto GetFile()
259    {
260        return pfile_.get();
261    }
262
263private:
264    std::unique_ptr<BytecodeOptIrInterface> ir_interface_;
265    pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps_;
266    ArenaAllocator allocator_ {SpaceType::SPACE_TYPE_COMPILER};
267    ArenaAllocator local_allocator_ {SpaceType::SPACE_TYPE_COMPILER};
268    std::unique_ptr<const panda_file::File> pfile_ {nullptr};
269};
270
271class GraphComparator {
272public:
273    bool Compare(Graph *graph1, Graph *graph2)
274    {
275        graph1->InvalidateAnalysis<compiler::Rpo>();
276        graph2->InvalidateAnalysis<compiler::Rpo>();
277        if (graph1->GetBlocksRPO().size() != graph2->GetBlocksRPO().size()) {
278            std::cerr << "Different number of blocks: " << graph1->GetBlocksRPO().size() << " and "
279                      << graph2->GetBlocksRPO().size() << std::endl;
280            return false;
281        }
282        return std::equal(graph1->GetBlocksRPO().begin(), graph1->GetBlocksRPO().end(), graph2->GetBlocksRPO().begin(),
283                          graph2->GetBlocksRPO().end(), [this](auto bb1, auto bb2) { return Compare(bb1, bb2); });
284    }
285
286    bool Compare(BasicBlock *block1, BasicBlock *block2)
287    {
288        if (block1->GetPredsBlocks().size() != block2->GetPredsBlocks().size()) {
289            std::cerr << "Different number of preds blocks\n";
290            block1->Dump(&std::cout);
291            block2->Dump(&std::cout);
292            return false;
293        }
294        if (block1->GetSuccsBlocks().size() != block2->GetSuccsBlocks().size()) {
295            std::cerr << "Different number of succs blocks\n";
296            block1->Dump(&std::cout);
297            block2->Dump(&std::cout);
298            return false;
299        }
300        return std::equal(block1->AllInsts().begin(), block1->AllInsts().end(), block2->AllInsts().begin(),
301                          block2->AllInsts().end(), [this](auto inst1, auto inst2) {
302                              ASSERT(inst2 != nullptr);
303                              bool t = Compare(inst1, inst2);
304                              if (!t) {
305                                  std::cerr << "Different instructions:\n";
306                                  inst1->Dump(&std::cout);
307                                  inst2->Dump(&std::cout);
308                              }
309                              return t;
310                          });
311    }
312
313    bool Compare(Inst *inst1, Inst *inst2)
314    {
315        if (auto it = inst_compare_map_.insert({inst1, inst2}); !it.second) {
316            if (inst2 == it.first->second) {
317                return true;
318            }
319            inst_compare_map_.erase(inst1);
320            return false;
321        }
322
323        if (inst1->GetOpcode() != inst2->GetOpcode() || inst1->GetType() != inst2->GetType() ||
324            inst1->GetInputsCount() != inst2->GetInputsCount()) {
325            inst_compare_map_.erase(inst1);
326            return false;
327        }
328
329        if (inst1->GetOpcode() == Opcode::Intrinsic || inst1->GetOpcode() == Opcode::Builtin) {
330            if (inst1->CastToIntrinsic()->GetIntrinsicId() != inst2->CastToIntrinsic()->GetIntrinsicId()) {
331                inst_compare_map_.erase(inst1);
332                return false;
333            }
334        }
335
336        if (inst1->GetOpcode() != Opcode::Phi) {
337            if (!std::equal(
338                    inst1->GetInputs().begin(), inst1->GetInputs().end(), inst2->GetInputs().begin(),
339                    [this](Input input1, Input input2) { return Compare(input1.GetInst(), input2.GetInst()); })) {
340                inst_compare_map_.erase(inst1);
341                return false;
342            }
343        } else {
344            for (auto input1 : inst1->GetInputs()) {
345                auto it =
346                    std::find_if(inst2->GetInputs().begin(), inst2->GetInputs().end(),
347                                 [this, &input1](Input input2) { return Compare(input1.GetInst(), input2.GetInst()); });
348                if (it == inst2->GetInputs().end()) {
349                    inst_compare_map_.erase(inst1);
350                    return false;
351                }
352            }
353        }
354
355#define CAST(Opc) CastTo##Opc()
356
357#define CHECK(Opc, Getter)                                                                               \
358    if (inst1->GetOpcode() == Opcode::Opc && inst1->CAST(Opc)->Getter() != inst2->CAST(Opc)->Getter()) { \
359        inst_compare_map_.erase(inst1);                                                                  \
360        return false;                                                                                    \
361    }
362
363        CHECK(Constant, GetRawValue)
364
365        CHECK(Cast, GetOperandsType)
366        CHECK(Cmp, GetOperandsType)
367
368        CHECK(Compare, GetCc)
369        CHECK(Compare, GetOperandsType)
370
371        CHECK(If, GetCc)
372        CHECK(If, GetOperandsType)
373
374        CHECK(IfImm, GetCc)
375        CHECK(IfImm, GetImm)
376        CHECK(IfImm, GetOperandsType)
377
378        CHECK(LoadArrayI, GetImm)
379        CHECK(LoadArrayPairI, GetImm)
380        CHECK(LoadPairPart, GetImm)
381        CHECK(StoreArrayI, GetImm)
382        CHECK(StoreArrayPairI, GetImm)
383        CHECK(BoundsCheckI, GetImm)
384        CHECK(ReturnI, GetImm)
385        CHECK(AddI, GetImm)
386        CHECK(SubI, GetImm)
387        CHECK(ShlI, GetImm)
388        CHECK(ShrI, GetImm)
389        CHECK(AShrI, GetImm)
390        CHECK(AndI, GetImm)
391        CHECK(OrI, GetImm)
392        CHECK(XorI, GetImm)
393
394        CHECK(LoadStatic, GetVolatile)
395        CHECK(StoreStatic, GetVolatile)
396        CHECK(LoadObject, GetVolatile)
397        CHECK(StoreObject, GetVolatile)
398#undef CHECK
399#undef CAST
400
401        if (inst1->GetOpcode() == Opcode::Cmp && IsFloatType(inst1->GetInput(0).GetInst()->GetType())) {
402            auto cmp1 = static_cast<compiler::CmpInst *>(inst1);
403            auto cmp2 = static_cast<compiler::CmpInst *>(inst2);
404            if (cmp1->IsFcmpg() != cmp2->IsFcmpg()) {
405                inst_compare_map_.erase(inst1);
406                return false;
407            }
408        }
409        for (uint32_t i = 0; i < inst2->GetInputsCount(); i++) {
410            if (inst1->GetInputType(i) != inst2->GetInputType(i)) {
411                inst_compare_map_.erase(inst1);
412                return false;
413            }
414        }
415        return true;
416    }
417
418private:
419    std::unordered_map<Inst *, Inst *> inst_compare_map_;
420};
421
422class IrBuilderTest : public AsmTest {
423public:
424    void CheckSimple(std::string inst_name, compiler::DataType::Type data_type, std::string inst_type)
425    {
426        ASSERT(inst_name == "mov" || inst_name == "lda" || inst_name == "sta");
427        std::string curr_type;
428        if (data_type == compiler::DataType::Type::REFERENCE) {
429            curr_type = "i64[]";
430        } else {
431            curr_type = ToString(data_type);
432        }
433
434        std::string source = ".function " + curr_type + " main(";
435        source += curr_type + " a0){\n";
436        if (inst_name == "mov") {
437            source += "mov" + inst_type + " v0, a0\n";
438            source += "lda" + inst_type + " v0\n";
439        } else if (inst_name == "lda") {
440            source += "lda" + inst_type + " a0\n";
441        } else if (inst_name == "sta") {
442            source += "lda" + inst_type + " a0\n";
443            source += "sta" + inst_type + " v0\n";
444            source += "lda" + inst_type + " v0\n";
445        } else {
446            UNREACHABLE();
447        }
448        source += "return" + inst_type + "\n";
449        source += "}";
450
451        ASSERT_TRUE(ParseToGraph(source, "main"));
452
453        auto graph = CreateEmptyGraph();
454        GRAPH(graph)
455        {
456            PARAMETER(0, 0);
457            INS(0).SetType(data_type);
458
459            BASIC_BLOCK(2, -1)
460            {
461                INST(1, Opcode::Return).Inputs(0);
462                INS(1).SetType(data_type);
463            }
464        }
465        ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
466    }
467
468    void CheckSimpleWithImm(std::string inst_name, compiler::DataType::Type data_type, std::string inst_type)
469    {
470        ASSERT(inst_name == "mov" || inst_name == "fmov" || inst_name == "lda" || inst_name == "flda");
471        std::string curr_type = ToString(data_type);
472
473        std::string source = ".function " + curr_type + " main(){\n";
474        if (inst_name == "mov") {
475            source += "movi" + inst_type + " v0, 0\n";
476            source += "lda" + inst_type + " v0\n";
477        } else if (inst_name == "fmov") {
478            source += "fmovi" + inst_type + " v0, 0.\n";
479            source += "lda" + inst_type + " v0\n";
480        } else if (inst_name == "lda") {
481            source += "ldai" + inst_type + " 0\n";
482        } else if (inst_name == "flda") {
483            source += "fldai" + inst_type + " 0.\n";
484        } else {
485            UNREACHABLE();
486        }
487        source += "return" + inst_type + "\n";
488        source += "}";
489
490        ASSERT_TRUE(ParseToGraph(source, "main"));
491
492        auto graph = CreateEmptyGraph();
493
494        GRAPH(graph)
495        {
496            CONSTANT(0, 0);
497            INS(0).SetType(data_type);
498
499            BASIC_BLOCK(2, -1)
500            {
501                INST(1, Opcode::Return).Inputs(0);
502                INS(1).SetType(data_type);
503            }
504        }
505        ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
506    }
507
508    void CheckCmp(std::string inst_name, compiler::DataType::Type data_type, std::string inst_type)
509    {
510        ASSERT(inst_name == "ucmp" || inst_name == "fcmpl" || inst_name == "fcmpg");
511        std::string curr_type;
512        if (data_type == compiler::DataType::Type::REFERENCE) {
513            curr_type = "i64[]";
514        } else {
515            curr_type = ToString(data_type);
516        }
517        std::string source = ".function i32 main(";
518        source += curr_type + " a0, ";
519        source += curr_type + " a1){\n";
520        source += "lda" + inst_type + " a0\n";
521        source += inst_name + inst_type + " a1\n";
522        source += "return\n";
523        source += "}";
524
525        ASSERT_TRUE(ParseToGraph(source, "main"));
526
527        auto graph = CreateEmptyGraph();
528        GRAPH(graph)
529        {
530            PARAMETER(0, 0);
531            INS(0).SetType(data_type);
532            PARAMETER(1, 1);
533            INS(1).SetType(data_type);
534
535            BASIC_BLOCK(2, -1)
536            {
537                INST(2, Opcode::Cmp).s32().Inputs(0, 1);
538                INST(3, Opcode::Return).s32().Inputs(2);
539            }
540        }
541        ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
542    }
543
544    void CheckFloatCmp(std::string inst_name, compiler::DataType::Type data_type, std::string inst_type, bool fcmpg)
545    {
546        ASSERT(inst_name == "fcmpl" || inst_name == "fcmpg");
547        std::string curr_type = ToString(data_type);
548
549        std::string source = ".function i32 main(";
550        source += curr_type + " a0, ";
551        source += curr_type + " a1){\n";
552        source += "lda" + inst_type + " a0\n";
553        source += inst_name + inst_type + " a1\n";
554        source += "return\n";
555        source += "}";
556
557        ASSERT_TRUE(ParseToGraph(source, "main"));
558
559        auto graph = CreateEmptyGraph();
560        GRAPH(graph)
561        {
562            PARAMETER(0, 0);
563            INS(0).SetType(data_type);
564            PARAMETER(1, 1);
565            INS(1).SetType(data_type);
566
567            BASIC_BLOCK(2, -1)
568            {
569                INST(2, Opcode::Cmp).s32().SrcType(data_type).Fcmpg(fcmpg).Inputs(0, 1);
570                INST(3, Opcode::Return).s32().Inputs(2);
571            }
572        }
573        ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
574    }
575
576    template <bool is_obj>
577    void CheckCondJumpWithZero(compiler::ConditionCode CC)
578    {
579        std::string cmd;
580        switch (CC) {
581            case compiler::ConditionCode::CC_EQ:
582                cmd = "jeqz";
583                break;
584            case compiler::ConditionCode::CC_NE:
585                cmd = "jnez";
586                break;
587            case compiler::ConditionCode::CC_LT:
588                cmd = "jltz";
589                break;
590            case compiler::ConditionCode::CC_GT:
591                cmd = "jgtz";
592                break;
593            case compiler::ConditionCode::CC_LE:
594                cmd = "jlez";
595                break;
596            case compiler::ConditionCode::CC_GE:
597                cmd = "jgez";
598                break;
599            default:
600                UNREACHABLE();
601        }
602
603        std::string inst_postfix = "";
604        std::string param_type = "i32";
605        auto type = compiler::DataType::INT32;
606        if constexpr (is_obj) {
607            inst_postfix = ".obj";
608            param_type = "i64[]";
609            type = compiler::DataType::REFERENCE;
610        }
611
612        std::string source = ".function void main(";
613        source += param_type + " a0) {\n";
614        source += "lda" + inst_postfix + " a0\n";
615        source += cmd + inst_postfix + " label\n";
616        source += "label: ";
617        source += "return.void\n}";
618
619        ASSERT_TRUE(ParseToGraph(source, "main"));
620
621        auto graph = CreateEmptyGraph();
622        GRAPH(graph)
623        {
624            PARAMETER(0, 0);
625            INS(0).SetType(type);
626            CONSTANT(2, 0).s64();
627
628            BASIC_BLOCK(2, 3, 4)
629            {
630                INST(1, Opcode::Compare).b().CC(CC).Inputs(0, 2);
631                INST(3, Opcode::IfImm)
632                    .SrcType(compiler::DataType::BOOL)
633                    .CC(compiler::ConditionCode::CC_NE)
634                    .Inputs(1)
635                    .Imm(0);
636            }
637            BASIC_BLOCK(3, 4) {}
638            BASIC_BLOCK(4, -1)
639            {
640                INST(4, Opcode::ReturnVoid).v0id();
641            }
642        }
643        ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
644    }
645
646    template <bool is_obj>
647    void CheckCondJump(compiler::ConditionCode CC)
648    {
649        std::string cmd;
650        switch (CC) {
651            case compiler::ConditionCode::CC_EQ:
652                cmd = "jeq";
653                break;
654            case compiler::ConditionCode::CC_NE:
655                cmd = "jne";
656                break;
657            case compiler::ConditionCode::CC_LT:
658                cmd = "jlt";
659                break;
660            case compiler::ConditionCode::CC_GT:
661                cmd = "jgt";
662                break;
663            case compiler::ConditionCode::CC_LE:
664                cmd = "jle";
665                break;
666            case compiler::ConditionCode::CC_GE:
667                cmd = "jge";
668                break;
669            default:
670                UNREACHABLE();
671        }
672        std::string inst_postfix = "";
673        std::string param_type = "i32";
674        auto type = compiler::DataType::INT32;
675        if constexpr (is_obj) {
676            inst_postfix = ".obj";
677            param_type = "i64[]";
678            type = compiler::DataType::REFERENCE;
679        }
680
681        std::string source = ".function void main(";
682        source += param_type + " a0, " + param_type + " a1) {\n";
683        source += "lda" + inst_postfix + " a0\n";
684        source += cmd + inst_postfix + " a1, label\n";
685        source += "label: ";
686        source += "return.void\n}";
687
688        ASSERT_TRUE(ParseToGraph(source, "main"));
689
690        auto graph = CreateEmptyGraph();
691        GRAPH(graph)
692        {
693            PARAMETER(0, 0);
694            INS(0).SetType(type);
695            PARAMETER(1, 1);
696            INS(1).SetType(type);
697
698            BASIC_BLOCK(2, 3, 4)
699            {
700                INST(2, Opcode::Compare).b().CC(CC).Inputs(0, 1);
701                INST(3, Opcode::IfImm)
702                    .SrcType(compiler::DataType::BOOL)
703                    .CC(compiler::ConditionCode::CC_NE)
704                    .Imm(0)
705                    .Inputs(2);
706            }
707            BASIC_BLOCK(3, 4) {}
708            BASIC_BLOCK(4, -1)
709            {
710                INST(4, Opcode::ReturnVoid).v0id();
711            }
712        }
713        ASSERT_TRUE(GraphComparator().Compare(GetGraph(), graph));
714    }
715
716    void CheckOtherPasses(panda::pandasm::Program *prog, std::string fun_name)
717    {
718        GetGraph()->RunPass<compiler::Cleanup>();
719        GetGraph()->RunPass<Canonicalization>();
720#ifndef NDEBUG
721        GetGraph()->SetLowLevelInstructionsEnabled();
722#endif
723        GetGraph()->RunPass<compiler::Cleanup>();
724        GetGraph()->RunPass<compiler::Lowering>();
725        GetGraph()->RunPass<compiler::Cleanup>();
726        EXPECT_TRUE(GetGraph()->RunPass<compiler::RegAllocLinearScan>(compiler::EmptyRegMask()));
727        GetGraph()->RunPass<compiler::Cleanup>();
728        EXPECT_TRUE(GetGraph()->RunPass<RegEncoder>());
729        ASSERT_TRUE(prog->function_table.find(fun_name) != prog->function_table.end());
730        auto &function = prog->function_table.at(fun_name);
731        GetGraph()->RunPass<compiler::Cleanup>();
732        EXPECT_TRUE(GetGraph()->RunPass<BytecodeGen>(&function, GetIrInterface()));
733        auto pf = pandasm::AsmEmitter::Emit(*prog);
734        ASSERT_NE(pf, nullptr);
735    }
736
737    void CheckConstArrayFilling(panda::pandasm::Program *prog, [[maybe_unused]] std::string class_name,
738                                std::string func_name)
739    {
740        if (prog->literalarray_table.size() == 1) {
741            EXPECT_TRUE(prog->literalarray_table["0"].literals_[0].tag_ == panda_file::LiteralTag::TAGVALUE);
742            EXPECT_TRUE(prog->literalarray_table["0"].literals_[1].tag_ == panda_file::LiteralTag::INTEGER);
743            EXPECT_TRUE(prog->literalarray_table["0"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_I32);
744            return;
745        }
746        EXPECT_TRUE(prog->literalarray_table.size() == 8);
747        for (const auto &elem : prog->literalarray_table) {
748            EXPECT_TRUE(elem.second.literals_.size() == 5);
749            EXPECT_TRUE(elem.second.literals_[0].tag_ == panda_file::LiteralTag::TAGVALUE);
750            EXPECT_TRUE(elem.second.literals_[1].tag_ == panda_file::LiteralTag::INTEGER);
751        }
752        EXPECT_TRUE(prog->literalarray_table["7"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_U1);
753        EXPECT_TRUE(prog->literalarray_table["6"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_I8);
754        EXPECT_TRUE(prog->literalarray_table["5"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_I16);
755        EXPECT_TRUE(prog->literalarray_table["4"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_I32);
756        EXPECT_TRUE(prog->literalarray_table["3"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_I64);
757        EXPECT_TRUE(prog->literalarray_table["2"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_F32);
758        EXPECT_TRUE(prog->literalarray_table["1"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_F64);
759        EXPECT_TRUE(prog->literalarray_table["0"].literals_[2].tag_ == panda_file::LiteralTag::ARRAY_STRING);
760
761        EXPECT_TRUE(GetGraph()->RunPass<RegEncoder>());
762        ASSERT_TRUE(prog->function_table.find(func_name) != prog->function_table.end());
763        auto &function = prog->function_table.at(func_name);
764        EXPECT_TRUE(GetGraph()->RunPass<BytecodeGen>(&function, GetIrInterface()));
765        ASSERT(pandasm::AsmEmitter::Emit(class_name + ".panda", *prog, nullptr, nullptr, false));
766    }
767
768    enum CheckConstArrayTypes { ACCESS, SKIP_MULTIDIM_ARRAYS };
769
770    void CheckConstArray(panda::pandasm::Program *prog, const char *class_name, std::string func_name,
771                         CheckConstArrayTypes type)
772    {
773        options.SetConstArrayResolver(true);
774
775        panda::pandasm::AsmEmitter::Emit(std::string(class_name) + ".panda", *prog, nullptr, nullptr, false);
776        auto temp_name = func_name.substr(func_name.find(".") + 1);
777        EXPECT_TRUE(ParseToGraph(prog, temp_name.substr(0, temp_name.find(":"))));
778        EXPECT_TRUE(RunOptimizations(GetGraph(), GetIrInterface()));
779
780        compiler::Inst *const_array_def_inst {nullptr};
781        for (auto bb : GetGraph()->GetBlocksRPO()) {
782            for (auto inst : bb->AllInsts()) {
783                switch (type) {
784                    case CheckConstArrayTypes::ACCESS: {
785                        if (inst->GetOpcode() == Opcode::LoadConstArray) {
786                            const_array_def_inst = inst;
787                            continue;
788                        }
789                        if (inst->GetOpcode() == Opcode::LoadArray) {
790                            EXPECT_TRUE(const_array_def_inst != nullptr);
791                            EXPECT_TRUE(inst->CastToLoadArray()->GetArray() == const_array_def_inst);
792                        }
793                        continue;
794                    }
795                    case CheckConstArrayTypes::SKIP_MULTIDIM_ARRAYS: {
796                        EXPECT_TRUE(inst->GetOpcode() != Opcode::LoadConstArray);
797                        continue;
798                    }
799                    default:
800                        UNREACHABLE();
801                }
802            }
803        }
804
805        EXPECT_TRUE(GetGraph()->RunPass<RegEncoder>());
806        ASSERT_TRUE(prog->function_table.find(func_name) != prog->function_table.end());
807        auto &function = prog->function_table.at(func_name);
808        EXPECT_TRUE(GetGraph()->RunPass<BytecodeGen>(&function, GetIrInterface()));
809        ASSERT(pandasm::AsmEmitter::Emit("LiteralArrayIntAccess.panda", *prog, nullptr, nullptr, false));
810    }
811};
812
813}  // namespace panda::bytecodeopt
814
815#endif  // BYTECODE_OPTIMIZER_TESTS_COMMON_H
816