1 /*
2 * Copyright (c) 2021-2023 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 <random>
17
18 #include "optimizer/code_generator/codegen.h"
19 #include "optimizer/ir/basicblock.h"
20 #include "optimizer/ir/inst.h"
21 #include "optimizer/optimizations/if_conversion.h"
22 #include "optimizer/optimizations/lowering.h"
23 #include "optimizer/optimizations/regalloc/reg_alloc.h"
24 #include "optimizer/optimizations/regalloc/reg_alloc_linear_scan.h"
25 #include "optimizer_run.h"
26 #include "compiler/inplace_task_runner.h"
27 #include "compiler/compiler_task_runner.h"
28
29 #include "libpandabase/macros.h"
30 #include "libpandabase/utils/utils.h"
31 #include "gtest/gtest.h"
32 #include "unit_test.h"
33 #include "utils/bit_utils.h"
34 #include "vixl_exec_module.h"
35
36 const uint64_t SEED = 0x1234;
37 #ifndef PANDA_NIGHTLY_TEST_ON
38 const uint64_t ITERATION = 40;
39 #else
40 const uint64_t ITERATION = 20000;
41 #endif
42 // NOLINTNEXTLINE(fuchsia-statically-constructed-objects,cert-msc51-cpp)
43 static inline auto g_randomGenerator = std::mt19937_64(SEED);
44
45 namespace panda::compiler {
46
47 class CodegenTest : public GraphTest {
48 public:
CodegenTest()49 CodegenTest() : execModule_(GetAllocator(), GetGraph()->GetRuntime())
50 {
51 #ifndef NDEBUG
52 // GraphChecker hack: LowLevel instructions may appear only after Lowering pass:
53 GetGraph()->SetLowLevelInstructionsEnabled();
54 #endif
55 }
56 ~CodegenTest() override = default;
57
58 NO_COPY_SEMANTIC(CodegenTest);
59 NO_MOVE_SEMANTIC(CodegenTest);
60
GetExecModule()61 VixlExecModule &GetExecModule()
62 {
63 return execModule_;
64 }
65
66 template <typename T>
67 void CheckStoreArray();
68
69 template <typename T>
70 void CheckLoadArray();
71
72 template <typename T>
73 void CheckStoreArrayPair(bool imm);
74
75 template <typename T>
76 void CheckLoadArrayPair(bool imm);
77
78 template <typename T>
79 void CheckCmp(bool isFcmpg = false);
80
81 template <typename T>
82 void CheckReturnValue(Graph *graph, T expectedValue);
83
84 template <typename T>
85 void CheckBounds(size_t count);
86
87 void TestBinaryOperationWithShiftedOperand(Opcode opcode, uint32_t l, uint32_t r, ShiftType shiftType,
88 uint32_t shift, uint32_t erv);
89
90 void CreateGraphForOverflowTest(Graph *graph, Opcode overflowOpcode);
91
92 private:
93 VixlExecModule execModule_;
94 };
95
RunCodegen(Graph * graph)96 bool RunCodegen(Graph *graph)
97 {
98 return graph->RunPass<Codegen>();
99 }
100
101 // NOLINTBEGIN(readability-magic-numbers,modernize-avoid-c-arrays,cppcoreguidelines-pro-bounds-pointer-arithmetic)
TEST_F(CodegenTest,SimpleProgramm)102 TEST_F(CodegenTest, SimpleProgramm)
103 {
104 /*
105 .function main()<main>{
106 movi.64 v0, 100000000 ## 0 -> 3 ## bb0
107 movi.64 v1, 4294967296 ## 1 -> 4 ## bb0
108 ldai 0 ## 2 -> 5 ## bb0
109 loop: ## ##
110 jeq v0, loop_exit ## 6, 7, 8 ## bb1
111 ## ##
112 sta.64 v2 ## 9 ## bb2
113 and.64 v1 ## 10 ## bb2
114 sta.64 v1 ## 11 ## bb2
115 lda.64 v2 ## 12 ## bb2
116 inc ## 13 ## bb2
117 jmp loop ## 14 ## bb2
118 loop_exit: ## ##
119 lda.64 v1 ## 14 ## bb3
120 return.64 ## 15 ## bb3
121 }
122 */
123
124 GRAPH(GetGraph())
125 {
126 CONSTANT(0U, 10UL); // r1
127 CONSTANT(1U, 4294967296UL); // r2
128 CONSTANT(2U, 0UL); // r3 -> acc(3)
129 CONSTANT(3U, 0x1UL); // r20 -> 0x1 (for inc constant)
130
131 BASIC_BLOCK(2U, 4U, 3U)
132 {
133 INST(16U, Opcode::Phi).Inputs(2U, 13U).s64(); // PHI acc
134 INST(17U, Opcode::Phi).Inputs(1U, 10U).s64(); // PHI v1
135 INST(20U, Opcode::Phi).Inputs(2U, 10U).s64(); // result to return
136
137 // NOTE (igorban): support CMP instr
138 INST(18U, Opcode::Compare).b().CC(CC_NE).Inputs(0U, 16U);
139 INST(7U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(18U);
140 }
141
142 BASIC_BLOCK(3U, 2U)
143 {
144 INST(10U, Opcode::And).Inputs(16U, 17U).s64(); // -> acc
145 INST(13U, Opcode::Add).Inputs(16U, 3U).s64(); // -> acc
146 }
147
148 BASIC_BLOCK(4U, -1L)
149 {
150 INST(19U, Opcode::Return).Inputs(20U).s64();
151 }
152 }
153
154 SetNumVirtRegs(0U);
155 SetNumArgs(1U);
156
157 RegAlloc(GetGraph());
158
159 // call codegen
160 EXPECT_TRUE(RunCodegen(GetGraph()));
161 auto entry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
162 auto exit = entry + GetGraph()->GetCode().Size();
163 ASSERT(entry != nullptr && exit != nullptr);
164 GetExecModule().SetInstructions(entry, exit);
165 GetExecModule().SetDump(false);
166
167 GetExecModule().Execute();
168
169 auto retData = GetExecModule().GetRetValue();
170 EXPECT_EQ(retData, 0U);
171
172 // Clear data for next execution
173 while (auto current = GetGraph()->GetFirstConstInst()) {
174 GetGraph()->RemoveConstFromList(current);
175 }
176 }
177
178 template <typename T>
CheckStoreArray()179 void CodegenTest::CheckStoreArray()
180 {
181 constexpr DataType::Type TYPE = VixlExecModule::GetType<T>();
182
183 // Create graph
184 auto graph = CreateEmptyGraph();
185 RuntimeInterfaceMock runtime;
186 graph->SetRuntime(&runtime);
187
188 auto entry = graph->CreateStartBlock();
189 auto exit = graph->CreateEndBlock();
190 auto block = graph->CreateEmptyBlock();
191 entry->AddSucc(block);
192 block->AddSucc(exit);
193
194 auto array = graph->AddNewParameter(0U, DataType::REFERENCE);
195 auto index = graph->AddNewParameter(1U, DataType::INT32);
196 auto storeValue = graph->AddNewParameter(2U, TYPE);
197
198 graph->ResetParameterInfo();
199 array->SetLocationData(graph->GetDataForNativeParam(DataType::REFERENCE));
200 index->SetLocationData(graph->GetDataForNativeParam(DataType::INT32));
201 storeValue->SetLocationData(graph->GetDataForNativeParam(TYPE));
202
203 auto stArr = graph->CreateInst(Opcode::StoreArray);
204 block->AppendInst(stArr);
205 stArr->SetType(TYPE);
206 stArr->SetInput(0U, array);
207 stArr->SetInput(1U, index);
208 stArr->SetInput(2U, storeValue);
209 auto ret = graph->CreateInst(Opcode::ReturnVoid);
210 block->AppendInst(ret);
211
212 SetNumVirtRegs(0U);
213 SetNumArgs(3U);
214
215 RegAlloc(graph);
216
217 // call codegen
218 EXPECT_TRUE(RunCodegen(graph));
219 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
220 auto codeExit = codeEntry + graph->GetCode().Size();
221 ASSERT(codeEntry != nullptr && codeExit != nullptr);
222 GetExecModule().SetInstructions(codeEntry, codeExit);
223
224 GetExecModule().SetDump(false);
225
226 T arrayData[4U];
227 auto defaultValue = CutValue<T>(0U, TYPE);
228 for (auto &data : arrayData) {
229 data = defaultValue;
230 }
231 auto param1 = GetExecModule().CreateArray(arrayData, 4U, GetObjectAllocator());
232 auto param2 = CutValue<int32_t>(2U, DataType::INT32);
233 auto param3 = CutValue<T>(10U, TYPE);
234 GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
235 GetExecModule().SetParameter(1U, param2);
236 GetExecModule().SetParameter(2U, param3);
237
238 GetExecModule().Execute();
239
240 GetExecModule().CopyArray(param1, arrayData);
241
242 for (size_t i = 0; i < 4U; i++) {
243 if (i == 2U) {
244 EXPECT_EQ(arrayData[i], param3);
245 } else {
246 EXPECT_EQ(arrayData[i], defaultValue);
247 }
248 }
249 GetExecModule().FreeArray(param1);
250 }
251
252 template <typename T>
CheckLoadArray()253 void CodegenTest::CheckLoadArray()
254 {
255 constexpr DataType::Type TYPE = VixlExecModule::GetType<T>();
256
257 // Create graph
258 auto graph = CreateEmptyGraph();
259 RuntimeInterfaceMock runtime;
260 graph->SetRuntime(&runtime);
261
262 auto entry = graph->CreateStartBlock();
263 auto exit = graph->CreateEndBlock();
264 auto block = graph->CreateEmptyBlock();
265 entry->AddSucc(block);
266 block->AddSucc(exit);
267
268 auto array = graph->AddNewParameter(0U, DataType::REFERENCE);
269 auto index = graph->AddNewParameter(1U, DataType::INT32);
270
271 graph->ResetParameterInfo();
272 array->SetLocationData(graph->GetDataForNativeParam(DataType::REFERENCE));
273 index->SetLocationData(graph->GetDataForNativeParam(DataType::INT32));
274
275 auto ldArr = graph->CreateInst(Opcode::LoadArray);
276 block->AppendInst(ldArr);
277 ldArr->SetType(TYPE);
278 ldArr->SetInput(0U, array);
279 ldArr->SetInput(1U, index);
280 auto ret = graph->CreateInst(Opcode::Return);
281 ret->SetType(TYPE);
282 ret->SetInput(0U, ldArr);
283 block->AppendInst(ret);
284
285 SetNumVirtRegs(0U);
286 SetNumArgs(2U);
287
288 RegAlloc(graph);
289
290 EXPECT_TRUE(RunCodegen(graph));
291 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
292 auto codeExit = codeEntry + graph->GetCode().Size();
293 ASSERT(codeEntry != nullptr && codeExit != nullptr);
294 GetExecModule().SetInstructions(codeEntry, codeExit);
295
296 GetExecModule().SetDump(false);
297
298 T arrayData[4U];
299 for (size_t i = 0; i < 4U; i++) {
300 arrayData[i] = CutValue<T>((-i), TYPE);
301 }
302 auto param1 = GetExecModule().CreateArray(arrayData, 4U, GetObjectAllocator());
303 auto param2 = CutValue<int32_t>(2U, DataType::INT32);
304 GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
305 GetExecModule().SetParameter(1U, param2);
306
307 GetExecModule().Execute();
308
309 GetExecModule().CopyArray(param1, arrayData);
310
311 GetExecModule().FreeArray(param1);
312
313 auto retData = GetExecModule().GetRetValue<T>();
314 EXPECT_EQ(retData, CutValue<T>(-2L, TYPE));
315 }
316
317 template <typename T>
CheckStoreArrayPair(bool imm)318 void CodegenTest::CheckStoreArrayPair(bool imm)
319 {
320 constexpr DataType::Type TYPE = VixlExecModule::GetType<T>();
321
322 // Create graph
323 auto graph = CreateEmptyGraph();
324 RuntimeInterfaceMock runtime;
325 graph->SetRuntime(&runtime);
326 #ifndef NDEBUG
327 // GraphChecker hack: LowLevel instructions may appear only after Lowering pass:
328 graph->SetLowLevelInstructionsEnabled();
329 #endif
330
331 auto entry = graph->CreateStartBlock();
332 auto exit = graph->CreateEndBlock();
333 auto block = graph->CreateEmptyBlock();
334 entry->AddSucc(block);
335 block->AddSucc(exit);
336
337 auto array = graph->AddNewParameter(0U, DataType::REFERENCE);
338 [[maybe_unused]] auto index = graph->AddNewParameter(1U, DataType::INT32);
339 auto val0 = graph->AddNewParameter(2U, TYPE);
340 auto val1 = graph->AddNewParameter(3U, TYPE);
341
342 graph->ResetParameterInfo();
343 array->SetLocationData(graph->GetDataForNativeParam(DataType::REFERENCE));
344 index->SetLocationData(graph->GetDataForNativeParam(DataType::INT32));
345 val0->SetLocationData(graph->GetDataForNativeParam(TYPE));
346 val1->SetLocationData(graph->GetDataForNativeParam(TYPE));
347
348 Inst *stpArr = nullptr;
349 if (imm) {
350 stpArr = graph->CreateInstStoreArrayPairI(TYPE, INVALID_PC, array, val0, val1, 2U);
351 block->AppendInst(stpArr);
352 } else {
353 stpArr = graph->CreateInstStoreArrayPair(TYPE, INVALID_PC, array, index, val0, val1);
354 block->AppendInst(stpArr);
355 }
356
357 auto ret = graph->CreateInst(Opcode::ReturnVoid);
358 block->AppendInst(ret);
359
360 GraphChecker(graph).Check();
361
362 SetNumVirtRegs(0U);
363 SetNumArgs(4U);
364
365 RegAlloc(graph);
366
367 EXPECT_TRUE(RunCodegen(graph));
368 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
369 auto codeExit = codeEntry + graph->GetCode().Size();
370 ASSERT(codeEntry != nullptr && codeExit != nullptr);
371 GetExecModule().SetInstructions(codeEntry, codeExit);
372
373 GetExecModule().SetDump(false);
374
375 T arrayData[6U] = {0U, 0U, 0U, 0U, 0U, 0U};
376 auto param1 = GetExecModule().CreateArray(arrayData, 6U, GetObjectAllocator());
377 auto param2 = CutValue<int32_t>(2U, DataType::INT32);
378 auto param3 = CutValue<T>(3U, TYPE);
379 auto param4 = CutValue<T>(5U, TYPE);
380 GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
381 GetExecModule().SetParameter(1U, param2);
382 GetExecModule().SetParameter(2U, param3);
383 GetExecModule().SetParameter(3U, param4);
384
385 GetExecModule().Execute();
386 GetExecModule().CopyArray(param1, arrayData);
387 GetExecModule().FreeArray(param1);
388
389 T arrayExpected[6U] = {0U, 0U, 3U, 5U, 0U, 0U};
390
391 for (size_t i = 0; i < 6U; ++i) {
392 EXPECT_EQ(arrayData[i], arrayExpected[i]);
393 }
394 }
395
396 template <typename T>
CheckLoadArrayPair(bool imm)397 void CodegenTest::CheckLoadArrayPair(bool imm)
398 {
399 constexpr DataType::Type TYPE = VixlExecModule::GetType<T>();
400
401 // Create graph
402 auto graph = CreateEmptyGraph();
403 RuntimeInterfaceMock runtime;
404 graph->SetRuntime(&runtime);
405 #ifndef NDEBUG
406 // GraphChecker hack: LowLevel instructions may appear only after Lowering pass:
407 graph->SetLowLevelInstructionsEnabled();
408 #endif
409
410 auto entry = graph->CreateStartBlock();
411 auto exit = graph->CreateEndBlock();
412 auto block = graph->CreateEmptyBlock();
413 entry->AddSucc(block);
414 block->AddSucc(exit);
415
416 auto array = graph->AddNewParameter(0U, DataType::REFERENCE);
417 [[maybe_unused]] auto index = graph->AddNewParameter(1U, DataType::INT32);
418
419 graph->ResetParameterInfo();
420 array->SetLocationData(graph->GetDataForNativeParam(DataType::REFERENCE));
421 index->SetLocationData(graph->GetDataForNativeParam(DataType::INT32));
422
423 Inst *ldpArr = nullptr;
424 if (imm) {
425 ldpArr = graph->CreateInstLoadArrayPairI(TYPE, INVALID_PC, array, 2U);
426 block->AppendInst(ldpArr);
427 } else {
428 ldpArr = graph->CreateInstLoadArrayPair(TYPE, INVALID_PC, array, index);
429 block->AppendInst(ldpArr);
430 }
431
432 auto loadHigh = graph->CreateInstLoadPairPart(TYPE, INVALID_PC, ldpArr, 0U);
433 block->AppendInst(loadHigh);
434
435 auto loadLow = graph->CreateInstLoadPairPart(TYPE, INVALID_PC, ldpArr, 1U);
436 block->AppendInst(loadLow);
437
438 auto sum = graph->CreateInst(Opcode::Add);
439 block->AppendInst(sum);
440 sum->SetType(TYPE);
441 sum->SetInput(0U, loadHigh);
442 sum->SetInput(1U, loadLow);
443
444 auto ret = graph->CreateInst(Opcode::Return);
445 ret->SetType(TYPE);
446 ret->SetInput(0U, sum);
447 block->AppendInst(ret);
448
449 GraphChecker(graph).Check();
450
451 SetNumVirtRegs(0U);
452 SetNumArgs(2U);
453
454 RegAlloc(graph);
455
456 EXPECT_TRUE(RunCodegen(graph));
457 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
458 auto codeExit = codeEntry + graph->GetCode().Size();
459 ASSERT(codeEntry != nullptr && codeExit != nullptr);
460 GetExecModule().SetInstructions(codeEntry, codeExit);
461
462 GetExecModule().SetDump(false);
463
464 T arrayData[6U];
465 // [ 1, 2, 3, 4, 5, 6] -> 7
466 for (size_t i = 0; i < 6U; i++) {
467 arrayData[i] = CutValue<T>(i + 1U, TYPE);
468 }
469 auto param1 = GetExecModule().CreateArray(arrayData, 6U, GetObjectAllocator());
470 auto param2 = CutValue<int32_t>(2U, DataType::INT32);
471 GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
472 GetExecModule().SetParameter(1U, param2);
473
474 GetExecModule().Execute();
475 GetExecModule().FreeArray(param1);
476
477 auto retData = GetExecModule().GetRetValue<T>();
478 EXPECT_EQ(retData, CutValue<T>(7U, TYPE));
479 }
480
481 template <typename T>
CheckBounds(uint64_t count)482 void CodegenTest::CheckBounds(uint64_t count)
483 {
484 constexpr DataType::Type TYPE = VixlExecModule::GetType<T>();
485 // Create graph
486 auto graph = CreateEmptyGraph();
487 RuntimeInterfaceMock runtime;
488 graph->SetRuntime(&runtime);
489
490 auto entry = graph->CreateStartBlock();
491 auto exit = graph->CreateEndBlock();
492 auto block = graph->CreateEmptyBlock();
493 entry->AddSucc(block);
494 block->AddSucc(exit);
495
496 auto param = graph->AddNewParameter(0U, TYPE);
497
498 graph->ResetParameterInfo();
499 param->SetLocationData(graph->GetDataForNativeParam(TYPE));
500
501 BinaryImmOperation *lastInst = nullptr;
502 // instruction_count + parameter + return
503 for (uint64_t i = count - 1U; i > 1U; --i) {
504 auto addInst = graph->CreateInstAddI(TYPE, 0U, 1U);
505 block->AppendInst(addInst);
506 if (lastInst == nullptr) {
507 addInst->SetInput(0U, param);
508 } else {
509 addInst->SetInput(0U, lastInst);
510 }
511 lastInst = addInst;
512 }
513 auto ret = graph->CreateInst(Opcode::Return);
514 ret->SetType(TYPE);
515 ret->SetInput(0U, lastInst);
516 block->AppendInst(ret);
517
518 #ifndef NDEBUG
519 // GraphChecker hack: LowLevel instructions may appear only after Lowering pass:
520 graph->SetLowLevelInstructionsEnabled();
521 #endif
522 GraphChecker(graph).Check();
523
524 SetNumVirtRegs(0U);
525 SetNumArgs(2U);
526
527 RegAlloc(graph);
528
529 auto instsPerByte = GetGraph()->GetEncoder()->MaxArchInstPerEncoded();
530 auto maxBitsInInst = GetInstructionSizeBits(GetGraph()->GetArch());
531 if (count * instsPerByte * maxBitsInInst > g_options.GetCompilerMaxGenCodeSize()) {
532 EXPECT_FALSE(RunCodegen(graph));
533 } else {
534 ASSERT_TRUE(RunCodegen(graph));
535 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
536 auto codeExit = codeEntry + graph->GetCode().Size();
537 ASSERT(codeEntry != nullptr && codeExit != nullptr);
538 GetExecModule().SetInstructions(codeEntry, codeExit);
539
540 GetExecModule().SetDump(false);
541
542 T param = 0;
543 GetExecModule().SetParameter(0U, param);
544 GetExecModule().Execute();
545
546 auto retData = GetExecModule().GetRetValue<T>();
547 EXPECT_EQ(retData, CutValue<T>(count - 2L, TYPE));
548 }
549 }
550
551 template <typename T>
CheckCmp(bool isFcmpg)552 void CodegenTest::CheckCmp(bool isFcmpg)
553 {
554 constexpr DataType::Type TYPE = VixlExecModule::GetType<T>();
555 bool isFloat = DataType::IsFloatType(TYPE);
556
557 // Create graph
558 auto graph = CreateEmptyGraph();
559 RuntimeInterfaceMock runtime;
560 graph->SetRuntime(&runtime);
561
562 auto entry = graph->CreateStartBlock();
563 auto exit = graph->CreateEndBlock();
564 auto block = graph->CreateEmptyBlock();
565 entry->AddSucc(block);
566 block->AddSucc(exit);
567
568 auto param1 = graph->AddNewParameter(0U, TYPE);
569 auto param2 = graph->AddNewParameter(1U, TYPE);
570
571 graph->ResetParameterInfo();
572 param1->SetLocationData(graph->GetDataForNativeParam(TYPE));
573 param2->SetLocationData(graph->GetDataForNativeParam(TYPE));
574
575 auto fcmp = graph->CreateInst(Opcode::Cmp);
576 block->AppendInst(fcmp);
577 fcmp->SetType(DataType::INT32);
578 fcmp->SetInput(0U, param1);
579 fcmp->SetInput(1U, param2);
580 static_cast<CmpInst *>(fcmp)->SetOperandsType(TYPE);
581 if (isFloat) {
582 static_cast<CmpInst *>(fcmp)->SetFcmpg(isFcmpg);
583 }
584 auto ret = graph->CreateInst(Opcode::Return);
585 ret->SetType(DataType::INT32);
586 ret->SetInput(0U, fcmp);
587 block->AppendInst(ret);
588
589 SetNumVirtRegs(0U);
590 SetNumArgs(2U);
591
592 RegAlloc(graph);
593
594 EXPECT_TRUE(RunCodegen(graph));
595 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
596 auto codeExit = codeEntry + graph->GetCode().Size();
597 ASSERT(codeEntry != nullptr && codeExit != nullptr);
598 GetExecModule().SetInstructions(codeEntry, codeExit);
599
600 GetExecModule().SetDump(false);
601 T paramData[3U];
602 if (TYPE == DataType::FLOAT32) {
603 paramData[0U] = std::nanf("0");
604 } else if (TYPE == DataType::FLOAT64) {
605 paramData[0U] = std::nan("0");
606 } else {
607 paramData[0U] = std::numeric_limits<T>::max();
608 paramData[2U] = std::numeric_limits<T>::min();
609 }
610 paramData[1U] = CutValue<T>(2U, TYPE);
611 if (isFloat) {
612 paramData[2U] = -paramData[1U];
613 }
614
615 for (size_t i = 0; i < 3U; i++) {
616 for (size_t j = 0; j < 3U; j++) {
617 auto param1 = paramData[i];
618 auto param2 = paramData[j];
619 GetExecModule().SetParameter(0U, param1);
620 GetExecModule().SetParameter(1U, param2);
621
622 GetExecModule().Execute();
623
624 auto retData = GetExecModule().GetRetValue<int32_t>();
625 if ((i == 0U || j == 0U) && isFloat) {
626 EXPECT_EQ(retData, isFcmpg ? 1U : -1L);
627 } else if (i == j) {
628 EXPECT_EQ(retData, 0U);
629 } else if (i > j) {
630 EXPECT_EQ(retData, -1L);
631 } else {
632 EXPECT_EQ(retData, 1U);
633 }
634 }
635 }
636 }
637
638 template <typename T>
CheckReturnValue(Graph * graph,T expectedValue)639 void CodegenTest::CheckReturnValue(Graph *graph, [[maybe_unused]] T expectedValue)
640 {
641 SetNumVirtRegs(0U);
642 RegAlloc(graph);
643 EXPECT_TRUE(RunCodegen(graph));
644
645 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
646 auto codeExit = codeEntry + graph->GetCode().Size();
647
648 ASSERT(codeEntry != nullptr && codeExit != nullptr);
649
650 GetExecModule().SetInstructions(codeEntry, codeExit);
651 GetExecModule().SetDump(false);
652
653 GetExecModule().Execute();
654 auto rv = GetExecModule().GetRetValue<T>();
655 EXPECT_EQ(rv, expectedValue);
656 }
657
TestBinaryOperationWithShiftedOperand(Opcode opcode,uint32_t l,uint32_t r,ShiftType shiftType,uint32_t shift,uint32_t erv)658 void CodegenTest::TestBinaryOperationWithShiftedOperand(Opcode opcode, uint32_t l, uint32_t r, ShiftType shiftType,
659 uint32_t shift, uint32_t erv)
660 {
661 GRAPH(GetGraph())
662 {
663 CONSTANT(0U, l);
664 CONSTANT(1U, r);
665
666 BASIC_BLOCK(2U, -1L)
667 {
668 INST(2U, opcode).Shift(shiftType, shift).u32().Inputs(0U, 1U);
669 INST(3U, Opcode::Return).u32().Inputs(2U);
670 }
671 }
672
673 CheckReturnValue(GetGraph(), erv);
674 }
675
CreateGraphForOverflowTest(Graph * graph,Opcode overflowOpcode)676 void CodegenTest::CreateGraphForOverflowTest(Graph *graph, Opcode overflowOpcode)
677 {
678 GRAPH(graph)
679 {
680 PARAMETER(0U, 0U).i32();
681 PARAMETER(1U, 1U).i32();
682 CONSTANT(2U, 0U);
683 BASIC_BLOCK(2U, 3U, 4U)
684 {
685 INST(4U, overflowOpcode).i32().SrcType(DataType::INT32).CC(CC_EQ).Inputs(0U, 1U);
686 }
687 BASIC_BLOCK(3U, -1L)
688 {
689 INST(5U, Opcode::Return).b().Inputs(2U);
690 }
691 BASIC_BLOCK(4U, -1L)
692 {
693 INST(6U, Opcode::Return).b().Inputs(4U);
694 }
695 }
696 }
697
TEST_F(CodegenTest,Cmp)698 TEST_F(CodegenTest, Cmp)
699 {
700 CheckCmp<float>(true);
701 CheckCmp<float>(false);
702 CheckCmp<double>(true);
703 CheckCmp<double>(false);
704 CheckCmp<uint8_t>();
705 CheckCmp<int8_t>();
706 CheckCmp<uint16_t>();
707 CheckCmp<int16_t>();
708 CheckCmp<uint32_t>();
709 CheckCmp<int32_t>();
710 CheckCmp<uint64_t>();
711 CheckCmp<int64_t>();
712 }
713
TEST_F(CodegenTest,StoreArray)714 TEST_F(CodegenTest, StoreArray)
715 {
716 CheckStoreArray<bool>();
717 CheckStoreArray<int8_t>();
718 CheckStoreArray<uint8_t>();
719 CheckStoreArray<int16_t>();
720 CheckStoreArray<uint16_t>();
721 CheckStoreArray<int32_t>();
722 CheckStoreArray<uint32_t>();
723 CheckStoreArray<int64_t>();
724 CheckStoreArray<uint64_t>();
725 CheckStoreArray<float>();
726 CheckStoreArray<double>();
727
728 GRAPH(GetGraph())
729 {
730 PARAMETER(0U, 0U).ref(); // array
731 PARAMETER(1U, 1U).u32(); // index
732 PARAMETER(2U, 2U).u32(); // store value
733 BASIC_BLOCK(2U, -1L)
734 {
735 INST(3U, Opcode::StoreArray).u32().Inputs(0U, 1U, 2U);
736 INST(4U, Opcode::ReturnVoid);
737 }
738 }
739 auto graph = GetGraph();
740 SetNumVirtRegs(0U);
741 SetNumArgs(3U);
742
743 RegAlloc(graph);
744
745 EXPECT_TRUE(RunCodegen(graph));
746 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
747 auto codeExit = codeEntry + graph->GetCode().Size();
748 ASSERT(codeEntry != nullptr && codeExit != nullptr);
749 GetExecModule().SetInstructions(codeEntry, codeExit);
750
751 GetExecModule().SetDump(false);
752
753 ObjectPointerType array[4U] = {0U, 0U, 0U, 0U};
754 auto param1 = GetExecModule().CreateArray(array, 4U, GetObjectAllocator());
755 auto param2 = CutValue<int32_t>(2U, DataType::INT32);
756 auto param3 = CutValue<ObjectPointerType>(10U, DataType::UINT64);
757 GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
758 GetExecModule().SetParameter(1U, param2);
759 GetExecModule().SetParameter(2U, param3);
760
761 GetExecModule().Execute();
762
763 GetExecModule().CopyArray(param1, array);
764
765 for (size_t i = 0; i < 4U; i++) {
766 if (i == 2U) {
767 EXPECT_EQ(array[i], 10U) << "value of i: " << i;
768 } else {
769 EXPECT_EQ(array[i], 0U) << "value of i: " << i;
770 }
771 }
772 GetExecModule().FreeArray(param1);
773 }
774
TEST_F(CodegenTest,StoreArrayPair)775 TEST_F(CodegenTest, StoreArrayPair)
776 {
777 CheckStoreArrayPair<uint32_t>(true);
778 CheckStoreArrayPair<int32_t>(false);
779 CheckStoreArrayPair<uint64_t>(true);
780 CheckStoreArrayPair<int64_t>(false);
781 CheckStoreArrayPair<float>(true);
782 CheckStoreArrayPair<float>(false);
783 CheckStoreArrayPair<double>(true);
784 CheckStoreArrayPair<double>(false);
785 }
786
TEST_F(CodegenTest,Compare)787 TEST_F(CodegenTest, Compare)
788 {
789 for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
790 auto cc = static_cast<ConditionCode>(ccint);
791 for (auto inverse : {true, false}) {
792 auto graph = CreateGraphStartEndBlocks();
793 RuntimeInterfaceMock runtime;
794 graph->SetRuntime(&runtime);
795
796 GRAPH(graph)
797 {
798 PARAMETER(0U, 0U).u64();
799 PARAMETER(1U, 1U).u64();
800 CONSTANT(2U, 0U);
801 CONSTANT(3U, 1U);
802 BASIC_BLOCK(2U, 3U, 4U)
803 {
804 INST(4U, Opcode::Compare).b().CC(cc).Inputs(0U, 1U);
805 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(inverse ? CC_EQ : CC_NE).Imm(0U).Inputs(4U);
806 }
807 BASIC_BLOCK(3U, -1L)
808 {
809 INST(6U, Opcode::Return).b().Inputs(3U);
810 }
811 BASIC_BLOCK(4U, -1L)
812 {
813 INST(7U, Opcode::Return).b().Inputs(2U);
814 }
815 }
816 SetNumVirtRegs(0U);
817 SetNumArgs(2U);
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 bool result;
830 auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
831 auto param2 = CutValue<uint64_t>(-1L, DataType::UINT64);
832
833 GetExecModule().SetParameter(0U, param1);
834 GetExecModule().SetParameter(1U, param2);
835
836 result = (cc == CC_NE || cc == CC_GT || cc == CC_GE || cc == CC_B || cc == CC_BE);
837 if (inverse) {
838 result = !result;
839 }
840
841 GetExecModule().Execute();
842
843 auto retData = GetExecModule().GetRetValue();
844 EXPECT_EQ(retData, result);
845
846 GetExecModule().SetParameter(0U, param2);
847 GetExecModule().SetParameter(1U, param1);
848
849 GetExecModule().Execute();
850
851 result = (cc == CC_NE || cc == CC_LT || cc == CC_LE || cc == CC_A || cc == CC_AE);
852 if (inverse) {
853 result = !result;
854 }
855
856 retData = GetExecModule().GetRetValue();
857 EXPECT_EQ(retData, result);
858
859 GetExecModule().SetParameter(0U, param1);
860 GetExecModule().SetParameter(1U, param1);
861
862 result = (cc == CC_EQ || cc == CC_LE || cc == CC_GE || cc == CC_AE || cc == CC_BE);
863 if (inverse) {
864 result = !result;
865 }
866
867 GetExecModule().Execute();
868
869 retData = GetExecModule().GetRetValue();
870 EXPECT_EQ(retData, result);
871 }
872 }
873 }
874
TEST_F(CodegenTest,GenIf)875 TEST_F(CodegenTest, GenIf)
876 {
877 for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
878 auto cc = static_cast<ConditionCode>(ccint);
879 auto graph = CreateGraphStartEndBlocks();
880 RuntimeInterfaceMock runtime;
881 graph->SetRuntime(&runtime);
882
883 GRAPH(graph)
884 {
885 PARAMETER(0U, 0U).u64();
886 PARAMETER(1U, 1U).u64();
887 CONSTANT(2U, 0U);
888 CONSTANT(3U, 1U);
889 BASIC_BLOCK(2U, 3U, 4U)
890 {
891 INST(4U, Opcode::Compare).b().CC(cc).Inputs(0U, 1U);
892 INST(5U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(4U);
893 }
894 BASIC_BLOCK(3U, -1L)
895 {
896 INST(6U, Opcode::Return).b().Inputs(3U);
897 }
898 BASIC_BLOCK(4U, -1L)
899 {
900 INST(7U, Opcode::Return).b().Inputs(2U);
901 }
902 }
903 SetNumVirtRegs(0U);
904 SetNumArgs(2U);
905 #ifndef NDEBUG
906 graph->SetLowLevelInstructionsEnabled();
907 #endif
908 EXPECT_TRUE(graph->RunPass<Lowering>());
909 ASSERT_EQ(INS(0U).GetUsers().Front().GetInst()->GetOpcode(), Opcode::If);
910
911 RegAlloc(graph);
912
913 EXPECT_TRUE(RunCodegen(graph));
914 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
915 auto codeExit = codeEntry + graph->GetCode().Size();
916 ASSERT(codeEntry != nullptr && codeExit != nullptr);
917 GetExecModule().SetInstructions(codeEntry, codeExit);
918
919 GetExecModule().SetDump(false);
920
921 bool result;
922 auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
923 auto param2 = CutValue<uint64_t>(-1L, DataType::UINT64);
924
925 GetExecModule().SetParameter(0U, param1);
926 GetExecModule().SetParameter(1U, param2);
927 result = Compare(cc, param1, param2);
928 GetExecModule().Execute();
929 auto retData = GetExecModule().GetRetValue();
930 EXPECT_EQ(retData, result);
931
932 GetExecModule().SetParameter(0U, param2);
933 GetExecModule().SetParameter(1U, param1);
934 GetExecModule().Execute();
935 result = Compare(cc, param2, param1);
936 retData = GetExecModule().GetRetValue();
937 EXPECT_EQ(retData, result);
938
939 GetExecModule().SetParameter(0U, param1);
940 GetExecModule().SetParameter(1U, param1);
941 result = Compare(cc, param1, param1);
942 GetExecModule().Execute();
943 retData = GetExecModule().GetRetValue();
944 EXPECT_EQ(retData, result);
945 }
946 }
947
TEST_F(CodegenTest,GenIfImm)948 TEST_F(CodegenTest, GenIfImm)
949 {
950 int32_t values[3U] = {-1L, 0U, 1U};
951 for (auto value : values) {
952 for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
953 auto cc = static_cast<ConditionCode>(ccint);
954 auto graph = CreateGraphStartEndBlocks();
955 RuntimeInterfaceMock runtime;
956 graph->SetRuntime(&runtime);
957 if ((cc == CC_TST_EQ || cc == CC_TST_NE) && !graph->GetEncoder()->CanEncodeImmLogical(value, WORD_SIZE)) {
958 continue;
959 }
960
961 GRAPH(graph)
962 {
963 PARAMETER(0U, 0U).s32();
964 CONSTANT(1U, 0U);
965 CONSTANT(2U, 1U);
966 BASIC_BLOCK(2U, 3U, 4U)
967 {
968 INST(3U, Opcode::IfImm).SrcType(DataType::INT32).CC(cc).Imm(value).Inputs(0U);
969 }
970 BASIC_BLOCK(3U, -1L)
971 {
972 INST(4U, Opcode::Return).b().Inputs(2U);
973 }
974 BASIC_BLOCK(4U, -1L)
975 {
976 INST(5U, Opcode::Return).b().Inputs(1U);
977 }
978 }
979 SetNumVirtRegs(0U);
980 SetNumArgs(2U);
981
982 RegAlloc(graph);
983
984 EXPECT_TRUE(RunCodegen(graph));
985 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
986 auto codeExit = codeEntry + graph->GetCode().Size();
987 ASSERT(codeEntry != nullptr && codeExit != nullptr);
988 GetExecModule().SetInstructions(codeEntry, codeExit);
989
990 GetExecModule().SetDump(false);
991
992 bool result;
993 auto param1 = CutValue<uint64_t>(value, DataType::INT32);
994 auto param2 = CutValue<uint64_t>(value + 5U, DataType::INT32);
995 auto param3 = CutValue<uint64_t>(value - 5L, DataType::INT32);
996
997 GetExecModule().SetParameter(0U, param1);
998 result = Compare(cc, param1, param1);
999 GetExecModule().Execute();
1000 auto retData = GetExecModule().GetRetValue();
1001 EXPECT_EQ(retData, result);
1002
1003 GetExecModule().SetParameter(0U, param2);
1004 GetExecModule().Execute();
1005 result = Compare(cc, param2, param1);
1006 retData = GetExecModule().GetRetValue();
1007 EXPECT_EQ(retData, result);
1008
1009 GetExecModule().SetParameter(0U, param3);
1010 result = Compare(cc, param3, param1);
1011 GetExecModule().Execute();
1012 retData = GetExecModule().GetRetValue();
1013 EXPECT_EQ(retData, result);
1014 }
1015 }
1016 }
1017
TEST_F(CodegenTest,If)1018 TEST_F(CodegenTest, If)
1019 {
1020 for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
1021 auto cc = static_cast<ConditionCode>(ccint);
1022 auto graph = CreateGraphStartEndBlocks();
1023 RuntimeInterfaceMock runtime;
1024 graph->SetRuntime(&runtime);
1025
1026 GRAPH(graph)
1027 {
1028 PARAMETER(0U, 0U).u64();
1029 PARAMETER(1U, 1U).u64();
1030 CONSTANT(2U, 0U);
1031 CONSTANT(3U, 1U);
1032 BASIC_BLOCK(2U, 3U, 4U)
1033 {
1034 INST(4U, Opcode::If).SrcType(DataType::UINT64).CC(cc).Inputs(0U, 1U);
1035 }
1036 BASIC_BLOCK(3U, -1L)
1037 {
1038 INST(5U, Opcode::Return).b().Inputs(3U);
1039 }
1040 BASIC_BLOCK(4U, -1L)
1041 {
1042 INST(6U, Opcode::Return).b().Inputs(2U);
1043 }
1044 }
1045 SetNumVirtRegs(0U);
1046 SetNumArgs(2U);
1047
1048 RegAlloc(graph);
1049
1050 EXPECT_TRUE(RunCodegen(graph));
1051 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1052 auto codeExit = codeEntry + graph->GetCode().Size();
1053 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1054 GetExecModule().SetInstructions(codeEntry, codeExit);
1055
1056 GetExecModule().SetDump(false);
1057
1058 bool result;
1059 auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
1060 auto param2 = CutValue<uint64_t>(-1L, DataType::UINT64);
1061
1062 GetExecModule().SetParameter(0U, param1);
1063 GetExecModule().SetParameter(1U, param2);
1064 result = Compare(cc, param1, param2);
1065 GetExecModule().Execute();
1066 auto retData = GetExecModule().GetRetValue();
1067 EXPECT_EQ(retData, result);
1068
1069 GetExecModule().SetParameter(0U, param2);
1070 GetExecModule().SetParameter(1U, param1);
1071 GetExecModule().Execute();
1072 result = Compare(cc, param2, param1);
1073 retData = GetExecModule().GetRetValue();
1074 EXPECT_EQ(retData, result);
1075
1076 GetExecModule().SetParameter(0U, param1);
1077 GetExecModule().SetParameter(1U, param1);
1078 result = Compare(cc, param1, param1);
1079 GetExecModule().Execute();
1080 retData = GetExecModule().GetRetValue();
1081 EXPECT_EQ(retData, result);
1082 }
1083 }
1084
TEST_F(CodegenTest,AddOverflow)1085 TEST_F(CodegenTest, AddOverflow)
1086 {
1087 auto graph = CreateGraphStartEndBlocks();
1088 RuntimeInterfaceMock runtime;
1089 graph->SetRuntime(&runtime);
1090
1091 CreateGraphForOverflowTest(graph, Opcode::AddOverflow);
1092
1093 SetNumVirtRegs(0U);
1094 SetNumArgs(2U);
1095
1096 InPlaceCompilerTaskRunner taskRunner;
1097 taskRunner.GetContext().SetGraph(graph);
1098 bool success = true;
1099 taskRunner.AddCallbackOnFail([&success]([[maybe_unused]] InPlaceCompilerContext &compilerCtx) { success = false; });
1100 RunOptimizations<INPLACE_MODE>(std::move(taskRunner));
1101 EXPECT_TRUE(success);
1102
1103 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1104 auto codeExit = codeEntry + graph->GetCode().Size();
1105 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1106 GetExecModule().SetInstructions(codeEntry, codeExit);
1107
1108 GetExecModule().SetDump(false);
1109
1110 int32_t min = std::numeric_limits<int32_t>::min();
1111 int32_t max = std::numeric_limits<int32_t>::max();
1112 std::array<int32_t, 7U> values = {0U, 2U, 5U, -7L, -10L, max, min};
1113 for (uint32_t i = 0; i < values.size(); ++i) {
1114 for (uint32_t j = 0; j < values.size(); ++j) {
1115 int32_t a0 = values[i];
1116 int32_t a1 = values[j];
1117 int32_t result;
1118 auto param1 = CutValue<int32_t>(a0, DataType::INT32);
1119 auto param2 = CutValue<int32_t>(a1, DataType::INT32);
1120
1121 if ((a0 > 0 && a1 > max - a0) || (a0 < 0 && a1 < min - a0)) {
1122 result = 0;
1123 } else {
1124 result = a0 + a1;
1125 }
1126 GetExecModule().SetParameter(0U, param1);
1127 GetExecModule().SetParameter(1U, param2);
1128 GetExecModule().Execute();
1129
1130 auto retData = GetExecModule().GetRetValue<int32_t>();
1131 EXPECT_EQ(retData, result);
1132 }
1133 }
1134 }
1135
TEST_F(CodegenTest,SubOverflow)1136 TEST_F(CodegenTest, SubOverflow)
1137 {
1138 auto graph = CreateGraphStartEndBlocks();
1139 RuntimeInterfaceMock runtime;
1140 graph->SetRuntime(&runtime);
1141
1142 CreateGraphForOverflowTest(graph, Opcode::SubOverflow);
1143
1144 SetNumVirtRegs(0U);
1145 SetNumArgs(2U);
1146
1147 InPlaceCompilerTaskRunner taskRunner;
1148 taskRunner.GetContext().SetGraph(graph);
1149 bool success = true;
1150 taskRunner.AddCallbackOnFail([&success]([[maybe_unused]] InPlaceCompilerContext &compilerCtx) { success = false; });
1151 RunOptimizations<INPLACE_MODE>(std::move(taskRunner));
1152 EXPECT_TRUE(success);
1153
1154 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1155 auto codeExit = codeEntry + graph->GetCode().Size();
1156 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1157 GetExecModule().SetInstructions(codeEntry, codeExit);
1158
1159 GetExecModule().SetDump(false);
1160
1161 int32_t min = std::numeric_limits<int32_t>::min();
1162 int32_t max = std::numeric_limits<int32_t>::max();
1163 std::array<int32_t, 7U> values = {0U, 2U, 5U, -7L, -10L, max, min};
1164 for (uint32_t i = 0; i < values.size(); ++i) {
1165 for (uint32_t j = 0; j < values.size(); ++j) {
1166 int32_t a0 = values[i];
1167 int32_t a1 = values[j];
1168 int32_t result;
1169 auto param1 = CutValue<int32_t>(a0, DataType::INT32);
1170 auto param2 = CutValue<int32_t>(a1, DataType::INT32);
1171
1172 if ((a1 > 0 && a0 < min + a1) || (a1 < 0 && a0 > max + a1)) {
1173 result = 0;
1174 } else {
1175 result = a0 - a1;
1176 }
1177 GetExecModule().SetParameter(0U, param1);
1178 GetExecModule().SetParameter(1U, param2);
1179 GetExecModule().Execute();
1180
1181 auto retData = GetExecModule().GetRetValue<int32_t>();
1182 EXPECT_EQ(retData, result);
1183 }
1184 }
1185 }
1186
TEST_F(CodegenTest,GenSelect)1187 TEST_F(CodegenTest, GenSelect)
1188 {
1189 for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
1190 auto cc = static_cast<ConditionCode>(ccint);
1191 auto graph = CreateGraphStartEndBlocks();
1192 RuntimeInterfaceMock runtime;
1193 graph->SetRuntime(&runtime);
1194
1195 GRAPH(graph)
1196 {
1197 PARAMETER(0U, 0U).s64();
1198 PARAMETER(1U, 1U).s64();
1199 CONSTANT(2U, 0U);
1200 CONSTANT(3U, 1U);
1201 BASIC_BLOCK(2U, 3U, 4U)
1202 {
1203 INST(4U, Opcode::If).SrcType(DataType::INT64).CC(cc).Inputs(0U, 1U);
1204 }
1205 BASIC_BLOCK(3U, 4U) {}
1206 BASIC_BLOCK(4U, -1L)
1207 {
1208 INST(5U, Opcode::Phi).b().Inputs({{3U, 3U}, {2U, 2U}});
1209 INST(6U, Opcode::Return).b().Inputs(5U);
1210 }
1211 }
1212 SetNumVirtRegs(0U);
1213 SetNumArgs(2U);
1214
1215 EXPECT_TRUE(graph->RunPass<IfConversion>());
1216 ASSERT_EQ(INS(6U).GetInput(0U).GetInst()->GetOpcode(), Opcode::Select);
1217
1218 RegAlloc(graph);
1219
1220 EXPECT_TRUE(RunCodegen(graph));
1221 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1222 auto codeExit = codeEntry + graph->GetCode().Size();
1223 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1224 GetExecModule().SetInstructions(codeEntry, codeExit);
1225
1226 GetExecModule().SetDump(false);
1227
1228 bool result;
1229 auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
1230 auto param2 = CutValue<uint64_t>(-1L, DataType::UINT64);
1231
1232 GetExecModule().SetParameter(0U, param1);
1233 GetExecModule().SetParameter(1U, param2);
1234 result = Compare(cc, param1, param2);
1235 GetExecModule().Execute();
1236 auto retData = GetExecModule().GetRetValue();
1237 EXPECT_EQ(retData, result);
1238
1239 GetExecModule().SetParameter(0U, param2);
1240 GetExecModule().SetParameter(1U, param1);
1241 GetExecModule().Execute();
1242 result = Compare(cc, param2, param1);
1243 retData = GetExecModule().GetRetValue();
1244 EXPECT_EQ(retData, result);
1245
1246 GetExecModule().SetParameter(0U, param1);
1247 GetExecModule().SetParameter(1U, param1);
1248 result = Compare(cc, param1, param1);
1249 GetExecModule().Execute();
1250 retData = GetExecModule().GetRetValue();
1251 EXPECT_EQ(retData, result);
1252 }
1253 }
1254
TEST_F(CodegenTest,BoolSelectImm)1255 TEST_F(CodegenTest, BoolSelectImm)
1256 {
1257 for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
1258 auto cc = static_cast<ConditionCode>(ccint);
1259 auto graph = CreateGraphStartEndBlocks();
1260 RuntimeInterfaceMock runtime;
1261 graph->SetRuntime(&runtime);
1262
1263 GRAPH(graph)
1264 {
1265 PARAMETER(0U, 0U).u64();
1266 PARAMETER(1U, 1U).u64();
1267 CONSTANT(2U, 0U);
1268 CONSTANT(3U, 1U);
1269 BASIC_BLOCK(2U, -1L)
1270 {
1271 INST(4U, Opcode::Compare).b().CC(cc).Inputs(0U, 1U);
1272 INST(5U, Opcode::SelectImm).b().SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(3U, 2U, 4U);
1273 INST(6U, Opcode::Return).b().Inputs(5U);
1274 }
1275 }
1276 SetNumVirtRegs(0U);
1277 SetNumArgs(2U);
1278
1279 RegAlloc(graph);
1280
1281 EXPECT_TRUE(RunCodegen(graph));
1282 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1283 auto codeExit = codeEntry + graph->GetCode().Size();
1284 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1285 GetExecModule().SetInstructions(codeEntry, codeExit);
1286 GetExecModule().SetDump(false);
1287
1288 auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
1289 auto param2 = CutValue<uint64_t>(-1L, DataType::UINT64);
1290
1291 GetExecModule().SetParameter(0U, param1);
1292 GetExecModule().SetParameter(1U, param2);
1293 bool result = Compare(cc, param1, param2);
1294 GetExecModule().Execute();
1295 auto retData = GetExecModule().GetRetValue();
1296 EXPECT_EQ(retData, result);
1297
1298 GetExecModule().SetParameter(0U, param2);
1299 GetExecModule().SetParameter(1U, param1);
1300 GetExecModule().Execute();
1301 result = Compare(cc, param2, param1);
1302 retData = GetExecModule().GetRetValue();
1303 EXPECT_EQ(retData, result);
1304
1305 GetExecModule().SetParameter(0U, param1);
1306 GetExecModule().SetParameter(1U, param1);
1307 result = Compare(cc, param1, param1);
1308 GetExecModule().Execute();
1309 retData = GetExecModule().GetRetValue();
1310 EXPECT_EQ(retData, result);
1311 }
1312 }
1313
TEST_F(CodegenTest,Select)1314 TEST_F(CodegenTest, Select)
1315 {
1316 for (int ccint = ConditionCode::CC_FIRST; ccint != ConditionCode::CC_LAST; ccint++) {
1317 auto cc = static_cast<ConditionCode>(ccint);
1318 auto graph = CreateGraphStartEndBlocks();
1319 RuntimeInterfaceMock runtime;
1320 graph->SetRuntime(&runtime);
1321
1322 GRAPH(graph)
1323 {
1324 PARAMETER(0U, 0U).u64();
1325 PARAMETER(1U, 1U).u64();
1326 CONSTANT(2U, 0U);
1327 CONSTANT(3U, 1U);
1328 BASIC_BLOCK(2U, -1L)
1329 {
1330 INST(5U, Opcode::Select).u64().SrcType(DataType::UINT64).CC(cc).Inputs(3U, 2U, 0U, 1U);
1331 INST(6U, Opcode::Return).u64().Inputs(5U);
1332 }
1333 }
1334 SetNumVirtRegs(0U);
1335 SetNumArgs(2U);
1336
1337 RegAlloc(graph);
1338
1339 EXPECT_TRUE(RunCodegen(graph));
1340 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1341 auto codeExit = codeEntry + graph->GetCode().Size();
1342 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1343 GetExecModule().SetInstructions(codeEntry, codeExit);
1344 GetExecModule().SetDump(false);
1345
1346 auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
1347 auto param2 = CutValue<uint64_t>(-1L, DataType::UINT64);
1348
1349 GetExecModule().SetParameter(0U, param1);
1350 GetExecModule().SetParameter(1U, param2);
1351 bool result = Compare(cc, param1, param2);
1352 GetExecModule().Execute();
1353 auto retData = GetExecModule().GetRetValue();
1354 EXPECT_EQ(retData, result);
1355
1356 GetExecModule().SetParameter(0U, param2);
1357 GetExecModule().SetParameter(1U, param1);
1358 GetExecModule().Execute();
1359 result = Compare(cc, param2, param1);
1360 retData = GetExecModule().GetRetValue();
1361 EXPECT_EQ(retData, result);
1362
1363 GetExecModule().SetParameter(0U, param1);
1364 GetExecModule().SetParameter(1U, param1);
1365 result = (cc == CC_EQ || cc == CC_LE || cc == CC_GE || cc == CC_AE || cc == CC_BE);
1366 GetExecModule().Execute();
1367 retData = GetExecModule().GetRetValue();
1368 EXPECT_EQ(retData, result);
1369 }
1370 }
1371
TEST_F(CodegenTest,CompareObj)1372 TEST_F(CodegenTest, CompareObj)
1373 {
1374 auto graph = CreateGraphStartEndBlocks();
1375 RuntimeInterfaceMock runtime;
1376 graph->SetRuntime(&runtime);
1377 GRAPH(graph)
1378 {
1379 PARAMETER(0U, 0U).ref();
1380 CONSTANT(1U, 0U);
1381
1382 BASIC_BLOCK(2U, -1L)
1383 {
1384 INST(2U, Opcode::Compare).b().SrcType(DataType::REFERENCE).CC(CC_NE).Inputs(0U, 1U);
1385 INST(3U, Opcode::Return).b().Inputs(2U);
1386 }
1387 }
1388 SetNumVirtRegs(0U);
1389 SetNumArgs(1U);
1390
1391 RegAlloc(graph);
1392
1393 EXPECT_TRUE(RunCodegen(graph));
1394 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1395 auto codeExit = codeEntry + graph->GetCode().Size();
1396 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1397 GetExecModule().SetInstructions(codeEntry, codeExit);
1398
1399 GetExecModule().SetDump(false);
1400
1401 auto param1 = CutValue<uint64_t>(1U, DataType::UINT64);
1402 auto param2 = CutValue<uint64_t>(0U, DataType::UINT64);
1403
1404 GetExecModule().SetParameter(0U, param1);
1405
1406 GetExecModule().Execute();
1407
1408 auto retData = GetExecModule().GetRetValue();
1409 EXPECT_EQ(retData, 1U);
1410
1411 GetExecModule().SetParameter(0U, param2);
1412
1413 GetExecModule().Execute();
1414
1415 retData = GetExecModule().GetRetValue();
1416 EXPECT_EQ(retData, 0U);
1417 }
1418
TEST_F(CodegenTest,LoadArray)1419 TEST_F(CodegenTest, LoadArray)
1420 {
1421 CheckLoadArray<bool>();
1422 CheckLoadArray<int8_t>();
1423 CheckLoadArray<uint8_t>();
1424 CheckLoadArray<int16_t>();
1425 CheckLoadArray<uint16_t>();
1426 CheckLoadArray<int32_t>();
1427 CheckLoadArray<uint32_t>();
1428 CheckLoadArray<int64_t>();
1429 CheckLoadArray<uint64_t>();
1430 CheckLoadArray<float>();
1431 CheckLoadArray<double>();
1432
1433 GRAPH(GetGraph())
1434 {
1435 PARAMETER(0U, 0U).ref(); // array
1436 PARAMETER(1U, 1U).u32(); // index
1437 BASIC_BLOCK(2U, -1L)
1438 {
1439 INST(2U, Opcode::LoadArray).u32().Inputs(0U, 1U);
1440 INST(3U, Opcode::Return).u32().Inputs(2U);
1441 }
1442 }
1443 auto graph = GetGraph();
1444 SetNumVirtRegs(0U);
1445 SetNumArgs(2U);
1446
1447 RegAlloc(graph);
1448
1449 EXPECT_TRUE(RunCodegen(graph));
1450 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1451 auto codeExit = codeEntry + graph->GetCode().Size();
1452 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1453 GetExecModule().SetInstructions(codeEntry, codeExit);
1454
1455 GetExecModule().SetDump(false);
1456
1457 ObjectPointerType array[4U] = {0xffffaaaaU, 0xffffbbbbU, 0xffffccccU, 0xffffddddU};
1458 auto param1 = GetExecModule().CreateArray(array, 4U, GetObjectAllocator());
1459 auto param2 = CutValue<int32_t>(2U, DataType::INT32);
1460 GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
1461 GetExecModule().SetParameter(1U, param2);
1462
1463 GetExecModule().Execute();
1464
1465 GetExecModule().CopyArray(param1, array);
1466
1467 GetExecModule().FreeArray(param1);
1468 auto retData = GetExecModule().GetRetValue();
1469 EXPECT_EQ(retData, array[2U]);
1470 }
1471
TEST_F(CodegenTest,LoadArrayPair)1472 TEST_F(CodegenTest, LoadArrayPair)
1473 {
1474 CheckLoadArrayPair<uint32_t>(true);
1475 CheckLoadArrayPair<int32_t>(false);
1476 CheckLoadArrayPair<uint64_t>(true);
1477 CheckLoadArrayPair<int64_t>(false);
1478 CheckLoadArrayPair<float>(true);
1479 CheckLoadArrayPair<float>(false);
1480 CheckLoadArrayPair<double>(true);
1481 CheckLoadArrayPair<double>(false);
1482 }
1483
1484 #ifndef USE_ADDRESS_SANITIZER
TEST_F(CodegenTest,CheckCodegenBounds)1485 TEST_F(CodegenTest, CheckCodegenBounds)
1486 {
1487 // Do not try to encode too large graph
1488 uint64_t instsPerByte = GetGraph()->GetEncoder()->MaxArchInstPerEncoded();
1489 uint64_t maxBitsInInst = GetInstructionSizeBits(GetGraph()->GetArch());
1490 uint64_t instCount = g_options.GetCompilerMaxGenCodeSize() / (instsPerByte * maxBitsInInst);
1491
1492 CheckBounds<uint32_t>(instCount - 1L);
1493 CheckBounds<uint32_t>(instCount + 1U);
1494
1495 CheckBounds<uint32_t>(instCount / 2U);
1496 }
1497 #endif
1498
TEST_F(CodegenTest,LenArray)1499 TEST_F(CodegenTest, LenArray)
1500 {
1501 GRAPH(GetGraph())
1502 {
1503 PARAMETER(0U, 0U).ref(); // array
1504 BASIC_BLOCK(2U, -1L)
1505 {
1506 INST(1U, Opcode::LenArray).s32().Inputs(0U);
1507 INST(2U, Opcode::Return).s32().Inputs(1U);
1508 }
1509 }
1510 auto graph = GetGraph();
1511 SetNumVirtRegs(0U);
1512 SetNumArgs(1U);
1513
1514 RegAlloc(graph);
1515
1516 EXPECT_TRUE(RunCodegen(graph));
1517 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1518 auto codeExit = codeEntry + graph->GetCode().Size();
1519 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1520 GetExecModule().SetInstructions(codeEntry, codeExit);
1521
1522 GetExecModule().SetDump(false);
1523
1524 uint64_t array[4U] = {0U, 0U, 0U, 0U};
1525 auto param1 = GetExecModule().CreateArray(array, 4U, GetObjectAllocator());
1526 GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
1527
1528 GetExecModule().Execute();
1529 GetExecModule().FreeArray(param1);
1530
1531 auto retData = GetExecModule().GetRetValue();
1532 EXPECT_EQ(retData, 4U);
1533 }
1534
TEST_F(CodegenTest,Parameter)1535 TEST_F(CodegenTest, Parameter)
1536 {
1537 GRAPH(GetGraph())
1538 {
1539 PARAMETER(0U, 0U).u64();
1540 PARAMETER(1U, 1U).s16();
1541 BASIC_BLOCK(2U, -1L)
1542 {
1543 INST(6U, Opcode::Add).u64().Inputs(0U, 0U);
1544 INST(8U, Opcode::Add).s16().Inputs(1U, 1U);
1545 INST(15U, Opcode::Return).u64().Inputs(6U);
1546 // Return parameter_0 + parameter_0
1547 }
1548 }
1549 SetNumVirtRegs(0U);
1550 SetNumArgs(2U);
1551
1552 auto graph = GetGraph();
1553
1554 RegAlloc(graph);
1555
1556 EXPECT_TRUE(RunCodegen(graph));
1557 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1558 auto codeExit = codeEntry + graph->GetCode().Size();
1559 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1560 GetExecModule().SetInstructions(codeEntry, codeExit);
1561
1562 GetExecModule().SetDump(false);
1563
1564 auto param1 = CutValue<uint64_t>(1234U, DataType::UINT64);
1565 auto param2 = CutValue<int16_t>(-1234L, DataType::INT16);
1566 GetExecModule().SetParameter(0U, param1);
1567 GetExecModule().SetParameter(1U, param2);
1568
1569 GetExecModule().Execute();
1570
1571 auto retData = GetExecModule().GetRetValue();
1572 EXPECT_EQ(retData, 1234U + 1234U);
1573
1574 // Clear data for next execution
1575 while (auto current = GetGraph()->GetFirstConstInst()) {
1576 GetGraph()->RemoveConstFromList(current);
1577 }
1578 }
1579
TEST_F(CodegenTest,RegallocTwoFreeRegs)1580 TEST_F(CodegenTest, RegallocTwoFreeRegs)
1581 {
1582 GRAPH(GetGraph())
1583 {
1584 CONSTANT(0U, 1U); // const a = 1
1585 CONSTANT(1U, 10U); // const b = 10
1586 CONSTANT(2U, 20U); // const c = 20
1587
1588 BASIC_BLOCK(2U, 3U, 4U)
1589 {
1590 INST(3U, Opcode::Phi).u64().Inputs({{0U, 0U}, {3U, 7U}}); // var a' = a
1591 INST(4U, Opcode::Phi).u64().Inputs({{0U, 1U}, {3U, 8U}}); // var b' = b
1592 INST(5U, Opcode::Compare).b().CC(CC_NE).Inputs(4U, 0U); // b' == 1 ?
1593 INST(6U, Opcode::IfImm).SrcType(DataType::BOOL).CC(CC_NE).Imm(0U).Inputs(5U); // if == (b' == 1) -> exit
1594 }
1595
1596 BASIC_BLOCK(3U, 2U)
1597 {
1598 INST(7U, Opcode::Mul).u64().Inputs(3U, 4U); // a' = a' * b'
1599 INST(8U, Opcode::Sub).u64().Inputs(4U, 0U); // b' = b' - 1
1600 }
1601
1602 BASIC_BLOCK(4U, -1L)
1603 {
1604 INST(10U, Opcode::Add).u64().Inputs(2U, 3U); // a' = c + a'
1605 INST(11U, Opcode::Return).u64().Inputs(10U); // return a'
1606 }
1607 }
1608 // Create reg_mask with 5 available general registers,
1609 // 3 of them will be reserved by Reg Alloc.
1610 {
1611 RegAllocLinearScan ra(GetGraph());
1612 ra.SetRegMask(RegMask {0xFFFFF07FU});
1613 ra.SetVRegMask(VRegMask {0U});
1614 EXPECT_TRUE(ra.Run());
1615 }
1616 GraphChecker(GetGraph()).Check();
1617 EXPECT_TRUE(RunCodegen(GetGraph()));
1618 auto codeEntry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
1619 auto codeExit = codeEntry + GetGraph()->GetCode().Size();
1620 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1621 GetExecModule().SetInstructions(codeEntry, codeExit);
1622
1623 GetExecModule().SetDump(false);
1624
1625 auto param1 = CutValue<uint64_t>(0x0U, DataType::UINT64);
1626 auto param2 = CutValue<uint16_t>(0x0U, DataType::INT32);
1627
1628 GetExecModule().SetParameter(0U, param1);
1629 GetExecModule().SetParameter(1U, param2);
1630
1631 GetExecModule().Execute();
1632
1633 auto retData = GetExecModule().GetRetValue();
1634 EXPECT_TRUE(retData == 10U * 9U * 8U * 7U * 6U * 5U * 4U * 3U * 2U * 1U + 20U);
1635
1636 // Clear data for next execution
1637 while (auto current = GetGraph()->GetFirstConstInst()) {
1638 GetGraph()->RemoveConstFromList(current);
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 GRAPH(GetGraph())
1646 {
1647 PARAMETER(0U, 0U).u64();
1648 PARAMETER(11U, 0U).f64();
1649 PARAMETER(12U, 0U).f32();
1650 CONSTANT(1U, 12U);
1651 CONSTANT(2U, -1L);
1652 CONSTANT(3U, 100000000U);
1653
1654 BASIC_BLOCK(2U, -1L)
1655 {
1656 INST(4U, Opcode::Add).u64().Inputs(0U, 1U);
1657 INST(5U, Opcode::Add).u64().Inputs(0U, 2U);
1658 INST(6U, Opcode::Add).u64().Inputs(0U, 3U);
1659 INST(7U, Opcode::Sub).u64().Inputs(0U, 1U);
1660 INST(8U, Opcode::Sub).u64().Inputs(0U, 2U);
1661 INST(9U, Opcode::Sub).u64().Inputs(0U, 3U);
1662 INST(17U, Opcode::Add).u64().Inputs(0U, 0U);
1663 INST(18U, Opcode::Sub).u64().Inputs(0U, 0U);
1664 INST(19U, Opcode::Add).u16().Inputs(0U, 1U);
1665 INST(20U, Opcode::Add).u16().Inputs(0U, 2U);
1666 INST(10U, Opcode::SaveState)
1667 .Inputs(0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 11U, 12U, 17U, 18U, 19U, 20U)
1668 .SrcVregs({0U, 1U, 2U, 3U, 4U, 5U, 6U, 7U, 8U, 9U, 10U, 11U, 12U, 13U, 14U});
1669 }
1670 }
1671 // NO RETURN value - will droped down to SaveState block!
1672
1673 SetNumVirtRegs(0U);
1674 SetNumArgs(3U);
1675 // Create reg_mask with 5 available general registers,
1676 // 3 of them will be reserved by Reg Alloc.
1677 {
1678 RegAllocLinearScan ra(GetGraph());
1679 ra.SetRegMask(RegMask {0xFFFFF07FU});
1680 ra.SetVRegMask(VRegMask {0U});
1681 EXPECT_TRUE(ra.Run());
1682 }
1683 GraphChecker(GetGraph()).Check();
1684
1685 EXPECT_TRUE(RunCodegen(GetGraph()));
1686 auto codeEntry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
1687 auto codeExit = codeEntry + GetGraph()->GetCode().Size();
1688 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1689 GetExecModule().SetInstructions(codeEntry, codeExit);
1690
1691 GetExecModule().SetDump(false);
1692
1693 auto param1 = CutValue<uint64_t>(0x12345U, DataType::UINT64);
1694 auto param2 = CutValue<float>(0x12345U, DataType::FLOAT32);
1695
1696 GetExecModule().SetParameter(0U, param1);
1697 GetExecModule().SetParameter(1U, param2);
1698 GetExecModule().SetParameter(2U, param2);
1699
1700 GetExecModule().Execute();
1701
1702 // Main check - return value get from SaveState return DEOPTIMIZATION
1703 EXPECT_EQ(GetExecModule().GetRetValue(), 1U);
1704
1705 // Clear data for next execution
1706 while (auto current = GetGraph()->GetFirstConstInst()) {
1707 GetGraph()->RemoveConstFromList(current);
1708 }
1709 }
1710
TEST_F(CodegenTest,SaveState)1711 TEST_F(CodegenTest, SaveState)
1712 {
1713 GRAPH(GetGraph())
1714 {
1715 PARAMETER(0U, 0U).ref(); // array
1716 PARAMETER(1U, 1U).u64(); // index
1717 BASIC_BLOCK(2U, 3U)
1718 {
1719 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
1720 INST(3U, Opcode::NullCheck).ref().Inputs(0U, 2U);
1721 INST(4U, Opcode::LenArray).s32().Inputs(3U);
1722 INST(5U, Opcode::BoundsCheck).s32().Inputs(4U, 1U, 2U);
1723 INST(6U, Opcode::LoadArray).u64().Inputs(3U, 5U);
1724 INST(7U, Opcode::Add).u64().Inputs(6U, 6U);
1725 INST(8U, Opcode::StoreArray).u64().Inputs(3U, 5U, 7U);
1726 }
1727 BASIC_BLOCK(3U, -1L)
1728 {
1729 INST(10U, Opcode::Add).u64().Inputs(7U, 7U); // Some return value
1730 INST(11U, Opcode::Return).u64().Inputs(10U);
1731 }
1732 }
1733
1734 SetNumVirtRegs(0U);
1735 SetNumArgs(2U);
1736
1737 auto graph = GetGraph();
1738
1739 RegAlloc(graph);
1740
1741 // Run codegen
1742 EXPECT_TRUE(RunCodegen(graph));
1743 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
1744 auto codeExit = codeEntry + graph->GetCode().Size();
1745 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1746 GetExecModule().SetInstructions(codeEntry, codeExit);
1747
1748 // Enable dumping
1749 GetExecModule().SetDump(false);
1750
1751 uint64_t arrayData[4U];
1752 for (size_t i = 0; i < 4U; i++) {
1753 arrayData[i] = i + 0x20U;
1754 }
1755 auto param1 = GetExecModule().CreateArray(arrayData, 4U, GetObjectAllocator());
1756 auto param2 = CutValue<uint64_t>(1U, DataType::UINT64);
1757 GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
1758 GetExecModule().SetParameter(1U, param2);
1759
1760 GetExecModule().Execute();
1761 GetExecModule().SetDump(false);
1762 // End dump
1763
1764 auto retData = GetExecModule().GetRetValue();
1765 // NOTE (igorban) : really need to check array changes
1766 EXPECT_EQ(retData, 4U * 0x21);
1767
1768 // Clear data for next execution
1769 while (auto current = GetGraph()->GetFirstConstInst()) {
1770 GetGraph()->RemoveConstFromList(current);
1771 }
1772 GetExecModule().FreeArray(param1);
1773 } // namespace panda::compiler
1774
TEST_F(CodegenTest,DeoptimizeIf)1775 TEST_F(CodegenTest, DeoptimizeIf)
1776 {
1777 GRAPH(GetGraph())
1778 {
1779 PARAMETER(0U, 0U).b();
1780 BASIC_BLOCK(2U, 1U)
1781 {
1782 INST(2U, Opcode::SaveStateDeoptimize).Inputs(0U).SrcVregs({0U});
1783 INST(3U, Opcode::DeoptimizeIf).Inputs(0U, 2U);
1784 INST(4U, Opcode::Return).b().Inputs(0U);
1785 }
1786 }
1787 RegAlloc(GetGraph());
1788
1789 EXPECT_TRUE(RunCodegen(GetGraph()));
1790 auto codeEntry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
1791 auto codeExit = codeEntry + GetGraph()->GetCode().Size();
1792 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1793 GetExecModule().SetInstructions(codeEntry, codeExit);
1794
1795 // param == false [OK]
1796 auto param = false;
1797 GetExecModule().SetParameter(0U, param);
1798 GetExecModule().Execute();
1799 EXPECT_EQ(GetExecModule().GetRetValue(), param);
1800
1801 // param == true [INTERPRET]
1802 }
1803
TEST_F(CodegenTest,ZeroCheck)1804 TEST_F(CodegenTest, ZeroCheck)
1805 {
1806 GRAPH(GetGraph())
1807 {
1808 PARAMETER(0U, 0U).s64();
1809 PARAMETER(1U, 1U).s64();
1810 BASIC_BLOCK(2U, 3U)
1811 {
1812 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
1813 INST(3U, Opcode::ZeroCheck).s64().Inputs(0U, 2U);
1814 INST(4U, Opcode::Div).s64().Inputs(1U, 3U);
1815 INST(5U, Opcode::Mod).s64().Inputs(1U, 3U);
1816 }
1817 BASIC_BLOCK(3U, -1L)
1818 {
1819 INST(6U, Opcode::Add).s64().Inputs(0U, 1U); // Some return value
1820 INST(7U, Opcode::Return).s64().Inputs(6U);
1821 }
1822 }
1823 RegAlloc(GetGraph());
1824
1825 SetNumVirtRegs(GetGraph()->GetVRegsCount());
1826
1827 EXPECT_TRUE(RunCodegen(GetGraph()));
1828 auto codeEntry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
1829 auto codeExit = codeEntry + GetGraph()->GetCode().Size();
1830 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1831 GetExecModule().SetInstructions(codeEntry, codeExit);
1832
1833 // param1 < 0 [OK]
1834 auto param1 = CutValue<uint64_t>(std::numeric_limits<int64_t>::min(), DataType::INT64);
1835 auto param2 = CutValue<uint64_t>(std::numeric_limits<int64_t>::max(), DataType::INT64);
1836 GetExecModule().SetParameter(0U, param1);
1837 GetExecModule().SetParameter(1U, param2);
1838 GetExecModule().Execute();
1839 EXPECT_EQ(GetExecModule().GetRetValue(), param1 + param2);
1840
1841 // param1 > 0 [OK]
1842 param1 = CutValue<uint64_t>(std::numeric_limits<int64_t>::max(), DataType::INT64);
1843 param2 = CutValue<uint64_t>(0U, DataType::INT64);
1844 GetExecModule().SetParameter(0U, param1);
1845 GetExecModule().SetParameter(1U, param2);
1846 GetExecModule().Execute();
1847 EXPECT_EQ(GetExecModule().GetRetValue(), param1 + param2);
1848
1849 // param1 == 0 [THROW]
1850 }
1851
TEST_F(CodegenTest,NegativeCheck)1852 TEST_F(CodegenTest, NegativeCheck)
1853 {
1854 GRAPH(GetGraph())
1855 {
1856 PARAMETER(0U, 0U).s64();
1857 PARAMETER(1U, 1U).s64();
1858 BASIC_BLOCK(2U, 3U)
1859 {
1860 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
1861 INST(3U, Opcode::NegativeCheck).s64().Inputs(0U, 2U);
1862 }
1863 BASIC_BLOCK(3U, -1L)
1864 {
1865 INST(6U, Opcode::Add).s64().Inputs(0U, 1U); // Some return value
1866 INST(7U, Opcode::Return).s64().Inputs(6U);
1867 }
1868 }
1869 RegAlloc(GetGraph());
1870
1871 SetNumVirtRegs(GetGraph()->GetVRegsCount());
1872
1873 EXPECT_TRUE(RunCodegen(GetGraph()));
1874 auto codeEntry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
1875 auto codeExit = codeEntry + GetGraph()->GetCode().Size();
1876 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1877 GetExecModule().SetInstructions(codeEntry, codeExit);
1878
1879 // param1 > 0 [OK]
1880 auto param1 = CutValue<uint64_t>(std::numeric_limits<int64_t>::max(), DataType::INT64);
1881 auto param2 = CutValue<uint64_t>(std::numeric_limits<int64_t>::min(), DataType::INT64);
1882 GetExecModule().SetParameter(0U, param1);
1883 GetExecModule().SetParameter(1U, param2);
1884 GetExecModule().Execute();
1885 EXPECT_EQ(GetExecModule().GetRetValue(), param1 + param2);
1886
1887 // param1 == 0 [OK]
1888 param1 = CutValue<uint64_t>(0U, DataType::INT64);
1889 param2 = CutValue<uint64_t>(std::numeric_limits<int64_t>::max(), DataType::INT64);
1890 GetExecModule().SetParameter(0U, param1);
1891 GetExecModule().SetParameter(1U, param2);
1892 GetExecModule().Execute();
1893 EXPECT_EQ(GetExecModule().GetRetValue(), param1 + param2);
1894
1895 // param1 < 0 [THROW]
1896 }
1897
TEST_F(CodegenTest,NullCheckBoundsCheck)1898 TEST_F(CodegenTest, NullCheckBoundsCheck)
1899 {
1900 static const unsigned ARRAY_LEN = 10;
1901
1902 GRAPH(GetGraph())
1903 {
1904 PARAMETER(0U, 0U).ref(); // array
1905 PARAMETER(1U, 1U).u64(); // index
1906 BASIC_BLOCK(2U, 3U)
1907 {
1908 INST(2U, Opcode::SaveState).Inputs(0U, 1U).SrcVregs({0U, 1U});
1909 INST(3U, Opcode::NullCheck).ref().Inputs(0U, 2U);
1910 INST(4U, Opcode::LenArray).s32().Inputs(3U);
1911 INST(5U, Opcode::BoundsCheck).s32().Inputs(4U, 1U, 2U);
1912 INST(6U, Opcode::LoadArray).u64().Inputs(3U, 5U);
1913 INST(7U, Opcode::Add).u64().Inputs(6U, 6U);
1914 INST(8U, Opcode::StoreArray).u64().Inputs(3U, 5U, 7U);
1915 }
1916 BASIC_BLOCK(3U, -1L)
1917 {
1918 INST(10U, Opcode::Add).u64().Inputs(7U, 7U); // Some return value
1919 INST(11U, Opcode::Return).u64().Inputs(10U);
1920 }
1921 }
1922 SetNumVirtRegs(0U);
1923 SetNumArgs(2U);
1924 RegAlloc(GetGraph());
1925
1926 EXPECT_TRUE(RunCodegen(GetGraph()));
1927 auto codeEntry = reinterpret_cast<char *>(GetGraph()->GetCode().Data());
1928 auto codeExit = codeEntry + GetGraph()->GetCode().Size();
1929 ASSERT(codeEntry != nullptr && codeExit != nullptr);
1930 GetExecModule().SetInstructions(codeEntry, codeExit);
1931
1932 // NOTE (igorban) : fill Frame array == nullptr [THROW]
1933
1934 uint64_t array[ARRAY_LEN];
1935 for (auto i = 0U; i < ARRAY_LEN; i++) {
1936 array[i] = i + 0x20U;
1937 }
1938 auto param1 = GetExecModule().CreateArray(array, ARRAY_LEN, GetObjectAllocator());
1939 GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param1));
1940
1941 // 0 <= index < ARRAY_LEN [OK]
1942 auto index = CutValue<uint64_t>(1U, DataType::UINT64);
1943 GetExecModule().SetParameter(1U, index);
1944 GetExecModule().Execute();
1945 EXPECT_EQ(GetExecModule().GetRetValue(), array[index] * 4U);
1946
1947 /*
1948 NOTE (igorban) : fill Frame
1949 // index < 0 [THROW]
1950 */
1951 GetExecModule().FreeArray(param1);
1952 }
1953
TEST_F(CodegenTest,ResolveParamSequence)1954 TEST_F(CodegenTest, ResolveParamSequence)
1955 {
1956 ArenaVector<std::pair<uint8_t, uint8_t>> someSequence(GetAllocator()->Adapter());
1957 someSequence.emplace_back(std::pair<uint8_t, uint8_t>(0U, 3U));
1958 someSequence.emplace_back(std::pair<uint8_t, uint8_t>(1U, 0U));
1959 someSequence.emplace_back(std::pair<uint8_t, uint8_t>(2U, 3U));
1960 someSequence.emplace_back(std::pair<uint8_t, uint8_t>(3U, 2U));
1961
1962 auto result = ResoveParameterSequence(&someSequence, 13U, GetAllocator());
1963 EXPECT_TRUE(someSequence.empty());
1964 ArenaVector<std::pair<uint8_t, uint8_t>> resultSequence(GetAllocator()->Adapter());
1965 resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(1U, 0U));
1966 resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(0U, 3U));
1967 resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(13U, 2U));
1968 resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(2U, 3U));
1969 resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(3U, 13U));
1970
1971 EXPECT_EQ(result, resultSequence);
1972
1973 {
1974 // Special loop-only case
1975 ArenaVector<std::pair<uint8_t, uint8_t>> someSequence(GetAllocator()->Adapter());
1976 someSequence.emplace_back(std::pair<uint8_t, uint8_t>(2U, 3U));
1977 someSequence.emplace_back(std::pair<uint8_t, uint8_t>(1U, 2U));
1978 someSequence.emplace_back(std::pair<uint8_t, uint8_t>(4U, 1U));
1979 someSequence.emplace_back(std::pair<uint8_t, uint8_t>(0U, 4U));
1980 someSequence.emplace_back(std::pair<uint8_t, uint8_t>(3U, 0U));
1981
1982 auto result = ResoveParameterSequence(&someSequence, 13U, GetAllocator());
1983 EXPECT_TRUE(someSequence.empty());
1984 ArenaVector<std::pair<uint8_t, uint8_t>> resultSequence(GetAllocator()->Adapter());
1985 resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(13U, 2U));
1986 resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(2U, 3U));
1987 resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(3U, 0U));
1988 resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(0U, 4U));
1989 resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(4U, 1U));
1990 resultSequence.emplace_back(std::pair<uint8_t, uint8_t>(1U, 13U));
1991
1992 EXPECT_EQ(result, resultSequence);
1993 }
1994 const uint32_t regSize = 30;
1995 const uint8_t tmpReg = regSize + 5U;
1996 for (uint64_t i = 0; i < ITERATION; ++i) {
1997 EXPECT_TRUE(someSequence.empty());
1998
1999 std::vector<uint8_t> iters;
2000 for (uint8_t j = 0; j < regSize; ++j) {
2001 iters.push_back(j);
2002 }
2003 std::shuffle(iters.begin(), iters.end(), g_randomGenerator);
2004 std::vector<std::pair<uint8_t, uint8_t>> origVector;
2005 for (uint8_t j = 0; j < regSize; ++j) {
2006 auto gen {g_randomGenerator()};
2007 auto randomValue = gen % regSize;
2008 origVector.emplace_back(std::pair<uint8_t, uint8_t>(iters[j], randomValue));
2009 }
2010 for (auto &pair : origVector) {
2011 someSequence.emplace_back(pair);
2012 }
2013 resultSequence = ResoveParameterSequence(&someSequence, tmpReg, GetAllocator());
2014 std::vector<std::pair<uint8_t, uint8_t>> result;
2015 for (auto &pair : resultSequence) {
2016 result.emplace_back(pair);
2017 }
2018
2019 // First analysis - there are no dst before src
2020 for (uint8_t j = 0; j < regSize; ++j) {
2021 auto dst = result[j].first;
2022 for (uint8_t k = j + 1U; k < regSize; ++k) {
2023 if (result[k].second == dst && result[k].second != tmpReg) {
2024 std::cerr << " first = " << result[k].first << " tmp = " << (regSize + 5U) << "\n";
2025 std::cerr << " Before:\n";
2026 for (auto &it : origVector) {
2027 std::cerr << " " << (size_t)it.first << "<-" << (size_t)it.second << "\n";
2028 }
2029 std::cerr << " After:\n";
2030 for (auto &it : result) {
2031 std::cerr << " " << (size_t)it.first << "<-" << (size_t)it.second << "\n";
2032 }
2033 std::cerr << "Fault on " << (size_t)j << " and " << (size_t)k << "\n";
2034 EXPECT_NE(result[k].second, dst);
2035 }
2036 }
2037 }
2038
2039 // Second analysis - if remove all same moves - there will be
2040 // only " move tmp <- reg " & " mov reg <- tmp"
2041 }
2042 }
2043
TEST_F(CodegenTest,BoundsCheckI)2044 TEST_F(CodegenTest, BoundsCheckI)
2045 {
2046 uint64_t arrayData[4098U];
2047 for (unsigned i = 0; i < 4098U; i++) {
2048 arrayData[i] = i;
2049 }
2050
2051 for (unsigned index = 4095U; index <= 4097U; index++) {
2052 auto graph = CreateEmptyGraph();
2053 GRAPH(graph)
2054 {
2055 PARAMETER(0U, 0U).ref(); // array
2056 BASIC_BLOCK(2U, -1L)
2057 {
2058 INST(1U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2059 INST(2U, Opcode::NullCheck).ref().Inputs(0U, 1U);
2060 INST(3U, Opcode::LenArray).s32().Inputs(2U);
2061 INST(4U, Opcode::BoundsCheckI).s32().Inputs(3U, 1U).Imm(index);
2062 INST(5U, Opcode::LoadArrayI).u64().Inputs(2U).Imm(index);
2063 INST(6U, Opcode::Return).u64().Inputs(5U);
2064 }
2065 }
2066
2067 SetNumVirtRegs(0U);
2068 SetNumArgs(1U);
2069
2070 RegAlloc(graph);
2071
2072 // Run codegen
2073 EXPECT_TRUE(RunCodegen(graph));
2074 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
2075 auto codeExit = codeEntry + graph->GetCode().Size();
2076 ASSERT(codeEntry != nullptr && codeExit != nullptr);
2077 GetExecModule().SetInstructions(codeEntry, codeExit);
2078
2079 // Enable dumping
2080 GetExecModule().SetDump(false);
2081
2082 auto param = GetExecModule().CreateArray(arrayData, index + 1U, GetObjectAllocator());
2083 GetExecModule().SetParameter(0U, reinterpret_cast<uint64_t>(param));
2084
2085 GetExecModule().Execute();
2086 GetExecModule().SetDump(false);
2087 // End dump
2088
2089 auto retData = GetExecModule().GetRetValue();
2090 EXPECT_EQ(retData, index);
2091
2092 GetExecModule().FreeArray(param);
2093 }
2094 }
2095
TEST_F(CodegenTest,MultiplyAddInteger)2096 TEST_F(CodegenTest, MultiplyAddInteger)
2097 {
2098 if (GetGraph()->GetArch() != Arch::AARCH64) {
2099 GTEST_SKIP() << "multiply-add instruction is only supported on Aarch64";
2100 }
2101
2102 GRAPH(GetGraph())
2103 {
2104 CONSTANT(0U, 10U);
2105 CONSTANT(1U, 42U);
2106 CONSTANT(2U, 13U);
2107
2108 BASIC_BLOCK(2U, -1L)
2109 {
2110 INST(3U, Opcode::MAdd).s64().Inputs(0U, 1U, 2U);
2111 INST(4U, Opcode::Return).s64().Inputs(3U);
2112 }
2113 }
2114
2115 CheckReturnValue(GetGraph(), 433U);
2116 }
2117
TEST_F(CodegenTest,MultiplyAddFloat)2118 TEST_F(CodegenTest, MultiplyAddFloat)
2119 {
2120 if (GetGraph()->GetArch() != Arch::AARCH64) {
2121 GTEST_SKIP() << "multiply-add instruction is only supported on Aarch64";
2122 }
2123
2124 GRAPH(GetGraph())
2125 {
2126 CONSTANT(0U, 10.0_D);
2127 CONSTANT(1U, 42.0_D);
2128 CONSTANT(2U, 13.0_D);
2129
2130 BASIC_BLOCK(2U, -1L)
2131 {
2132 INST(3U, Opcode::MAdd).f64().Inputs(0U, 1U, 2U);
2133 INST(4U, Opcode::Return).f64().Inputs(3U);
2134 }
2135 }
2136
2137 CheckReturnValue(GetGraph(), 433.0_D);
2138 }
2139
TEST_F(CodegenTest,MultiplySubtractInteger)2140 TEST_F(CodegenTest, MultiplySubtractInteger)
2141 {
2142 if (GetGraph()->GetArch() != Arch::AARCH64) {
2143 GTEST_SKIP() << "multiply-subtract instruction is only supported on Aarch64";
2144 }
2145
2146 GRAPH(GetGraph())
2147 {
2148 CONSTANT(0U, 10U);
2149 CONSTANT(1U, 42U);
2150 CONSTANT(2U, 13U);
2151
2152 BASIC_BLOCK(2U, -1L)
2153 {
2154 INST(3U, Opcode::MSub).s64().Inputs(0U, 1U, 2U);
2155 INST(4U, Opcode::Return).s64().Inputs(3U);
2156 }
2157 }
2158
2159 CheckReturnValue(GetGraph(), -407L);
2160 }
2161
TEST_F(CodegenTest,MultiplySubtractFloat)2162 TEST_F(CodegenTest, MultiplySubtractFloat)
2163 {
2164 if (GetGraph()->GetArch() != Arch::AARCH64) {
2165 GTEST_SKIP() << "multiply-subtract instruction is only supported on Aarch64";
2166 }
2167
2168 GRAPH(GetGraph())
2169 {
2170 CONSTANT(0U, 10.0_D);
2171 CONSTANT(1U, 42.0_D);
2172 CONSTANT(2U, 13.0_D);
2173
2174 BASIC_BLOCK(2U, -1L)
2175 {
2176 INST(3U, Opcode::MSub).f64().Inputs(0U, 1U, 2U);
2177 INST(4U, Opcode::Return).f64().Inputs(3U);
2178 }
2179 }
2180
2181 CheckReturnValue(GetGraph(), -407.0_D);
2182 }
2183
TEST_F(CodegenTest,MultiplyNegateInteger)2184 TEST_F(CodegenTest, MultiplyNegateInteger)
2185 {
2186 if (GetGraph()->GetArch() != Arch::AARCH64) {
2187 GTEST_SKIP() << "multiply-negate instruction is only supported on Aarch64";
2188 }
2189
2190 GRAPH(GetGraph())
2191 {
2192 CONSTANT(0U, 5U);
2193 CONSTANT(1U, 5U);
2194
2195 BASIC_BLOCK(2U, -1L)
2196 {
2197 INST(2U, Opcode::MNeg).s64().Inputs(0U, 1U);
2198 INST(3U, Opcode::Return).s64().Inputs(2U);
2199 }
2200 }
2201
2202 CheckReturnValue(GetGraph(), -25L);
2203 }
2204
TEST_F(CodegenTest,MultiplyNegateFloat)2205 TEST_F(CodegenTest, MultiplyNegateFloat)
2206 {
2207 if (GetGraph()->GetArch() != Arch::AARCH64) {
2208 GTEST_SKIP() << "multiply-negate instruction is only supported on Aarch64";
2209 }
2210
2211 GRAPH(GetGraph())
2212 {
2213 CONSTANT(0U, 5.0_D);
2214 CONSTANT(1U, 5.0_D);
2215
2216 BASIC_BLOCK(2U, -1L)
2217 {
2218 INST(2U, Opcode::MNeg).f64().Inputs(0U, 1U);
2219 INST(3U, Opcode::Return).f64().Inputs(2U);
2220 }
2221 }
2222
2223 CheckReturnValue(GetGraph(), -25.0_D);
2224 }
2225
TEST_F(CodegenTest,OrNot)2226 TEST_F(CodegenTest, OrNot)
2227 {
2228 if (GetGraph()->GetArch() != Arch::AARCH64) {
2229 GTEST_SKIP() << "multiply-negate instruction is only supported on Aarch64";
2230 }
2231
2232 GRAPH(GetGraph())
2233 {
2234 CONSTANT(0U, 0x0000beefU);
2235 CONSTANT(1U, 0x2152ffffU);
2236
2237 BASIC_BLOCK(2U, -1L)
2238 {
2239 INST(2U, Opcode::OrNot).u32().Inputs(0U, 1U);
2240 INST(3U, Opcode::Return).u32().Inputs(2U);
2241 }
2242 }
2243
2244 CheckReturnValue(GetGraph(), 0xdeadbeefU);
2245 }
2246
TEST_F(CodegenTest,AndNot)2247 TEST_F(CodegenTest, AndNot)
2248 {
2249 if (GetGraph()->GetArch() != Arch::AARCH64) {
2250 GTEST_SKIP() << "multiply-negate instruction is only supported on Aarch64";
2251 }
2252
2253 GRAPH(GetGraph())
2254 {
2255 CONSTANT(0U, 0xf0000003U);
2256 CONSTANT(1U, 0x1U);
2257
2258 BASIC_BLOCK(2U, -1L)
2259 {
2260 INST(2U, Opcode::AndNot).u32().Inputs(0U, 1U);
2261 INST(3U, Opcode::Return).u32().Inputs(2U);
2262 }
2263 }
2264
2265 CheckReturnValue(GetGraph(), 0xf0000002U);
2266 }
2267
TEST_F(CodegenTest,XorNot)2268 TEST_F(CodegenTest, XorNot)
2269 {
2270 if (GetGraph()->GetArch() != Arch::AARCH64) {
2271 GTEST_SKIP() << "multiply-negate instruction is only supported on Aarch64";
2272 }
2273
2274 GRAPH(GetGraph())
2275 {
2276 CONSTANT(0U, 0xf0f1ffd0U);
2277 CONSTANT(1U, 0xcf0fc0f1U);
2278
2279 BASIC_BLOCK(2U, -1L)
2280 {
2281 INST(2U, Opcode::XorNot).u32().Inputs(0U, 1U);
2282 INST(3U, Opcode::Return).u32().Inputs(2U);
2283 }
2284 }
2285
2286 CheckReturnValue(GetGraph(), 0xc001c0deU);
2287 }
2288
TEST_F(CodegenTest,AddSR)2289 TEST_F(CodegenTest, AddSR)
2290 {
2291 if (GetGraph()->GetArch() != Arch::AARCH64) {
2292 GTEST_SKIP() << "AddSR instruction is only supported on Aarch64";
2293 }
2294
2295 TestBinaryOperationWithShiftedOperand(Opcode::AddSR, 10U, 2U, ShiftType::LSL, 1U, 14U);
2296 }
2297
TEST_F(CodegenTest,SubSR)2298 TEST_F(CodegenTest, SubSR)
2299 {
2300 if (GetGraph()->GetArch() != Arch::AARCH64) {
2301 GTEST_SKIP() << "SubSR instruction is only supported on Aarch64";
2302 }
2303
2304 TestBinaryOperationWithShiftedOperand(Opcode::SubSR, 10U, 4U, ShiftType::LSR, 2U, 9U);
2305 }
2306
TEST_F(CodegenTest,AndSR)2307 TEST_F(CodegenTest, AndSR)
2308 {
2309 if (GetGraph()->GetArch() != Arch::AARCH64) {
2310 GTEST_SKIP() << "AndSR instruction is only supported on Aarch64";
2311 }
2312
2313 TestBinaryOperationWithShiftedOperand(Opcode::AndSR, 1U, 1U, ShiftType::LSL, 1U, 0U);
2314 }
2315
TEST_F(CodegenTest,OrSR)2316 TEST_F(CodegenTest, OrSR)
2317 {
2318 if (GetGraph()->GetArch() != Arch::AARCH64) {
2319 GTEST_SKIP() << "OrSR instruction is only supported on Aarch64";
2320 }
2321
2322 TestBinaryOperationWithShiftedOperand(Opcode::OrSR, 1U, 1U, ShiftType::LSL, 1U, 3U);
2323 }
2324
TEST_F(CodegenTest,XorSR)2325 TEST_F(CodegenTest, XorSR)
2326 {
2327 if (GetGraph()->GetArch() != Arch::AARCH64) {
2328 GTEST_SKIP() << "XorSR instruction is only supported on Aarch64";
2329 }
2330
2331 TestBinaryOperationWithShiftedOperand(Opcode::XorSR, 3U, 1U, ShiftType::LSL, 1U, 1U);
2332 }
2333
TEST_F(CodegenTest,AndNotSR)2334 TEST_F(CodegenTest, AndNotSR)
2335 {
2336 if (GetGraph()->GetArch() != Arch::AARCH64) {
2337 GTEST_SKIP() << "AndNotSR instruction is only supported on Aarch64";
2338 }
2339
2340 TestBinaryOperationWithShiftedOperand(Opcode::AndNotSR, 6U, 12U, ShiftType::LSR, 2U, 4U);
2341 }
2342
TEST_F(CodegenTest,OrNotSR)2343 TEST_F(CodegenTest, OrNotSR)
2344 {
2345 if (GetGraph()->GetArch() != Arch::AARCH64) {
2346 GTEST_SKIP() << "OrNotSR instruction is only supported on Aarch64";
2347 }
2348
2349 TestBinaryOperationWithShiftedOperand(Opcode::OrNotSR, 1U, 12U, ShiftType::LSR, 2U, 0xfffffffdU);
2350 }
2351
TEST_F(CodegenTest,XorNotSR)2352 TEST_F(CodegenTest, XorNotSR)
2353 {
2354 if (GetGraph()->GetArch() != Arch::AARCH64) {
2355 GTEST_SKIP() << "XorNotSR instruction is only supported on Aarch64";
2356 }
2357
2358 TestBinaryOperationWithShiftedOperand(Opcode::XorNotSR, -1L, 12U, ShiftType::LSR, 2U, 3U);
2359 }
2360
TEST_F(CodegenTest,NegSR)2361 TEST_F(CodegenTest, NegSR)
2362 {
2363 if (GetGraph()->GetArch() != Arch::AARCH64) {
2364 GTEST_SKIP() << "NegSR instruction is only supported on Aarch64";
2365 }
2366
2367 GRAPH(GetGraph())
2368 {
2369 CONSTANT(0U, 0x80000000U);
2370
2371 BASIC_BLOCK(2U, -1L)
2372 {
2373 INST(1U, Opcode::NegSR).Shift(ShiftType::ASR, 1U).u32().Inputs(0U);
2374 INST(2U, Opcode::Return).u32().Inputs(1U);
2375 }
2376 }
2377
2378 CheckReturnValue(GetGraph(), 0x40000000U);
2379 }
2380
TEST_F(CodegenTest,LoadArrayPairLivenessInfo)2381 TEST_F(CodegenTest, LoadArrayPairLivenessInfo)
2382 {
2383 auto graph = GetGraph();
2384
2385 GRAPH(graph)
2386 {
2387 PARAMETER(0U, 0U).ref();
2388 PARAMETER(1U, 1U).s32();
2389
2390 BASIC_BLOCK(2U, -1L)
2391 {
2392 INST(2U, Opcode::LoadArrayPair).s32().Inputs(0U, 1U);
2393 INST(4U, Opcode::LoadPairPart).s32().Inputs(2U).Imm(0U);
2394 INST(5U, Opcode::LoadPairPart).s32().Inputs(2U).Imm(1U);
2395 INST(12U, Opcode::SaveState).Inputs(0U).SrcVregs({0U});
2396 INST(10U, Opcode::LoadClass)
2397 .ref()
2398 .Inputs(12U)
2399 .TypeId(42U)
2400 .Class(reinterpret_cast<RuntimeInterface::ClassPtr>(1U));
2401 INST(3U, Opcode::IsInstance).b().Inputs(0U, 10U, 12U).TypeId(42U);
2402 INST(6U, Opcode::Cast).s32().SrcType(DataType::BOOL).Inputs(3U);
2403 INST(7U, Opcode::Add).s32().Inputs(4U, 5U);
2404 INST(8U, Opcode::Add).s32().Inputs(7U, 6U);
2405 INST(9U, Opcode::Return).s32().Inputs(8U);
2406 }
2407 }
2408
2409 SetNumVirtRegs(0U);
2410 SetNumArgs(2U);
2411 RegAlloc(graph);
2412 EXPECT_TRUE(RunCodegen(graph));
2413
2414 RegMask ldpRegs {};
2415
2416 auto cg = Codegen(graph);
2417 for (auto &bb : graph->GetBlocksLinearOrder()) {
2418 for (auto inst : bb->AllInsts()) {
2419 if (inst->GetOpcode() == Opcode::LoadArrayPair) {
2420 ldpRegs.set(inst->GetDstReg(0U));
2421 ldpRegs.set(inst->GetDstReg(1U));
2422 } else if (inst->GetOpcode() == Opcode::IsInstance) {
2423 auto liveRegs = cg.GetLiveRegisters(inst).first;
2424 // Both dst registers should be alive during IsInstance call
2425 ASSERT_EQ(ldpRegs & liveRegs, ldpRegs);
2426 }
2427 }
2428 }
2429 }
2430
TEST_F(CodegenTest,CompareAnyTypeInst)2431 TEST_F(CodegenTest, CompareAnyTypeInst)
2432 {
2433 auto graph = GetGraph();
2434 graph->SetDynamicMethod();
2435 graph->SetDynamicStub();
2436 GRAPH(graph)
2437 {
2438 PARAMETER(0U, 0U);
2439 INS(0U).SetType(DataType::Type::ANY);
2440
2441 BASIC_BLOCK(2U, -1L)
2442 {
2443 INST(2U, Opcode::CompareAnyType).b().AnyType(AnyBaseType::UNDEFINED_TYPE).Inputs(0U);
2444 INST(3U, Opcode::Return).b().Inputs(2U);
2445 }
2446 }
2447
2448 SetNumVirtRegs(0U);
2449 ASSERT_TRUE(RegAlloc(graph));
2450 ASSERT_TRUE(RunCodegen(graph));
2451
2452 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
2453 auto codeExit = codeEntry + graph->GetCode().Size();
2454
2455 ASSERT(codeEntry != nullptr && codeExit != nullptr);
2456
2457 GetExecModule().SetInstructions(codeEntry, codeExit);
2458 GetExecModule().SetDump(false);
2459
2460 GetExecModule().Execute();
2461 auto rv = GetExecModule().GetRetValue<bool>();
2462 EXPECT_EQ(rv, true);
2463 }
2464
TEST_F(CodegenTest,CastAnyTypeValueInst)2465 TEST_F(CodegenTest, CastAnyTypeValueInst)
2466 {
2467 auto graph = GetGraph();
2468 graph->SetDynamicMethod();
2469 graph->SetDynamicStub();
2470 GRAPH(graph)
2471 {
2472 PARAMETER(0U, 0U);
2473 INS(0U).SetType(DataType::Type::ANY);
2474
2475 BASIC_BLOCK(2U, -1L)
2476 {
2477 INST(2U, Opcode::CastAnyTypeValue).b().AnyType(AnyBaseType::UNDEFINED_TYPE).Inputs(0U);
2478 INST(3U, Opcode::Return).b().Inputs(2U);
2479 }
2480 }
2481
2482 SetNumVirtRegs(0U);
2483 ASSERT_TRUE(RegAlloc(graph));
2484 ASSERT_TRUE(RunCodegen(graph));
2485
2486 auto codeEntry = reinterpret_cast<char *>(graph->GetCode().Data());
2487 auto codeExit = codeEntry + graph->GetCode().Size();
2488
2489 ASSERT(codeEntry != nullptr && codeExit != nullptr);
2490
2491 GetExecModule().SetInstructions(codeEntry, codeExit);
2492 GetExecModule().SetDump(false);
2493
2494 GetExecModule().Execute();
2495 auto rv = GetExecModule().GetRetValue<uint32_t>();
2496 EXPECT_EQ(rv, 0U);
2497 }
2498 // NOLINTEND(readability-magic-numbers,modernize-avoid-c-arrays,cppcoreguidelines-pro-bounds-pointer-arithmetic)
2499
2500 } // namespace panda::compiler
2501