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