• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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>(&param_1, &param_2, &param_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