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 "unit_test.h"
17 #include "inst_generator.h"
18 #include "optimizer/analysis/alias_analysis.h"
19 #include "optimizer/code_generator/codegen.h"
20 #include "optimizer/optimizations/regalloc/reg_alloc_linear_scan.h"
21
22 namespace panda::compiler {
23 class InstGeneratorTest : public GraphTest {
24 public:
InstGeneratorTest()25 InstGeneratorTest() : GraphTest()
26 {
27 // Disable regalloc verification as generated code may operate on
28 // registers/stack slots containing uninitialized values
29 options.SetCompilerVerifyRegalloc(false);
30 }
31 };
32
33 class CodegenStatisticGenerator : public StatisticGenerator {
34 public:
35 using StatisticGenerator::StatisticGenerator;
36
37 ~CodegenStatisticGenerator() override = default;
38
Generate()39 void Generate() override
40 {
41 for (auto op : inst_generator_.GetMap()) {
42 ASSERT(op.first != Opcode::Builtin);
43 if (op.first == Opcode::Intrinsic) {
44 continue;
45 }
46 auto it = inst_generator_.Generate(op.first);
47 FULL_INST_STAT full_inst_stat = tmplt_;
48 for (auto i = it.begin(); i != it.end(); ++i) {
49 ASSERT(graph_creator_.GetAllocator()->GetAllocatedSize() == 0);
50 auto graph = graph_creator_.GenerateGraph(*i);
51 graph->GetAnalysis<LoopAnalyzer>().Run();
52 GraphChecker(graph).Check();
53 RegAllocLinearScan(graph).Run();
54 graph->SetStackSlotsCount(4U);
55 bool status = graph->RunPass<Codegen>();
56 full_inst_stat[(*i)->GetType()] = status;
57 all_inst_number_++;
58 positive_inst_number += status;
59 // To consume less memory
60 graph->~Graph();
61 graph_creator_.GetAllocator()->Resize(0);
62 }
63 statistic_.first.insert({op.first, full_inst_stat});
64 }
65 auto intrinsics = inst_generator_.Generate(Opcode::Intrinsic);
66 for (auto i = intrinsics.begin(); i != intrinsics.end(); ++i) {
67 ASSERT(graph_creator_.GetAllocator()->GetAllocatedSize() == 0);
68 auto graph = graph_creator_.GenerateGraph(*i);
69 graph->GetAnalysis<LoopAnalyzer>().Run();
70 GraphChecker(graph).Check();
71 RegAllocLinearScan(graph).Run();
72 graph->SetStackSlotsCount(2U);
73 bool status = graph->RunPass<Codegen>();
74 statistic_.second[(*i)->CastToIntrinsic()->GetIntrinsicId()] = status;
75 all_inst_number_++;
76 positive_inst_number += status;
77 graph->~Graph();
78 graph_creator_.GetAllocator()->Resize(0);
79 }
80 for (auto i = 0; i != static_cast<int>(Opcode::NUM_OPCODES); ++i) {
81 auto opc = static_cast<Opcode>(i);
82 if (opc == Opcode::NOP || opc == Opcode::Intrinsic || opc == Opcode::Builtin) {
83 continue;
84 }
85 all_opcode_number_++;
86 implemented_opcode_number_ += (statistic_.first.find(opc) != statistic_.first.end());
87 }
88 }
89 };
90
TEST_F(InstGeneratorTest,AllInstTestARM64)91 TEST_F(InstGeneratorTest, AllInstTestARM64)
92 {
93 ArenaAllocator inst_alloc(SpaceType::SPACE_TYPE_COMPILER);
94 InstGenerator inst_gen(inst_alloc);
95
96 ArenaAllocator graph_alloc(SpaceType::SPACE_TYPE_COMPILER);
97 ArenaAllocator graph_local_alloc(SpaceType::SPACE_TYPE_COMPILER);
98 GraphCreator graph_creator(graph_alloc, graph_local_alloc);
99
100 // ARM64
101 CodegenStatisticGenerator stat_gen_arm64(inst_gen, graph_creator);
102 stat_gen_arm64.Generate();
103 stat_gen_arm64.GenerateHTMLPage("CodegenStatisticARM64.html");
104 }
105
TEST_F(InstGeneratorTest,AllInstTestARM32)106 TEST_F(InstGeneratorTest, AllInstTestARM32)
107 {
108 ArenaAllocator inst_alloc(SpaceType::SPACE_TYPE_COMPILER);
109 InstGenerator inst_gen(inst_alloc);
110
111 ArenaAllocator graph_alloc(SpaceType::SPACE_TYPE_COMPILER);
112 ArenaAllocator graph_local_alloc(SpaceType::SPACE_TYPE_COMPILER);
113 GraphCreator graph_creator(graph_alloc, graph_local_alloc);
114
115 // ARM32
116 graph_creator.SetRuntimeTargetArch(Arch::AARCH32);
117 CodegenStatisticGenerator stat_gen_arm32(inst_gen, graph_creator);
118 stat_gen_arm32.Generate();
119 stat_gen_arm32.GenerateHTMLPage("CodegenStatisticARM32.html");
120 }
121
TEST_F(InstGeneratorTest,AllInstTestAMD64)122 TEST_F(InstGeneratorTest, AllInstTestAMD64)
123 {
124 ArenaAllocator inst_alloc(SpaceType::SPACE_TYPE_COMPILER);
125 InstGenerator inst_gen(inst_alloc);
126
127 ArenaAllocator graph_alloc(SpaceType::SPACE_TYPE_COMPILER);
128 ArenaAllocator graph_local_alloc(SpaceType::SPACE_TYPE_COMPILER);
129 GraphCreator graph_creator(graph_alloc, graph_local_alloc);
130
131 // AMD64
132 graph_creator.SetRuntimeTargetArch(Arch::X86_64);
133 CodegenStatisticGenerator stat_gen_amd64(inst_gen, graph_creator);
134 stat_gen_amd64.Generate();
135 stat_gen_amd64.GenerateHTMLPage("CodegenStatisticAMD64.html");
136 }
137
138 } // namespace panda::compiler
139
140 #ifdef USE_VIXL_ARM64
141 #include "vixl_exec_module.h"
142 #include <random>
143 #include <cstring>
144
145 #else
146 #error "Not supported!"
147 #endif
148
149 namespace panda::compiler {
150 class ArithGenerator : public CodegenStatisticGenerator {
151 // Seed for random generator
152 static constexpr uint64_t SEED = 0x1234;
153
154 public:
155 #ifndef PANDA_NIGHTLY_TEST_ON
156 static constexpr uint64_t ITERATION = 20;
157 #else
158 static constexpr uint64_t ITERATION = 250;
159 #endif
160 ArithGenerator() = delete;
161
ArithGenerator(InstGenerator & inst_generator,GraphCreator & graph_creator)162 explicit ArithGenerator(InstGenerator &inst_generator, GraphCreator &graph_creator)
163 : CodegenStatisticGenerator(inst_generator, graph_creator),
164 exec_module_(graph_creator_.GetAllocator(), graph_creator_.GetRuntime()) {};
165
GetRandomData()166 uint64_t GetRandomData()
167 {
168 static auto random_gen_ = std::mt19937_64(SEED);
169 return random_gen_();
170 };
171
172 template <class T>
FixParams(T * param_1,T * param_2,T * param_3,Opcode opc)173 void FixParams([[maybe_unused]] T *param_1, [[maybe_unused]] T *param_2, [[maybe_unused]] T *param_3, Opcode opc)
174 {
175 switch (opc) {
176 case Opcode::Neg:
177 case Opcode::NegSR:
178 case Opcode::Abs:
179 case Opcode::Not:
180 case Opcode::Cast:
181 case Opcode::Min:
182 case Opcode::Max: {
183 // single instructions - do not need to fix
184 return;
185 }
186 case Opcode::Add:
187 case Opcode::Sub:
188
189 case Opcode::And:
190 case Opcode::Or:
191 case Opcode::Xor:
192 case Opcode::AddI:
193 case Opcode::SubI:
194 case Opcode::AndI:
195 case Opcode::OrI:
196 case Opcode::XorI:
197 case Opcode::AndNot:
198 case Opcode::OrNot:
199 case Opcode::XorNot: {
200 if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
201 return;
202 } else {
203 // shift parameters to prevent overflow
204 *param_1 >>= 2;
205 *param_2 >>= 2;
206 return;
207 }
208 }
209 case Opcode::Mul:
210 case Opcode::Div:
211 case Opcode::Mod:
212 case Opcode::MNeg: {
213 if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
214 return;
215 } else {
216 // shift parameters to prevent overflow
217 *param_1 >>= sizeof(T) * 4;
218 *param_2 >>= (sizeof(T) * 4 + 1);
219 if (*param_2 == 0) {
220 *param_2 = *param_1 + 1;
221 }
222 if (*param_2 == 0) {
223 *param_2 = *param_2 + 1;
224 };
225 return;
226 }
227 }
228 case Opcode::MAdd:
229 case Opcode::MSub:
230 if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
231 return;
232 } else {
233 // shift parameters to prevent overflow
234 *param_1 >>= sizeof(T) * 4;
235 *param_2 >>= (sizeof(T) * 4 + 2);
236 *param_3 >>= sizeof(T) * 4;
237 if (*param_2 == 0) {
238 *param_2 = *param_1 + 1;
239 }
240 if (*param_2 == 0) {
241 *param_2 = *param_2 + 1;
242 };
243 if (*param_3 == 0) {
244 *param_3 = *param_3 + 1;
245 }
246 }
247 return;
248 case Opcode::Shl:
249 case Opcode::Shr:
250 case Opcode::AShr:
251 if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
252 return;
253 } else {
254 *param_1 >>= 2;
255 return;
256 }
257 case Opcode::ShlI:
258 case Opcode::ShrI:
259 case Opcode::AShrI:
260 case Opcode::AddSR:
261 case Opcode::SubSR:
262 case Opcode::AndSR:
263 case Opcode::OrSR:
264 case Opcode::XorSR:
265 case Opcode::AndNotSR:
266 case Opcode::OrNotSR:
267 case Opcode::XorNotSR:
268 if constexpr (std::is_same_v<T, float> || std::is_same_v<T, double>) {
269 return;
270 } else {
271 *param_1 >>= 2;
272 // mask for shift
273 *param_2 &= (sizeof(T) - 1);
274 return;
275 }
276 default:
277 ASSERT_DO(0, std::cerr << (int)opc << "\n");
278 }
279 }
280
isImmOps(Opcode opc)281 bool isImmOps(Opcode opc)
282 {
283 return (opc == Opcode::AddI || opc == Opcode::SubI || opc == Opcode::ShlI || opc == Opcode::ShrI ||
284 opc == Opcode::AShrI || opc == Opcode::AndI || opc == Opcode::OrI || opc == Opcode::XorI);
285 }
286
isUnaryShiftedRegisterOps(Opcode opc)287 bool isUnaryShiftedRegisterOps(Opcode opc)
288 {
289 return opc == Opcode::NegSR;
290 }
291
isBinaryShiftedRegisterOps(Opcode opc)292 bool isBinaryShiftedRegisterOps(Opcode opc)
293 {
294 return (opc == Opcode::AddSR || opc == Opcode::SubSR || opc == Opcode::AndSR || opc == Opcode::OrSR ||
295 opc == Opcode::XorSR || opc == Opcode::AndNotSR || opc == Opcode::OrNotSR || opc == Opcode::XorNotSR);
296 }
297
isTernary(Opcode opc)298 bool isTernary(Opcode opc)
299 {
300 return opc == Opcode::MAdd || opc == Opcode::MSub;
301 }
302
GetRandValues()303 std::tuple<uint64_t, uint64_t, uint64_t> GetRandValues()
304 {
305 return {GetRandomData(), GetRandomData(), GetRandomData()};
306 }
307
GetRandValue()308 uint64_t GetRandValue()
309 {
310 return GetRandomData();
311 }
312
313 template <class param_type>
Generate(Opcode opc,std::pair<param_type,param_type> vals)314 void Generate(Opcode opc, std::pair<param_type, param_type> vals)
315 {
316 Generate(opc, std::make_tuple(vals.first, vals.second, static_cast<param_type>(0)));
317 }
318
319 template <class param_type>
Generate(Opcode opc,std::tuple<param_type,param_type,param_type> vals)320 void Generate(Opcode opc, std::tuple<param_type, param_type, param_type> vals)
321 {
322 auto it = inst_generator_.Generate(opc);
323 for (auto i = it.begin(); i != it.end(); ++i) {
324 auto inst = *i;
325 auto type = inst->GetType();
326 auto shift_type = ShiftType::INVALID_SHIFT;
327 if (VixlExecModule::GetType<param_type>() == type) {
328 auto param_1 = std::get<0>(vals);
329 auto param_2 = std::get<1>(vals);
330 auto param_3 = std::get<2>(vals);
331 FixParams<param_type>(¶m_1, ¶m_2, ¶m_3, opc);
332 if (isImmOps(opc)) {
333 static_cast<BinaryImmOperation *>(inst)->SetImm(param_2);
334 } else if (isUnaryShiftedRegisterOps(opc)) {
335 static_cast<UnaryShiftedRegisterOperation *>(inst)->SetImm(param_2);
336 shift_type = static_cast<UnaryShiftedRegisterOperation *>(inst)->GetShiftType();
337 } else if (isBinaryShiftedRegisterOps(opc)) {
338 static_cast<BinaryShiftedRegisterOperation *>(inst)->SetImm(param_3);
339 shift_type = static_cast<BinaryShiftedRegisterOperation *>(inst)->GetShiftType();
340 }
341 auto graph = graph_creator_.GenerateGraph(*i);
342
343 auto finalizer = [&graph]([[maybe_unused]] void *ptr) {
344 if (graph != nullptr) {
345 graph->~Graph();
346 }
347 };
348 std::unique_ptr<void, decltype(finalizer)> fin(&finalizer, finalizer);
349
350 graph->GetAnalysis<LoopAnalyzer>().Run();
351 GraphChecker(graph).Check();
352
353 RegAllocLinearScan(graph).Run();
354 GraphChecker(graph).Check();
355
356 if (!graph->RunPass<Codegen>()) {
357 return;
358 };
359 auto code_entry = reinterpret_cast<char *>(graph->GetData().Data());
360 auto code_exit = code_entry + graph->GetData().Size();
361 ASSERT(code_entry != nullptr && code_exit != nullptr);
362 exec_module_.SetInstructions(code_entry, code_exit);
363
364 exec_module_.SetDump(false);
365
366 exec_module_.SetParameter(0U, param_1);
367
368 if (!isImmOps(opc)) {
369 exec_module_.SetParameter(1U, param_2);
370 }
371
372 if (isTernary(opc)) {
373 exec_module_.SetParameter(2U, param_3);
374 }
375
376 exec_module_.Execute();
377
378 struct RetValue {
379 uint64_t data;
380 uint64_t type;
381 };
382
383 auto ret_data = exec_module_.GetRetValue<param_type>();
384 auto calc_data = DoLogic<param_type>(opc, param_1, param_2, param_3, shift_type,
385 DataType::GetTypeSize(type, graph->GetArch()));
386 if (calc_data != ret_data) {
387 std::cout << " data " << ret_data << " sizeof type " << (uint64_t)(sizeof(param_type) * 8)
388 << " \n";
389 std::cout << std::hex << "parameter_1 = " << param_1 << " parameter_2 = " << param_2
390 << "parameter_3 = " << param_3 << "\n";
391 inst->Dump(&std::cerr);
392 std::cout << "calculated = " << calc_data << " returned " << ret_data << "\n";
393 exec_module_.SetDump(true);
394 exec_module_.PrintInstructions();
395 exec_module_.SetParameter(0U, param_1);
396 exec_module_.SetParameter(1U, param_2);
397 exec_module_.SetParameter(2U, param_3);
398 exec_module_.Execute();
399 exec_module_.SetDump(false);
400 }
401 ASSERT_EQ(calc_data, ret_data);
402 }
403 }
404 }
405
406 template <class param_type, class result_type>
GenCast(param_type val)407 void GenCast(param_type val)
408 {
409 auto opc = Opcode::Cast;
410
411 auto it = inst_generator_.Generate(opc);
412 for (auto i = it.begin(); i != it.end(); ++i) {
413 auto inst = *i;
414 auto type = inst->GetType();
415 if (VixlExecModule::GetType<param_type>() == type) {
416 param_type param_1 = val;
417
418 auto graph = graph_creator_.GenerateGraph(*i);
419
420 auto finalizer = [&graph]([[maybe_unused]] void *ptr) {
421 if (graph != nullptr) {
422 graph->~Graph();
423 }
424 };
425 std::unique_ptr<void, decltype(finalizer)> fin(&finalizer, finalizer);
426
427 // Reset type for Cast-destination:
428 (*i)->SetType(VixlExecModule::GetType<result_type>());
429 // Fix return value
430 for (auto iter = (*i)->GetUsers().begin(); iter != (*i)->GetUsers().end(); ++iter) {
431 iter->GetInst()->SetType(VixlExecModule::GetType<result_type>());
432 }
433
434 graph->GetAnalysis<LoopAnalyzer>().Run();
435 GraphChecker(graph).Check();
436 RegAllocLinearScan(graph).Run();
437 GraphChecker(graph).Check();
438
439 if (!graph->RunPass<Codegen>()) {
440 return;
441 };
442 auto code_entry = reinterpret_cast<char *>(graph->GetData().Data());
443 auto code_exit = code_entry + graph->GetData().Size();
444 ASSERT(code_entry != nullptr && code_exit != nullptr);
445 exec_module_.SetInstructions(code_entry, code_exit);
446
447 exec_module_.SetDump(false);
448 exec_module_.SetParameter(0U, param_1);
449 exec_module_.Execute();
450
451 auto ret_data = exec_module_.GetRetValue<result_type>();
452 result_type calc_data;
453
454 if constexpr ((GetCommonType(VixlExecModule::GetType<result_type>()) == DataType::Type::INT64) &&
455 (std::is_same_v<param_type, float> || std::is_same_v<param_type, double>)) {
456 if (param_1 > (param_type)std::numeric_limits<result_type>::max()) {
457 calc_data = std::numeric_limits<result_type>::max();
458 } else if (param_1 < (param_type)std::numeric_limits<result_type>::min()) {
459 calc_data = std::numeric_limits<result_type>::min();
460 } else {
461 calc_data = static_cast<result_type>(param_1);
462 }
463 } else {
464 calc_data = static_cast<result_type>(param_1);
465 }
466
467 if (calc_data != ret_data) {
468 if constexpr (std::is_same_v<result_type, double> || std::is_same_v<param_type, double>) {
469 std::cout << std::hex << " in parameter = " << param_1 << "\n";
470 std::cout << std::hex << "parameter_1 = " << (double)param_1 << "\n";
471 }
472
473 #ifndef NDEBUG
474 std::cout << " cast from " << DataType::ToString(VixlExecModule::GetType<param_type>()) << " to "
475 << DataType::ToString(VixlExecModule::GetType<result_type>()) << "\n";
476 #endif
477 std::cout << " data " << ret_data << " sizeof type " << (uint64_t)(sizeof(param_type) * 8)
478 << " \n";
479 inst->Dump(&std::cerr);
480 exec_module_.SetDump(true);
481 exec_module_.SetParameter(0U, param_1);
482 exec_module_.Execute();
483 exec_module_.SetDump(false);
484 }
485 ASSERT_EQ(calc_data, ret_data);
486 }
487 }
488 }
489
490 template <class T>
DoShift(T value,ShiftType shift_type,uint64_t scale,uint8_t type_size)491 T DoShift(T value, ShiftType shift_type, uint64_t scale, uint8_t type_size)
492 {
493 switch (shift_type) {
494 case ShiftType::LSL:
495 return static_cast<uint64_t>value << scale;
496 case ShiftType::ROR:
497 return (static_cast<uint64_t>value >> scale) | (value << (type_size - scale));
498 case ShiftType::LSR:
499 if constexpr (std::is_same_v<T, int32_t> || std::is_same_v<T, uint32_t>) {
500 return (static_cast<uint32_t>value) >> static_cast<uint32_t>scale;
501 }
502 if constexpr (std::is_same_v<T, int64_t> || std::is_same_v<T, uint64_t>) {
503 return (static_cast<uint64_t>value) >> scale;
504 }
505 /* fall-through */
506 case ShiftType::ASR:
507 return static_cast<int64_t>value >> scale;
508 default:
509 UNREACHABLE();
510 }
511 }
512
513 // Make logic with parameters (default - first parameter)
514 template <class T>
DoLogic(Opcode opc,T param_1,T param_2,T param_3,ShiftType shift_type,uint8_t type_size)515 T DoLogic(Opcode opc, T param_1, [[maybe_unused]] T param_2, [[maybe_unused]] T param_3,
516 [[maybe_unused]] ShiftType shift_type, [[maybe_unused]] uint8_t type_size)
517 {
518 constexpr DataType::Type type = ConstantInst::GetTypeFromCType<T>();
519 constexpr bool arithmetic_type = (type == DataType::INT64);
520 switch (opc) {
521 case Opcode::Neg:
522 return -param_1;
523 case Opcode::MAdd:
524 return param_1 * param_2 + param_3;
525 case Opcode::MSub:
526 return param_3 - param_1 * param_2;
527 case Opcode::Not:
528 return (-param_1 - 1);
529 case Opcode::Add:
530 case Opcode::AddI:
531 return (param_1 + param_2);
532 case Opcode::Sub:
533 case Opcode::SubI:
534 return (param_1 - param_2);
535 case Opcode::Mul:
536 return (param_1 * param_2);
537 case Opcode::MNeg:
538 return -(param_1 * param_2);
539 case Opcode::Div:
540 ASSERT_PRINT(param_2 != 0, "If you got this assert, you may change SEED");
541 return (param_1 / param_2);
542 case Opcode::Mod:
543 if constexpr (arithmetic_type) {
544 ASSERT_PRINT(param_2 != 0, "If you got this assert, you may change SEED");
545 return param_1 % param_2;
546 } else {
547 return fmod(param_1, param_2);
548 }
549 case Opcode::Min:
550 return std::min(param_1, param_2);
551 case Opcode::Max:
552 return std::max(param_1, param_2);
553
554 case Opcode::NegSR:
555 if constexpr (arithmetic_type) {
556 return -(DoShift(param_1, shift_type, param_2, type_size));
557 }
558 /* fall-through */
559 case Opcode::ShlI:
560 if constexpr (arithmetic_type)
561 return DoShift(param_1, ShiftType::LSL, param_2, type_size);
562 /* fall-through */
563 case Opcode::Shl:
564 if constexpr (arithmetic_type) {
565 return DoShift(param_1, ShiftType::LSL, param_2 & (type_size - 1), type_size);
566 }
567 /* fall-through */
568 case Opcode::ShrI:
569 if constexpr (arithmetic_type) {
570 return DoShift(param_1, ShiftType::LSR, param_2, type_size);
571 }
572 /* fall-through */
573 case Opcode::Shr:
574 if constexpr (arithmetic_type) {
575 return DoShift(param_1, ShiftType::LSR, param_2 & (type_size - 1), type_size);
576 }
577 /* fall-through */
578 case Opcode::AShr:
579 if constexpr (arithmetic_type) {
580 return DoShift(param_1, ShiftType::ASR, param_2 & (type_size - 1), type_size);
581 }
582 /* fall-through */
583 case Opcode::AShrI:
584 if constexpr (arithmetic_type) {
585 return DoShift(param_1, ShiftType::ASR, param_2, type_size);
586 }
587 /* fall-through */
588 case Opcode::And:
589 case Opcode::AndI:
590 if constexpr (arithmetic_type) {
591 return param_1 & param_2;
592 }
593 /* fall-through */
594 case Opcode::AndSR:
595 if constexpr (arithmetic_type) {
596 return param_1 & DoShift(param_2, shift_type, param_3, type_size);
597 }
598 /* fall-through */
599 case Opcode::Or:
600 case Opcode::OrI:
601 if constexpr (arithmetic_type) {
602 return param_1 | param_2;
603 }
604 /* fall-through */
605 case Opcode::OrSR:
606 if constexpr (arithmetic_type) {
607 return param_1 | DoShift(param_2, shift_type, param_3, type_size);
608 }
609 /* fall-through */
610 case Opcode::Xor:
611 case Opcode::XorI:
612 if constexpr (arithmetic_type) {
613 return param_1 ^ param_2;
614 }
615 /* fall-through */
616 case Opcode::XorSR:
617 if constexpr (arithmetic_type) {
618 return param_1 ^ DoShift(param_2, shift_type, param_3, type_size);
619 }
620 /* fall-through */
621 case Opcode::AndNot:
622 if constexpr (arithmetic_type) {
623 return param_1 & (~param_2);
624 }
625 /* fall-through */
626 case Opcode::AndNotSR:
627 if constexpr (arithmetic_type) {
628 return param_1 & (~DoShift(param_2, shift_type, param_3, type_size));
629 }
630 /* fall-through */
631 case Opcode::OrNot:
632 if constexpr (arithmetic_type) {
633 return param_1 | (~param_2);
634 }
635 /* fall-through */
636 case Opcode::OrNotSR:
637 if constexpr (arithmetic_type) {
638 return param_1 | (~DoShift(param_2, shift_type, param_3, type_size));
639 }
640 /* fall-through */
641 case Opcode::XorNot:
642 if constexpr (arithmetic_type) {
643 return param_1 ^ (~param_2);
644 }
645 /* fall-through */
646 case Opcode::XorNotSR:
647 if constexpr (arithmetic_type) {
648 return param_1 ^ (~DoShift(param_2, shift_type, param_3, type_size));
649 }
650 /* fall-through */
651 case Opcode::Abs:
652 if constexpr (std::is_same_v<T, int8_t> || std::is_same_v<T, int16_t> || std::is_same_v<T, int32_t> ||
653 std::is_same_v<T, int64_t>) {
654 return std::abs(param_1);
655 }
656 /* fall-through */
657 default:
658 ASSERT_DO(false, std::cerr << "Unsupported!" << (int)opc << "\n");
659 return -1;
660 }
661 }
662
663 private:
664 VixlExecModule exec_module_;
665 };
666
OneTest(ArithGenerator & gen,Opcode opc)667 void OneTest(ArithGenerator &gen, Opcode opc)
668 {
669 gen.Generate<int8_t>(opc, gen.GetRandValues());
670 gen.Generate<int16_t>(opc, gen.GetRandValues());
671 gen.Generate<int32_t>(opc, gen.GetRandValues());
672 gen.Generate<int64_t>(opc, gen.GetRandValues());
673 gen.Generate<uint8_t>(opc, gen.GetRandValues());
674 gen.Generate<uint16_t>(opc, gen.GetRandValues());
675 gen.Generate<uint32_t>(opc, gen.GetRandValues());
676 gen.Generate<uint64_t>(opc, gen.GetRandValues());
677 }
678
OneTestCast(ArithGenerator & gen)679 void OneTestCast(ArithGenerator &gen)
680 {
681 gen.GenCast<int8_t, uint8_t>(gen.GetRandValue());
682 gen.GenCast<int8_t, uint16_t>(gen.GetRandValue());
683 gen.GenCast<int8_t, uint32_t>(gen.GetRandValue());
684 gen.GenCast<int8_t, uint64_t>(gen.GetRandValue());
685 gen.GenCast<int8_t, int8_t>(gen.GetRandValue());
686 gen.GenCast<int8_t, int16_t>(gen.GetRandValue());
687 gen.GenCast<int8_t, int32_t>(gen.GetRandValue());
688 gen.GenCast<int8_t, int64_t>(gen.GetRandValue());
689 gen.GenCast<int8_t, float>(gen.GetRandValue());
690 gen.GenCast<int8_t, double>(gen.GetRandValue());
691
692 gen.GenCast<int16_t, uint8_t>(gen.GetRandValue());
693 gen.GenCast<int16_t, uint16_t>(gen.GetRandValue());
694 gen.GenCast<int16_t, uint32_t>(gen.GetRandValue());
695 gen.GenCast<int16_t, uint64_t>(gen.GetRandValue());
696 gen.GenCast<int16_t, int8_t>(gen.GetRandValue());
697 gen.GenCast<int16_t, int16_t>(gen.GetRandValue());
698 gen.GenCast<int16_t, int32_t>(gen.GetRandValue());
699 gen.GenCast<int16_t, int64_t>(gen.GetRandValue());
700 gen.GenCast<int16_t, float>(gen.GetRandValue());
701 gen.GenCast<int16_t, double>(gen.GetRandValue());
702
703 gen.GenCast<int32_t, uint8_t>(gen.GetRandValue());
704 gen.GenCast<int32_t, uint16_t>(gen.GetRandValue());
705 gen.GenCast<int32_t, uint32_t>(gen.GetRandValue());
706 gen.GenCast<int32_t, uint64_t>(gen.GetRandValue());
707 gen.GenCast<int32_t, int8_t>(gen.GetRandValue());
708 gen.GenCast<int32_t, int16_t>(gen.GetRandValue());
709 gen.GenCast<int32_t, int32_t>(gen.GetRandValue());
710 gen.GenCast<int32_t, int64_t>(gen.GetRandValue());
711 gen.GenCast<int32_t, float>(gen.GetRandValue());
712 gen.GenCast<int32_t, double>(gen.GetRandValue());
713
714 gen.GenCast<int64_t, uint8_t>(gen.GetRandValue());
715 gen.GenCast<int64_t, uint16_t>(gen.GetRandValue());
716 gen.GenCast<int64_t, uint32_t>(gen.GetRandValue());
717 gen.GenCast<int64_t, uint64_t>(gen.GetRandValue());
718 gen.GenCast<int64_t, int8_t>(gen.GetRandValue());
719 gen.GenCast<int64_t, int16_t>(gen.GetRandValue());
720 gen.GenCast<int64_t, int32_t>(gen.GetRandValue());
721 gen.GenCast<int64_t, int64_t>(gen.GetRandValue());
722 gen.GenCast<int64_t, float>(gen.GetRandValue());
723 gen.GenCast<int64_t, double>(gen.GetRandValue());
724
725 gen.GenCast<uint8_t, uint8_t>(gen.GetRandValue());
726 gen.GenCast<uint8_t, uint16_t>(gen.GetRandValue());
727 gen.GenCast<uint8_t, uint32_t>(gen.GetRandValue());
728 gen.GenCast<uint8_t, uint64_t>(gen.GetRandValue());
729 gen.GenCast<uint8_t, int8_t>(gen.GetRandValue());
730 gen.GenCast<uint8_t, int16_t>(gen.GetRandValue());
731 gen.GenCast<uint8_t, int32_t>(gen.GetRandValue());
732 gen.GenCast<uint8_t, int64_t>(gen.GetRandValue());
733 gen.GenCast<uint8_t, float>(gen.GetRandValue());
734 gen.GenCast<uint8_t, double>(gen.GetRandValue());
735
736 gen.GenCast<uint16_t, uint8_t>(gen.GetRandValue());
737 gen.GenCast<uint16_t, uint16_t>(gen.GetRandValue());
738 gen.GenCast<uint16_t, uint32_t>(gen.GetRandValue());
739 gen.GenCast<uint16_t, uint64_t>(gen.GetRandValue());
740 gen.GenCast<uint16_t, int8_t>(gen.GetRandValue());
741 gen.GenCast<uint16_t, int16_t>(gen.GetRandValue());
742 gen.GenCast<uint16_t, int32_t>(gen.GetRandValue());
743 gen.GenCast<uint16_t, int64_t>(gen.GetRandValue());
744 gen.GenCast<uint16_t, float>(gen.GetRandValue());
745 gen.GenCast<uint16_t, double>(gen.GetRandValue());
746
747 gen.GenCast<uint32_t, uint8_t>(gen.GetRandValue());
748 gen.GenCast<uint32_t, uint16_t>(gen.GetRandValue());
749 gen.GenCast<uint32_t, uint32_t>(gen.GetRandValue());
750 gen.GenCast<uint32_t, uint64_t>(gen.GetRandValue());
751 gen.GenCast<uint32_t, int8_t>(gen.GetRandValue());
752 gen.GenCast<uint32_t, int16_t>(gen.GetRandValue());
753 gen.GenCast<uint32_t, int32_t>(gen.GetRandValue());
754 gen.GenCast<uint32_t, int64_t>(gen.GetRandValue());
755 gen.GenCast<uint32_t, float>(gen.GetRandValue());
756 gen.GenCast<uint32_t, double>(gen.GetRandValue());
757
758 gen.GenCast<uint64_t, uint8_t>(gen.GetRandValue());
759 gen.GenCast<uint64_t, uint16_t>(gen.GetRandValue());
760 gen.GenCast<uint64_t, uint32_t>(gen.GetRandValue());
761 gen.GenCast<uint64_t, uint64_t>(gen.GetRandValue());
762 gen.GenCast<uint64_t, int8_t>(gen.GetRandValue());
763 gen.GenCast<uint64_t, int16_t>(gen.GetRandValue());
764 gen.GenCast<uint64_t, int32_t>(gen.GetRandValue());
765 gen.GenCast<uint64_t, int64_t>(gen.GetRandValue());
766 gen.GenCast<uint64_t, float>(gen.GetRandValue());
767 gen.GenCast<uint64_t, double>(gen.GetRandValue());
768
769 // We DON'T support cast from float32 to int8/16.
770 gen.GenCast<float, uint32_t>(gen.GetRandValue());
771 gen.GenCast<float, uint64_t>(gen.GetRandValue());
772 gen.GenCast<float, int32_t>(gen.GetRandValue());
773 gen.GenCast<float, int64_t>(gen.GetRandValue());
774 gen.GenCast<float, float>(gen.GetRandValue());
775 gen.GenCast<float, double>(gen.GetRandValue());
776
777 // We DON'T support cast from float64 to int8/16.
778 gen.GenCast<double, uint32_t>(gen.GetRandValue());
779 gen.GenCast<double, uint64_t>(gen.GetRandValue());
780 gen.GenCast<double, int32_t>(gen.GetRandValue());
781 gen.GenCast<double, int64_t>(gen.GetRandValue());
782 gen.GenCast<double, float>(gen.GetRandValue());
783 gen.GenCast<double, double>(gen.GetRandValue());
784 }
785
OneTestSign(ArithGenerator & gen,Opcode opc)786 void OneTestSign(ArithGenerator &gen, Opcode opc)
787 {
788 gen.Generate<int8_t>(opc, gen.GetRandValues());
789 gen.Generate<int16_t>(opc, gen.GetRandValues());
790 gen.Generate<int32_t>(opc, gen.GetRandValues());
791 gen.Generate<int64_t>(opc, gen.GetRandValues());
792 }
793
OneTestFP(ArithGenerator & gen,Opcode opc)794 void OneTestFP(ArithGenerator &gen, Opcode opc)
795 {
796 gen.Generate<float>(opc, gen.GetRandValues());
797 gen.Generate<double>(opc, gen.GetRandValues());
798 }
799
OneTestShift(ArithGenerator & gen,Opcode opc)800 void OneTestShift(ArithGenerator &gen, Opcode opc)
801 {
802 gen.Generate<uint64_t>(opc, {0x8899aabbccddeeff, 32});
803 gen.Generate<uint64_t>(opc, {0x8899aabbccddeeff, 32 + 64});
804 gen.Generate<int64_t>(opc, {0x8899aabbccddeeff, 32});
805 gen.Generate<int64_t>(opc, {0x8899aabbccddeeff, 32 + 64});
806 gen.Generate<uint32_t>(opc, {0xccddeeff, 16});
807 gen.Generate<uint32_t>(opc, {0xccddeeff, 16 + 32});
808 gen.Generate<int32_t>(opc, {0xccddeeff, 0xffffffff});
809 gen.Generate<int32_t>(opc, {0xccddeeff, 16});
810 gen.Generate<int32_t>(opc, {0xccddeeff, 16 + 32});
811 gen.Generate<uint16_t>(opc, {0xeeff, 8});
812 gen.Generate<uint16_t>(opc, {0xeeff, 8 + 16});
813 gen.Generate<int16_t>(opc, {0xeeff, 8});
814 gen.Generate<int16_t>(opc, {0xeeff, 8 + 16});
815 gen.Generate<uint8_t>(opc, {0xff, 4});
816 gen.Generate<uint8_t>(opc, {0xff, 4 + 8});
817 gen.Generate<int8_t>(opc, {0xff, 4});
818 gen.Generate<int8_t>(opc, {0xff, 4 + 8});
819 }
820
821 // There is not enough memory in the arena allocator, so it is divided into 2 parts
RandomTestsPart1()822 void RandomTestsPart1()
823 {
824 ArenaAllocator alloc(SpaceType::SPACE_TYPE_COMPILER);
825 ArenaAllocator local_alloc(SpaceType::SPACE_TYPE_COMPILER);
826 InstGenerator inst_gen(alloc);
827 GraphCreator graph_creator(alloc, local_alloc);
828 ArithGenerator stat_gen(inst_gen, graph_creator);
829
830 OneTest(stat_gen, Opcode::Add);
831 OneTestFP(stat_gen, Opcode::Add);
832
833 OneTest(stat_gen, Opcode::Sub);
834 OneTestFP(stat_gen, Opcode::Sub);
835
836 OneTest(stat_gen, Opcode::Mul);
837 OneTestFP(stat_gen, Opcode::Mul);
838
839 OneTest(stat_gen, Opcode::Div);
840 OneTestFP(stat_gen, Opcode::Div);
841
842 OneTest(stat_gen, Opcode::Mod);
843 // Disabled, because external fmod() call is currently x86_64 -- incompatible with aarch64 runtime :(
844 // stat_gen Opcode::Mod
845
846 OneTest(stat_gen, Opcode::Min);
847 OneTestFP(stat_gen, Opcode::Min);
848
849 OneTest(stat_gen, Opcode::Max);
850 OneTestFP(stat_gen, Opcode::Max);
851
852 OneTest(stat_gen, Opcode::Shl);
853 OneTest(stat_gen, Opcode::Shr);
854 OneTest(stat_gen, Opcode::AShr);
855 }
856
RandomTestsPart2()857 void RandomTestsPart2()
858 {
859 ArenaAllocator alloc(SpaceType::SPACE_TYPE_COMPILER);
860 ArenaAllocator local_alloc(SpaceType::SPACE_TYPE_COMPILER);
861 InstGenerator inst_gen(alloc);
862 GraphCreator graph_creator(alloc, local_alloc);
863 ArithGenerator stat_gen(inst_gen, graph_creator);
864
865 OneTest(stat_gen, Opcode::And);
866 // Float unsupported
867
868 OneTest(stat_gen, Opcode::Or);
869 // Float unsupported
870
871 OneTest(stat_gen, Opcode::Xor);
872 // Float unsupported
873
874 OneTest(stat_gen, Opcode::Neg);
875
876 OneTestSign(stat_gen, Opcode::Abs);
877
878 OneTest(stat_gen, Opcode::Not);
879
880 OneTest(stat_gen, Opcode::AddI);
881
882 OneTest(stat_gen, Opcode::SubI);
883
884 OneTest(stat_gen, Opcode::ShlI);
885
886 OneTest(stat_gen, Opcode::ShrI);
887
888 OneTest(stat_gen, Opcode::AShrI);
889
890 OneTest(stat_gen, Opcode::AndI);
891
892 OneTest(stat_gen, Opcode::OrI);
893
894 OneTest(stat_gen, Opcode::XorI);
895
896 // Special case for Case-instruction - generate inputs types.
897 OneTestCast(stat_gen);
898 }
899
NotRandomTests()900 void NotRandomTests()
901 {
902 ArenaAllocator alloc(SpaceType::SPACE_TYPE_COMPILER);
903 ArenaAllocator local_alloc(SpaceType::SPACE_TYPE_COMPILER);
904 InstGenerator inst_gen(alloc);
905 GraphCreator graph_creator(alloc, local_alloc);
906 ArithGenerator stat_gen(inst_gen, graph_creator);
907
908 stat_gen.Generate<uint64_t>(Opcode::Min, {UINT64_MAX, 0});
909 stat_gen.Generate<uint64_t>(Opcode::Min, {0, UINT64_MAX});
910 stat_gen.Generate<int64_t>(Opcode::Min, {0, UINT64_MAX});
911 stat_gen.Generate<int64_t>(Opcode::Min, {0, UINT64_MAX});
912 OneTestShift(stat_gen, Opcode::Shl);
913 OneTestShift(stat_gen, Opcode::Shr);
914 OneTestShift(stat_gen, Opcode::AShr);
915 }
916
TEST_F(InstGeneratorTest,GenArithVixlCode)917 TEST_F(InstGeneratorTest, GenArithVixlCode)
918 {
919 for (uint64_t i = 0; i < ArithGenerator::ITERATION; ++i) {
920 RandomTestsPart1();
921 RandomTestsPart2();
922 }
923 NotRandomTests();
924 }
925
926 /**
927 * Check that all possible instructions that introduce a reference as a result
928 * are handled in analysis. On failed test add a case to AliasVisitor.
929 */
TEST_F(InstGeneratorTest,AliasAnalysisSupportTest)930 TEST_F(InstGeneratorTest, AliasAnalysisSupportTest)
931 {
932 ArenaAllocator inst_alloc(SpaceType::SPACE_TYPE_COMPILER);
933 InstGenerator inst_gen(inst_alloc);
934
935 ArenaAllocator graph_alloc(SpaceType::SPACE_TYPE_COMPILER);
936 ArenaAllocator graph_local_alloc(SpaceType::SPACE_TYPE_COMPILER);
937 GraphCreator graph_creator(graph_alloc, graph_local_alloc);
938
939 for (auto op : inst_gen.GetMap()) {
940 auto it = inst_gen.Generate(op.first);
941 for (auto i = it.begin(); i != it.end(); ++i) {
942 if ((*i)->GetType() != DataType::REFERENCE) {
943 continue;
944 }
945 auto graph = graph_creator.GenerateGraph(*i);
946
947 auto finalizer = [&graph]([[maybe_unused]] void *ptr) {
948 if (graph != nullptr) {
949 graph->~Graph();
950 }
951 };
952 std::unique_ptr<void, decltype(finalizer)> fin(&finalizer, finalizer);
953
954 graph->RunPass<AliasAnalysis>();
955 EXPECT_TRUE(graph->IsAnalysisValid<AliasAnalysis>());
956 }
957 }
958 }
959 } // namespace panda::compiler
960