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