• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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