1 /*
2 * Copyright (c) 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 #include <gtest/gtest.h>
17
18 #include "assembler/assembly-parser.h"
19 #include "codegen.h"
20 #include "compiler/optimizer/optimizations/cleanup.h"
21 #include "compiler/optimizer/optimizations/lowering.h"
22 #include "compiler/optimizer/optimizations/move_constants.h"
23 #include "compiler/optimizer/optimizations/regalloc/reg_alloc.h"
24 #include "compiler/optimizer/optimizations/vn.h"
25 #include "reg_acc_alloc.h"
26 #include "reg_encoder.h"
27 #include "graph_test.h"
28 #include "mem/pool_manager.h"
29
30 using namespace testing::ext;
31
32 namespace panda::bytecodeopt {
33 using namespace compiler;
34 using namespace pandasm;
35 using namespace panda;
36 class CodegenTest : public testing::Test {
37 public:
CodegenTest()38 CodegenTest()
39 {
40 options.SetCompilerUseSafepoint(false);
41 }
SetUpTestCase(void)42 static void SetUpTestCase(void) {}
TearDownTestCase(void)43 static void TearDownTestCase(void) {}
SetUp()44 void SetUp() {}
TearDown()45 void TearDown() {}
46
47 GraphTest graph_test_;
48 };
49
ParseAndEmit(const std::string & source)50 std::unique_ptr<const panda_file::File> ParseAndEmit(const std::string &source)
51 {
52 panda::pandasm::Parser parser;
53 auto res = parser.Parse(source);
54 EXPECT_EQ(parser.ShowError().err, Error::ErrorType::ERR_NONE);
55 auto &program = res.Value();
56 return AsmEmitter::Emit(program);
57 }
58
59 /**
60 * @tc.name: codegen_test_001
61 * @tc.desc: Verify the DoLda function.
62 * @tc.type: FUNC
63 * @tc.require: issueNumber
64 */
65 HWTEST_F(CodegenTest, codegen_test_001, TestSize.Level1)
66 {
67 Register reg = 1; // 1: It's a random number
68 std::vector<Ins> result;
69 DoLda(reg, result);
70 EXPECT_EQ(result[0].regs[0], reg);
71 EXPECT_EQ(result[0].opcode, panda::pandasm::Opcode::LDA);
72 }
73
74 /**
75 * @tc.name: codegen_test_002
76 * @tc.desc: Verify the DoSta function.
77 * @tc.type: FUNC
78 * @tc.require: issueNumber
79 */
80 HWTEST_F(CodegenTest, codegen_test_002, TestSize.Level1)
81 {
82 Register reg = 1; // 1: It's a random number
83 std::vector<Ins> result;
84 DoSta(reg, result);
85 EXPECT_EQ(result[0].regs[0], reg);
86 EXPECT_EQ(result[0].opcode, panda::pandasm::Opcode::STA);
87 }
88
89 /**
90 * @tc.name: codegen_test_003
91 * @tc.desc: Verify the EmitJump function.
92 * @tc.type: FUNC
93 * @tc.require: issueNumber
94 */
95 HWTEST_F(CodegenTest, codegen_test_003, TestSize.Level1)
96 {
97 std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
98 const char *test_method_name = "func1";
99 bool status = false;
100
__anon2d774a330102(Graph* graph, std::string &method_name) 101 graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
102 if (test_method_name != method_name) {
103 return;
104 }
105
106 EXPECT_NE(graph, nullptr);
107 for (auto bb : graph->GetBlocksRPO()) {
108 EXPECT_NE(bb, nullptr);
109 if (bb->IsTryBegin()) {
110 status = true;
111 Function *function = nullptr;
112 BytecodeOptIrInterface *interface = nullptr;
113 Program *prog = nullptr;
114 BytecodeGen bc_gen(graph, function, interface, prog);
115 bc_gen.EmitJump(bb->GetSuccessor(1));
116 EXPECT_FALSE(bc_gen.GetResult().empty());
117 EXPECT_EQ(bc_gen.GetResult().back().ids[0], "label_6");
118 bc_gen.EmitJump(bb->GetSuccessor(0));
119 EXPECT_EQ(bc_gen.GetResult().back().ids[0], "label_3");
120 }
121 }
122 });
123 EXPECT_TRUE(status);
124 }
125
126 /**
127 * @tc.name: codegen_test_004
128 * @tc.desc: Verify the EmitJump function.
129 * @tc.type: FUNC
130 * @tc.require: issueNumber
131 */
132 HWTEST_F(CodegenTest, codegen_test_004, TestSize.Level1)
133 {
134 std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
135 const char *test_method_name = "func2";
136 bool status = false;
137
__anon2d774a330202(Graph* graph, std::string &method_name) 138 graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
139 if (test_method_name != method_name) {
140 return;
141 }
142
143 EXPECT_NE(graph, nullptr);
144 for (auto bb : graph->GetBlocksRPO()) {
145 EXPECT_NE(bb, nullptr);
146 for (auto inst : bb->AllInsts()) {
147 if (inst->GetOpcode() == Opcode::IfImm) {
148 status = true;
149 Function *function = nullptr;
150 BytecodeOptIrInterface *interface = nullptr;
151 Program *prog = nullptr;
152 BytecodeGen bc_gen(graph, function, interface, prog);
153 bc_gen.EmitJump(bb);
154 EXPECT_FALSE(bc_gen.GetResult().empty());
155 EXPECT_EQ(bc_gen.GetResult().back().opcode, panda::pandasm::Opcode::JMP);
156 }
157 }
158 }
159 });
160 EXPECT_TRUE(status);
161 }
162
163 /**
164 * @tc.name: codegen_test_005
165 * @tc.desc: Verify the VisitConstant function.
166 * @tc.type: FUNC
167 * @tc.require: issueNumber
168 */
169 HWTEST_F(CodegenTest, codegen_test_005, TestSize.Level1)
170 {
171 std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
172 const char *test_method_name = "func5";
173 bool status = false;
174
__anon2d774a330302(Graph* graph, std::string &method_name) 175 graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
176 if (test_method_name != method_name) {
177 return;
178 }
179
180 EXPECT_NE(graph, nullptr);
181 for (auto bb : graph->GetBlocksRPO()) {
182 for (auto inst : bb->AllInsts()) {
183 if (inst->GetOpcode() != Opcode::Constant) {
184 continue;
185 }
186 status = true;
187 Function *function = nullptr;
188 BytecodeOptIrInterface *interface = nullptr;
189 Program *prog = nullptr;
190
191 BytecodeGen graph_visitor(graph, function, interface, prog);
192
193 ConstantInst *const_inst = inst->CastToConstant();
194 const_inst->SetType(DataType::Type::INT64);
195 EXPECT_EQ(const_inst->GetType(), DataType::Type::INT64);
196 Register reg = INVALID_REG;
197 const_inst->SetDstReg(reg);
198 EXPECT_EQ(const_inst->GetDstReg(), reg);
199 BytecodeGen::VisitConstant(&graph_visitor, const_inst);
200 EXPECT_FALSE(graph_visitor.GetResult().empty());
201 EXPECT_EQ(graph_visitor.GetResult().back().opcode, panda::pandasm::Opcode::STA);
202
203 ConstantInst *const_inst1 = inst->CastToConstant();
204 const_inst1->SetType(DataType::Type::FLOAT64);
205 EXPECT_EQ(const_inst->GetType(), DataType::Type::FLOAT64);
206 const_inst1->SetDstReg(reg);
207 EXPECT_EQ(const_inst->GetDstReg(), reg);
208 BytecodeGen::VisitConstant(&graph_visitor, const_inst1);
209 EXPECT_FALSE(graph_visitor.GetResult().empty());
210 EXPECT_EQ(graph_visitor.GetResult().back().opcode, panda::pandasm::Opcode::STA);
211
212 ConstantInst *const_inst2 = inst->CastToConstant();
213 const_inst2->SetType(DataType::Type::INT32);
214 EXPECT_EQ(const_inst->GetType(), DataType::Type::INT32);
215 const_inst2->SetDstReg(reg);
216 EXPECT_EQ(const_inst->GetDstReg(), reg);
217 BytecodeGen::VisitConstant(&graph_visitor, const_inst2);
218 EXPECT_FALSE(graph_visitor.GetResult().empty());
219 EXPECT_EQ(graph_visitor.GetResult().back().opcode, panda::pandasm::Opcode::STA);
220 }
221 }
222 });
223 EXPECT_TRUE(status);
224 }
225
226 /**
227 * @tc.name: codegen_test_006
228 * @tc.desc: Verify the EncodeSta function.
229 * @tc.type: FUNC
230 * @tc.require: issueNumber
231 */
232 HWTEST_F(CodegenTest, codegen_test_006, TestSize.Level1)
233 {
234 std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
235 const char *test_method_name = "func1";
236 bool status = false;
237
__anon2d774a330402(Graph* graph, std::string &method_name) 238 graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
239 if (test_method_name != method_name) {
240 return;
241 }
242
243 EXPECT_NE(graph, nullptr);
244 for (auto bb : graph->GetBlocksRPO()) {
245 EXPECT_NE(bb, nullptr);
246 if (bb->IsTryBegin()) {
247 status = true;
248 Register reg = 1; // 1: It's a random number
249 Function *function = nullptr;
250 BytecodeOptIrInterface *interface = nullptr;
251 Program *prog = nullptr;
252 BytecodeGen bc_gen(graph, function, interface, prog);
253 bc_gen.EncodeSta(reg, DataType::Type::ANY);
254 EXPECT_FALSE(bc_gen.GetResult().empty());
255 EXPECT_EQ(bc_gen.GetResult().back().opcode, panda::pandasm::Opcode::STA);
256 }
257 }
258 });
259 EXPECT_TRUE(status);
260 }
261
262 /**
263 * @tc.name: codegen_test_007
264 * @tc.desc: Verify the VisitLoadString function.
265 * @tc.type: FUNC
266 * @tc.require: issueNumber
267 */
268 HWTEST_F(CodegenTest, codegen_test_007, TestSize.Level1)
269 {
270 std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
271 const char *test_method_name = "func6";
272 bool status = false;
273
__anon2d774a330502(Graph* graph, std::string &method_name) 274 graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
275 if (test_method_name != method_name) {
276 return;
277 }
278
279 EXPECT_NE(graph, nullptr);
280 for (auto bb : graph->GetVectorBlocks()) {
281 EXPECT_NE(bb, nullptr);
282 for (auto inst1 : bb->AllInsts()) {
283 if (inst1->GetOpcode() != Opcode::LoadString) {
284 continue;
285 }
286 status = true;
287 Function *function = nullptr;
288 Program *prog = nullptr;
289 AsmEmitter::PandaFileToPandaAsmMaps maps;
290 uint32_t id = 2; // 2: It's a random number
291 maps.strings.emplace(id, "i32[]");
292 BytecodeOptIrInterface interface(&maps, prog);
293
294 BytecodeGen graph_visitor(graph, function, &interface, prog);
295 auto inst = inst1->CastToLoadString();
296 inst->SetTypeId(2);
297
298 unsigned index = 5; // 5: It's a random number
299 unsigned size = 6; // 6: It's a random number
300 User user(true, index, size);
301 inst->AddUser(&user);
302
303 Register reg1 = ACC_REG_ID;
304 inst->SetDstReg(reg1);
305 BytecodeGen::VisitLoadString(&graph_visitor, inst);
306 EXPECT_FALSE(graph_visitor.GetResult().empty());
307 EXPECT_EQ(graph_visitor.GetResult().back().opcode, panda::pandasm::Opcode::LDA_STR);
308
309 Register reg2 = INVALID_REG;
310 inst->SetDstReg(reg2);
311 BytecodeGen::VisitLoadString(&graph_visitor, inst);
312 EXPECT_FALSE(graph_visitor.GetResult().empty());
313 EXPECT_EQ(graph_visitor.GetResult().back().opcode, panda::pandasm::Opcode::STA);
314 }
315 }
316 });
317 EXPECT_TRUE(status);
318 }
319
320 /**
321 * @tc.name: codegen_test_008
322 * @tc.desc: Verify the VisitDefault function.
323 * @tc.type: FUNC
324 * @tc.require: issueNumber
325 */
326 HWTEST_F(CodegenTest, codegen_test_008, TestSize.Level1)
327 {
328 std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
329 const char *test_method_name = "func1";
330 bool status = false;
331
__anon2d774a330602(Graph* graph, std::string &method_name) 332 graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
333 if (test_method_name != method_name) {
334 return;
335 }
336
337 EXPECT_NE(graph, nullptr);
338 for (auto bb : graph->GetBlocksRPO()) {
339 EXPECT_NE(bb, nullptr);
340 if (bb->IsTryBegin()) {
341 status = true;
342 Function *function = nullptr;
343 BytecodeOptIrInterface *interface = nullptr;
344 Program *prog = nullptr;
345 BytecodeGen bc_gen(graph, function, interface, prog);
346 bc_gen.VisitDefault(bb->GetFirstInst());
347 EXPECT_FALSE(bc_gen.GetStatus());
348 }
349 }
350 });
351 EXPECT_TRUE(status);
352 }
353
354 /**
355 * @tc.name: codegen_test_009
356 * @tc.desc: Verify the GetLiteralArrayByOffset function.
357 * @tc.type: FUNC
358 * @tc.require: issueNumber
359 */
360 HWTEST_F(CodegenTest, codegen_test_009, TestSize.Level1)
361 {
362 AsmEmitter::PandaFileToPandaAsmMaps maps;
363 uint32_t id = 2; // 2: It's a random number
364 maps.literalarrays.emplace(id, "i32[]");
365 maps.strings.emplace(id, "i33[]");
366 Program *prog = nullptr;
367 BytecodeOptIrInterface interface(&maps, prog);
368
369 uint32_t offset = 2; // 2: It's a random number
370 EXPECT_EQ(interface.GetLiteralArrayByOffset(offset), "i32[]");
371 }
372
373 /**
374 * @tc.name: codegen_test_010
375 * @tc.desc: Verify the GetTypeIdByOffset function.
376 * @tc.type: FUNC
377 * @tc.require: issueNumber
378 */
379 HWTEST_F(CodegenTest, codegen_test_010, TestSize.Level1)
380 {
381 AsmEmitter::PandaFileToPandaAsmMaps maps;
382 uint32_t id = 2; // 2: It's a random number
383 maps.classes.emplace(id, "i32[]");
384 Program *prog = nullptr;
385 BytecodeOptIrInterface interface(&maps, prog);
386
387 uint32_t offset = 2; // 2: It's a random number
388 EXPECT_EQ(interface.GetTypeIdByOffset(offset), "i32[]");
389 }
390
391 /**
392 * @tc.name: codegen_test_011
393 * @tc.desc: Verify the GetFieldIdByOffset function.
394 * @tc.type: FUNC
395 * @tc.require: issueNumber
396 */
397 HWTEST_F(CodegenTest, codegen_test_011, TestSize.Level1)
398 {
399 AsmEmitter::PandaFileToPandaAsmMaps maps;
400 uint32_t id = 2; // 2: It's a random number
401 maps.fields.emplace(id, "i32[]");
402 Program *prog = nullptr;
403 BytecodeOptIrInterface interface(&maps, prog);
404
405 uint32_t offset = 2; // 2: It's a random number
406 interface.GetFieldIdByOffset(offset);
407 EXPECT_EQ(interface.GetFieldIdByOffset(offset), "i32[]");
408 EXPECT_TRUE(interface.IsMapsSet());
409 }
410
411
412 /**
413 * @tc.name: codegen_test_012
414 * @tc.desc: Verify the GetMethodArgumentsCount function.
415 * @tc.type: FUNC
416 * @tc.require: issueNumber
417 */
418 HWTEST_F(CodegenTest, codegen_test_012, TestSize.Level1)
419 {
420 auto source = std::string(R"(
421 .function u1 foo() {
422 sta v4
423 lda v4
424 return
425 }
426 )");
427 std::unique_ptr<const panda_file::File> pfile = ParseAndEmit(source);
428 BytecodeOptimizerRuntimeAdapter runtime_adapter(*pfile.get());
429
430 RuntimeInterface::MethodPtr caller = nullptr;
431 auto class_id = pfile->GetClassId(utf::CStringAsMutf8("L_GLOBAL;"));
432 EXPECT_TRUE(class_id.IsValid());
433 panda_file::ClassDataAccessor cda(*pfile.get(), class_id);
434
__anon2d774a330702(panda_file::MethodDataAccessor &mda) 435 cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
436 BytecodeOptimizerRuntimeAdapter::MethodId id = mda.GetMethodId().GetOffset();
437 EXPECT_EQ(runtime_adapter.GetMethodArgumentsCount(caller, id), 0);
438 });
439 }
440
441 /**
442 * @tc.name: codegen_test_013
443 * @tc.desc: Verify the GetMethodFullName function.
444 * @tc.type: FUNC
445 * @tc.require: issueNumber
446 */
447 HWTEST_F(CodegenTest, codegen_test_013, TestSize.Level1)
448 {
449 auto source = std::string(R"(
450 .function u1 foo() {
451 sta v4
452 lda v4
453 return
454 }
455 )");
456 std::unique_ptr<const panda_file::File> pfile = ParseAndEmit(source);
457 BytecodeOptimizerRuntimeAdapter runtime_adapter(*pfile.get());
458
459 auto class_id = pfile->GetClassId(utf::CStringAsMutf8("L_GLOBAL;"));
460 EXPECT_TRUE(class_id.IsValid());
461 panda_file::ClassDataAccessor cda(*pfile.get(), class_id);
462
__anon2d774a330802(panda_file::MethodDataAccessor &mda) 463 cda.EnumerateMethods([&](panda_file::MethodDataAccessor &mda) {
464 BytecodeOptimizerRuntimeAdapter::MethodId id = mda.GetMethodId().GetOffset();
465 RuntimeInterface::MethodPtr method;
466 method=(void*)(long)id;
467 EXPECT_EQ(runtime_adapter.GetMethodFullName(method, false), "L_GLOBAL;::foo");
468 });
469 }
470
471 /**
472 * @tc.name: codegen_test_014
473 * @tc.desc: Verify the GetBlocksToVisit function.
474 * @tc.type: FUNC
475 * @tc.require: issueNumber
476 */
477 HWTEST_F(CodegenTest, codegen_test_014, TestSize.Level1)
478 {
479 std::string pfile = GRAPH_TEST_ABC_DIR "codegenTryCatch.abc";
480 const char *test_method_name = "func1";
481 bool status = false;
482
__anon2d774a330902(Graph* graph, std::string &method_name) 483 graph_test_.TestBuildGraphFromFile(pfile, [test_method_name, &status](Graph* graph, std::string &method_name) {
484 if (test_method_name != method_name) {
485 return;
486 }
487 status = true;
488 EXPECT_NE(graph, nullptr);
489
490 Function *function = nullptr;
491 BytecodeOptIrInterface *interface = nullptr;
492 Program *prog = nullptr;
493 size_t size = 10; // 10: It's block size
494 BytecodeGen bc_gen(graph, function, interface, prog);
495 EXPECT_EQ(bc_gen.GetBlocksToVisit().size(), size);
496 });
497 EXPECT_TRUE(status);
498 }
499
500 /**
501 * @tc.name: codegen_test_015
502 * @tc.desc: Verify the RunImpl function.
503 * @tc.type: FUNC
504 * @tc.require: issueNumber
505 */
506 HWTEST_F(CodegenTest, codegen_test_015, TestSize.Level1)
507 {
508 const auto source = R"(
509 .function any func_main_0(any a0, any a1, any a2) <static> {
510 mov v0, a0
511 mov v1, a1
512 mov v2, a2
513 try_begin_label_0:
514 ldai 0x1
515 trystglobalbyname 0x0, "a"
516 try_end_label_0:
517 jmp handler_end_label_0_0
518 handler_begin_label_0_0:
519 sta v4
520 ldai 0x2
521 trystglobalbyname 0x1, "a"
522 handler_end_label_0_0:
523 ldundefined
524 returnundefined
525
526 .catchall try_begin_label_0, try_end_label_0, handler_begin_label_0_0, handler_end_label_0_0
527 }
528 )";
529
530 panda::pandasm::Parser parser;
531 auto res = parser.Parse(source);
532 auto &prog = res.Value();
533
534 auto &function = prog.function_table.at("func_main_0:(any,any,any)");
535
536 pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps {};
537 auto ir_interface = panda::bytecodeopt::BytecodeOptIrInterface(&maps, &prog);
538 bool status = false;
539
540 graph_test_.TestBuildGraphFromFunc(prog, "func_main_0:(any,any,any)", maps, ir_interface, [&function,
__anon2d774a330a02(Graph* graph) 541 &ir_interface, &prog, &status](Graph* graph) {
542
543 EXPECT_NE(graph, nullptr);
544
545 for (auto bb : graph->GetBlocksRPO()) {
546 EXPECT_NE(bb, nullptr);
547 if (bb->IsTryBegin()) {
548 status = true;
549 graph->AppendTryBeginBlock(bb);
550 }
551 }
552 EXPECT_TRUE(graph->RunPass<compiler::Cleanup>());
553 EXPECT_FALSE(graph->RunPass<compiler::Cleanup>());
554 EXPECT_FALSE(graph->RunPass<panda::compiler::ValNum>());
555 EXPECT_TRUE(graph->RunPass<panda::compiler::Lowering>());
556 EXPECT_TRUE(graph->RunPass<panda::compiler::MoveConstants>());
557 EXPECT_FALSE(graph->RunPass<compiler::Cleanup>());
558 EXPECT_TRUE(graph->RunPass<RegAccAlloc>());
559 EXPECT_FALSE(graph->RunPass<compiler::Cleanup>());
560 EXPECT_TRUE(RegAlloc(graph));
561 EXPECT_FALSE(graph->RunPass<compiler::Cleanup>());
562 EXPECT_TRUE(graph->RunPass<RegEncoder>());
563
564 EXPECT_TRUE(graph->RunPass<BytecodeGen>(&function, &ir_interface, &prog));
565 });
566 EXPECT_TRUE(status);
567 }
568 } // namespace panda::bytecodeopt
569