1 /*
2 * Copyright (c) 2023-2025 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 "common.h"
17 #include "bytecode_optimizer/reg_acc_alloc.h"
18 #include <gtest/gtest.h>
19 #include "bytecode_optimizer/optimize_bytecode.h"
20
21 namespace ark::bytecodeopt::test {
22
23 // NOLINTBEGIN(readability-magic-numbers)
24
25 class RegAccAllocTest : public CommonTest {
26 public:
CheckInstructionsDestRegIsAcc(std::vector<int> && instIds)27 void CheckInstructionsDestRegIsAcc(std::vector<int> &&instIds)
28 {
29 for (auto id : instIds) {
30 ASSERT_EQ(INS(id).GetDstReg(), compiler::ACC_REG_ID);
31 }
32 }
33
CheckInstructionsSrcRegIsAcc(std::vector<int> && instIds)34 void CheckInstructionsSrcRegIsAcc(std::vector<int> &&instIds)
35 {
36 for (auto id : instIds) {
37 uint8_t idx = 0;
38 switch (INS(id).GetOpcode()) {
39 case compiler::Opcode::LoadArray:
40 case compiler::Opcode::StoreObject:
41 case compiler::Opcode::StoreStatic:
42 idx = 1;
43 break;
44 case compiler::Opcode::StoreArray:
45 idx = 2U;
46 break;
47 default:
48 break;
49 }
50
51 ASSERT_EQ(INS(id).GetSrcReg(idx), compiler::ACC_REG_ID);
52 }
53 }
54 };
55
56 /*
57 * Test if two arithmetic instructions follow each other.
58 */
TEST_F(RegAccAllocTest,ArithmeticInstructions)59 TEST_F(RegAccAllocTest, ArithmeticInstructions)
60 {
61 auto graph = CreateEmptyGraph();
62 GRAPH(graph)
63 {
64 CONSTANT(0U, 1U).s32();
65 CONSTANT(1U, 10U).s32();
66 CONSTANT(2U, 20U).s32();
67
68 BASIC_BLOCK(2U, 3U)
69 {
70 INST(3U, Opcode::Mul).s32().Inputs(0U, 2U);
71 INST(4U, Opcode::Add).s32().Inputs(3U, 1U);
72 }
73
74 BASIC_BLOCK(3U, -1L)
75 {
76 INST(5U, Opcode::Return).s32().Inputs(4U);
77 }
78 }
79
80 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
81
82 CheckInstructionsDestRegIsAcc({3U, 4U});
83 CheckInstructionsSrcRegIsAcc({4U, 5U});
84 }
85
86 /*
87 * Test if two arithmetic instructions follow each other.
88 * Take the advantage of commutativity.
89 */
TEST_F(RegAccAllocTest,Commutativity1)90 TEST_F(RegAccAllocTest, Commutativity1)
91 {
92 auto graph = CreateEmptyGraph();
93 GRAPH(graph)
94 {
95 CONSTANT(0U, 1U).s32();
96 CONSTANT(1U, 10U).s32();
97 CONSTANT(2U, 20U).s32();
98
99 BASIC_BLOCK(2U, 3U)
100 {
101 INST(3U, Opcode::Mul).s32().Inputs(0U, 2U);
102 INST(4U, Opcode::Add).s32().Inputs(1U, 3U);
103 }
104
105 BASIC_BLOCK(3U, -1L)
106 {
107 INST(5U, Opcode::Return).s32().Inputs(4U);
108 }
109 }
110
111 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
112
113 CheckInstructionsDestRegIsAcc({3U, 4U});
114 CheckInstructionsSrcRegIsAcc({4U, 5U});
115 }
116
117 /*
118 * Test if two arithmetic instructions follow each other.
119 * Cannot take the advantage of commutativity at the Sub instruction.
120 */
TEST_F(RegAccAllocTest,Commutativity2)121 TEST_F(RegAccAllocTest, Commutativity2)
122 {
123 auto graph = CreateEmptyGraph();
124 GRAPH(graph)
125 {
126 CONSTANT(0U, 1U).s32();
127 CONSTANT(1U, 10U).s32();
128 CONSTANT(2U, 20U).s32();
129
130 BASIC_BLOCK(2U, 3U)
131 {
132 INST(3U, Opcode::Mul).s32().Inputs(0U, 2U);
133 INST(4U, Opcode::Sub).s32().Inputs(1U, 3U);
134 }
135
136 BASIC_BLOCK(3U, -1L)
137 {
138 INST(5U, Opcode::Return).s32().Inputs(4U);
139 }
140 }
141
142 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
143
144 CheckInstructionsDestRegIsAcc({4U});
145 CheckInstructionsSrcRegIsAcc({5U});
146 }
147
148 /*
149 * Test if the first arithmetic instruction has multiple users.
150 * That (3, Opcode::Mul) is spilled out to register becasue the subsequent
151 * instruction (4, Opcode::Add) makes the accumulator dirty.
152 */
TEST_F(RegAccAllocTest,ArithmeticInstructionsWithDirtyAccumulator)153 TEST_F(RegAccAllocTest, ArithmeticInstructionsWithDirtyAccumulator)
154 {
155 auto graph = CreateEmptyGraph();
156 GRAPH(graph)
157 {
158 CONSTANT(0U, 1U).s32();
159 CONSTANT(1U, 10U).s32();
160 CONSTANT(2U, 20U).s32();
161
162 BASIC_BLOCK(2U, 3U)
163 {
164 INST(3U, Opcode::Mul).s32().Inputs(0U, 2U);
165 INST(4U, Opcode::Add).s32().Inputs(1U, 3U);
166 INST(5U, Opcode::Sub).s32().Inputs(3U, 4U);
167 INST(6U, Opcode::Mul).s32().Inputs(5U, 3U);
168 }
169
170 BASIC_BLOCK(3U, -1L)
171 {
172 INST(7U, Opcode::Return).s32().Inputs(6U);
173 }
174 }
175
176 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
177
178 CheckInstructionsDestRegIsAcc({5U, 6U});
179 CheckInstructionsSrcRegIsAcc({6U, 7U});
180 }
181
182 /*
183 * Test if arithmetic instructions are used as Phi inputs.
184 *
185 * Test Graph:
186 * [0]
187 * |
188 * v
189 * /----[2]----\
190 * | |
191 * v v
192 * [3] [4]
193 * | |
194 * \--->[4]<---/
195 * |
196 * v
197 * [exit]
198 */
TEST_F(RegAccAllocTest,SimplePhi)199 TEST_F(RegAccAllocTest, SimplePhi)
200 {
201 auto graph = CreateEmptyGraph();
202 GRAPH(graph)
203 {
204 CONSTANT(0U, 1U).s32();
205 CONSTANT(1U, 10U).s32();
206 CONSTANT(2U, 20U).s32();
207
208 BASIC_BLOCK(2U, 3U, 4U)
209 {
210 INST(4U, Opcode::Add).s32().Inputs(1U, 2U);
211 INST(5U, Opcode::Compare).b().Inputs(4U, 0U);
212 INST(6U, Opcode::IfImm).SrcType(compiler::DataType::BOOL).CC(compiler::CC_NE).Imm(0U).Inputs(5U);
213 }
214
215 BASIC_BLOCK(3U, 5U)
216 {
217 INST(8U, Opcode::Sub).s32().Inputs(4U, 0U);
218 }
219
220 BASIC_BLOCK(4U, 5U)
221 {
222 INST(9U, Opcode::Add).s32().Inputs(4U, 0U);
223 }
224
225 BASIC_BLOCK(5U, -1L)
226 {
227 INST(10U, Opcode::Phi).s32().Inputs({{3U, 8U}, {4U, 9U}});
228 INST(11U, Opcode::Add).s32().Inputs(4U, 10U);
229 INST(7U, Opcode::Return).s32().Inputs(11U);
230 }
231 }
232
233 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
234
235 CheckInstructionsDestRegIsAcc({5U, 8U, 9U, 11U});
236 CheckInstructionsSrcRegIsAcc({6U, 7U, 11U});
237 }
238
239 /*
240 * Test Cast instructions which follow each other.
241 * Each instruction refers to the previos one.
242 */
TEST_F(RegAccAllocTest,CastInstructions)243 TEST_F(RegAccAllocTest, CastInstructions)
244 {
245 auto graph = CreateEmptyGraph();
246 GRAPH(graph)
247 {
248 PARAMETER(0U, 0U).u32();
249
250 BASIC_BLOCK(2U, -1L)
251 {
252 INST(1U, Opcode::Cast).f64().SrcType(compiler::DataType::UINT32).Inputs(0U);
253 INST(2U, Opcode::Cast).s32().SrcType(compiler::DataType::FLOAT64).Inputs(1U);
254 INST(3U, Opcode::Cast).s16().SrcType(compiler::DataType::INT32).Inputs(2U);
255 INST(4U, Opcode::Return).s16().Inputs(3U);
256 }
257 }
258
259 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
260
261 CheckInstructionsDestRegIsAcc({1U, 2U, 3U});
262 CheckInstructionsSrcRegIsAcc({2U, 3U, 4U});
263 }
264
265 /*
266 * Test Abs and Sqrt instructions.
267 * Each instruction refers to the previous instruction.
268 */
TEST_F(RegAccAllocTest,AbsAndSqrtInstructions)269 TEST_F(RegAccAllocTest, AbsAndSqrtInstructions)
270 {
271 auto graph = CreateEmptyGraph();
272 GRAPH(graph)
273 {
274 CONSTANT(0U, 1.22_D).f64();
275 PARAMETER(1U, 10U).f64();
276
277 BASIC_BLOCK(2U, -1L)
278 {
279 INST(2U, Opcode::Sub).f64().Inputs(0U, 1U);
280 INST(3U, Opcode::Abs).f64().Inputs(2U);
281 INST(4U, Opcode::Sqrt).f64().Inputs(3U);
282 INST(5U, Opcode::Return).f64().Inputs(4U);
283 }
284 }
285
286 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
287
288 CheckInstructionsDestRegIsAcc({4U});
289 CheckInstructionsSrcRegIsAcc({5U});
290 }
291
292 /*
293 * Test LoadArray instruction that reads accumulator as second input.
294 * Note: most instructions read accumulator as first input.
295 */
TEST_F(RegAccAllocTest,LoadArrayInstruction)296 TEST_F(RegAccAllocTest, LoadArrayInstruction)
297 {
298 auto graph = CreateEmptyGraph();
299 GRAPH(graph)
300 {
301 PARAMETER(0U, 1U).s32();
302 PARAMETER(1U, 10U).ref();
303
304 BASIC_BLOCK(2U, -1L)
305 {
306 INST(2U, Opcode::SaveState).Inputs(0U, 1U, 0U).SrcVregs({0U, 1U, 2U});
307 INST(3U, Opcode::NullCheck).ref().Inputs(1U, 2U);
308 INST(4U, Opcode::LenArray).s32().Inputs(3U);
309 INST(5U, Opcode::BoundsCheck).s32().Inputs(4U, 0U, 2U);
310 INST(6U, Opcode::LoadArray).s32().Inputs(3U, 5U);
311 INST(7U, Opcode::Cast).f64().SrcType(compiler::DataType::INT32).Inputs(6U);
312 INST(8U, Opcode::Return).f64().Inputs(7U);
313 }
314 }
315
316 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
317
318 CheckInstructionsDestRegIsAcc({4U, 6U, 7U});
319 CheckInstructionsSrcRegIsAcc({5U, 7U, 8U});
320 }
321
322 /*
323 * Test throw instruction.
324 * Currently, just linear block-flow is supported.
325 * Nothing happens in this test.
326 */
TEST_F(RegAccAllocTest,ThrowInstruction)327 TEST_F(RegAccAllocTest, ThrowInstruction)
328 {
329 auto graph = CreateEmptyGraph();
330 GRAPH(graph)
331 {
332 CONSTANT(0U, 1U).s32();
333 CONSTANT(1U, 0U).s32();
334
335 BASIC_BLOCK(2U, 3U)
336 {
337 INST(2U, Opcode::SaveState).Inputs().SrcVregs({});
338 INST(3U, Opcode::LoadAndInitClass).ref().Inputs(2U);
339 INST(4U, Opcode::NewObject).ref().Inputs(3U, 2U);
340 }
341 BASIC_BLOCK(3U, 4U, 5U, 6U)
342 {
343 INST(5U, Opcode::Try).CatchTypeIds({0x0U, 0xE1U});
344 }
345 BASIC_BLOCK(4U, -1L)
346 {
347 INST(6U, Opcode::SaveState).Inputs(4U).SrcVregs({0U});
348 INST(7U, Opcode::Throw).Inputs(4U, 6U);
349 }
350 BASIC_BLOCK(5U, -1L)
351 {
352 INST(8U, Opcode::Return).b().Inputs(1U);
353 }
354 BASIC_BLOCK(6U, -1L)
355 {
356 INST(9U, Opcode::Return).b().Inputs(0U);
357 }
358 }
359
360 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
361 }
362
363 /*
364 * Test Phi instruction in loop.
365 * This test is copied from reg_alloc_linear_scan_test.cpp file.
366 */
TEST_F(RegAccAllocTest,PhiInstructionInLoop)367 TEST_F(RegAccAllocTest, PhiInstructionInLoop)
368 {
369 auto graph = CreateEmptyGraph();
370 GRAPH(graph)
371 {
372 CONSTANT(0U, 1U).s32();
373 CONSTANT(1U, 10U).s32();
374 CONSTANT(2U, 20U).s32();
375
376 BASIC_BLOCK(2U, 3U, 4U)
377 {
378 INST(3U, Opcode::Phi).s32().Inputs({{0U, 0U}, {3U, 8U}});
379 INST(4U, Opcode::Phi).s32().Inputs({{0U, 1U}, {3U, 9U}});
380 INST(5U, Opcode::SafePoint).Inputs(0U, 3U, 4U).SrcVregs({0U, 1U, 2U});
381 INST(6U, Opcode::Compare).b().Inputs(4U, 0U);
382 INST(7U, Opcode::IfImm).SrcType(compiler::DataType::BOOL).CC(compiler::CC_NE).Imm(0U).Inputs(6U);
383 }
384
385 BASIC_BLOCK(3U, 2U)
386 {
387 INST(8U, Opcode::Mul).s32().Inputs(3U, 4U);
388 INST(9U, Opcode::Sub).s32().Inputs(4U, 0U);
389 }
390
391 BASIC_BLOCK(4U, -1L)
392 {
393 INST(10U, Opcode::Add).s32().Inputs(2U, 3U);
394 INST(11U, Opcode::Return).s32().Inputs(10U);
395 }
396 }
397
398 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
399
400 CheckInstructionsDestRegIsAcc({6U, 10U});
401 CheckInstructionsSrcRegIsAcc({7U, 11U});
402 }
403
404 /*
405 * Test multiple branches.
406 * Test Graph:
407 * /---[2]---\
408 * | |
409 * v v
410 * [3]<------[4]
411 * |
412 * v
413 * [5]
414 */
TEST_F(RegAccAllocTest,MultipleBranches)415 TEST_F(RegAccAllocTest, MultipleBranches)
416 {
417 auto graph = CreateEmptyGraph();
418 GRAPH(graph)
419 {
420 PARAMETER(0U, 0U).u16();
421 PARAMETER(1U, 1U).s64();
422 CONSTANT(2U, 0x20U).s64();
423 CONSTANT(3U, 0x25U).s64();
424 BASIC_BLOCK(2U, 3U, 4U)
425 {
426 INST(5U, Opcode::Compare).b().CC(compiler::CC_GT).Inputs(0U, 2U);
427 INST(6U, Opcode::IfImm).SrcType(compiler::DataType::BOOL).CC(compiler::CC_NE).Imm(0U).Inputs(5U);
428 }
429 // v0 <= 0x20
430 BASIC_BLOCK(4U, 3U, 5U)
431 {
432 INST(8U, Opcode::Compare).b().CC(compiler::CC_GT).Inputs(1U, 3U);
433 INST(9U, Opcode::IfImm).SrcType(compiler::DataType::BOOL).CC(compiler::CC_NE).Imm(0U).Inputs(8U);
434 }
435 // v0 <= 0x20 && v1 <= 0x25
436 BASIC_BLOCK(5U, -1L)
437 {
438 INST(11U, Opcode::Return).u16().Inputs(0U);
439 }
440 BASIC_BLOCK(3U, -1L)
441 {
442 INST(14U, Opcode::Return).u16().Inputs(1U);
443 }
444 }
445
446 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
447
448 CheckInstructionsDestRegIsAcc({5U, 8U});
449 CheckInstructionsSrcRegIsAcc({6U, 9U});
450 }
451
452 /*
453 * Test Phi with multiple inputs.
454 * Phi cannot be optimized because one of the inputs is a CONSTANT.
455 * Test Graph:
456 * /---[2]---\
457 * | |
458 * v |
459 * /--[3]--\ |
460 * | | |
461 * v v |
462 * [4] [5] |
463 * | | |
464 * | v |
465 * \----->[6]<---/
466 * |
467 * [exit]
468 */
TEST_F(RegAccAllocTest,PhiWithMultipleInputs)469 TEST_F(RegAccAllocTest, PhiWithMultipleInputs)
470 {
471 auto graph = CreateEmptyGraph();
472 GRAPH(graph)
473 {
474 PARAMETER(0U, 0U).s32();
475 PARAMETER(1U, 1U).s32();
476 BASIC_BLOCK(2U, 3U, 6U)
477 {
478 INST(2U, Opcode::Compare).b().Inputs(0U, 1U);
479 INST(3U, Opcode::IfImm).SrcType(compiler::DataType::BOOL).CC(compiler::CC_NE).Imm(0U).Inputs(2U);
480 }
481 BASIC_BLOCK(3U, 4U, 5U)
482 {
483 INST(4U, Opcode::Mul).s32().Inputs(0U, 0U);
484 INST(5U, Opcode::Compare).b().Inputs(4U, 1U);
485 INST(6U, Opcode::IfImm).SrcType(compiler::DataType::BOOL).CC(compiler::CC_NE).Imm(0U).Inputs(5U);
486 }
487 BASIC_BLOCK(4U, 6U)
488 {
489 INST(7U, Opcode::Mul).s32().Inputs(4U, 1U);
490 }
491 BASIC_BLOCK(5U, 6U)
492 {
493 INST(8U, Opcode::Add).s32().Inputs(4U, 1U);
494 }
495 BASIC_BLOCK(6U, -1L)
496 {
497 INST(9U, Opcode::Phi).s32().Inputs({{2U, 0U}, {4U, 7U}, {5U, 8U}});
498 INST(10U, Opcode::Return).s32().Inputs(9U);
499 }
500 }
501
502 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
503
504 CheckInstructionsDestRegIsAcc({2U, 5U});
505 CheckInstructionsSrcRegIsAcc({3U, 6U});
506 }
507
508 /*
509 * Test for Phi. Phi cannot be optimized because the subsequent
510 * Compare instruction makes the accumulator dirty.
511 */
TEST_F(RegAccAllocTest,PhiWithSubsequentCompareInstruction)512 TEST_F(RegAccAllocTest, PhiWithSubsequentCompareInstruction)
513 {
514 auto graph = CreateEmptyGraph();
515 GRAPH(graph)
516 {
517 CONSTANT(0U, 1U).s32();
518 CONSTANT(1U, 10U).s32();
519 CONSTANT(2U, 20U).s32();
520
521 BASIC_BLOCK(2U, 3U)
522 {
523 INST(3U, Opcode::Add).s32().Inputs(0U, 1U);
524 }
525
526 BASIC_BLOCK(3U, 4U, 5U)
527 {
528 INST(4U, Opcode::Phi).s32().Inputs({{2U, 3U}, {4U, 8U}});
529 INST(5U, Opcode::SafePoint).Inputs(0U, 4U).SrcVregs({0U, 1U});
530 INST(6U, Opcode::Compare).b().Inputs(4U, 0U);
531 INST(7U, Opcode::IfImm).SrcType(compiler::DataType::BOOL).CC(compiler::CC_NE).Imm(0U).Inputs(6U);
532 }
533
534 BASIC_BLOCK(4U, 3U)
535 {
536 INST(8U, Opcode::Mul).s32().Inputs(3U, 2U);
537 }
538
539 BASIC_BLOCK(5U, -1L)
540 {
541 INST(9U, Opcode::Add).s32().Inputs(2U, 4U);
542 INST(10U, Opcode::Return).s32().Inputs(9U);
543 }
544 }
545
546 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
547
548 CheckInstructionsDestRegIsAcc({6U, 9U});
549 CheckInstructionsSrcRegIsAcc({7U, 10U});
550 }
551
552 /*
553 * A switch-case example. There are different arithmetic instructions
554 * in the case blocks. These instructions are the inputs of a Phi.
555 */
556 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST_F(RegAccAllocTest,SwitchCase)557 TEST_F(RegAccAllocTest, SwitchCase)
558 {
559 auto graph = CreateEmptyGraph();
560 GRAPH(graph)
561 {
562 PARAMETER(0U, 0U).ref();
563 CONSTANT(1U, 0x1U).s32();
564 CONSTANT(5U, 0xaU).s32();
565 CONSTANT(8U, 0x2U).s32();
566 CONSTANT(10U, 0x3U).s32();
567
568 BASIC_BLOCK(2U, 3U, 4U)
569 {
570 INST(2U, Opcode::LoadArray).ref().Inputs(0U, 1U);
571 INST(3U, Opcode::SaveState).Inputs(2U, 1U, 0U).SrcVregs({0U, 1U, 2U});
572 INST(4U, Opcode::CallStatic)
573 .s32()
574 .Inputs({{compiler::DataType::REFERENCE, 2U}, {compiler::DataType::NO_TYPE, 3U}});
575 INST(7U, Opcode::If).SrcType(compiler::DataType::INT32).CC(compiler::CC_EQ).Inputs(1U, 4U);
576 }
577
578 BASIC_BLOCK(4U, 5U, 6U)
579 {
580 INST(9U, Opcode::If).SrcType(compiler::DataType::INT32).CC(compiler::CC_EQ).Inputs(8U, 4U);
581 }
582
583 BASIC_BLOCK(6U, 7U, 9U)
584 {
585 INST(11U, Opcode::If).SrcType(compiler::DataType::INT32).CC(compiler::CC_EQ).Inputs(10U, 4U);
586 }
587
588 BASIC_BLOCK(9U, 10U)
589 {
590 INST(12U, Opcode::Mod).s32().Inputs(5U, 4U);
591 }
592
593 BASIC_BLOCK(7U, 10U)
594 {
595 INST(13U, Opcode::Mul).s32().Inputs(4U, 5U);
596 }
597
598 BASIC_BLOCK(5U, 10U)
599 {
600 INST(14U, Opcode::Add).s32().Inputs(4U, 5U);
601 }
602
603 BASIC_BLOCK(3U, 10U)
604 {
605 INST(15U, Opcode::Sub).s32().Inputs(5U, 4U);
606 }
607
608 BASIC_BLOCK(10U, -1L)
609 {
610 INST(16U, Opcode::Phi).s32().Inputs({{3U, 15U}, {5U, 14U}, {7U, 13U}, {9U, 12U}});
611 INST(17U, Opcode::Return).s32().Inputs(16U);
612 }
613 }
614
615 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
616
617 CheckInstructionsDestRegIsAcc({12U, 13U, 14U, 15U, 16U});
618 CheckInstructionsSrcRegIsAcc({17U});
619 }
620
621 /*
622 * This test creates an array and does modifications in that.
623 */
TEST_F(RegAccAllocTest,CreateArray)624 TEST_F(RegAccAllocTest, CreateArray)
625 {
626 auto graph = CreateEmptyGraph();
627 GRAPH(graph)
628 {
629 PARAMETER(0U, 0U).ref();
630 CONSTANT(1U, 0x2U).s32();
631 CONSTANT(4U, 0x1U).s32();
632 CONSTANT(10U, 0x0U).s32();
633 CONSTANT(11U, 0xaU).s32();
634 CONSTANT(20U, 0xcU).s32();
635
636 BASIC_BLOCK(2U, -1L)
637 {
638 INST(2U, Opcode::SaveState).Inputs(1U, 0U).SrcVregs({0U, 6U});
639 INST(44U, Opcode::LoadAndInitClass).ref().Inputs(2U).TypeId(68U);
640 INST(3U, Opcode::NewArray).ref().Inputs(44U, 1U, 2U);
641 INST(5U, Opcode::LoadArray).ref().Inputs(0U, 4U);
642 INST(6U, Opcode::SaveState).Inputs(5U, 4U, 3U, 0U).SrcVregs({0U, 1U, 4U, 6U});
643 INST(7U, Opcode::CallStatic)
644 .s32()
645 .Inputs({{compiler::DataType::REFERENCE, 5U}, {compiler::DataType::NO_TYPE, 6U}});
646 INST(9U, Opcode::Add).s32().Inputs(7U, 1U);
647 INST(12U, Opcode::StoreArray).s32().Inputs(5U, 10U, 11U);
648 INST(14U, Opcode::Add).s32().Inputs(7U, 20U);
649 INST(15U, Opcode::StoreArray).s32().Inputs(3U, 4U, 14U);
650 INST(17U, Opcode::Mul).s32().Inputs(14U, 9U);
651 INST(18U, Opcode::Return).s32().Inputs(17U);
652 }
653 }
654
655 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
656
657 CheckInstructionsDestRegIsAcc({17U});
658 CheckInstructionsSrcRegIsAcc({9U, 15U, 18U});
659 }
660
661 /*
662 * Test StoreObject instruction that reads accumulator as second input.
663 * Note: most instructions read accumulator as first input.
664 */
TEST_F(RegAccAllocTest,StoreObjectInstruction)665 TEST_F(RegAccAllocTest, StoreObjectInstruction)
666 {
667 auto graph = CreateEmptyGraph();
668 GRAPH(graph)
669 {
670 CONSTANT(0U, nullptr).ref();
671 CONSTANT(1U, 0x2aU).s64();
672 CONSTANT(2U, 0x1U).s64();
673 BASIC_BLOCK(2U, -1L)
674 {
675 INST(3U, Opcode::SaveState).Inputs(0U, 2U).SrcVregs({0U, 1U});
676 INST(4U, Opcode::NullCheck).ref().Inputs(0U, 3U);
677 INST(5U, Opcode::Add).s64().Inputs(1U, 2U);
678 INST(6U, Opcode::StoreObject).s64().Inputs(4U, 5U);
679 INST(7U, Opcode::Return).s64().Inputs(1U);
680 }
681 }
682
683 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
684
685 CheckInstructionsDestRegIsAcc({5U});
686 CheckInstructionsSrcRegIsAcc({6U});
687 }
688
689 /*
690 * Test StoreStatic instruction that reads accumulator as second input.
691 * Note: most instructions read accumulator as first input.
692 */
TEST_F(RegAccAllocTest,StoreStaticInstruction)693 TEST_F(RegAccAllocTest, StoreStaticInstruction)
694 {
695 auto graph = CreateEmptyGraph();
696 GRAPH(graph)
697 {
698 PARAMETER(0U, 0U).s32();
699 PARAMETER(1U, 1U).s32();
700
701 BASIC_BLOCK(2U, -1L)
702 {
703 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
704 INST(3U, Opcode::LoadAndInitClass).ref().Inputs(2U);
705 INST(4U, Opcode::Add).s32().Inputs(0U, 1U);
706 INST(5U, Opcode::StoreStatic).s32().Inputs(3U, 4U).Volatile();
707 INST(6U, Opcode::ReturnVoid).v0id();
708 }
709 }
710
711 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
712
713 CheckInstructionsDestRegIsAcc({4U});
714 CheckInstructionsSrcRegIsAcc({5U});
715 }
716
717 /*
718 * Test if Phi uses Phi as input.
719 * This case is not supported right now.
720 */
721 // CC-OFFNXT(huge_method[C++], G.FUN.01-CPP) solid logic
TEST_F(RegAccAllocTest,PhiUsesPhiAsInput)722 TEST_F(RegAccAllocTest, PhiUsesPhiAsInput)
723 {
724 auto graph = CreateEmptyGraph();
725 GRAPH(graph)
726 {
727 PARAMETER(0U, 0U).ref();
728 CONSTANT(1U, 0x1U).s32();
729 CONSTANT(5U, 0xaU).s32();
730 CONSTANT(8U, 0x2U).s32();
731 CONSTANT(10U, 0x3U).s32();
732
733 BASIC_BLOCK(2U, 3U, 12U)
734 {
735 INST(2U, Opcode::LoadArray).ref().Inputs(0U, 1U);
736 INST(3U, Opcode::SaveState).Inputs(2U, 1U, 0U).SrcVregs({0U, 1U, 2U});
737 INST(4U, Opcode::CallStatic)
738 .s32()
739 .Inputs({{compiler::DataType::REFERENCE, 2U}, {compiler::DataType::NO_TYPE, 3U}});
740 INST(23U, Opcode::If).SrcType(compiler::DataType::INT32).CC(compiler::CC_EQ).Inputs(5U, 4U);
741 }
742
743 BASIC_BLOCK(3U, 4U, 5U)
744 {
745 INST(7U, Opcode::If).SrcType(compiler::DataType::INT32).CC(compiler::CC_EQ).Inputs(1U, 4U);
746 }
747
748 BASIC_BLOCK(5U, 6U, 7U)
749 {
750 INST(9U, Opcode::If).SrcType(compiler::DataType::INT32).CC(compiler::CC_EQ).Inputs(8U, 4U);
751 }
752
753 BASIC_BLOCK(7U, 8U, 10U)
754 {
755 INST(11U, Opcode::If).SrcType(compiler::DataType::INT32).CC(compiler::CC_EQ).Inputs(10U, 4U);
756 }
757
758 BASIC_BLOCK(10U, 11U)
759 {
760 INST(12U, Opcode::Mod).s32().Inputs(5U, 4U);
761 }
762
763 BASIC_BLOCK(8U, 11U)
764 {
765 INST(13U, Opcode::Mul).s32().Inputs(4U, 5U);
766 }
767
768 BASIC_BLOCK(6U, 11U)
769 {
770 INST(14U, Opcode::Add).s32().Inputs(4U, 5U);
771 }
772
773 BASIC_BLOCK(4U, 11U)
774 {
775 INST(15U, Opcode::Sub).s32().Inputs(5U, 4U);
776 }
777
778 BASIC_BLOCK(11U, 13U)
779 {
780 INST(16U, Opcode::Phi).s32().Inputs({{4U, 15U}, {6U, 14U}, {8U, 13U}, {10U, 12U}});
781 }
782
783 BASIC_BLOCK(12U, 13U)
784 {
785 INST(17U, Opcode::Sub).s32().Inputs(5U, 1U);
786 }
787
788 BASIC_BLOCK(13U, -1L)
789 {
790 INST(18U, Opcode::Phi).s32().Inputs({{11U, 16U}, {12U, 17U}});
791 INST(19U, Opcode::Return).s32().Inputs(18U);
792 }
793 }
794
795 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
796 }
797
798 /*
799 * This test covers case with LoadObject which user can not read from accumulator
800 */
TEST_F(RegAccAllocTest,NotUseAccDstRegLoadObject)801 TEST_F(RegAccAllocTest, NotUseAccDstRegLoadObject)
802 {
803 auto graph = CreateEmptyGraph();
804 GRAPH(graph)
805 {
806 // NOLINTNEXTLINE(google-build-using-namespace)
807 using namespace compiler::DataType;
808 PARAMETER(0U, 0U).ref();
809
810 BASIC_BLOCK(2U, 3U, 4U)
811 {
812 INST(3U, Opcode::LoadObject).s32().Inputs(0U);
813 INST(6U, Opcode::LoadObject).ref().Inputs(0U);
814 INST(7U, Opcode::SaveState).NoVregs();
815 INST(9U, Opcode::CallVirtual).s32().Inputs({{REFERENCE, 6U}, {NO_TYPE, 7U}});
816 INST(24U, Opcode::If).CC(compiler::ConditionCode::CC_NE).SrcType(INT32).Inputs(9U, 3U);
817 }
818
819 BASIC_BLOCK(4U, -1L)
820 {
821 CONSTANT(25U, 0xffffU).s32();
822 INST(13U, Opcode::Return).u16().Inputs(25U);
823 }
824 BASIC_BLOCK(3U, -1L)
825 {
826 INST(16U, Opcode::LoadObject).ref().Inputs(0U);
827 INST(19U, Opcode::LoadObject).s32().Inputs(0U);
828 INST(20U, Opcode::SaveState).NoVregs();
829 INST(22U, Opcode::CallVirtual).u16().Inputs({{REFERENCE, 16U}, {INT32, 19U}, {NO_TYPE, 20U}});
830 INST(23U, Opcode::Return).u16().Inputs(22U);
831 }
832 }
833
834 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
835
836 ASSERT_NE(INS(3U).GetDstReg(), compiler::ACC_REG_ID);
837 ASSERT_EQ(INS(6U).GetDstReg(), compiler::ACC_REG_ID);
838 ASSERT_NE(INS(16U).GetDstReg(), compiler::ACC_REG_ID);
839 ASSERT_EQ(INS(19U).GetDstReg(), compiler::ACC_REG_ID);
840 }
841
842 /*
843 * Test checks if accumulator gets dirty between input and inst where input is LoadObject
844 */
TEST_F(RegAccAllocTest,IsAccWriteBetweenLoadObject)845 TEST_F(RegAccAllocTest, IsAccWriteBetweenLoadObject)
846 {
847 auto graph = CreateEmptyGraph();
848 GRAPH(graph)
849 {
850 // NOLINTNEXTLINE(google-build-using-namespace)
851 using namespace compiler::DataType;
852 PARAMETER(0U, 0U).ref();
853
854 BASIC_BLOCK(2U, 3U, 4U)
855 {
856 INST(3U, Opcode::LoadObject).s32().Inputs(0U);
857 INST(6U, Opcode::IfImm).CC(compiler::ConditionCode::CC_NE).SrcType(INT32).Inputs(3U).Imm(0U);
858 }
859
860 BASIC_BLOCK(4U, -1L)
861 {
862 CONSTANT(22U, 0xffffU).s32();
863 INST(8U, Opcode::Return).u16().Inputs(22U);
864 }
865 BASIC_BLOCK(3U, -1L)
866 {
867 INST(21U, Opcode::SubI).s32().Imm(1U).Inputs(3U);
868 INST(16U, Opcode::StoreObject).s32().Inputs(0U, 21U);
869 INST(17U, Opcode::SaveState).NoVregs();
870 INST(19U, Opcode::CallVirtual).u16().Inputs({{REFERENCE, 0U}, {NO_TYPE, 17U}});
871 INST(20U, Opcode::Return).u16().Inputs(19U);
872 }
873 }
874
875 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
876
877 ASSERT_NE(INS(3U).GetDstReg(), compiler::ACC_REG_ID);
878 }
879
880 /*
881 * Test calls with accumulator
882 */
TEST_F(RegAccAllocTest,CallWithAcc)883 TEST_F(RegAccAllocTest, CallWithAcc)
884 {
885 auto graph = CreateEmptyGraph();
886 GRAPH(graph)
887 {
888 // NOLINTNEXTLINE(google-build-using-namespace)
889 using namespace compiler::DataType;
890
891 CONSTANT(0U, 0U).s32();
892 CONSTANT(1U, 1U).s32();
893 CONSTANT(2U, 2U).s32();
894
895 BASIC_BLOCK(2U, -1L)
896 {
897 INST(3U, Opcode::SaveState).NoVregs();
898 INST(4U, Opcode::LoadString).ref().Inputs(3U).TypeId(42U);
899 INST(5U, Opcode::CallStatic)
900 .v0id()
901 .Inputs({{INT32, 0U}, {INT32, 1U}, {INT32, 2U}, {REFERENCE, 4U}, {NO_TYPE, 3U}});
902 INST(6U, Opcode::LoadString).ref().Inputs(3U).TypeId(43U);
903 INST(7U, Opcode::CallStatic).v0id().Inputs({{INT32, 0U}, {INT32, 1U}, {REFERENCE, 6U}, {NO_TYPE, 3U}});
904 INST(8U, Opcode::LoadString).ref().Inputs(3U).TypeId(44U);
905 INST(9U, Opcode::CallStatic).v0id().Inputs({{INT32, 0U}, {REFERENCE, 8U}, {NO_TYPE, 3U}});
906 INST(10U, Opcode::LoadString).ref().Inputs(3U).TypeId(45U);
907 INST(11U, Opcode::CallStatic).v0id().Inputs({{REFERENCE, 10U}, {NO_TYPE, 3U}});
908 INST(12U, Opcode::ReturnVoid).v0id();
909 }
910 }
911 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
912
913 EXPECT_EQ(INS(4U).GetDstReg(), compiler::ACC_REG_ID);
914 EXPECT_EQ(INS(6U).GetDstReg(), compiler::ACC_REG_ID);
915 EXPECT_EQ(INS(8U).GetDstReg(), compiler::ACC_REG_ID);
916 EXPECT_EQ(INS(10U).GetDstReg(), compiler::ACC_REG_ID);
917 }
918
TEST_F(RegAccAllocTest,Ldai)919 TEST_F(RegAccAllocTest, Ldai)
920 {
921 auto graph = CreateEmptyGraph();
922 GRAPH(graph)
923 {
924 PARAMETER(0U, 0U).s32();
925
926 BASIC_BLOCK(2U, 3U, 4U)
927 {
928 CONSTANT(1U, 3U).s32();
929 INST(10U, Opcode::If).CC(compiler::CC_GE).SrcType(compiler::DataType::INT32).Inputs(1U, 0U);
930 }
931 BASIC_BLOCK(3U, -1L)
932 {
933 CONSTANT(9U, 8U).s32();
934 INST(7U, Opcode::Return).s32().Inputs(9U);
935 }
936 BASIC_BLOCK(4U, -1L)
937 {
938 INST(8U, Opcode::Return).s32().Inputs(0U);
939 }
940 }
941 EXPECT_TRUE(graph->RunPass<bytecodeopt::RegAccAlloc>());
942
943 EXPECT_EQ(INS(1U).GetDstReg(), compiler::ACC_REG_ID);
944 }
945
TEST_F(RegAccAllocTest,Ldai_Call)946 TEST_F(RegAccAllocTest, Ldai_Call)
947 {
948 auto graph = CreateEmptyGraph();
949 GRAPH(graph)
950 {
951 // NOLINTNEXTLINE(google-build-using-namespace)
952 using namespace compiler::DataType;
953 BASIC_BLOCK(2U, -1L)
954 {
955 CONSTANT(1U, 1U).s32();
956 INST(2U, Opcode::SaveState).Inputs().SrcVregs({});
957 INST(3U, Opcode::LoadAndInitClass).ref().Inputs(2U);
958 INST(4U, Opcode::SaveState).Inputs().SrcVregs({});
959 INST(5U, Opcode::InitObject).ref().Inputs({{REFERENCE, 3U}, {INT32, 1U}, {NO_TYPE, 4U}});
960 INST(6U, Opcode::SaveState).Inputs().SrcVregs({});
961 CONSTANT(7U, 11U).s32();
962 INST(8U, Opcode::CallVirtual).s32().Inputs({{REFERENCE, 5U}, {INT32, 7U}, {NO_TYPE, 6U}});
963
964 INST(9U, Opcode::ReturnVoid).v0id();
965 }
966 }
967 graph->RunPass<bytecodeopt::RegAccAlloc>();
968
969 EXPECT_NE(INS(1U).GetDstReg(), compiler::ACC_REG_ID);
970 EXPECT_EQ(INS(5U).GetDstReg(), compiler::ACC_REG_ID);
971 EXPECT_NE(INS(7U).GetDstReg(), compiler::ACC_REG_ID);
972 }
973
TEST_F(RegAccAllocTest,Ldai_Call_2)974 TEST_F(RegAccAllocTest, Ldai_Call_2)
975 {
976 auto graph = CreateEmptyGraph();
977 GRAPH(graph)
978 {
979 // NOLINTNEXTLINE(google-build-using-namespace)
980 using namespace compiler::DataType;
981 BASIC_BLOCK(2U, -1L)
982 {
983 CONSTANT(1U, 1U).s32();
984 INST(2U, Opcode::SaveState).Inputs().SrcVregs({});
985 INST(3U, Opcode::LoadAndInitClass).ref().Inputs(2U);
986 INST(4U, Opcode::SaveState).Inputs().SrcVregs({});
987 INST(5U, Opcode::InitObject).ref().Inputs({{REFERENCE, 3U}, {INT32, 1U}, {NO_TYPE, 4U}});
988 INST(6U, Opcode::SaveState).Inputs().SrcVregs({});
989 CONSTANT(7U, 11U).s32();
990 CONSTANT(8U, 11U).s32();
991 INST(9U, Opcode::CallVirtual).s32().Inputs({{REFERENCE, 5U}, {INT32, 7U}, {INT32, 8U}, {NO_TYPE, 6U}});
992
993 INST(10U, Opcode::Return).ref().Inputs(5U);
994 }
995 }
996 graph->RunPass<bytecodeopt::RegAccAlloc>();
997
998 EXPECT_EQ(INS(7U).GetDstReg(), compiler::ACC_REG_ID);
999 EXPECT_NE(INS(8U).GetDstReg(), compiler::ACC_REG_ID);
1000 }
1001
TEST_F(RegAccAllocTest,Ldai_Cast)1002 TEST_F(RegAccAllocTest, Ldai_Cast)
1003 {
1004 auto graph = CreateEmptyGraph();
1005 GRAPH(graph)
1006 {
1007 PARAMETER(0U, 0U).s64();
1008 BASIC_BLOCK(2U, -1L)
1009 {
1010 INST(1U, Opcode::Cast).s32().SrcType(compiler::DataType::UINT64).Inputs(0U);
1011 CONSTANT(2U, 159U).s32();
1012 INST(3U, Opcode::Add).s32().Inputs(1U, 2U);
1013 INST(4U, Opcode::Return).s32().Inputs(3U);
1014 }
1015 }
1016 graph->RunPass<bytecodeopt::RegAccAlloc>();
1017
1018 EXPECT_EQ(INS(1U).GetDstReg(), compiler::ACC_REG_ID);
1019 EXPECT_EQ(INS(3U).GetDstReg(), compiler::ACC_REG_ID);
1020 EXPECT_EQ(INS(3U).GetInput(1U).GetInst()->GetOpcode(), Opcode::Constant);
1021 }
1022
TEST_F(RegAccAllocTest,Ldai_Cast2)1023 TEST_F(RegAccAllocTest, Ldai_Cast2)
1024 {
1025 auto graph = CreateEmptyGraph();
1026 GRAPH(graph)
1027 {
1028 PARAMETER(0U, 0U).s64();
1029 BASIC_BLOCK(2U, -1L)
1030 {
1031 CONSTANT(2U, 159U).s32();
1032 INST(1U, Opcode::Cast).s32().SrcType(compiler::DataType::INT64).Inputs(0U);
1033 INST(3U, Opcode::Add).s32().Inputs(2U, 1U);
1034 INST(4U, Opcode::Return).s32().Inputs(3U);
1035 }
1036 }
1037 graph->RunPass<bytecodeopt::RegAccAlloc>();
1038
1039 EXPECT_NE(INS(2U).GetDstReg(), compiler::ACC_REG_ID);
1040 EXPECT_EQ(INS(1U).GetDstReg(), compiler::ACC_REG_ID);
1041 EXPECT_EQ(INS(3U).GetInput(0U).GetInst()->GetOpcode(), Opcode::Cast);
1042 EXPECT_EQ(INS(3U).GetInput(1U).GetInst()->GetOpcode(), Opcode::Constant);
1043 }
1044
TEST_F(RegAccAllocTest,Ldai_Exist)1045 TEST_F(RegAccAllocTest, Ldai_Exist)
1046 {
1047 pandasm::Parser p;
1048 auto source = std::string(R"(
1049 .record A {
1050 f32 a1 <static>
1051 }
1052 .function i32 main() {
1053 fmovi v1, 0x42280000
1054 lda v1
1055 ststatic A.a1
1056 lda v1
1057 f32toi32
1058 return
1059 }
1060 )");
1061
1062 auto res = p.Parse(source);
1063 auto &program = res.Value();
1064 pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps;
1065 std::string fileName = "Ldai_Exist";
1066 auto pfile = pandasm::AsmEmitter::Emit(fileName, program, nullptr, &maps);
1067 ASSERT_NE(pfile, false);
1068
1069 auto oldOptions = ark::bytecodeopt::g_options;
1070 ark::bytecodeopt::g_options = ark::bytecodeopt::Options("--opt-level=2");
1071 EXPECT_TRUE(OptimizeBytecode(&program, &maps, fileName, false, true));
1072 ark::bytecodeopt::g_options = oldOptions;
1073 bool fldaiExists = false;
1074 for (const auto &inst : program.functionStaticTable.find("main:()")->second.ins) {
1075 if (inst.opcode == ark::pandasm::Opcode::FLDAI) {
1076 fldaiExists = true;
1077 break;
1078 }
1079 }
1080 EXPECT_EQ(fldaiExists, true);
1081 }
1082
TEST_F(RegAccAllocTest,Lda_Extra1)1083 TEST_F(RegAccAllocTest, Lda_Extra1)
1084 {
1085 pandasm::Parser p;
1086 auto source = std::string(R"(
1087 .function u1 main() {
1088 fldai 0x42280000
1089 f32toi32
1090 sta v1
1091 movi v2, 0x2a
1092 lda v1
1093 jeq v2, jump_label_0
1094 ldai 0x1
1095 return
1096 jump_label_0:
1097 ldai 0x0
1098 return
1099 }
1100 )");
1101
1102 auto res = p.Parse(source);
1103 auto &program = res.Value();
1104 pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps;
1105 std::string fileName = "Lda_Extra1";
1106 auto pfile = pandasm::AsmEmitter::Emit(fileName, program, nullptr, &maps);
1107 ASSERT_NE(pfile, false);
1108
1109 auto oldOptions = ark::bytecodeopt::g_options;
1110 ark::bytecodeopt::g_options = ark::bytecodeopt::Options("--opt-level=2");
1111 EXPECT_TRUE(OptimizeBytecode(&program, &maps, fileName, false, true));
1112 ark::bytecodeopt::g_options = oldOptions;
1113 bool ldaExists = false;
1114 for (const auto &inst : program.functionStaticTable.find("main:()")->second.ins) {
1115 if (inst.opcode == ark::pandasm::Opcode::LDA) {
1116 ldaExists = true;
1117 break;
1118 }
1119 }
1120 EXPECT_EQ(ldaExists, false);
1121 }
1122
TEST_F(RegAccAllocTest,Lda_Extra2)1123 TEST_F(RegAccAllocTest, Lda_Extra2)
1124 {
1125 pandasm::Parser p;
1126 auto source = std::string(R"(
1127 .function i32[] main(i32 a0) {
1128 movi v0, 0x4
1129 newarr v4, v0, i32[]
1130 mov v1, a0
1131 add v0, a0
1132 sta v0
1133 movi v2, 0x1
1134 lda v0
1135 starr v4, v2
1136 lda v1
1137 add2 a0
1138 sta v0
1139 movi v2, 0x2
1140 lda v0
1141 starr v4, v2
1142 lda v1
1143 add2 a0
1144 sta v0
1145 movi v1, 0x3
1146 lda v0
1147 starr v4, v1
1148 lda.obj v4
1149 return.obj
1150 }
1151 )");
1152
1153 auto res = p.Parse(source);
1154 auto &program = res.Value();
1155 pandasm::AsmEmitter::PandaFileToPandaAsmMaps maps;
1156 std::string fileName = "Lda_Extra2";
1157 auto pfile = pandasm::AsmEmitter::Emit(fileName, program, nullptr, &maps);
1158 ASSERT_NE(pfile, false);
1159
1160 auto oldOptions = ark::bytecodeopt::g_options;
1161 ark::bytecodeopt::g_options = ark::bytecodeopt::Options("--opt-level=2");
1162 EXPECT_TRUE(OptimizeBytecode(&program, &maps, fileName, false, true));
1163 ark::bytecodeopt::g_options = oldOptions;
1164 int ldaAmount = 0;
1165 for (const auto &inst : program.functionStaticTable.find("main:(i32)")->second.ins) {
1166 if (inst.opcode == ark::pandasm::Opcode::LDA) {
1167 ldaAmount += 1;
1168 }
1169 }
1170 EXPECT_EQ(ldaAmount, 1U);
1171 }
1172
TEST_F(RegAccAllocTest,Const_Phi)1173 TEST_F(RegAccAllocTest, Const_Phi)
1174 {
1175 auto graph = GetAllocator()->New<compiler::Graph>(GetAllocator(), GetLocalAllocator(), Arch::X86_64, false, true);
1176 GRAPH(graph)
1177 {
1178 PARAMETER(0U, 0U).s32();
1179 CONSTANT(8U, 0U).f64();
1180 BASIC_BLOCK(2U, 3U)
1181 {
1182 CONSTANT(1U, 6U).f64();
1183 }
1184 BASIC_BLOCK(3U, 4U, 5U)
1185 {
1186 INST(3U, Opcode::Phi).Inputs(0U, 7U).s32();
1187 INST(6U, Opcode::Phi).Inputs(8U, 1U).f64();
1188 INST(4U, Opcode::Cmp).s32().SrcType(compiler::DataType::FLOAT64).Fcmpg(true).Inputs(6U, 8U);
1189 INST(5U, Opcode::IfImm).SrcType(compiler::DataType::INT32).CC(compiler::CC_GE).Imm(0U).Inputs(4U);
1190 }
1191 BASIC_BLOCK(4U, 3U)
1192 {
1193 INST(7U, Opcode::AddI).Imm(1U).Inputs(3U).s32();
1194 }
1195 BASIC_BLOCK(5U, -1L)
1196 {
1197 INST(9U, Opcode::Return).Inputs(3U).s32();
1198 }
1199 }
1200 graph->RunPass<bytecodeopt::RegAccAlloc>();
1201
1202 EXPECT_NE(INS(6U).GetDstReg(), compiler::ACC_REG_ID);
1203 EXPECT_EQ(INS(4U).GetDstReg(), compiler::ACC_REG_ID);
1204 }
1205
1206 // NOLINTEND(readability-magic-numbers)
1207
1208 } // namespace ark::bytecodeopt::test
1209