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