• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 
49 namespace panda::bytecodeopt {
50 
51 using compiler::BasicBlock;
52 using compiler::Graph;
53 using compiler::Input;
54 using compiler::Inst;
55 using compiler::Opcode;
56 
57 struct RuntimeInterfaceMock : public compiler::RuntimeInterface {
58     size_t argument_count {0};
59     bool is_constructor {true};
60 
RuntimeInterfaceMockRuntimeInterfaceMock61     explicit RuntimeInterfaceMock(size_t arg_count) : RuntimeInterfaceMock(arg_count, true) {}
62 
RuntimeInterfaceMockRuntimeInterfaceMock63     RuntimeInterfaceMock(size_t arg_count, bool is_ctor) : argument_count(arg_count), is_constructor(is_ctor) {}
64 
GetMethodTotalArgumentsCountRuntimeInterfaceMock65     size_t GetMethodTotalArgumentsCount([[maybe_unused]] MethodPtr method) const override
66     {
67         return argument_count;
68     }
69 
IsConstructorRuntimeInterfaceMock70     bool IsConstructor([[maybe_unused]] MethodPtr method, [[maybe_unused]] uint32_t class_id) override
71     {
72         return is_constructor;
73     }
74 };
75 
76 class IrInterfaceTest : public BytecodeOptIrInterface {
77 public:
78     explicit IrInterfaceTest(pandasm::Program *prog = nullptr,
79                              const pandasm::AsmEmitter::PandaFileToPandaAsmMaps *maps = nullptr)
BytecodeOptIrInterface(maps,prog)80         : BytecodeOptIrInterface(maps, prog)
81     {
82     }
83 
GetFieldIdByOffset(uint32_t offset)84     std::string GetFieldIdByOffset([[maybe_unused]] uint32_t offset) const override
85     {
86         return "";
87     }
88 
GetTypeIdByOffset(uint32_t offset)89     std::string GetTypeIdByOffset([[maybe_unused]] uint32_t offset) const override
90     {
91         return IsMapsSet() ? BytecodeOptIrInterface::GetTypeIdByOffset(offset) : "";
92     }
93 
GetMethodIdByOffset(uint32_t offset)94     std::string GetMethodIdByOffset([[maybe_unused]] uint32_t offset) const override
95     {
96         return "";
97     }
98 
GetStringIdByOffset(uint32_t offset)99     std::string GetStringIdByOffset([[maybe_unused]] uint32_t offset) const override
100     {
101         return "";
102     }
103 };
104 
105 namespace test {
106 
107 extern std::string glob_argv0;
108 
109 }  // namespace test
110 
111 class CommonTest : public ::testing::Test {
112 public:
CommonTest()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     }
~CommonTest()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     }
GetAllocator()137     ArenaAllocator *GetAllocator()
138     {
139         return allocator_;
140     }
GetLocalAllocator()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 
GetGraph()153     compiler::Graph *GetGraph()
154     {
155         return graph_;
156     }
157 
SetGraph(compiler::Graph * graph)158     void SetGraph(compiler::Graph *graph)
159     {
160         graph_ = graph;
161     }
162 
FuncHasInst(pandasm::Function * func,pandasm::Opcode opcode)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 
173 protected:
174     compiler::IrConstructor *builder_;
175 
176 private:
177     ArenaAllocator *allocator_;
178     ArenaAllocator *local_allocator_;
179     compiler::Graph *graph_ {nullptr};
180 };
181 
182 class AsmTest : public CommonTest {
183 public:
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 
ParseToGraph(pandasm::Program * prog,const std::string & func_name)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 
GetIrInterface()248     bytecodeopt::BytecodeOptIrInterface *GetIrInterface()
249     {
250         return ir_interface_.get();
251     }
252 
GetMaps()253     pandasm::AsmEmitter::PandaFileToPandaAsmMaps *GetMaps()
254     {
255         return &maps_;
256     }
257 
GetFile()258     auto GetFile()
259     {
260         return pfile_.get();
261     }
262 
263 private:
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 
271 class GraphComparator {
272 public:
Compare(Graph * graph1,Graph * graph2)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 
Compare(BasicBlock * block1,BasicBlock * block2)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 
Compare(Inst * inst1,Inst * inst2)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 
418 private:
419     std::unordered_map<Inst *, Inst *> inst_compare_map_;
420 };
421 
422 class IrBuilderTest : public AsmTest {
423 public:
CheckSimple(std::string inst_name,compiler::DataType::Type data_type,std::string inst_type)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 
CheckSimpleWithImm(std::string inst_name,compiler::DataType::Type data_type,std::string inst_type)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 
CheckCmp(std::string inst_name,compiler::DataType::Type data_type,std::string inst_type)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 
CheckFloatCmp(std::string inst_name,compiler::DataType::Type data_type,std::string inst_type,bool fcmpg)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>
CheckCondJumpWithZero(compiler::ConditionCode CC)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>
CheckCondJump(compiler::ConditionCode CC)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 
CheckOtherPasses(panda::pandasm::Program * prog,std::string fun_name)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 
CheckConstArrayFilling(panda::pandasm::Program * prog,std::string class_name,std::string func_name)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 
CheckConstArray(panda::pandasm::Program * prog,const char * class_name,std::string func_name,CheckConstArrayTypes type)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