• 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 "codegen_test.h"
17 #include "optimizer/ir/datatype.h"
18 #include "optimizer/ir/graph.h"
19 #include "optimizer/ir/inst.h"
20 #include "optimizer/ir/ir_constructor.h"
21 #include "tests/unit_test.h"
22 
23 namespace ark::compiler {
24 
25 constexpr uint64_t SEED = 0x1234;
26 #ifndef PANDA_NIGHTLY_TEST_ON
27 constexpr uint64_t ITERATION = 40;
28 #else
29 constexpr uint64_t ITERATION = 20000;
30 #endif
31 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects,cert-msc51-cpp)
32 static auto g_randomGenerator = std::mt19937_64(SEED);
33 
34 // NOLINTBEGIN(readability-magic-numbers,modernize-avoid-c-arrays,cppcoreguidelines-pro-bounds-pointer-arithmetic)
TEST_F(CodegenTest,SimpleProgramm)35 TEST_F(CodegenTest, SimpleProgramm)
36 {
37     /*
38    .function main()<main>{
39        movi.64 v0, 100000000           ##      0 -> 3      ##  bb0
40        movi.64 v1, 4294967296          ##      1 -> 4      ##  bb0
41        ldai 0                          ##      2 -> 5      ##  bb0
42    loop:                               ##                  ##
43        jeq v0, loop_exit               ##      6, 7, 8     ##  bb1
44                                        ##                  ##
45        sta.64 v2                       ##      9           ##  bb2
46        and.64 v1                       ##      10          ##  bb2
47        sta.64 v1                       ##      11          ##  bb2
48        lda.64 v2                       ##      12          ##  bb2
49        inc                             ##      13          ##  bb2
50        jmp loop                        ##      14          ##  bb2
51    loop_exit:                          ##                  ##
52        lda.64 v1                       ##      14          ##  bb3
53        return.64                       ##      15          ##  bb3
54    }
55    */
56 
57     GRAPH(GetGraph())
58     {
59         CONSTANT(0U, 10UL);          // r1
60         CONSTANT(1U, 4294967296UL);  // r2
61         CONSTANT(2U, 0UL);           // r3 -> acc(3)
62         CONSTANT(3U, 0x1UL);         // r20 -> 0x1 (for inc constant)
63 
64         BASIC_BLOCK(2U, 4U, 3U)
65         {
66             INST(16U, Opcode::Phi).Inputs(2U, 13U).s64();  // PHI acc
67             INST(17U, Opcode::Phi).Inputs(1U, 10U).s64();  // PHI  v1
68             INST(20U, Opcode::Phi).Inputs(2U, 10U).s64();  // result to return
69 
70             // NOTE (igorban): support CMP instr
71             INST(18U, Opcode::Compare).b().CC(CC_NE).Inputs(0U, 16U);
72             INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(18U);
73         }
74 
75         BASIC_BLOCK(3U, 2U)
76         {
77             INST(10U, Opcode::And).Inputs(16U, 17U).s64();  // -> acc
78             INST(13U, Opcode::Add).Inputs(16U, 3U).s64();   // -> acc
79         }
80 
81         BASIC_BLOCK(4U, -1L)
82         {
83             INST(19U, Opcode::Return).Inputs(20U).s64();
84         }
85     }
86 
87     SetNumVirtRegs(0U);
88     SetNumArgs(1U);
89 
90     RegAlloc(GetGraph());
91 
92     // call codegen
93     EXPECT_TRUE(RunCodegen(GetGraph()));
94     auto entry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
95     auto exit = entry + GetGraph()->GetCode().Size();
96     ASSERT(entry != nullptr && exit != nullptr);
97     GetExecModule().SetInstructions(entry, exit);
98     GetExecModule().SetDump(false);
99 
100     GetExecModule().Execute();
101 
102     auto retData = GetExecModule().GetRetValue();
103     EXPECT_EQ(retData, 0U);
104 
105     // Clear data for next execution
106     while (auto current = GetGraph()->GetFirstConstInst()) {
107         GetGraph()->RemoveConstFromList(current);
108     }
109 }
110 
SRC_GRAPH(CheckStoreArray,Graph * graph,DataType::Type type)111 SRC_GRAPH(CheckStoreArray, Graph *graph, DataType::Type type)
112 {
113     auto entry = graph->CreateStartBlock();
114     auto exit = graph->CreateEndBlock();
115     auto block = graph->CreateEmptyBlock();
116     entry->AddSucc(block);
117     block->AddSucc(exit);
118 
119     auto array = graph->AddNewParameter(0U, DataType::REFERENCE);
120     auto index = graph->AddNewParameter(1U, DataType::INT32);
121     auto storeValue = graph->AddNewParameter(2U, type);
122 
123     graph->ResetParameterInfo();
124     array->SetLocationData(graph->GetDataForNativeParam(DataType::REFERENCE));
125     index->SetLocationData(graph->GetDataForNativeParam(DataType::INT32));
126     storeValue->SetLocationData(graph->GetDataForNativeParam(type));
127 
128     auto stArr = graph->CreateInst(Opcode::StoreArray);
129     block->AppendInst(stArr);
130     stArr->SetType(type);
131     stArr->SetInput(0U, array);
132     stArr->SetInput(1U, index);
133     stArr->SetInput(2U, storeValue);
134     auto ret = graph->CreateInst(Opcode::ReturnVoid);
135     block->AppendInst(ret);
136 }
137 
138 template <typename T>
CheckStoreArray()139 void CodegenTest::CheckStoreArray()
140 {
141     constexpr DataType::Type TYPE = VixlExecModule::GetType<T>();
142 
143     // Create graph
144     auto graph = CreateEmptyGraph();
145     RuntimeInterfaceMock runtime;
146     graph->SetRuntime(&runtime);
147     src_graph::CheckStoreArray::CREATE(graph, TYPE);
148     SetNumVirtRegs(0U);
149     SetNumArgs(3U);
150 
151     RegAlloc(graph);
152 
153     // call codegen
154     EXPECT_TRUE(RunCodegen(graph));
155     auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
156     auto codeExit = codeEntry + graph->GetCode().Size();
157     ASSERT(codeEntry != nullptr && codeExit != nullptr);
158     GetExecModule().SetInstructions(codeEntry, codeExit);
159 
160     GetExecModule().SetDump(false);
161 
162     T arrayData[4U];
163     auto defaultValue = CutValue<T>(0U, TYPE);
164     for (auto &data : arrayData) {
165         data = defaultValue;
166     }
167     auto param1 = GetExecModule().CreateArray(arrayData, 4U, GetObjectAllocator());
168     auto param2 = CutValue<int32_t>(2U, DataType::INT32);
169     auto param3 = CutValue<T>(10U, TYPE);
170     GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
171     GetExecModule().SetParameter(1U, param2);
172     GetExecModule().SetParameter(2U, param3);
173 
174     GetExecModule().Execute();
175 
176     GetExecModule().CopyArray(param1, arrayData);
177 
178     for (size_t i = 0; i < 4U; i++) {
179         if (i == 2U) {
180             EXPECT_EQ(arrayData[i], param3);
181         } else {
182             EXPECT_EQ(arrayData[i], defaultValue);
183         }
184     }
185     GetExecModule().FreeArray(param1);
186 }
187 
SRC_GRAPH(CheckLoadArray,Graph * graph,DataType::Type type)188 SRC_GRAPH(CheckLoadArray, Graph *graph, DataType::Type type)
189 {
190     auto entry = graph->CreateStartBlock();
191     auto exit = graph->CreateEndBlock();
192     auto block = graph->CreateEmptyBlock();
193     entry->AddSucc(block);
194     block->AddSucc(exit);
195 
196     auto array = graph->AddNewParameter(0U, DataType::REFERENCE);
197     auto index = graph->AddNewParameter(1U, DataType::INT32);
198 
199     graph->ResetParameterInfo();
200     array->SetLocationData(graph->GetDataForNativeParam(DataType::REFERENCE));
201     index->SetLocationData(graph->GetDataForNativeParam(DataType::INT32));
202 
203     auto ldArr = graph->CreateInst(Opcode::LoadArray);
204     block->AppendInst(ldArr);
205     ldArr->SetType(type);
206     ldArr->SetInput(0U, array);
207     ldArr->SetInput(1U, index);
208     auto ret = graph->CreateInst(Opcode::Return);
209     ret->SetType(type);
210     ret->SetInput(0U, ldArr);
211     block->AppendInst(ret);
212 }
213 
214 template <typename T>
CheckLoadArray()215 void CodegenTest::CheckLoadArray()
216 {
217     constexpr DataType::Type TYPE = VixlExecModule::GetType<T>();
218     // Create graph
219     auto graph = CreateEmptyGraph();
220     RuntimeInterfaceMock runtime;
221     graph->SetRuntime(&runtime);
222 
223     src_graph::CheckLoadArray::CREATE(graph, TYPE);
224 
225     SetNumVirtRegs(0U);
226     SetNumArgs(2U);
227 
228     RegAlloc(graph);
229 
230     EXPECT_TRUE(RunCodegen(graph));
231     auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
232     auto codeExit = codeEntry + graph->GetCode().Size();
233     ASSERT(codeEntry != nullptr && codeExit != nullptr);
234     GetExecModule().SetInstructions(codeEntry, codeExit);
235 
236     GetExecModule().SetDump(false);
237 
238     T arrayData[4U];
239     for (size_t i = 0; i < 4U; i++) {
240         arrayData[i] = CutValue<T>((-i), TYPE);
241     }
242     auto param1 = GetExecModule().CreateArray(arrayData, 4U, GetObjectAllocator());
243     auto param2 = CutValue<int32_t>(2U, DataType::INT32);
244     GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
245     GetExecModule().SetParameter(1U, param2);
246 
247     GetExecModule().Execute();
248 
249     GetExecModule().CopyArray(param1, arrayData);
250 
251     GetExecModule().FreeArray(param1);
252 
253     auto retData = GetExecModule().GetRetValue<T>();
254     EXPECT_EQ(retData, CutValue<T>(-2L, TYPE));
255 }
256 
SRC_GRAPH(CheckStoreArrayPair,Graph * graph,DataType::Type type,bool imm)257 SRC_GRAPH(CheckStoreArrayPair, Graph *graph, DataType::Type type, bool imm)
258 {
259     auto entry = graph->CreateStartBlock();
260     auto exit = graph->CreateEndBlock();
261     auto block = graph->CreateEmptyBlock();
262     entry->AddSucc(block);
263     block->AddSucc(exit);
264 
265     auto array = graph->AddNewParameter(0U, DataType::REFERENCE);
266     [[maybe_unused]] auto index = graph->AddNewParameter(1U, DataType::INT32);
267     auto val0 = graph->AddNewParameter(2U, type);
268     auto val1 = graph->AddNewParameter(3U, type);
269 
270     graph->ResetParameterInfo();
271     array->SetLocationData(graph->GetDataForNativeParam(DataType::REFERENCE));
272     index->SetLocationData(graph->GetDataForNativeParam(DataType::INT32));
273     val0->SetLocationData(graph->GetDataForNativeParam(type));
274     val1->SetLocationData(graph->GetDataForNativeParam(type));
275 
276     Inst *stpArr = nullptr;
277     if (imm) {
278         stpArr = graph->CreateInstStoreArrayPairI(type, INVALID_PC, array, val0, val1, 2U);
279         block->AppendInst(stpArr);
280     } else {
281         stpArr = graph->CreateInstStoreArrayPair(type, INVALID_PC, std::array<Inst *, 4U> {array, index, val0, val1});
282         block->AppendInst(stpArr);
283     }
284 
285     auto ret = graph->CreateInst(Opcode::ReturnVoid);
286     block->AppendInst(ret);
287 
288     GraphChecker(graph).Check();
289 }
290 
291 template <typename T>
CheckStoreArrayPair(bool imm)292 void CodegenTest::CheckStoreArrayPair(bool imm)
293 {
294     constexpr DataType::Type TYPE = VixlExecModule::GetType<T>();
295 
296     // Create graph
297     auto graph = CreateEmptyGraph();
298     RuntimeInterfaceMock runtime;
299     graph->SetRuntime(&runtime);
300 #ifndef NDEBUG
301     // GraphChecker hack: LowLevel instructions may appear only after Lowering pass:
302     graph->SetLowLevelInstructionsEnabled();
303 #endif
304 
305     src_graph::CheckStoreArrayPair::CREATE(graph, TYPE, imm);
306 
307     SetNumVirtRegs(0U);
308     SetNumArgs(4U);
309 
310     RegAlloc(graph);
311 
312     EXPECT_TRUE(RunCodegen(graph));
313     auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
314     auto codeExit = codeEntry + graph->GetCode().Size();
315     ASSERT(codeEntry != nullptr && codeExit != nullptr);
316     GetExecModule().SetInstructions(codeEntry, codeExit);
317 
318     GetExecModule().SetDump(false);
319 
320     T arrayData[6U] = {0U, 0U, 0U, 0U, 0U, 0U};
321     auto param1 = GetExecModule().CreateArray(arrayData, 6U, GetObjectAllocator());
322     auto param2 = CutValue<int32_t>(2U, DataType::INT32);
323     auto param3 = CutValue<T>(3U, TYPE);
324     auto param4 = CutValue<T>(5U, TYPE);
325     GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
326     GetExecModule().SetParameter(1U, param2);
327     GetExecModule().SetParameter(2U, param3);
328     GetExecModule().SetParameter(3U, param4);
329 
330     GetExecModule().Execute();
331     GetExecModule().CopyArray(param1, arrayData);
332     GetExecModule().FreeArray(param1);
333 
334     T arrayExpected[6U] = {0U, 0U, 3U, 5U, 0U, 0U};
335 
336     for (size_t i = 0; i < 6U; ++i) {
337         EXPECT_EQ(arrayData[i], arrayExpected[i]);
338     }
339 }
340 
SRC_GRAPH(CheckLoadArrayPair,Graph * graph,DataType::Type type,bool imm)341 SRC_GRAPH(CheckLoadArrayPair, Graph *graph, DataType::Type type, bool imm)
342 {
343     auto entry = graph->CreateStartBlock();
344     auto exit = graph->CreateEndBlock();
345     auto block = graph->CreateEmptyBlock();
346     entry->AddSucc(block);
347     block->AddSucc(exit);
348 
349     auto array = graph->AddNewParameter(0U, DataType::REFERENCE);
350     [[maybe_unused]] auto index = graph->AddNewParameter(1U, DataType::INT32);
351 
352     graph->ResetParameterInfo();
353     array->SetLocationData(graph->GetDataForNativeParam(DataType::REFERENCE));
354     index->SetLocationData(graph->GetDataForNativeParam(DataType::INT32));
355 
356     Inst *ldpArr = nullptr;
357     if (imm) {
358         ldpArr = graph->CreateInstLoadArrayPairI(type, INVALID_PC, array, 2U);
359         block->AppendInst(ldpArr);
360     } else {
361         ldpArr = graph->CreateInstLoadArrayPair(type, INVALID_PC, array, index);
362         block->AppendInst(ldpArr);
363     }
364 
365     auto loadHigh = graph->CreateInstLoadPairPart(type, INVALID_PC, ldpArr, 0U);
366     block->AppendInst(loadHigh);
367 
368     auto loadLow = graph->CreateInstLoadPairPart(type, INVALID_PC, ldpArr, 1U);
369     block->AppendInst(loadLow);
370 
371     auto sum = graph->CreateInst(Opcode::Add);
372     block->AppendInst(sum);
373     sum->SetType(type);
374     sum->SetInput(0U, loadHigh);
375     sum->SetInput(1U, loadLow);
376 
377     auto ret = graph->CreateInst(Opcode::Return);
378     ret->SetType(type);
379     ret->SetInput(0U, sum);
380     block->AppendInst(ret);
381 
382     GraphChecker(graph).Check();
383 }
384 
385 template <typename T>
CheckLoadArrayPair(bool imm)386 void CodegenTest::CheckLoadArrayPair(bool imm)
387 {
388     constexpr DataType::Type TYPE = VixlExecModule::GetType<T>();
389 
390     // Create graph
391     auto graph = CreateEmptyGraph();
392     RuntimeInterfaceMock runtime;
393     graph->SetRuntime(&runtime);
394 #ifndef NDEBUG
395     // GraphChecker hack: LowLevel instructions may appear only after Lowering pass:
396     graph->SetLowLevelInstructionsEnabled();
397 #endif
398 
399     src_graph::CheckLoadArrayPair::CREATE(graph, TYPE, imm);
400 
401     SetNumVirtRegs(0U);
402     SetNumArgs(2U);
403 
404     RegAlloc(graph);
405 
406     EXPECT_TRUE(RunCodegen(graph));
407     auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
408     auto codeExit = codeEntry + graph->GetCode().Size();
409     ASSERT(codeEntry != nullptr && codeExit != nullptr);
410     GetExecModule().SetInstructions(codeEntry, codeExit);
411 
412     GetExecModule().SetDump(false);
413 
414     T arrayData[6U];
415     // [ 1, 2, 3, 4, 5, 6] -> 7
416     for (size_t i = 0; i < 6U; i++) {
417         arrayData[i] = CutValue<T>(i + 1U, TYPE);
418     }
419     auto param1 = GetExecModule().CreateArray(arrayData, 6U, GetObjectAllocator());
420     auto param2 = CutValue<int32_t>(2U, DataType::INT32);
421     GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
422     GetExecModule().SetParameter(1U, param2);
423 
424     GetExecModule().Execute();
425     GetExecModule().FreeArray(param1);
426 
427     auto retData = GetExecModule().GetRetValue<T>();
428     EXPECT_EQ(retData, CutValue<T>(7U, TYPE));
429 }
430 
SRC_GRAPH(CheckBounds,Graph * graph,DataType::Type type,uint64_t count)431 SRC_GRAPH(CheckBounds, Graph *graph, DataType::Type type, uint64_t count)
432 {
433     auto entry = graph->CreateStartBlock();
434     auto exit = graph->CreateEndBlock();
435     auto block = graph->CreateEmptyBlock();
436     entry->AddSucc(block);
437     block->AddSucc(exit);
438 
439     auto param = graph->AddNewParameter(0U, type);
440 
441     graph->ResetParameterInfo();
442     param->SetLocationData(graph->GetDataForNativeParam(type));
443 
444     Inst *lastInst = param;
445     // instruction_count + parameter + return
446     for (uint64_t i = count - 1U; i > 1U; --i) {
447         auto addInst = graph->CreateInstAddI(type, 0U, lastInst, 1U);
448         block->AppendInst(addInst);
449         lastInst = addInst;
450     }
451     auto ret = graph->CreateInst(Opcode::Return);
452     ret->SetType(type);
453     ret->SetInput(0U, lastInst);
454     block->AppendInst(ret);
455 
456 #ifndef NDEBUG
457     // GraphChecker hack: LowLevel instructions may appear only after Lowering pass:
458     graph->SetLowLevelInstructionsEnabled();
459 #endif
460     GraphChecker(graph).Check();
461 }
462 
463 template <typename T>
CheckBounds(uint64_t count)464 void CodegenTest::CheckBounds(uint64_t count)
465 {
466     constexpr DataType::Type TYPE = VixlExecModule::GetType<T>();
467     // Create graph
468     auto graph = CreateEmptyGraph();
469     RuntimeInterfaceMock runtime;
470     graph->SetRuntime(&runtime);
471 
472     src_graph::CheckBounds::CREATE(graph, TYPE, count);
473 
474     SetNumVirtRegs(0U);
475     SetNumArgs(2U);
476 
477     RegAlloc(graph);
478 
479     auto instsPerByte = GetGraph()->GetEncoder()->MaxArchInstPerEncoded();
480     auto maxBitsInInst = GetInstructionSizeBits(GetGraph()->GetArch());
481     if (count * instsPerByte * maxBitsInInst > g_options.GetCompilerMaxGenCodeSize()) {
482         EXPECT_FALSE(RunCodegen(graph));
483     } else {
484         ASSERT_TRUE(RunCodegen(graph));
485         auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
486         auto codeExit = codeEntry + graph->GetCode().Size();
487         ASSERT(codeEntry != nullptr && codeExit != nullptr);
488         GetExecModule().SetInstructions(codeEntry, codeExit);
489 
490         GetExecModule().SetDump(false);
491 
492         T zeroParam = 0;
493         GetExecModule().SetParameter(0U, zeroParam);
494         GetExecModule().Execute();
495 
496         auto retData = GetExecModule().GetRetValue<T>();
497         EXPECT_EQ(retData, CutValue<T>(count - 2L, TYPE));
498     }
499 }
500 
SRC_GRAPH(CheckCmp,Graph * graph,DataType::Type type,bool isFcmpg)501 SRC_GRAPH(CheckCmp, Graph *graph, DataType::Type type, bool isFcmpg)
502 {
503     auto entry = graph->CreateStartBlock();
504     auto exit = graph->CreateEndBlock();
505     auto block = graph->CreateEmptyBlock();
506     entry->AddSucc(block);
507     block->AddSucc(exit);
508 
509     auto param1 = graph->AddNewParameter(0U, type);
510     auto param2 = graph->AddNewParameter(1U, type);
511 
512     graph->ResetParameterInfo();
513     param1->SetLocationData(graph->GetDataForNativeParam(type));
514     param2->SetLocationData(graph->GetDataForNativeParam(type));
515 
516     auto fcmp = graph->CreateInst(Opcode::Cmp);
517     block->AppendInst(fcmp);
518     fcmp->SetType(DataType::INT32);
519     fcmp->SetInput(0U, param1);
520     fcmp->SetInput(1U, param2);
521     static_cast<CmpInst *>(fcmp)->SetOperandsType(type);
522     if (DataType::IsFloatType(type)) {
523         static_cast<CmpInst *>(fcmp)->SetFcmpg(isFcmpg);
524     }
525     auto ret = graph->CreateInst(Opcode::Return);
526     ret->SetType(DataType::INT32);
527     ret->SetInput(0U, fcmp);
528     block->AppendInst(ret);
529 }
530 
531 template <typename T>
CheckCmp(bool isFcmpg)532 void CodegenTest::CheckCmp(bool isFcmpg)
533 {
534     constexpr DataType::Type TYPE = VixlExecModule::GetType<T>();
535 
536     // Create graph
537     auto graph = CreateEmptyGraph();
538     RuntimeInterfaceMock runtime;
539     graph->SetRuntime(&runtime);
540 
541     src_graph::CheckCmp::CREATE(graph, TYPE, isFcmpg);
542 
543     SetNumVirtRegs(0U);
544     SetNumArgs(2U);
545 
546     RegAlloc(graph);
547 
548     EXPECT_TRUE(RunCodegen(graph));
549     auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
550     auto codeExit = codeEntry + graph->GetCode().Size();
551     ASSERT(codeEntry != nullptr && codeExit != nullptr);
552     GetExecModule().SetInstructions(codeEntry, codeExit);
553 
554     GetExecModule().SetDump(false);
555     T paramData[3U];
556     if (TYPE == DataType::FLOAT32) {
557         paramData[0U] = std::nanf("0");
558     } else if (TYPE == DataType::FLOAT64) {
559         paramData[0U] = std::nan("0");
560     } else {
561         paramData[0U] = std::numeric_limits<T>::max();
562         paramData[2U] = std::numeric_limits<T>::min();
563     }
564     paramData[1U] = CutValue<T>(2U, TYPE);
565     if (DataType::IsFloatType(TYPE)) {
566         paramData[2U] = -paramData[1U];
567     }
568 
569     for (size_t i = 0; i < 3U; i++) {
570         for (size_t j = 0; j < 3U; j++) {
571             auto paramData1 = paramData[i];
572             auto paramData2 = paramData[j];
573             GetExecModule().SetParameter(0U, paramData1);
574             GetExecModule().SetParameter(1U, paramData2);
575 
576             GetExecModule().Execute();
577 
578             auto retData = GetExecModule().GetRetValue<int32_t>();
579             if ((i == 0U || j == 0U) && DataType::IsFloatType(TYPE)) {
580                 EXPECT_EQ(retData, isFcmpg ? 1U : -1L);
581             } else if (i == j) {
582                 EXPECT_EQ(retData, 0U);
583             } else if (i > j) {
584                 EXPECT_EQ(retData, -1L);
585             } else {
586                 EXPECT_EQ(retData, 1U);
587             }
588         }
589     }
590 }
591 
TEST_F(CodegenTest,Cmp)592 TEST_F(CodegenTest, Cmp)
593 {
594     CheckCmp<float>(true);
595     CheckCmp<float>(false);
596     CheckCmp<double>(true);
597     CheckCmp<double>(false);
598     CheckCmp<uint8_t>();
599     CheckCmp<int8_t>();
600     CheckCmp<uint16_t>();
601     CheckCmp<int16_t>();
602     CheckCmp<uint32_t>();
603     CheckCmp<int32_t>();
604     CheckCmp<uint64_t>();
605     CheckCmp<int64_t>();
606 }
607 
SRC_GRAPH(StoreArray,Graph * graph)608 SRC_GRAPH(StoreArray, Graph *graph)
609 {
610     GRAPH(graph)
611     {
612         PARAMETER(0U, 0U).ref();  // array
613         PARAMETER(1U, 1U).u32();  // index
614         PARAMETER(2U, 2U).u32();  // store value
615         BASIC_BLOCK(2U, -1L)
616         {
617             INST(3U, Opcode::StoreArray).u32().Inputs(0U, 1U, 2U);
618             INST(4U, Opcode::ReturnVoid);
619         }
620     }
621 }
622 
TEST_F(CodegenTest,StoreArray)623 TEST_F(CodegenTest, StoreArray)
624 {
625     CheckStoreArray<bool>();
626     CheckStoreArray<int8_t>();
627     CheckStoreArray<uint8_t>();
628     CheckStoreArray<int16_t>();
629     CheckStoreArray<uint16_t>();
630     CheckStoreArray<int32_t>();
631     CheckStoreArray<uint32_t>();
632     CheckStoreArray<int64_t>();
633     CheckStoreArray<uint64_t>();
634     CheckStoreArray<float>();
635     CheckStoreArray<double>();
636 
637     src_graph::StoreArray::CREATE(GetGraph());
638 
639     auto graph = GetGraph();
640     SetNumVirtRegs(0U);
641     SetNumArgs(3U);
642 
643     RegAlloc(graph);
644 
645     EXPECT_TRUE(RunCodegen(graph));
646     auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
647     auto codeExit = codeEntry + graph->GetCode().Size();
648     ASSERT(codeEntry != nullptr && codeExit != nullptr);
649     GetExecModule().SetInstructions(codeEntry, codeExit);
650 
651     GetExecModule().SetDump(false);
652 
653     ObjectPointerType array[4U] = {0U, 0U, 0U, 0U};
654     auto param1 = GetExecModule().CreateArray(array, 4U, GetObjectAllocator());
655     auto param2 = CutValue<int32_t>(2U, DataType::INT32);
656     auto param3 = CutValue<ObjectPointerType>(10U, DataType::UINT64);
657     GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
658     GetExecModule().SetParameter(1U, param2);
659     GetExecModule().SetParameter(2U, param3);
660 
661     GetExecModule().Execute();
662 
663     GetExecModule().CopyArray(param1, array);
664 
665     for (size_t i = 0; i < 4U; i++) {
666         if (i == 2U) {
667             EXPECT_EQ(array[i], 10U) << "value of i: " << i;
668         } else {
669             EXPECT_EQ(array[i], 0U) << "value of i: " << i;
670         }
671     }
672     GetExecModule().FreeArray(param1);
673 }
674 
TEST_F(CodegenTest,StoreArrayPair)675 TEST_F(CodegenTest, StoreArrayPair)
676 {
677     CheckStoreArrayPair<uint32_t>(true);
678     CheckStoreArrayPair<int32_t>(false);
679     CheckStoreArrayPair<uint64_t>(true);
680     CheckStoreArrayPair<int64_t>(false);
681     CheckStoreArrayPair<float>(true);
682     CheckStoreArrayPair<float>(false);
683     CheckStoreArrayPair<double>(true);
684     CheckStoreArrayPair<double>(false);
685 }
686 
SRC_GRAPH(Compare,Graph * graph,ConditionCode cc,bool inverse)687 SRC_GRAPH(Compare, Graph *graph, ConditionCode cc, bool inverse)
688 {
689     GRAPH(graph)
690     {
691         PARAMETER(0U, 0U).u64();
692         PARAMETER(1U, 1U).u64();
693         CONSTANT(2U, 0U);
694         CONSTANT(3U, 1U);
695         BASIC_BLOCK(2U, 3U, 4U)
696         {
697             INST(4U, Opcode::Compare).b().CC(cc).Inputs(0U, 1U);
698             INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(inverse ? CC_EQ : CC_NE).Imm(0U).Inputs(4U);
699         }
700         BASIC_BLOCK(3U, -1L)
701         {
702             INST(6U, Opcode::Return).b().Inputs(3U);
703         }
704         BASIC_BLOCK(4U, -1L)
705         {
706             INST(7U, Opcode::Return).b().Inputs(2U);
707         }
708     }
709 }
710 
TEST_F(CodegenTest,Compare)711 TEST_F(CodegenTest, Compare)
712 {
713     for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
714         auto cc = static_cast<ConditionCode>(ccint);
715         for (auto inverse : {true, false}) {
716             auto graph = CreateGraphStartEndBlocks();
717             RuntimeInterfaceMock runtime;
718             graph->SetRuntime(&runtime);
719             src_graph::Compare::CREATE(graph, cc, inverse);
720 
721             SetNumVirtRegs(0U);
722             SetNumArgs(2U);
723             RegAlloc(graph);
724 
725             EXPECT_TRUE(RunCodegen(graph));
726             auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
727             auto codeExit = codeEntry + graph->GetCode().Size();
728             ASSERT(codeEntry != nullptr && codeExit != nullptr);
729             GetExecModule().SetInstructions(codeEntry, codeExit);
730 
731             GetExecModule().SetDump(false);
732 
733             auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
734             auto param2 = CutValue<uint64_t>(-1L, DataType::UINT64);
735 
736             GetExecModule().SetParameter(0U, param1);
737             GetExecModule().SetParameter(1U, param2);
738 
739             bool result = (cc == CC_NE || cc == CC_GT || cc == CC_GE || cc == CC_B || cc == CC_BE);
740             if (inverse) {
741                 result = !result;
742             }
743 
744             GetExecModule().Execute();
745 
746             auto retData = GetExecModule().GetRetValue();
747             EXPECT_EQ(retData, result);
748 
749             GetExecModule().SetParameter(0U, param2);
750             GetExecModule().SetParameter(1U, param1);
751             GetExecModule().Execute();
752 
753             result = (cc == CC_NE || cc == CC_LT || cc == CC_LE || cc == CC_A || cc == CC_AE);
754             if (inverse) {
755                 result = !result;
756             }
757 
758             retData = GetExecModule().GetRetValue();
759             EXPECT_EQ(retData, result);
760 
761             GetExecModule().SetParameter(0U, param1);
762             GetExecModule().SetParameter(1U, param1);
763 
764             result = (cc == CC_EQ || cc == CC_LE || cc == CC_GE || cc == CC_AE || cc == CC_BE);
765             if (inverse) {
766                 result = !result;
767             }
768 
769             GetExecModule().Execute();
770 
771             retData = GetExecModule().GetRetValue();
772             EXPECT_EQ(retData, result);
773         }
774     }
775 }
776 
SRC_GRAPH(GenIf,Graph * graph,ConditionCode cc)777 SRC_GRAPH(GenIf, Graph *graph, ConditionCode cc)
778 {
779     GRAPH(graph)
780     {
781         PARAMETER(0U, 0U).u64();
782         PARAMETER(1U, 1U).u64();
783         CONSTANT(2U, 0U);
784         CONSTANT(3U, 1U);
785         BASIC_BLOCK(2U, 3U, 4U)
786         {
787             INST(4U, Opcode::Compare).b().CC(cc).Inputs(0U, 1U);
788             INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
789         }
790         BASIC_BLOCK(3U, -1L)
791         {
792             INST(6U, Opcode::Return).b().Inputs(3U);
793         }
794         BASIC_BLOCK(4U, -1L)
795         {
796             INST(7U, Opcode::Return).b().Inputs(2U);
797         }
798     }
799 }
800 
TEST_F(CodegenTest,GenIf)801 TEST_F(CodegenTest, GenIf)
802 {
803     for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
804         auto cc = static_cast<ConditionCode>(ccint);
805         auto graph = CreateGraphStartEndBlocks();
806         RuntimeInterfaceMock runtime;
807         graph->SetRuntime(&runtime);
808 
809         src_graph::GenIf::CREATE(graph, cc);
810 
811         SetNumVirtRegs(0U);
812         SetNumArgs(2U);
813 #ifndef NDEBUG
814         graph->SetLowLevelInstructionsEnabled();
815 #endif
816         EXPECT_TRUE(graph->RunPass<Lowering>());
817         ASSERT_EQ(INS(0U).GetUsers().Front().GetInst()->GetOpcode(), Opcode::If);
818 
819         RegAlloc(graph);
820 
821         EXPECT_TRUE(RunCodegen(graph));
822         auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
823         auto codeExit = codeEntry + graph->GetCode().Size();
824         ASSERT(codeEntry != nullptr && codeExit != nullptr);
825         GetExecModule().SetInstructions(codeEntry, codeExit);
826 
827         GetExecModule().SetDump(false);
828 
829         auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
830         auto param2 = CutValue<uint64_t>(-1L, DataType::UINT64);
831 
832         GetExecModule().SetParameter(0U, param1);
833         GetExecModule().SetParameter(1U, param2);
834         bool result = Compare(cc, param1, param2);
835         GetExecModule().Execute();
836         auto retData = GetExecModule().GetRetValue();
837         EXPECT_EQ(retData, result);
838 
839         GetExecModule().SetParameter(0U, param2);
840         GetExecModule().SetParameter(1U, param1);
841         GetExecModule().Execute();
842         result = Compare(cc, param2, param1);
843         retData = GetExecModule().GetRetValue();
844         EXPECT_EQ(retData, result);
845 
846         GetExecModule().SetParameter(0U, param1);
847         GetExecModule().SetParameter(1U, param1);
848         result = Compare(cc, param1, param1);
849         GetExecModule().Execute();
850         retData = GetExecModule().GetRetValue();
851         EXPECT_EQ(retData, result);
852     }
853 }
854 
SRC_GRAPH(GenIfImm,Graph * graph,ConditionCode cc,int32_t value)855 SRC_GRAPH(GenIfImm, Graph *graph, ConditionCode cc, int32_t value)
856 {
857     GRAPH(graph)
858     {
859         PARAMETER(0U, 0U).s32();
860         CONSTANT(1U, 0U);
861         CONSTANT(2U, 1U);
862         BASIC_BLOCK(2U, 3U, 4U)
863         {
864             INST(3U, Opcode::IfImm).SrcType(DataType::INT32).CC(cc).Imm(value).Inputs(0U);
865         }
866         BASIC_BLOCK(3U, -1L)
867         {
868             INST(4U, Opcode::Return).b().Inputs(2U);
869         }
870         BASIC_BLOCK(4U, -1L)
871         {
872             INST(5U, Opcode::Return).b().Inputs(1U);
873         }
874     }
875 }
TEST_F(CodegenTest,GenIfImm)876 TEST_F(CodegenTest, GenIfImm)
877 {
878     int32_t values[3U] = {-1L, 0U, 1U};
879     for (auto value : values) {
880         for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
881             auto cc = static_cast<ConditionCode>(ccint);
882             auto graph = CreateGraphStartEndBlocks();
883             RuntimeInterfaceMock runtime;
884             graph->SetRuntime(&runtime);
885             if ((cc == CC_TST_EQ || cc == CC_TST_NE) && !graph->GetEncoder()->CanEncodeImmLogical(value, WORD_SIZE)) {
886                 continue;
887             }
888             src_graph::GenIfImm::CREATE(graph, cc, value);
889 
890             SetNumVirtRegs(0U);
891             SetNumArgs(2U);
892 
893             RegAlloc(graph);
894 
895             EXPECT_TRUE(RunCodegen(graph));
896             auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
897             auto codeExit = codeEntry + graph->GetCode().Size();
898             ASSERT(codeEntry != nullptr && codeExit != nullptr);
899             GetExecModule().SetInstructions(codeEntry, codeExit);
900 
901             GetExecModule().SetDump(false);
902 
903             bool result;
904             auto param1 = CutValue<uint64_t>(value, DataType::INT32);
905             auto param2 = CutValue<uint64_t>(value + 5U, DataType::INT32);
906             auto param3 = CutValue<uint64_t>(value - 5L, DataType::INT32);
907 
908             GetExecModule().SetParameter(0U, param1);
909             result = Compare(cc, param1, param1);
910             GetExecModule().Execute();
911             auto retData = GetExecModule().GetRetValue();
912             EXPECT_EQ(retData, result);
913 
914             GetExecModule().SetParameter(0U, param2);
915             GetExecModule().Execute();
916             result = Compare(cc, param2, param1);
917             retData = GetExecModule().GetRetValue();
918             EXPECT_EQ(retData, result);
919 
920             GetExecModule().SetParameter(0U, param3);
921             result = Compare(cc, param3, param1);
922             GetExecModule().Execute();
923             retData = GetExecModule().GetRetValue();
924             EXPECT_EQ(retData, result);
925         }
926     }
927 }
928 
SRC_GRAPH(If,Graph * graph,ConditionCode cc)929 SRC_GRAPH(If, Graph *graph, ConditionCode cc)
930 {
931     GRAPH(graph)
932     {
933         PARAMETER(0U, 0U).u64();
934         PARAMETER(1U, 1U).u64();
935         CONSTANT(2U, 0U);
936         CONSTANT(3U, 1U);
937         BASIC_BLOCK(2U, 3U, 4U)
938         {
939             INST(4U, Opcode::If).SrcType(DataType::UINT64).CC(cc).Inputs(0U, 1U);
940         }
941         BASIC_BLOCK(3U, -1L)
942         {
943             INST(5U, Opcode::Return).b().Inputs(3U);
944         }
945         BASIC_BLOCK(4U, -1L)
946         {
947             INST(6U, Opcode::Return).b().Inputs(2U);
948         }
949     }
950 }
TEST_F(CodegenTest,If)951 TEST_F(CodegenTest, If)
952 {
953     for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
954         auto cc = static_cast<ConditionCode>(ccint);
955         auto graph = CreateGraphStartEndBlocks();
956         RuntimeInterfaceMock runtime;
957         graph->SetRuntime(&runtime);
958 
959         src_graph::If::CREATE(graph, cc);
960 
961         SetNumVirtRegs(0U);
962         SetNumArgs(2U);
963 
964         RegAlloc(graph);
965 
966         EXPECT_TRUE(RunCodegen(graph));
967         auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
968         auto codeExit = codeEntry + graph->GetCode().Size();
969         ASSERT(codeEntry != nullptr && codeExit != nullptr);
970         GetExecModule().SetInstructions(codeEntry, codeExit);
971 
972         GetExecModule().SetDump(false);
973 
974         bool result;
975         auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
976         auto param2 = CutValue<uint64_t>(-1L, DataType::UINT64);
977 
978         GetExecModule().SetParameter(0U, param1);
979         GetExecModule().SetParameter(1U, param2);
980         result = Compare(cc, param1, param2);
981         GetExecModule().Execute();
982         auto retData = GetExecModule().GetRetValue();
983         EXPECT_EQ(retData, result);
984 
985         GetExecModule().SetParameter(0U, param2);
986         GetExecModule().SetParameter(1U, param1);
987         GetExecModule().Execute();
988         result = Compare(cc, param2, param1);
989         retData = GetExecModule().GetRetValue();
990         EXPECT_EQ(retData, result);
991 
992         GetExecModule().SetParameter(0U, param1);
993         GetExecModule().SetParameter(1U, param1);
994         result = Compare(cc, param1, param1);
995         GetExecModule().Execute();
996         retData = GetExecModule().GetRetValue();
997         EXPECT_EQ(retData, result);
998     }
999 }
1000 
SRC_GRAPH(Overflow,Graph * graph,Opcode overflowOpcode)1001 SRC_GRAPH(Overflow, Graph *graph, Opcode overflowOpcode)
1002 {
1003     GRAPH(graph)
1004     {
1005         PARAMETER(0U, 0U).i32();
1006         PARAMETER(1U, 1U).i32();
1007         CONSTANT(2U, 0U);
1008         BASIC_BLOCK(2U, 3U, 4U)
1009         {
1010             INST(4U, overflowOpcode).i32().SrcType(DataType::INT32).CC(CC_EQ).Inputs(0U, 1U);
1011         }
1012         BASIC_BLOCK(3U, -1L)
1013         {
1014             INST(5U, Opcode::Return).b().Inputs(2U);
1015         }
1016         BASIC_BLOCK(4U, -1L)
1017         {
1018             INST(6U, Opcode::Return).b().Inputs(4U);
1019         }
1020     }
1021 }
1022 
TEST_F(CodegenTest,AddOverflow)1023 TEST_F(CodegenTest, AddOverflow)
1024 {
1025     auto graph = CreateGraphStartEndBlocks();
1026     RuntimeInterfaceMock runtime;
1027     graph->SetRuntime(&runtime);
1028 
1029     src_graph::Overflow::CREATE(graph, Opcode::AddOverflow);
1030 
1031     SetNumVirtRegs(0U);
1032     SetNumArgs(2U);
1033 
1034     InPlaceCompilerTaskRunner taskRunner;
1035     taskRunner.GetContext().SetGraph(graph);
1036     bool success = true;
1037     taskRunner.AddCallbackOnFail([&success]([[maybe_unused]] InPlaceCompilerContext &compilerCtx) { success = false; });
1038     RunOptimizations<INPLACE_MODE>(std::move(taskRunner));
1039     EXPECT_TRUE(success);
1040 
1041     auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1042     auto codeExit = codeEntry + graph->GetCode().Size();
1043     ASSERT(codeEntry != nullptr && codeExit != nullptr);
1044     GetExecModule().SetInstructions(codeEntry, codeExit);
1045 
1046     GetExecModule().SetDump(false);
1047 
1048     int32_t min = std::numeric_limits<int32_t>::min();
1049     int32_t max = std::numeric_limits<int32_t>::max();
1050     std::array<int32_t, 7U> values = {0U, 2U, 5U, -7L, -10L, max, min};
1051     for (uint32_t i = 0; i < values.size(); ++i) {
1052         for (uint32_t j = 0; j < values.size(); ++j) {
1053             int32_t a0 = values[i];
1054             int32_t a1 = values[j];
1055             int32_t result;
1056             auto param1 = CutValue<int32_t>(a0, DataType::INT32);
1057             auto param2 = CutValue<int32_t>(a1, DataType::INT32);
1058 
1059             if ((a0 > 0 && a1 > max - a0) || (a0 < 0 && a1 < min - a0)) {
1060                 result = 0;
1061             } else {
1062                 result = a0 + a1;
1063             }
1064             GetExecModule().SetParameter(0U, param1);
1065             GetExecModule().SetParameter(1U, param2);
1066             GetExecModule().Execute();
1067 
1068             auto retData = GetExecModule().GetRetValue<int32_t>();
1069             EXPECT_EQ(retData, result);
1070         }
1071     }
1072 }
1073 
TEST_F(CodegenTest,SubOverflow)1074 TEST_F(CodegenTest, SubOverflow)
1075 {
1076     auto graph = CreateGraphStartEndBlocks();
1077     RuntimeInterfaceMock runtime;
1078     graph->SetRuntime(&runtime);
1079 
1080     src_graph::Overflow::CREATE(graph, Opcode::SubOverflow);
1081 
1082     SetNumVirtRegs(0U);
1083     SetNumArgs(2U);
1084 
1085     InPlaceCompilerTaskRunner taskRunner;
1086     taskRunner.GetContext().SetGraph(graph);
1087     bool success = true;
1088     taskRunner.AddCallbackOnFail([&success]([[maybe_unused]] InPlaceCompilerContext &compilerCtx) { success = false; });
1089     RunOptimizations<INPLACE_MODE>(std::move(taskRunner));
1090     EXPECT_TRUE(success);
1091 
1092     auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1093     auto codeExit = codeEntry + graph->GetCode().Size();
1094     ASSERT(codeEntry != nullptr && codeExit != nullptr);
1095     GetExecModule().SetInstructions(codeEntry, codeExit);
1096 
1097     GetExecModule().SetDump(false);
1098 
1099     int32_t min = std::numeric_limits<int32_t>::min();
1100     int32_t max = std::numeric_limits<int32_t>::max();
1101     std::array<int32_t, 7U> values = {0U, 2U, 5U, -7L, -10L, max, min};
1102     for (uint32_t i = 0; i < values.size(); ++i) {
1103         for (uint32_t j = 0; j < values.size(); ++j) {
1104             int32_t a0 = values[i];
1105             int32_t a1 = values[j];
1106             int32_t result;
1107             auto param1 = CutValue<int32_t>(a0, DataType::INT32);
1108             auto param2 = CutValue<int32_t>(a1, DataType::INT32);
1109 
1110             if ((a1 > 0 && a0 < min + a1) || (a1 < 0 && a0 > max + a1)) {
1111                 result = 0;
1112             } else {
1113                 result = a0 - a1;
1114             }
1115             GetExecModule().SetParameter(0U, param1);
1116             GetExecModule().SetParameter(1U, param2);
1117             GetExecModule().Execute();
1118 
1119             auto retData = GetExecModule().GetRetValue<int32_t>();
1120             EXPECT_EQ(retData, result);
1121         }
1122     }
1123 }
1124 
SRC_GRAPH(GenSelect,Graph * graph,ConditionCode cc)1125 SRC_GRAPH(GenSelect, Graph *graph, ConditionCode cc)
1126 {
1127     GRAPH(graph)
1128     {
1129         PARAMETER(0U, 0U).s64();
1130         PARAMETER(1U, 1U).s64();
1131         CONSTANT(2U, 0U);
1132         CONSTANT(3U, 1U);
1133         BASIC_BLOCK(2U, 3U, 4U)
1134         {
1135             INST(4U, Opcode::If).SrcType(DataType::INT64).CC(cc).Inputs(0U, 1U);
1136         }
1137         BASIC_BLOCK(3U, 4U) {}
1138         BASIC_BLOCK(4U, -1L)
1139         {
1140             INST(5U, Opcode::Phi).b().Inputs({{3U, 3U}, {2U, 2U}});
1141             INST(6U, Opcode::Return).b().Inputs(5U);
1142         }
1143     }
1144 }
1145 
TEST_F(CodegenTest,GenSelect)1146 TEST_F(CodegenTest, GenSelect)
1147 {
1148     for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
1149         auto cc = static_cast<ConditionCode>(ccint);
1150         auto graph = CreateGraphStartEndBlocks();
1151         RuntimeInterfaceMock runtime;
1152         graph->SetRuntime(&runtime);
1153 
1154         src_graph::GenSelect::CREATE(graph, cc);
1155 
1156         SetNumVirtRegs(0U);
1157         SetNumArgs(2U);
1158 
1159         EXPECT_TRUE(graph->RunPass<IfConversion>());
1160         ASSERT_EQ(INS(6U).GetInput(0U).GetInst()->GetOpcode(), Opcode::Select);
1161 
1162         RegAlloc(graph);
1163 
1164         EXPECT_TRUE(RunCodegen(graph));
1165         auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1166         auto codeExit = codeEntry + graph->GetCode().Size();
1167         ASSERT(codeEntry != nullptr && codeExit != nullptr);
1168         GetExecModule().SetInstructions(codeEntry, codeExit);
1169 
1170         GetExecModule().SetDump(false);
1171 
1172         bool result;
1173         auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
1174         auto param2 = CutValue<uint64_t>(-1L, DataType::UINT64);
1175 
1176         GetExecModule().SetParameter(0U, param1);
1177         GetExecModule().SetParameter(1U, param2);
1178         result = Compare(cc, param1, param2);
1179         GetExecModule().Execute();
1180         auto retData = GetExecModule().GetRetValue();
1181         EXPECT_EQ(retData, result);
1182 
1183         GetExecModule().SetParameter(0U, param2);
1184         GetExecModule().SetParameter(1U, param1);
1185         GetExecModule().Execute();
1186         result = Compare(cc, param2, param1);
1187         retData = GetExecModule().GetRetValue();
1188         EXPECT_EQ(retData, result);
1189 
1190         GetExecModule().SetParameter(0U, param1);
1191         GetExecModule().SetParameter(1U, param1);
1192         result = Compare(cc, param1, param1);
1193         GetExecModule().Execute();
1194         retData = GetExecModule().GetRetValue();
1195         EXPECT_EQ(retData, result);
1196     }
1197 }
1198 
SRC_GRAPH(BoolSelectImm,Graph * graph,ConditionCode cc)1199 SRC_GRAPH(BoolSelectImm, Graph *graph, ConditionCode cc)
1200 {
1201     GRAPH(graph)
1202     {
1203         PARAMETER(0U, 0U).u64();
1204         PARAMETER(1U, 1U).u64();
1205         CONSTANT(2U, 0U);
1206         CONSTANT(3U, 1U);
1207         BASIC_BLOCK(2U, -1L)
1208         {
1209             INST(4U, Opcode::Compare).b().CC(cc).Inputs(0U, 1U);
1210             INST(5U, Opcode::SelectImm).b().SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(3U, 2U, 4U);
1211             INST(6U, Opcode::Return).b().Inputs(5U);
1212         }
1213     }
1214 }
TEST_F(CodegenTest,BoolSelectImm)1215 TEST_F(CodegenTest, BoolSelectImm)
1216 {
1217     for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
1218         auto cc = static_cast<ConditionCode>(ccint);
1219         auto graph = CreateGraphStartEndBlocks();
1220         RuntimeInterfaceMock runtime;
1221         graph->SetRuntime(&runtime);
1222 
1223         src_graph::BoolSelectImm::CREATE(graph, cc);
1224 
1225         SetNumVirtRegs(0U);
1226         SetNumArgs(2U);
1227 
1228         RegAlloc(graph);
1229 
1230         EXPECT_TRUE(RunCodegen(graph));
1231         auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1232         auto codeExit = codeEntry + graph->GetCode().Size();
1233         ASSERT(codeEntry != nullptr && codeExit != nullptr);
1234         GetExecModule().SetInstructions(codeEntry, codeExit);
1235         GetExecModule().SetDump(false);
1236 
1237         auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
1238         auto param2 = CutValue<uint64_t>(-1L, DataType::UINT64);
1239 
1240         GetExecModule().SetParameter(0U, param1);
1241         GetExecModule().SetParameter(1U, param2);
1242         bool result = Compare(cc, param1, param2);
1243         GetExecModule().Execute();
1244         auto retData = GetExecModule().GetRetValue();
1245         EXPECT_EQ(retData, result);
1246 
1247         GetExecModule().SetParameter(0U, param2);
1248         GetExecModule().SetParameter(1U, param1);
1249         GetExecModule().Execute();
1250         result = Compare(cc, param2, param1);
1251         retData = GetExecModule().GetRetValue();
1252         EXPECT_EQ(retData, result);
1253 
1254         GetExecModule().SetParameter(0U, param1);
1255         GetExecModule().SetParameter(1U, param1);
1256         result = Compare(cc, param1, param1);
1257         GetExecModule().Execute();
1258         retData = GetExecModule().GetRetValue();
1259         EXPECT_EQ(retData, result);
1260     }
1261 }
1262 
SRC_GRAPH(Select,Graph * graph,ConditionCode cc)1263 SRC_GRAPH(Select, Graph *graph, ConditionCode cc)
1264 {
1265     GRAPH(graph)
1266     {
1267         PARAMETER(0U, 0U).u64();
1268         PARAMETER(1U, 1U).u64();
1269         CONSTANT(2U, 0U);
1270         CONSTANT(3U, 1U);
1271         BASIC_BLOCK(2U, -1L)
1272         {
1273             INST(5U, Opcode::Select).u64().SrcType(DataType::UINT64).CC(cc).Inputs(3U, 2U, 0U, 1U);
1274             INST(6U, Opcode::Return).u64().Inputs(5U);
1275         }
1276     }
1277 }
TEST_F(CodegenTest,Select)1278 TEST_F(CodegenTest, Select)
1279 {
1280     for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
1281         auto cc = static_cast<ConditionCode>(ccint);
1282         auto graph = CreateGraphStartEndBlocks();
1283         RuntimeInterfaceMock runtime;
1284         graph->SetRuntime(&runtime);
1285 
1286         src_graph::Select::CREATE(graph, cc);
1287 
1288         SetNumVirtRegs(0U);
1289         SetNumArgs(2U);
1290 
1291         RegAlloc(graph);
1292 
1293         EXPECT_TRUE(RunCodegen(graph));
1294         auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1295         auto codeExit = codeEntry + graph->GetCode().Size();
1296         ASSERT(codeEntry != nullptr && codeExit != nullptr);
1297         GetExecModule().SetInstructions(codeEntry, codeExit);
1298         GetExecModule().SetDump(false);
1299 
1300         auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
1301         auto param2 = CutValue<uint64_t>(-1L, DataType::UINT64);
1302 
1303         GetExecModule().SetParameter(0U, param1);
1304         GetExecModule().SetParameter(1U, param2);
1305         bool result = Compare(cc, param1, param2);
1306         GetExecModule().Execute();
1307         auto retData = GetExecModule().GetRetValue();
1308         EXPECT_EQ(retData, result);
1309 
1310         GetExecModule().SetParameter(0U, param2);
1311         GetExecModule().SetParameter(1U, param1);
1312         GetExecModule().Execute();
1313         result = Compare(cc, param2, param1);
1314         retData = GetExecModule().GetRetValue();
1315         EXPECT_EQ(retData, result);
1316 
1317         GetExecModule().SetParameter(0U, param1);
1318         GetExecModule().SetParameter(1U, param1);
1319         result = (cc == CC_EQ || cc == CC_LE || cc == CC_GE || cc == CC_AE || cc == CC_BE);
1320         GetExecModule().Execute();
1321         retData = GetExecModule().GetRetValue();
1322         EXPECT_EQ(retData, result);
1323     }
1324 }
1325 
SRC_GRAPH(CompareObj,Graph * graph)1326 SRC_GRAPH(CompareObj, Graph *graph)
1327 {
1328     GRAPH(graph)
1329     {
1330         PARAMETER(0U, 0U).ref();
1331         CONSTANT(1U, 0U);
1332 
1333         BASIC_BLOCK(2U, -1L)
1334         {
1335             INST(2U, Opcode::Compare).b().SrcType(DataType::REFERENCE).CC(CC_NE).Inputs(0U, 1U);
1336             INST(3U, Opcode::Return).b().Inputs(2U);
1337         }
1338     }
1339 }
1340 
TEST_F(CodegenTest,CompareObj)1341 TEST_F(CodegenTest, CompareObj)
1342 {
1343     auto graph = CreateGraphStartEndBlocks();
1344     RuntimeInterfaceMock runtime;
1345     graph->SetRuntime(&runtime);
1346 
1347     src_graph::CompareObj::CREATE(graph);
1348     SetNumVirtRegs(0U);
1349     SetNumArgs(1U);
1350 
1351     RegAlloc(graph);
1352 
1353     EXPECT_TRUE(RunCodegen(graph));
1354     auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1355     auto codeExit = codeEntry + graph->GetCode().Size();
1356     ASSERT(codeEntry != nullptr && codeExit != nullptr);
1357     GetExecModule().SetInstructions(codeEntry, codeExit);
1358 
1359     GetExecModule().SetDump(false);
1360 
1361     auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
1362     auto param2 = CutValue<uint64_t>(0U, DataType::UINT64);
1363 
1364     GetExecModule().SetParameter(0U, param1);
1365 
1366     GetExecModule().Execute();
1367 
1368     auto retData = GetExecModule().GetRetValue();
1369     EXPECT_EQ(retData, 1U);
1370 
1371     GetExecModule().SetParameter(0U, param2);
1372 
1373     GetExecModule().Execute();
1374 
1375     retData = GetExecModule().GetRetValue();
1376     EXPECT_EQ(retData, 0U);
1377 }
1378 
SRC_GRAPH(LoadArray,Graph * graph)1379 SRC_GRAPH(LoadArray, Graph *graph)
1380 {
1381     GRAPH(graph)
1382     {
1383         PARAMETER(0U, 0U).ref();  // array
1384         PARAMETER(1U, 1U).u32();  // index
1385         BASIC_BLOCK(2U, -1L)
1386         {
1387             INST(2U, Opcode::LoadArray).u32().Inputs(0U, 1U);
1388             INST(3U, Opcode::Return).u32().Inputs(2U);
1389         }
1390     }
1391 }
1392 
TEST_F(CodegenTest,LoadArray)1393 TEST_F(CodegenTest, LoadArray)
1394 {
1395     CheckLoadArray<bool>();
1396     CheckLoadArray<int8_t>();
1397     CheckLoadArray<uint8_t>();
1398     CheckLoadArray<int16_t>();
1399     CheckLoadArray<uint16_t>();
1400     CheckLoadArray<int32_t>();
1401     CheckLoadArray<uint32_t>();
1402     CheckLoadArray<int64_t>();
1403     CheckLoadArray<uint64_t>();
1404     CheckLoadArray<float>();
1405     CheckLoadArray<double>();
1406 
1407     src_graph::LoadArray::CREATE(GetGraph());
1408     auto graph = GetGraph();
1409     SetNumVirtRegs(0U);
1410     SetNumArgs(2U);
1411 
1412     RegAlloc(graph);
1413 
1414     EXPECT_TRUE(RunCodegen(graph));
1415     auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1416     auto codeExit = codeEntry + graph->GetCode().Size();
1417     ASSERT(codeEntry != nullptr && codeExit != nullptr);
1418     GetExecModule().SetInstructions(codeEntry, codeExit);
1419 
1420     GetExecModule().SetDump(false);
1421 
1422     ObjectPointerType array[4U] = {0xffffaaaaU, 0xffffbbbbU, 0xffffccccU, 0xffffddddU};
1423     auto param1 = GetExecModule().CreateArray(array, 4U, GetObjectAllocator());
1424     auto param2 = CutValue<int32_t>(2U, DataType::INT32);
1425     GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
1426     GetExecModule().SetParameter(1U, param2);
1427 
1428     GetExecModule().Execute();
1429 
1430     GetExecModule().CopyArray(param1, array);
1431 
1432     GetExecModule().FreeArray(param1);
1433     auto retData = GetExecModule().GetRetValue();
1434     EXPECT_EQ(retData, array[2U]);
1435 }
1436 
TEST_F(CodegenTest,LoadArrayPair)1437 TEST_F(CodegenTest, LoadArrayPair)
1438 {
1439     CheckLoadArrayPair<uint32_t>(true);
1440     CheckLoadArrayPair<int32_t>(false);
1441     CheckLoadArrayPair<uint64_t>(true);
1442     CheckLoadArrayPair<int64_t>(false);
1443     CheckLoadArrayPair<float>(true);
1444     CheckLoadArrayPair<float>(false);
1445     CheckLoadArrayPair<double>(true);
1446     CheckLoadArrayPair<double>(false);
1447 }
1448 
1449 #ifndef USE_ADDRESS_SANITIZER
TEST_F(CodegenTest,CheckCodegenBounds)1450 TEST_F(CodegenTest, CheckCodegenBounds)
1451 {
1452     // Do not try to encode too large graph
1453     uint64_t instsPerByte = GetGraph()->GetEncoder()->MaxArchInstPerEncoded();
1454     uint64_t maxBitsInInst = GetInstructionSizeBits(GetGraph()->GetArch());
1455     uint64_t instCount = g_options.GetCompilerMaxGenCodeSize() / (instsPerByte * maxBitsInInst);
1456 
1457     CheckBounds<uint32_t>(instCount - 1L);
1458     CheckBounds<uint32_t>(instCount + 1U);
1459 
1460     CheckBounds<uint32_t>(instCount / 2U);
1461 }
1462 #endif
1463 
TEST_F(CodegenTest,LenArray)1464 TEST_F(CodegenTest, LenArray)
1465 {
1466     GRAPH(GetGraph())
1467     {
1468         PARAMETER(0U, 0U).ref();  // array
1469         BASIC_BLOCK(2U, -1L)
1470         {
1471             INST(1U, Opcode::LenArray).s32().Inputs(0U);
1472             INST(2U, Opcode::Return).s32().Inputs(1U);
1473         }
1474     }
1475     auto graph = GetGraph();
1476     SetNumVirtRegs(0U);
1477     SetNumArgs(1U);
1478 
1479     RegAlloc(graph);
1480 
1481     EXPECT_TRUE(RunCodegen(graph));
1482     auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1483     auto codeExit = codeEntry + graph->GetCode().Size();
1484     ASSERT(codeEntry != nullptr && codeExit != nullptr);
1485     GetExecModule().SetInstructions(codeEntry, codeExit);
1486 
1487     GetExecModule().SetDump(false);
1488 
1489     uint64_t array[4U] = {0U, 0U, 0U, 0U};
1490     auto param1 = GetExecModule().CreateArray(array, 4U, GetObjectAllocator());
1491     GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
1492 
1493     GetExecModule().Execute();
1494     GetExecModule().FreeArray(param1);
1495 
1496     auto retData = GetExecModule().GetRetValue();
1497     EXPECT_EQ(retData, 4U);
1498 }
1499 
TEST_F(CodegenTest,Parameter)1500 TEST_F(CodegenTest, Parameter)
1501 {
1502     GRAPH(GetGraph())
1503     {
1504         PARAMETER(0U, 0U).u64();
1505         PARAMETER(1U, 1U).s16();
1506         BASIC_BLOCK(2U, -1L)
1507         {
1508             INST(6U, Opcode::Add).u64().Inputs(0U, 0U);
1509             INST(8U, Opcode::Add).s16().Inputs(1U, 1U);
1510             INST(15U, Opcode::Return).u64().Inputs(6U);
1511             // Return parameter_0 + parameter_0
1512         }
1513     }
1514     SetNumVirtRegs(0U);
1515     SetNumArgs(2U);
1516 
1517     auto graph = GetGraph();
1518 
1519     RegAlloc(graph);
1520 
1521     EXPECT_TRUE(RunCodegen(graph));
1522     auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1523     auto codeExit = codeEntry + graph->GetCode().Size();
1524     ASSERT(codeEntry != nullptr && codeExit != nullptr);
1525     GetExecModule().SetInstructions(codeEntry, codeExit);
1526 
1527     GetExecModule().SetDump(false);
1528 
1529     auto param1 = CutValue<uint64_t>(1234U, DataType::UINT64);
1530     auto param2 = CutValue<int16_t>(-1234L, DataType::INT16);
1531     GetExecModule().SetParameter(0U, param1);
1532     GetExecModule().SetParameter(1U, param2);
1533 
1534     GetExecModule().Execute();
1535 
1536     auto retData = GetExecModule().GetRetValue();
1537     EXPECT_EQ(retData, 1234U + 1234U);
1538 
1539     // Clear data for next execution
1540     while (auto current = GetGraph()->GetFirstConstInst()) {
1541         GetGraph()->RemoveConstFromList(current);
1542     }
1543 }
1544 
SRC_GRAPH(RegallocTwoFreeRegs,Graph * graph)1545 SRC_GRAPH(RegallocTwoFreeRegs, Graph *graph)
1546 {
1547     GRAPH(graph)
1548     {
1549         CONSTANT(0U, 1U);   // const a = 1
1550         CONSTANT(1U, 10U);  // const b = 10
1551         CONSTANT(2U, 20U);  // const c = 20
1552 
1553         BASIC_BLOCK(2U, 3U, 4U)
1554         {
1555             INST(3U, Opcode::Phi).u64().Inputs({{0U, 0U}, {3U, 7U}});                      // var a' = a
1556             INST(4U, Opcode::Phi).u64().Inputs({{0U, 1U}, {3U, 8U}});                      // var b' = b
1557             INST(5U, Opcode::Compare).b().CC(CC_NE).Inputs(4U, 0U);                        // b' == 1 ?
1558             INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U);  // if == (b' == 1) -> exit
1559         }
1560 
1561         BASIC_BLOCK(3U, 2U)
1562         {
1563             INST(7U, Opcode::Mul).u64().Inputs(3U, 4U);  // a' = a' * b'
1564             INST(8U, Opcode::Sub).u64().Inputs(4U, 0U);  // b' = b' - 1
1565         }
1566 
1567         BASIC_BLOCK(4U, -1L)
1568         {
1569             INST(10U, Opcode::Add).u64().Inputs(2U, 3U);  // a' = c + a'
1570             INST(11U, Opcode::Return).u64().Inputs(10U);  // return a'
1571         }
1572     }
1573 }
1574 
TEST_F(CodegenTest,RegallocTwoFreeRegs)1575 TEST_F(CodegenTest, RegallocTwoFreeRegs)
1576 {
1577     src_graph::RegallocTwoFreeRegs::CREATE(GetGraph());
1578     // Create reg_mask with 5 available general registers,
1579     // 3 of them will be reserved by Reg Alloc.
1580     {
1581         RegAllocLinearScan ra(GetGraph());
1582         ra.SetRegMask(RegMask {0xFFFFF07FU});
1583         ra.SetVRegMask(VRegMask {0U});
1584         EXPECT_TRUE(ra.Run());
1585     }
1586     GraphChecker(GetGraph()).Check();
1587     EXPECT_TRUE(RunCodegen(GetGraph()));
1588     auto codeEntry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
1589     auto codeExit = codeEntry + GetGraph()->GetCode().Size();
1590     ASSERT(codeEntry != nullptr && codeExit != nullptr);
1591     GetExecModule().SetInstructions(codeEntry, codeExit);
1592 
1593     GetExecModule().SetDump(false);
1594 
1595     auto param1 = CutValue<uint64_t>(0x0U, DataType::UINT64);
1596     auto param2 = CutValue<uint16_t>(0x0U, DataType::INT32);
1597 
1598     GetExecModule().SetParameter(0U, param1);
1599     GetExecModule().SetParameter(1U, param2);
1600 
1601     GetExecModule().Execute();
1602 
1603     auto retData = GetExecModule().GetRetValue();
1604     EXPECT_TRUE(retData == 10U * 9U * 8U * 7U * 6U * 5U * 4U * 3U * 2U * 1U + 20U);
1605 
1606     // Clear data for next execution
1607     while (auto current = GetGraph()->GetFirstConstInst()) {
1608         GetGraph()->RemoveConstFromList(current);
1609     }
1610 }
1611 
SRC_GRAPH(TwoFreeRegsAdditionSaveState,Graph * graph)1612 SRC_GRAPH(TwoFreeRegsAdditionSaveState, Graph *graph)
1613 {
1614     GRAPH(graph)
1615     {
1616         PARAMETER(0U, 0U).u64();
1617         PARAMETER(11U, 0U).f64();
1618         PARAMETER(12U, 0U).f32();
1619         CONSTANT(1U, 12U);
1620         CONSTANT(2U, -1L);
1621         CONSTANT(3U, 100000000U);
1622 
1623         BASIC_BLOCK(2U, -1L)
1624         {
1625             INST(4U, Opcode::Add).u64().Inputs(0U, 1U);
1626             INST(5U, Opcode::Add).u64().Inputs(0U, 2U);
1627             INST(6U, Opcode::Add).u64().Inputs(0U, 3U);
1628             INST(7U, Opcode::Sub).u64().Inputs(0U, 1U);
1629             INST(8U, Opcode::Sub).u64().Inputs(0U, 2U);
1630             INST(9U, Opcode::Sub).u64().Inputs(0U, 3U);
1631             INST(17U, Opcode::Add).u64().Inputs(0U, 0U);
1632             INST(18U, Opcode::Sub).u64().Inputs(0U, 0U);
1633             INST(19U, Opcode::Add).u16().Inputs(0U, 1U);
1634             INST(20U, Opcode::Add).u16().Inputs(0U, 2U);
1635             INST(10U, Opcode::SaveState)
1636                 .Inputs(0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 11U, 12U, 17U, 18U, 19U, 20U)
1637                 .SrcVregs({0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 11U, 12U, 13U, 14U});
1638         }
1639     }
1640 }
1641 
1642 // NOTE (igorban): Update FillSaveStates() with filling SaveState from SpillFill
TEST_F(CodegenTest,DISABLED_TwoFreeRegsAdditionSaveState)1643 TEST_F(CodegenTest, DISABLED_TwoFreeRegsAdditionSaveState)
1644 {
1645     src_graph::TwoFreeRegsAdditionSaveState::CREATE(GetGraph());
1646     // NO RETURN value - will droped down to SaveState block!
1647 
1648     SetNumVirtRegs(0U);
1649     SetNumArgs(3U);
1650     // Create reg_mask with 5 available general registers,
1651     // 3 of them will be reserved by Reg Alloc.
1652     {
1653         RegAllocLinearScan ra(GetGraph());
1654         ra.SetRegMask(RegMask {0xFFFFF07FU});
1655         ra.SetVRegMask(VRegMask {0U});
1656         EXPECT_TRUE(ra.Run());
1657     }
1658     GraphChecker(GetGraph()).Check();
1659 
1660     EXPECT_TRUE(RunCodegen(GetGraph()));
1661     auto codeEntry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
1662     auto codeExit = codeEntry + GetGraph()->GetCode().Size();
1663     ASSERT(codeEntry != nullptr && codeExit != nullptr);
1664     GetExecModule().SetInstructions(codeEntry, codeExit);
1665 
1666     GetExecModule().SetDump(false);
1667 
1668     auto param1 = CutValue<uint64_t>(0x12345U, DataType::UINT64);
1669     auto param2 = CutValue<float>(0x12345U, DataType::FLOAT32);
1670 
1671     GetExecModule().SetParameter(0U, param1);
1672     GetExecModule().SetParameter(1U, param2);
1673     GetExecModule().SetParameter(2U, param2);
1674 
1675     GetExecModule().Execute();
1676 
1677     // Main check - return value get from SaveState return DEOPTIMIZATION
1678     EXPECT_EQ(GetExecModule().GetRetValue(), 1U);
1679 
1680     // Clear data for next execution
1681     while (auto current = GetGraph()->GetFirstConstInst()) {
1682         GetGraph()->RemoveConstFromList(current);
1683     }
1684 }
1685 
SRC_GRAPH(SaveState,Graph * graph)1686 SRC_GRAPH(SaveState, Graph *graph)
1687 {
1688     GRAPH(graph)
1689     {
1690         PARAMETER(0U, 0U).ref();  // array
1691         PARAMETER(1U, 1U).u64();  // index
1692         BASIC_BLOCK(2U, 3U)
1693         {
1694             INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
1695             INST(3U, Opcode::NullCheck).ref().Inputs(0U, 2U);
1696             INST(4U, Opcode::LenArray).s32().Inputs(3U);
1697             INST(5U, Opcode::BoundsCheck).s32().Inputs(4U, 1U, 2U);
1698             INST(6U, Opcode::LoadArray).u64().Inputs(3U, 5U);
1699             INST(7U, Opcode::Add).u64().Inputs(6U, 6U);
1700             INST(8U, Opcode::StoreArray).u64().Inputs(3U, 5U, 7U);
1701         }
1702         BASIC_BLOCK(3U, -1L)
1703         {
1704             INST(10U, Opcode::Add).u64().Inputs(7U, 7U);  // Some return value
1705             INST(11U, Opcode::Return).u64().Inputs(10U);
1706         }
1707     }
1708 }
1709 
TEST_F(CodegenTest,SaveState)1710 TEST_F(CodegenTest, SaveState)
1711 {
1712     src_graph::SaveState::CREATE(GetGraph());
1713     SetNumVirtRegs(0U);
1714     SetNumArgs(2U);
1715 
1716     auto graph = GetGraph();
1717 
1718     RegAlloc(graph);
1719 
1720     // Run codegen
1721     EXPECT_TRUE(RunCodegen(graph));
1722     auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1723     auto codeExit = codeEntry + graph->GetCode().Size();
1724     ASSERT(codeEntry != nullptr && codeExit != nullptr);
1725     GetExecModule().SetInstructions(codeEntry, codeExit);
1726 
1727     // Enable dumping
1728     GetExecModule().SetDump(false);
1729 
1730     uint64_t arrayData[4U];
1731     for (size_t i = 0; i < 4U; i++) {
1732         arrayData[i] = i + 0x20U;
1733     }
1734     auto param1 = GetExecModule().CreateArray(arrayData, 4U, GetObjectAllocator());
1735     auto param2 = CutValue<uint64_t>(1U, DataType::UINT64);
1736     GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
1737     GetExecModule().SetParameter(1U, param2);
1738 
1739     GetExecModule().Execute();
1740     GetExecModule().SetDump(false);
1741     // End dump
1742 
1743     auto retData = GetExecModule().GetRetValue();
1744     // NOTE (igorban) : really need to check array changes
1745     EXPECT_EQ(retData, 4U * 0x21);
1746 
1747     // Clear data for next execution
1748     while (auto current = GetGraph()->GetFirstConstInst()) {
1749         GetGraph()->RemoveConstFromList(current);
1750     }
1751     GetExecModule().FreeArray(param1);
1752 }  // namespace ark::compiler
1753 
TEST_F(CodegenTest,DeoptimizeIf)1754 TEST_F(CodegenTest, DeoptimizeIf)
1755 {
1756     GRAPH(GetGraph())
1757     {
1758         PARAMETER(0U, 0U).b();
1759         BASIC_BLOCK(2U, 1U)
1760         {
1761             INST(2U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
1762             INST(3U, Opcode::DeoptimizeIf).Inputs(0U, 2U);
1763             INST(4U, Opcode::Return).b().Inputs(0U);
1764         }
1765     }
1766     RegAlloc(GetGraph());
1767 
1768     EXPECT_TRUE(RunCodegen(GetGraph()));
1769     auto codeEntry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
1770     auto codeExit = codeEntry + GetGraph()->GetCode().Size();
1771     ASSERT(codeEntry != nullptr && codeExit != nullptr);
1772     GetExecModule().SetInstructions(codeEntry, codeExit);
1773 
1774     // param == false [OK]
1775     auto param = false;
1776     GetExecModule().SetParameter(0U, param);
1777     GetExecModule().Execute();
1778     EXPECT_EQ(GetExecModule().GetRetValue(), param);
1779 
1780     // param == true [INTERPRET]
1781 }
1782 
ResoveParameterSequence(ArenaVector<std::pair<uint8_t,uint8_t>> * movedRegisters,uint8_t tmp,ArenaAllocator * allocator)1783 static ArenaVector<std::pair<uint8_t, uint8_t>> ResoveParameterSequence(
1784     ArenaVector<std::pair<uint8_t, uint8_t>> *movedRegisters, uint8_t tmp, ArenaAllocator *allocator)
1785 {
1786     constexpr uint8_t INVALID_FIRST = -1;
1787     constexpr uint8_t INVALID_SECOND = -2;
1788 
1789     movedRegisters->emplace_back(std::pair<uint8_t, uint8_t>(INVALID_FIRST, INVALID_SECOND));
1790     /*
1791         Example:
1792         1. mov x0 <- x3
1793         2. mov x1 <- x0
1794         3. mov x2 <- x3
1795         4. mov x3 <- x2
1796         Agreement - dst-s can't hold same registers multiple times (double move to one register)
1797                   - src for movs can hold same register multiply times
1798 
1799         Algorithm:
1800             1. Find handing edges (x1 - just in dst)
1801                 emit "2. mov x1 <- x0"
1802                 goto 1.
1803                 emit "1. mov x0 <- x3"
1804             2. Assert all registers used just one time (loop from registers sequence)
1805                All multiply-definitions must be resolved on previous step
1806                 emit ".. mov xtmp <- x2" (strore xtmp == x3)
1807                 emit "3. mov x2 <- x3"
1808                 emit "4. mov x3 <- xtmp" (ASSERT(4->GetReg == x3) - there is no other possible situations here)
1809     */
1810     // Calculate weigth
1811     ArenaVector<std::pair<uint8_t, uint8_t>> result(allocator->Adapter());
1812     // --moved_registers->end() - for remove marker-element
1813     for (auto pair = movedRegisters->begin(); pair != --movedRegisters->end();) {
1814         auto conflict = std::find_if(movedRegisters->begin(), movedRegisters->end(), [pair](auto inPair) {
1815             return (inPair.second == pair->first && (inPair != *pair));
1816         });
1817         if (conflict == movedRegisters->end()) {
1818             // emit immediate - there are no another possible combinations
1819             result.emplace_back(*pair);
1820             movedRegisters->erase(pair);
1821             pair = movedRegisters->begin();
1822         } else {
1823             ++pair;
1824         }
1825     }
1826     // Here just loops
1827     auto currPair = movedRegisters->begin();
1828     while (!(currPair->first == INVALID_FIRST && currPair->second == INVALID_SECOND)) {
1829         /* Need support single mov x1 <- x1:
1830            ASSERT(moved_registers->size() != 1);
1831         */
1832 
1833         auto savedReg = currPair->first;
1834         result.emplace_back(std::pair<uint8_t, uint8_t>(tmp, currPair->first));
1835         result.emplace_back(*currPair);  // we already save dst_register
1836 
1837         // Remove current instruction
1838         auto currReg = currPair->second;
1839         movedRegisters->erase(currPair);
1840 
1841         while (currPair != movedRegisters->end()) {
1842             currPair = std::find_if(movedRegisters->begin(), movedRegisters->end(),
1843                                     [currReg](auto inPair) { return inPair.first == currReg; });
1844             ASSERT(currPair != movedRegisters->end());
1845             if (currPair->second == savedReg) {
1846                 result.emplace_back(std::pair<uint8_t, uint8_t>(currPair->first, tmp));
1847                 movedRegisters->erase(currPair);
1848                 break;
1849             };
1850             result.emplace_back(*currPair);
1851             currReg = currPair->second;
1852             movedRegisters->erase(currPair);
1853         }
1854         currPair = movedRegisters->begin();
1855     }
1856     movedRegisters->erase(currPair);
1857 
1858     return result;
1859 }
1860 
TEST_F(CodegenTest,ResolveParamSequence1)1861 TEST_F(CodegenTest, ResolveParamSequence1)
1862 {
1863     ArenaVector<std::pair<uint8_t, uint8_t>> someSequence(GetAllocator()->Adapter());
1864     someSequence.emplace_back(std::pair<uint8_t, uint8_t>(0U, 3U));
1865     someSequence.emplace_back(std::pair<uint8_t, uint8_t>(1U, 0U));
1866     someSequence.emplace_back(std::pair<uint8_t, uint8_t>(2U, 3U));
1867     someSequence.emplace_back(std::pair<uint8_t, uint8_t>(3U, 2U));
1868 
1869     auto result = ResoveParameterSequence(&someSequence, 13U, GetAllocator());
1870     EXPECT_TRUE(someSequence.empty());
1871     ArenaVector<std::pair<uint8_t, uint8_t>> resultSequence(GetAllocator()->Adapter());
1872     resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(1U, 0U));
1873     resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(0U, 3U));
1874     resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(13U, 2U));
1875     resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(2U, 3U));
1876     resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(3U, 13U));
1877 
1878     EXPECT_EQ(result, resultSequence);
1879 }
TEST_F(CodegenTest,ResolveParamSequence2)1880 TEST_F(CodegenTest, ResolveParamSequence2)
1881 {
1882     // Special loop-only case
1883     ArenaVector<std::pair<uint8_t, uint8_t>> someSequenceLoop(GetAllocator()->Adapter());
1884     someSequenceLoop.emplace_back(std::pair<uint8_t, uint8_t>(2U, 3U));
1885     someSequenceLoop.emplace_back(std::pair<uint8_t, uint8_t>(1U, 2U));
1886     someSequenceLoop.emplace_back(std::pair<uint8_t, uint8_t>(4U, 1U));
1887     someSequenceLoop.emplace_back(std::pair<uint8_t, uint8_t>(0U, 4U));
1888     someSequenceLoop.emplace_back(std::pair<uint8_t, uint8_t>(3U, 0U));
1889 
1890     auto resultLoop = ResoveParameterSequence(&someSequenceLoop, 13U, GetAllocator());
1891     EXPECT_TRUE(someSequenceLoop.empty());
1892     ArenaVector<std::pair<uint8_t, uint8_t>> resultSequenceLoop(GetAllocator()->Adapter());
1893     resultSequenceLoop.emplace_back(std::pair<uint8_t, uint8_t>(13U, 2U));
1894     resultSequenceLoop.emplace_back(std::pair<uint8_t, uint8_t>(2U, 3U));
1895     resultSequenceLoop.emplace_back(std::pair<uint8_t, uint8_t>(3U, 0U));
1896     resultSequenceLoop.emplace_back(std::pair<uint8_t, uint8_t>(0U, 4U));
1897     resultSequenceLoop.emplace_back(std::pair<uint8_t, uint8_t>(4U, 1U));
1898     resultSequenceLoop.emplace_back(std::pair<uint8_t, uint8_t>(1U, 13U));
1899 
1900     EXPECT_EQ(resultLoop, resultSequenceLoop);
1901 }
1902 
1903 template <uint8_t REG_SIZE, uint8_t TMP_REG, typename T>
CheckParamSequence(uint8_t j,const T & resultVect,const T & origVector)1904 void CheckParamSequence(uint8_t j, const T &resultVect, const T &origVector)
1905 {
1906     auto dst = resultVect[j].first;
1907 
1908     for (uint8_t k = j + 1U; k < REG_SIZE; ++k) {
1909         if (resultVect[k].second == dst && resultVect[k].second != TMP_REG) {
1910             std::cerr << " first = " << resultVect[k].first << " tmp = " << TMP_REG << "\n";
1911             std::cerr << " Before:\n";
1912             for (const auto &it : origVector) {
1913                 std::cerr << " " << (size_t)it.first << "<-" << (size_t)it.second << "\n";
1914             }
1915             std::cerr << " After:\n";
1916             for (const auto &it : resultVect) {
1917                 std::cerr << " " << (size_t)it.first << "<-" << (size_t)it.second << "\n";
1918             }
1919             std::cerr << "Fault on " << (size_t)j << " and " << (size_t)k << "\n";
1920             EXPECT_NE(resultVect[k].second, dst);
1921         }
1922     }
1923 }
1924 
TEST_F(CodegenTest,ResolveParamSequence3)1925 TEST_F(CodegenTest, ResolveParamSequence3)
1926 {
1927     ArenaVector<std::pair<uint8_t, uint8_t>> someSequence(GetAllocator()->Adapter());
1928     ArenaVector<std::pair<uint8_t, uint8_t>> resultSequence(GetAllocator()->Adapter());
1929     constexpr uint8_t REG_SIZE = 30;
1930     constexpr uint8_t TMP_REG = REG_SIZE + 5U;
1931     for (uint64_t i = 0; i < ITERATION; ++i) {
1932         EXPECT_TRUE(someSequence.empty());
1933 
1934         std::vector<uint8_t> iters;
1935         for (uint8_t j = 0; j < REG_SIZE; ++j) {
1936             iters.push_back(j);
1937         }
1938         std::shuffle(iters.begin(), iters.end(), g_randomGenerator);
1939         std::vector<std::pair<uint8_t, uint8_t>> origVector;
1940         for (uint8_t j = 0; j < REG_SIZE; ++j) {
1941             auto gen {g_randomGenerator()};
1942             auto randomValue = gen % REG_SIZE;
1943             origVector.emplace_back(std::pair<uint8_t, uint8_t>(iters[j], randomValue));
1944         }
1945         for (auto &pair : origVector) {
1946             someSequence.emplace_back(pair);
1947         }
1948         resultSequence = ResoveParameterSequence(&someSequence, TMP_REG, GetAllocator());
1949         std::vector<std::pair<uint8_t, uint8_t>> resultVect;
1950         for (auto &pair : resultSequence) {
1951             resultVect.emplace_back(pair);
1952         }
1953 
1954         // First analysis - there are no dst before src
1955         for (uint8_t j = 0; j < REG_SIZE; ++j) {
1956             CheckParamSequence<REG_SIZE, TMP_REG>(j, resultVect, origVector);
1957         }
1958 
1959         // Second analysis - if remove all same moves - there will be
1960         // only " move tmp <- reg " & " mov reg <- tmp"
1961     }
1962 }
1963 // NOLINTEND(readability-magic-numbers,modernize-avoid-c-arrays,cppcoreguidelines-pro-bounds-pointer-arithmetic)
1964 
1965 }  // namespace ark::compiler
1966