1 /*
2 * Copyright (c) 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 <sys/types.h>
17 #include "codegen_test.h"
18 #include "optimizer/ir/inst.h"
19
20 namespace ark::compiler {
21
22 // NOLINTBEGIN(readability-magic-numbers,modernize-avoid-c-arrays,cppcoreguidelines-pro-bounds-pointer-arithmetic)
TEST_F(CodegenTest,ZeroCheck)23 TEST_F(CodegenTest, ZeroCheck)
24 {
25 GRAPH(GetGraph())
26 {
27 PARAMETER(0U, 0U).s64();
28 PARAMETER(1U, 1U).s64();
29 BASIC_BLOCK(2U, 3U)
30 {
31 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
32 INST(3U, Opcode::ZeroCheck).s64().Inputs(0U, 2U);
33 INST(4U, Opcode::Div).s64().Inputs(1U, 3U);
34 INST(5U, Opcode::Mod).s64().Inputs(1U, 3U);
35 }
36 BASIC_BLOCK(3U, -1L)
37 {
38 INST(6U, Opcode::Add).s64().Inputs(0U, 1U); // Some return value
39 INST(7U, Opcode::Return).s64().Inputs(6U);
40 }
41 }
42 RegAlloc(GetGraph());
43
44 SetNumVirtRegs(GetGraph()->GetVRegsCount());
45
46 EXPECT_TRUE(RunCodegen(GetGraph()));
47 auto codeEntry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
48 auto codeExit = codeEntry + GetGraph()->GetCode().Size();
49 ASSERT(codeEntry != nullptr && codeExit != nullptr);
50 GetExecModule().SetInstructions(codeEntry, codeExit);
51
52 // param1 < 0 [OK]
53 auto param1 = CutValue<uint64_t>(std::numeric_limits<int64_t>::min(), DataType::INT64);
54 auto param2 = CutValue<uint64_t>(std::numeric_limits<int64_t>::max(), DataType::INT64);
55 GetExecModule().SetParameter(0U, param1);
56 GetExecModule().SetParameter(1U, param2);
57 GetExecModule().Execute();
58 EXPECT_EQ(GetExecModule().GetRetValue(), param1 + param2);
59
60 // param1 > 0 [OK]
61 param1 = CutValue<uint64_t>(std::numeric_limits<int64_t>::max(), DataType::INT64);
62 param2 = CutValue<uint64_t>(0U, DataType::INT64);
63 GetExecModule().SetParameter(0U, param1);
64 GetExecModule().SetParameter(1U, param2);
65 GetExecModule().Execute();
66 EXPECT_EQ(GetExecModule().GetRetValue(), param1 + param2);
67
68 // param1 == 0 [THROW]
69 }
70
SRC_GRAPH(BoundsCheckI,Graph * graph,unsigned index)71 SRC_GRAPH(BoundsCheckI, Graph *graph, unsigned index)
72 {
73 GRAPH(graph)
74 {
75 PARAMETER(0U, 0U).ref(); // array
76 BASIC_BLOCK(2U, -1L)
77 {
78 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
79 INST(2U, Opcode::NullCheck).ref().Inputs(0U, 1U);
80 INST(3U, Opcode::LenArray).s32().Inputs(2U);
81 INST(4U, Opcode::BoundsCheckI).s32().Inputs(3U, 1U).Imm(index);
82 INST(5U, Opcode::LoadArrayI).u64().Inputs(2U).Imm(index);
83 INST(6U, Opcode::Return).u64().Inputs(5U);
84 }
85 }
86 }
87
TEST_F(CodegenTest,BoundsCheckI)88 TEST_F(CodegenTest, BoundsCheckI)
89 {
90 uint64_t arrayData[4098U];
91 for (unsigned i = 0; i < 4098U; i++) {
92 arrayData[i] = i;
93 }
94
95 for (unsigned index = 4095U; index <= 4097U; index++) {
96 auto graph = CreateEmptyGraph();
97 src_graph::BoundsCheckI::CREATE(graph, index);
98
99 SetNumVirtRegs(0U);
100 SetNumArgs(1U);
101
102 RegAlloc(graph);
103
104 // Run codegen
105 EXPECT_TRUE(RunCodegen(graph));
106 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
107 auto codeExit = codeEntry + graph->GetCode().Size();
108 ASSERT(codeEntry != nullptr && codeExit != nullptr);
109 GetExecModule().SetInstructions(codeEntry, codeExit);
110
111 // Enable dumping
112 GetExecModule().SetDump(false);
113
114 auto param = GetExecModule().CreateArray(arrayData, index + 1U, GetObjectAllocator());
115 GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param));
116
117 GetExecModule().Execute();
118 GetExecModule().SetDump(false);
119 // End dump
120
121 auto retData = GetExecModule().GetRetValue();
122 EXPECT_EQ(retData, index);
123
124 GetExecModule().FreeArray(param);
125 }
126 }
127
TEST_F(CodegenTest,MultiplyAddInteger)128 TEST_F(CodegenTest, MultiplyAddInteger)
129 {
130 if (GetGraph()->GetArch() != Arch::AARCH64) {
131 GTEST_SKIP() << "multiply-add instruction is only supported on Aarch64";
132 }
133
134 GRAPH(GetGraph())
135 {
136 CONSTANT(0U, 10U);
137 CONSTANT(1U, 42U);
138 CONSTANT(2U, 13U);
139
140 BASIC_BLOCK(2U, -1L)
141 {
142 INST(3U, Opcode::MAdd).s64().Inputs(0U, 1U, 2U);
143 INST(4U, Opcode::Return).s64().Inputs(3U);
144 }
145 }
146
147 CheckReturnValue(GetGraph(), 433U);
148 }
149
TEST_F(CodegenTest,MultiplyAddFloat)150 TEST_F(CodegenTest, MultiplyAddFloat)
151 {
152 if (GetGraph()->GetArch() != Arch::AARCH64) {
153 GTEST_SKIP() << "multiply-add instruction is only supported on Aarch64";
154 }
155
156 GRAPH(GetGraph())
157 {
158 CONSTANT(0U, 10.0_D);
159 CONSTANT(1U, 42.0_D);
160 CONSTANT(2U, 13.0_D);
161
162 BASIC_BLOCK(2U, -1L)
163 {
164 INST(3U, Opcode::MAdd).f64().Inputs(0U, 1U, 2U);
165 INST(4U, Opcode::Return).f64().Inputs(3U);
166 }
167 }
168
169 CheckReturnValue(GetGraph(), 433.0_D);
170 }
171
TEST_F(CodegenTest,MultiplySubtractInteger)172 TEST_F(CodegenTest, MultiplySubtractInteger)
173 {
174 if (GetGraph()->GetArch() != Arch::AARCH64) {
175 GTEST_SKIP() << "multiply-subtract instruction is only supported on Aarch64";
176 }
177
178 GRAPH(GetGraph())
179 {
180 CONSTANT(0U, 10U);
181 CONSTANT(1U, 42U);
182 CONSTANT(2U, 13U);
183
184 BASIC_BLOCK(2U, -1L)
185 {
186 INST(3U, Opcode::MSub).s64().Inputs(0U, 1U, 2U);
187 INST(4U, Opcode::Return).s64().Inputs(3U);
188 }
189 }
190
191 CheckReturnValue(GetGraph(), -407L);
192 }
193
TEST_F(CodegenTest,MultiplySubtractFloat)194 TEST_F(CodegenTest, MultiplySubtractFloat)
195 {
196 if (GetGraph()->GetArch() != Arch::AARCH64) {
197 GTEST_SKIP() << "multiply-subtract instruction is only supported on Aarch64";
198 }
199
200 GRAPH(GetGraph())
201 {
202 CONSTANT(0U, 10.0_D);
203 CONSTANT(1U, 42.0_D);
204 CONSTANT(2U, 13.0_D);
205
206 BASIC_BLOCK(2U, -1L)
207 {
208 INST(3U, Opcode::MSub).f64().Inputs(0U, 1U, 2U);
209 INST(4U, Opcode::Return).f64().Inputs(3U);
210 }
211 }
212
213 CheckReturnValue(GetGraph(), -407.0_D);
214 }
215
TEST_F(CodegenTest,MultiplyNegateInteger)216 TEST_F(CodegenTest, MultiplyNegateInteger)
217 {
218 if (GetGraph()->GetArch() != Arch::AARCH64) {
219 GTEST_SKIP() << "multiply-negate instruction is only supported on Aarch64";
220 }
221
222 GRAPH(GetGraph())
223 {
224 CONSTANT(0U, 5U);
225 CONSTANT(1U, 5U);
226
227 BASIC_BLOCK(2U, -1L)
228 {
229 INST(2U, Opcode::MNeg).s64().Inputs(0U, 1U);
230 INST(3U, Opcode::Return).s64().Inputs(2U);
231 }
232 }
233
234 CheckReturnValue(GetGraph(), -25L);
235 }
236
TEST_F(CodegenTest,MultiplyNegateFloat)237 TEST_F(CodegenTest, MultiplyNegateFloat)
238 {
239 if (GetGraph()->GetArch() != Arch::AARCH64) {
240 GTEST_SKIP() << "multiply-negate instruction is only supported on Aarch64";
241 }
242
243 GRAPH(GetGraph())
244 {
245 CONSTANT(0U, 5.0_D);
246 CONSTANT(1U, 5.0_D);
247
248 BASIC_BLOCK(2U, -1L)
249 {
250 INST(2U, Opcode::MNeg).f64().Inputs(0U, 1U);
251 INST(3U, Opcode::Return).f64().Inputs(2U);
252 }
253 }
254
255 CheckReturnValue(GetGraph(), -25.0_D);
256 }
257
TEST_F(CodegenTest,OrNot)258 TEST_F(CodegenTest, OrNot)
259 {
260 if (GetGraph()->GetArch() != Arch::AARCH64) {
261 GTEST_SKIP() << "multiply-negate instruction is only supported on Aarch64";
262 }
263
264 GRAPH(GetGraph())
265 {
266 CONSTANT(0U, 0x0000beefU);
267 CONSTANT(1U, 0x2152ffffU);
268
269 BASIC_BLOCK(2U, -1L)
270 {
271 INST(2U, Opcode::OrNot).u32().Inputs(0U, 1U);
272 INST(3U, Opcode::Return).u32().Inputs(2U);
273 }
274 }
275
276 CheckReturnValue(GetGraph(), 0xdeadbeefU);
277 }
278
TEST_F(CodegenTest,AndNot)279 TEST_F(CodegenTest, AndNot)
280 {
281 if (GetGraph()->GetArch() != Arch::AARCH64) {
282 GTEST_SKIP() << "multiply-negate instruction is only supported on Aarch64";
283 }
284
285 GRAPH(GetGraph())
286 {
287 CONSTANT(0U, 0xf0000003U);
288 CONSTANT(1U, 0x1U);
289
290 BASIC_BLOCK(2U, -1L)
291 {
292 INST(2U, Opcode::AndNot).u32().Inputs(0U, 1U);
293 INST(3U, Opcode::Return).u32().Inputs(2U);
294 }
295 }
296
297 CheckReturnValue(GetGraph(), 0xf0000002U);
298 }
299
TEST_F(CodegenTest,XorNot)300 TEST_F(CodegenTest, XorNot)
301 {
302 if (GetGraph()->GetArch() != Arch::AARCH64) {
303 GTEST_SKIP() << "multiply-negate instruction is only supported on Aarch64";
304 }
305
306 GRAPH(GetGraph())
307 {
308 CONSTANT(0U, 0xf0f1ffd0U);
309 CONSTANT(1U, 0xcf0fc0f1U);
310
311 BASIC_BLOCK(2U, -1L)
312 {
313 INST(2U, Opcode::XorNot).u32().Inputs(0U, 1U);
314 INST(3U, Opcode::Return).u32().Inputs(2U);
315 }
316 }
317
318 CheckReturnValue(GetGraph(), 0xc001c0deU);
319 }
320
321 template <Opcode OPCODE, uint32_t L, uint32_t R, ShiftType SHIFT_TYPE, uint32_t SHIFT, uint32_t ERV>
TestBinaryOperationWithShiftedOperand()322 void CodegenTest::TestBinaryOperationWithShiftedOperand()
323 {
324 GRAPH(GetGraph())
325 {
326 CONSTANT(0U, L);
327 CONSTANT(1U, R);
328
329 BASIC_BLOCK(2U, -1L)
330 {
331 INST(2U, OPCODE).Shift(SHIFT_TYPE, SHIFT).u32().Inputs(0U, 1U);
332 INST(3U, Opcode::Return).u32().Inputs(2U);
333 }
334 }
335
336 CheckReturnValue(GetGraph(), ERV);
337 }
338
TEST_F(CodegenTest,AddSR)339 TEST_F(CodegenTest, AddSR)
340 {
341 if (GetGraph()->GetArch() != Arch::AARCH64) {
342 GTEST_SKIP() << "AddSR instruction is only supported on Aarch64";
343 }
344
345 TestBinaryOperationWithShiftedOperand<Opcode::AddSR, 10U, 2U, ShiftType::LSL, 1U, 14U>();
346 }
347
TEST_F(CodegenTest,SubSR)348 TEST_F(CodegenTest, SubSR)
349 {
350 if (GetGraph()->GetArch() != Arch::AARCH64) {
351 GTEST_SKIP() << "SubSR instruction is only supported on Aarch64";
352 }
353
354 TestBinaryOperationWithShiftedOperand<Opcode::SubSR, 10U, 4U, ShiftType::LSR, 2U, 9U>();
355 }
356
TEST_F(CodegenTest,AndSR)357 TEST_F(CodegenTest, AndSR)
358 {
359 if (GetGraph()->GetArch() != Arch::AARCH64) {
360 GTEST_SKIP() << "AndSR instruction is only supported on Aarch64";
361 }
362
363 TestBinaryOperationWithShiftedOperand<Opcode::AndSR, 1U, 1U, ShiftType::LSL, 1U, 0U>();
364 }
365
TEST_F(CodegenTest,OrSR)366 TEST_F(CodegenTest, OrSR)
367 {
368 if (GetGraph()->GetArch() != Arch::AARCH64) {
369 GTEST_SKIP() << "OrSR instruction is only supported on Aarch64";
370 }
371
372 TestBinaryOperationWithShiftedOperand<Opcode::OrSR, 1U, 1U, ShiftType::LSL, 1U, 3U>();
373 }
374
TEST_F(CodegenTest,XorSR)375 TEST_F(CodegenTest, XorSR)
376 {
377 if (GetGraph()->GetArch() != Arch::AARCH64) {
378 GTEST_SKIP() << "XorSR instruction is only supported on Aarch64";
379 }
380
381 TestBinaryOperationWithShiftedOperand<Opcode::XorSR, 3U, 1U, ShiftType::LSL, 1U, 1U>();
382 }
383
TEST_F(CodegenTest,AndNotSR)384 TEST_F(CodegenTest, AndNotSR)
385 {
386 if (GetGraph()->GetArch() != Arch::AARCH64) {
387 GTEST_SKIP() << "AndNotSR instruction is only supported on Aarch64";
388 }
389
390 TestBinaryOperationWithShiftedOperand<Opcode::AndNotSR, 6U, 12U, ShiftType::LSR, 2U, 4U>();
391 }
392
TEST_F(CodegenTest,OrNotSR)393 TEST_F(CodegenTest, OrNotSR)
394 {
395 if (GetGraph()->GetArch() != Arch::AARCH64) {
396 GTEST_SKIP() << "OrNotSR instruction is only supported on Aarch64";
397 }
398
399 TestBinaryOperationWithShiftedOperand<Opcode::OrNotSR, 1U, 12U, ShiftType::LSR, 2U, 0xfffffffdU>();
400 }
401
TEST_F(CodegenTest,XorNotSR)402 TEST_F(CodegenTest, XorNotSR)
403 {
404 if (GetGraph()->GetArch() != Arch::AARCH64) {
405 GTEST_SKIP() << "XorNotSR instruction is only supported on Aarch64";
406 }
407
408 TestBinaryOperationWithShiftedOperand<Opcode::XorNotSR, static_cast<uint32_t>(-1L), 12U, ShiftType::LSR, 2U, 3U>();
409 }
410
TEST_F(CodegenTest,NegSR)411 TEST_F(CodegenTest, NegSR)
412 {
413 if (GetGraph()->GetArch() != Arch::AARCH64) {
414 GTEST_SKIP() << "NegSR instruction is only supported on Aarch64";
415 }
416
417 GRAPH(GetGraph())
418 {
419 CONSTANT(0U, 0x80000000U);
420
421 BASIC_BLOCK(2U, -1L)
422 {
423 INST(1U, Opcode::NegSR).Shift(ShiftType::ASR, 1U).u32().Inputs(0U);
424 INST(2U, Opcode::Return).u32().Inputs(1U);
425 }
426 }
427
428 CheckReturnValue(GetGraph(), 0x40000000U);
429 }
430
TEST_F(CodegenTest,LoadArrayPairLivenessInfo)431 TEST_F(CodegenTest, LoadArrayPairLivenessInfo)
432 {
433 auto graph = GetGraph();
434
435 GRAPH(graph)
436 {
437 PARAMETER(0U, 0U).ref();
438 PARAMETER(1U, 1U).s32();
439
440 BASIC_BLOCK(2U, -1L)
441 {
442 INST(2U, Opcode::LoadArrayPair).s32().Inputs(0U, 1U);
443 INST(4U, Opcode::LoadPairPart).s32().Inputs(2U).Imm(0U);
444 INST(5U, Opcode::LoadPairPart).s32().Inputs(2U).Imm(1U);
445 INST(12U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
446 INST(10U, Opcode::LoadClass)
447 .ref()
448 .Inputs(12U)
449 .TypeId(42U)
450 .Class(reinterpret_cast<RuntimeInterface::ClassPtr>(1U));
451 INST(3U, Opcode::IsInstance).b().Inputs(0U, 10U, 12U).TypeId(42U);
452 INST(6U, Opcode::Cast).s32().SrcType(DataType::BOOL).Inputs(3U);
453 INST(7U, Opcode::Add).s32().Inputs(4U, 5U);
454 INST(8U, Opcode::Add).s32().Inputs(7U, 6U);
455 INST(9U, Opcode::Return).s32().Inputs(8U);
456 }
457 }
458
459 SetNumVirtRegs(0U);
460 SetNumArgs(2U);
461 RegAlloc(graph);
462 EXPECT_TRUE(RunCodegen(graph));
463
464 RegMask ldpRegs {};
465
466 auto cg = Codegen(graph);
467 for (auto &bb : graph->GetBlocksLinearOrder()) {
468 for (auto inst : bb->AllInsts()) {
469 if (inst->GetOpcode() == Opcode::LoadArrayPair) {
470 ldpRegs.set(inst->GetDstReg(0U));
471 ldpRegs.set(inst->GetDstReg(1U));
472 } else if (inst->GetOpcode() == Opcode::IsInstance) {
473 auto liveRegs = cg.GetLiveRegisters(inst).first;
474 // Both dst registers should be alive during IsInstance call
475 ASSERT_EQ(ldpRegs & liveRegs, ldpRegs);
476 }
477 }
478 }
479 }
480
TEST_F(CodegenTest,CompareAnyTypeInst)481 TEST_F(CodegenTest, CompareAnyTypeInst)
482 {
483 auto graph = GetGraph();
484 graph->SetDynamicMethod();
485 graph->SetDynamicStub();
486 GRAPH(graph)
487 {
488 PARAMETER(0U, 0U);
489 INS(0U).SetType(DataType::Type::ANY);
490
491 BASIC_BLOCK(2U, -1L)
492 {
493 INST(2U, Opcode::CompareAnyType).b().AnyType(AnyBaseType::UNDEFINED_TYPE).Inputs(0U);
494 INST(3U, Opcode::Return).b().Inputs(2U);
495 }
496 }
497
498 SetNumVirtRegs(0U);
499 ASSERT_TRUE(RegAlloc(graph));
500 ASSERT_TRUE(RunCodegen(graph));
501
502 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
503 auto codeExit = codeEntry + graph->GetCode().Size();
504
505 ASSERT(codeEntry != nullptr && codeExit != nullptr);
506
507 GetExecModule().SetInstructions(codeEntry, codeExit);
508 GetExecModule().SetDump(false);
509
510 GetExecModule().Execute();
511 auto rv = GetExecModule().GetRetValue<bool>();
512 EXPECT_EQ(rv, true);
513 }
514
TEST_F(CodegenTest,CastAnyTypeValueInst)515 TEST_F(CodegenTest, CastAnyTypeValueInst)
516 {
517 auto graph = GetGraph();
518 graph->SetDynamicMethod();
519 graph->SetDynamicStub();
520 GRAPH(graph)
521 {
522 PARAMETER(0U, 0U);
523 INS(0U).SetType(DataType::Type::ANY);
524
525 BASIC_BLOCK(2U, -1L)
526 {
527 INST(2U, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::UNDEFINED_TYPE).Inputs(0U);
528 INST(3U, Opcode::Return).b().Inputs(2U);
529 }
530 }
531
532 SetNumVirtRegs(0U);
533 ASSERT_TRUE(RegAlloc(graph));
534 ASSERT_TRUE(RunCodegen(graph));
535
536 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
537 auto codeExit = codeEntry + graph->GetCode().Size();
538
539 ASSERT(codeEntry != nullptr && codeExit != nullptr);
540
541 GetExecModule().SetInstructions(codeEntry, codeExit);
542 GetExecModule().SetDump(false);
543
544 GetExecModule().Execute();
545 auto rv = GetExecModule().GetRetValue<uint32_t>();
546 EXPECT_EQ(rv, 0U);
547 }
548
TEST_F(CodegenTest,NegativeCheck)549 TEST_F(CodegenTest, NegativeCheck)
550 {
551 GRAPH(GetGraph())
552 {
553 PARAMETER(0U, 0U).s64();
554 PARAMETER(1U, 1U).s64();
555 BASIC_BLOCK(2U, 3U)
556 {
557 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
558 INST(3U, Opcode::NegativeCheck).s64().Inputs(0U, 2U);
559 }
560 BASIC_BLOCK(3U, -1L)
561 {
562 INST(6U, Opcode::Add).s64().Inputs(0U, 1U); // Some return value
563 INST(7U, Opcode::Return).s64().Inputs(6U);
564 }
565 }
566 RegAlloc(GetGraph());
567
568 SetNumVirtRegs(GetGraph()->GetVRegsCount());
569
570 EXPECT_TRUE(RunCodegen(GetGraph()));
571 auto codeEntry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
572 auto codeExit = codeEntry + GetGraph()->GetCode().Size();
573 ASSERT(codeEntry != nullptr && codeExit != nullptr);
574 GetExecModule().SetInstructions(codeEntry, codeExit);
575
576 // param1 > 0 [OK]
577 auto param1 = CutValue<uint64_t>(std::numeric_limits<int64_t>::max(), DataType::INT64);
578 auto param2 = CutValue<uint64_t>(std::numeric_limits<int64_t>::min(), DataType::INT64);
579 GetExecModule().SetParameter(0U, param1);
580 GetExecModule().SetParameter(1U, param2);
581 GetExecModule().Execute();
582 EXPECT_EQ(GetExecModule().GetRetValue(), param1 + param2);
583
584 // param1 == 0 [OK]
585 param1 = CutValue<uint64_t>(0U, DataType::INT64);
586 param2 = CutValue<uint64_t>(std::numeric_limits<int64_t>::max(), DataType::INT64);
587 GetExecModule().SetParameter(0U, param1);
588 GetExecModule().SetParameter(1U, param2);
589 GetExecModule().Execute();
590 EXPECT_EQ(GetExecModule().GetRetValue(), param1 + param2);
591
592 // param1 < 0 [THROW]
593 }
594
TEST_F(CodegenTest,NullCheckBoundsCheck)595 TEST_F(CodegenTest, NullCheckBoundsCheck)
596 {
597 constexpr unsigned ARRAY_LEN = 10;
598
599 GRAPH(GetGraph())
600 {
601 PARAMETER(0U, 0U).ref(); // array
602 PARAMETER(1U, 1U).u64(); // index
603 BASIC_BLOCK(2U, 3U)
604 {
605 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
606 INST(3U, Opcode::NullCheck).ref().Inputs(0U, 2U);
607 INST(4U, Opcode::LenArray).s32().Inputs(3U);
608 INST(5U, Opcode::BoundsCheck).s32().Inputs(4U, 1U, 2U);
609 INST(6U, Opcode::LoadArray).u64().Inputs(3U, 5U);
610 INST(7U, Opcode::Add).u64().Inputs(6U, 6U);
611 INST(8U, Opcode::StoreArray).u64().Inputs(3U, 5U, 7U);
612 }
613 BASIC_BLOCK(3U, -1L)
614 {
615 INST(10U, Opcode::Add).u64().Inputs(7U, 7U); // Some return value
616 INST(11U, Opcode::Return).u64().Inputs(10U);
617 }
618 }
619 SetNumVirtRegs(0U);
620 SetNumArgs(2U);
621 RegAlloc(GetGraph());
622
623 EXPECT_TRUE(RunCodegen(GetGraph()));
624 auto codeEntry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
625 auto codeExit = codeEntry + GetGraph()->GetCode().Size();
626 ASSERT(codeEntry != nullptr && codeExit != nullptr);
627 GetExecModule().SetInstructions(codeEntry, codeExit);
628
629 // NOTE (igorban) : fill Frame array == nullptr [THROW]
630
631 uint64_t array[ARRAY_LEN];
632 for (auto i = 0U; i < ARRAY_LEN; i++) {
633 array[i] = i + 0x20U;
634 }
635 auto param1 = GetExecModule().CreateArray(array, ARRAY_LEN, GetObjectAllocator());
636 GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
637
638 // 0 <= index < ARRAY_LEN [OK]
639 auto index = CutValue<uint64_t>(1U, DataType::UINT64);
640 GetExecModule().SetParameter(1U, index);
641 GetExecModule().Execute();
642 EXPECT_EQ(GetExecModule().GetRetValue(), array[index] * 4U);
643
644 /*
645 NOTE (igorban) : fill Frame
646 // index < 0 [THROW]
647 */
648 GetExecModule().FreeArray(param1);
649 }
650 // NOLINTEND(readability-magic-numbers,modernize-avoid-c-arrays,cppcoreguidelines-pro-bounds-pointer-arithmetic)
651
652 } // namespace ark::compiler
653