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