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