• 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 <climits>
17 
18 #include <random>
19 #include <gtest/gtest.h>
20 
21 #include "mem/code_allocator.h"
22 #include "mem/pool_manager.h"
23 #include "target/aarch32/target.h"
24 #include "mem/base_mem_stats.h"
25 
26 template <typename T>
27 const char *TypeName(void);
28 template <typename T>
TypeName(T)29 const char *TypeName(T)
30 {
31     return TypeName<T>();
32 }
33 
34 #define CLASS_NAME(type)             \
35     template <>                      \
36     const char *TypeName<type>(void) \
37     {                                \
38         return #type;                \
39     }
40 
41 CLASS_NAME(int8_t)
42 CLASS_NAME(int16_t)
43 CLASS_NAME(int32_t)
44 CLASS_NAME(int64_t)
45 CLASS_NAME(uint8_t)
46 CLASS_NAME(uint16_t)
47 CLASS_NAME(uint32_t)
48 CLASS_NAME(uint64_t)
49 CLASS_NAME(float)
50 CLASS_NAME(double)
51 
52 const uint64_t SEED = 0x1234;
53 #ifndef PANDA_NIGHTLY_TEST_ON
54 const uint64_t ITERATION = 40;
55 #else
56 const uint64_t ITERATION = 4000;
57 #endif
58 static inline auto random_generator = std::mt19937_64(SEED);
59 
60 // Max and min exponent on the basus of two float and double
61 static const float MIN_EXP_BASE2_FLOAT = std::log2(FLT_MIN);
62 static const float MAX_EXP_BASE2_FLOAT = std::log2(FLT_MAX) - 1.0;
63 static const double MIN_EXP_BASE2_DOUBLE = std::log2(DBL_MIN);
64 static const double MAX_EXP_BASE2_DOUBLE = std::log2(DBL_MAX) - 1.0;
65 
66 // Masks for generete denormal float numbers
67 static const uint32_t MASK_DENORMAL_FLOAT = 0x807FFFFF;
68 static const uint64_t MASK_DENORMAL_DOUBLE = 0x800FFFFFFFFFFFFF;
69 
70 // A32-encoding of immediates allow to encode any value in range [0, 255],
71 // other values should be encoded as masks.
72 static const uint64_t MAX_IMM_VALUE = 0x0FF;
73 
74 template <typename T>
random_gen()75 static T random_gen()
76 {
77     auto gen {random_generator()};
78 
79     if constexpr (std::is_integral_v<T>) {
80         return gen;
81     } else {
82         switch (gen % 20U) {
83             case (0U):
84                 return std::numeric_limits<T>::quiet_NaN();
85 
86             case (1U):
87                 return std::numeric_limits<T>::infinity();
88 
89             case (2U):
90                 return -std::numeric_limits<T>::infinity();
91 
92             case (3U): {
93                 if constexpr (std::is_same_v<T, float>) {
94                     return panda::bit_cast<float, uint32_t>(gen & MASK_DENORMAL_FLOAT);
95                 } else {
96                     return panda::bit_cast<double, uint64_t>(gen & MASK_DENORMAL_DOUBLE);
97                 }
98             }
99             default:
100                 break;
101         }
102 
103         // Uniform distribution floating value
104         std::uniform_real_distribution<T> dis_num(1.0, 2.0);
105         int8_t sign = (gen % 2) == 0 ? 1 : -1;
106         if constexpr (std::is_same_v<T, float>) {
107             std::uniform_real_distribution<float> dis(MIN_EXP_BASE2_FLOAT, MAX_EXP_BASE2_FLOAT);
108             return sign * dis_num(random_generator) * std::pow(2.0F, dis(random_generator));
109         } else if constexpr (std::is_same_v<T, double>) {
110             std::uniform_real_distribution<double> dis(MIN_EXP_BASE2_DOUBLE, MAX_EXP_BASE2_DOUBLE);
111             return sign * dis_num(random_generator) * std::pow(2.0, dis(random_generator));
112         }
113 
114         UNREACHABLE();
115     }
116 }
117 
118 namespace panda::compiler {
119 class Encoder32Test : public ::testing::Test {
120 public:
Encoder32Test()121     Encoder32Test()
122     {
123         panda::mem::MemConfig::Initialize(64_MB, 64_MB, 64_MB, 32_MB);
124         PoolManager::Initialize();
125         allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER);
126         encoder_ = Encoder::Create(allocator_, Arch::AARCH32, false);
127         encoder_->InitMasm();
128         regfile_ = RegistersDescription::Create(allocator_, Arch::AARCH32);
129         callconv_ = CallingConvention::Create(allocator_, encoder_, regfile_, Arch::AARCH32);
130         mem_stats_ = new BaseMemStats();
131         code_alloc_ = new (std::nothrow) CodeAllocator(mem_stats_);
132     }
~Encoder32Test()133     ~Encoder32Test()
134     {
135         encoder_->~Encoder();
136         delete allocator_;
137         delete code_alloc_;
138         delete mem_stats_;
139         PoolManager::Finalize();
140         panda::mem::MemConfig::Finalize();
141     }
142 
GetCodeAllocator()143     CodeAllocator *GetCodeAllocator()
144     {
145         return code_alloc_;
146     }
147 
ResetCodeAllocator(void * ptr,size_t size)148     void ResetCodeAllocator(void *ptr, size_t size)
149     {
150         os::mem::MapRange<std::byte> mem_range(static_cast<std::byte *>(ptr), size);
151         mem_range.MakeReadWrite();
152         delete code_alloc_;
153         code_alloc_ = new (std::nothrow) CodeAllocator(mem_stats_);
154     }
155 
GetAllocator()156     ArenaAllocator *GetAllocator()
157     {
158         return allocator_;
159     }
160 
GetEncoder()161     Encoder *GetEncoder()
162     {
163         return encoder_;
164     }
165 
GetRegfile()166     RegistersDescription *GetRegfile()
167     {
168         return regfile_;
169     }
170 
GetCallconv()171     CallingConvention *GetCallconv()
172     {
173         return callconv_;
174     }
175 
GetCursor()176     size_t GetCursor()
177     {
178         return curr_cursor_;
179     }
180 
181     // Warning! Do not use multiply times with different types!
GetParameter(TypeInfo type,int id=0)182     Reg GetParameter(TypeInfo type, int id = 0)
183     {
184         if (id == 0) {
185             auto default_param = Target::Current().GetParamReg(0);
186             return Reg(default_param.GetId(), type);
187         }
188         // Not supported id > 1
189         ASSERT(id == 1);
190         if (type == FLOAT64_TYPE) {
191             auto default_param = Target::Current().GetParamReg(2);
192             return Reg(default_param.GetId(), type);
193         }
194 
195         if (type.GetSize() == DOUBLE_WORD_SIZE) {
196             auto default_param = Target::Current().GetParamReg(2);
197             return Reg(default_param.GetId(), type);
198         }
199         auto default_param = Target::Current().GetParamReg(1);
200         return Reg(default_param.GetId(), type);
201     }
202 
203     template <typename T>
PreWork(ArenaVector<Reg> * saved_regs=nullptr)204     void PreWork(ArenaVector<Reg> *saved_regs = nullptr)
205     {
206         // Curor need to encode multiply tests due one execution
207         curr_cursor_ = 0;
208         encoder_->SetCursorOffset(0);
209 
210         ArenaVector<Reg> used_regs(GetAllocator()->Adapter());
211         if (saved_regs != nullptr) {
212             used_regs.insert(used_regs.end(), saved_regs->begin(), saved_regs->end());
213         }
214         for (auto reg_code : aarch32::AARCH32_TMP_REG) {
215             used_regs.emplace_back(Reg(reg_code, INT32_TYPE));
216         }
217         GetRegfile()->SetUsedRegs(used_regs);
218         callconv_->GeneratePrologue(FrameInfo::FullPrologue());
219 
220         // Move real parameters to getter parameters
221 #if (PANDA_TARGET_ARM32_ABI_HARD)
222         if constexpr (std::is_same<float, T>::value) {
223             auto param2 = GetParameter(TypeInfo(T(0)), 1);
224             auto s1_register = vixl::aarch32::s1;
225             static_cast<aarch32::Aarch32Encoder *>(GetEncoder())
226                 ->GetMasm()
227                 ->Vmov(aarch32::VixlVReg(param2).S(), s1_register);
228         }
229 #else
230         if constexpr (std::is_same<float, T>::value) {
231             auto param1 = GetParameter(TypeInfo(T(0)), 0);
232             auto param2 = GetParameter(TypeInfo(T(0)), 1);
233 
234             auto stored_value_1 = GetParameter(TypeInfo(uint32_t(0)), 0);
235             auto stored_value_2 = GetParameter(TypeInfo(uint32_t(0)), 1);
236 
237             GetEncoder()->EncodeMov(param1, stored_value_1);
238             GetEncoder()->EncodeMov(param2, stored_value_2);
239         }
240         if constexpr (std::is_same<double, T>::value) {
241             auto param1 = GetParameter(TypeInfo(T(0)), 0);
242             auto param2 = GetParameter(TypeInfo(T(0)), 1);
243 
244             auto stored_value_1 = GetParameter(TypeInfo(uint64_t(0)), 0);
245             auto stored_value_2 = GetParameter(TypeInfo(uint64_t(0)), 1);
246             GetEncoder()->EncodeMov(param1, stored_value_1);
247             GetEncoder()->EncodeMov(param2, stored_value_2);
248         }
249 #endif
250     }
251 
252     template <typename T>
PostWork()253     void PostWork()
254     {
255         // Move getted parameters to real return value
256 #if (PANDA_TARGET_ARM32_ABI_HARD)
257         // Default parameter 0 == return value
258 #else
259         if constexpr (std::is_same<float, T>::value) {
260             auto param = GetParameter(TypeInfo(T(0)), 0);
261             auto return_reg = GetParameter(TypeInfo(uint32_t(0)), 0);
262             GetEncoder()->EncodeMov(return_reg, param);
263         }
264         if constexpr (std::is_same<double, T>::value) {
265             auto param = GetParameter(TypeInfo(T(0)), 0);
266             auto return_reg = GetParameter(TypeInfo(uint64_t(0)), 0);
267             GetEncoder()->EncodeMov(return_reg, param);
268         }
269 #endif
270         callconv_->GenerateEpilogue(FrameInfo::FullPrologue(), []() {});
271         encoder_->Finalize();
272     }
273 
Dump(bool enabled)274     void Dump(bool enabled)
275     {
276         if (enabled) {
277             auto size = callconv_->GetCodeSize() - curr_cursor_;
278             for (uint32_t i = curr_cursor_; i < curr_cursor_ + size;) {
279                 i = encoder_->DisasmInstr(std::cout, i, 0);
280                 std::cout << std::endl;
281             }
282         }
283     }
284 
285     template <typename T, typename U>
CallCode(const T & param,const U & result)286     bool CallCode(const T &param, const U &result)
287     {
288         return CallCodeVariadic<U, T>(result, param);
289     }
290 
291     template <typename T, typename U>
CallCode(const T & param1,const T & param2,const U & result)292     bool CallCode(const T &param1, const T &param2, const U &result)
293     {
294         return CallCodeVariadic<U, T, T>(result, param1, param2);
295     }
296 
297     template <typename T>
CallCode(const T & param,const T & result)298     bool CallCode(const T &param, const T &result)
299     {
300         return CallCodeVariadic<T, T>(result, param);
301     }
302 
303     // Using max size type: type result "T" or 32bit to check result, because in our ISA min type is 32bit.
304     template <typename T, typename U = std::conditional_t<
305                               (std::is_integral_v<T> && (sizeof(T) * BYTE_SIZE < WORD_SIZE)), uint32_t, T>>
WidenIntegralArgToUint32(T arg)306     U WidenIntegralArgToUint32(T arg)
307     {
308         return static_cast<U>(arg);
309     }
310 
311     template <typename U, typename... Args>
CallCodeVariadic(const U result,Args...args)312     bool CallCodeVariadic(const U result, Args... args)
313     {
314         return CallCodeVariadicImpl<>(WidenIntegralArgToUint32<>(result), WidenIntegralArgToUint32<>(args)...);
315     }
316 
317     template <size_t param_idx, bool is_binary>
PrintParams()318     void PrintParams()
319     {
320     }
321 
322     template <size_t param_idx, bool is_binary, typename Arg, typename... Args>
PrintParams(Arg arg,Args...args)323     void PrintParams(Arg arg, Args... args)
324     {
325         std::cerr << " param" << param_idx << "=";
326         if constexpr (is_binary && std::is_same<double, Arg>::value) {
327             std::cerr << bit_cast<uint64_t>(arg);
328         } else if constexpr (is_binary && std::is_same<float, Arg>::value) {
329             std::cerr << bit_cast<uint32_t>(arg);
330         } else {
331             std::cerr << arg;
332         }
333         PrintParams<param_idx + 1, is_binary>(args...);
334     }
335 
336     template <typename U, typename... Args>
CallCodeVariadicImpl(const U result,Args...args)337     bool CallCodeVariadicImpl(const U result, Args... args)
338     {
339         using funct_ptr = U (*)(Args...);
340         auto size = callconv_->GetCodeSize() - curr_cursor_;
341         void *offset = (static_cast<uint8_t *>(callconv_->GetCodeEntry()));
342         void *ptr = code_alloc_->AllocateCode(size, offset);
343         auto func = reinterpret_cast<funct_ptr>(ptr);
344         const U curr_result = func(args...);
345         ResetCodeAllocator(ptr, size);
346         bool ret = false;
347         if constexpr (std::is_floating_point_v<U>) {
348             ret = (curr_result == result) || (std::isnan(curr_result) && std::isnan(result));
349         } else {
350             ret = (curr_result - result == 0);
351         }
352 
353         if (!ret) {
354             std::cerr << std::hex << "Failed CallCode for";
355             PrintParams<1, false>(args...);
356             std::cerr << " and result=" << result << " current_result=" << curr_result << "\n";
357             if constexpr (std::is_floating_point_v<U> || (std::is_floating_point_v<Args> || ...)) {
358                 std::cerr << "In binary :";
359                 PrintParams<1, true>(args...);
360                 if constexpr (std::is_same<double, U>::value) {
361                     std::cerr << " reslt=" << bit_cast<uint64_t>(result);
362                     std::cerr << " current_reslt=" << bit_cast<uint64_t>(curr_result);
363                 } else if constexpr (std::is_same<float, U>::value) {
364                     std::cerr << " result=" << bit_cast<uint32_t>(result);
365                     std::cerr << " current_result=" << bit_cast<uint32_t>(curr_result);
366                 } else {
367                     std::cerr << " result=" << result;
368                     std::cerr << " current_result=" << curr_result;
369                 }
370                 std::cerr << "\n";
371             }
372 #if !defined(NDEBUG)
373             Dump(true);
374 #endif
375         }
376         return ret;
377     }
378 
379     template <typename T>
CallCode(const T & param1,const T & param2,const T & result)380     bool CallCode(const T &param1, const T &param2, const T &result)
381     {
382         return CallCodeVariadic<T, T, T>(result, param1, param2);
383     }
384 
385     template <typename T>
CallCodeStore(uint32_t address,T param)386     T CallCodeStore(uint32_t address, T param)
387     {
388         using funct_ptr = T (*)(uint32_t param1, T param2);
389         auto size = callconv_->GetCodeSize() - curr_cursor_;
390         void *offset = (static_cast<uint8_t *>(callconv_->GetCodeEntry()));
391         void *ptr = code_alloc_->AllocateCode(size, offset);
392         auto func = reinterpret_cast<funct_ptr>(ptr);
393         const T curr_result = func(address, param);
394         ResetCodeAllocator(ptr, size);
395         return curr_result;
396     }
397 
398     template <typename T, typename U>
CallCodeCall(T param1,T param2)399     U CallCodeCall(T param1, T param2)
400     {
401         using funct_ptr = U (*)(T param1, T param2);
402         auto size = callconv_->GetCodeSize() - curr_cursor_;
403         void *offset = (static_cast<uint8_t *>(callconv_->GetCodeEntry()));
404         void *ptr = code_alloc_->AllocateCode(size, offset);
405         auto func = reinterpret_cast<funct_ptr>(ptr);
406         const U curr_result = func(param1, param2);
407         ResetCodeAllocator(ptr, size);
408         return curr_result;
409     }
410 
411 private:
412     ArenaAllocator *allocator_ {nullptr};
413     Encoder *encoder_ {nullptr};
414     RegistersDescription *regfile_ {nullptr};
415     CallingConvention *callconv_ {nullptr};
416     CodeAllocator *code_alloc_ {nullptr};
417     BaseMemStats *mem_stats_ {nullptr};
418     size_t curr_cursor_ {0};
419 };
420 
421 /*
422     Tests work with one default parameter:
423         r0 - for 32-bit parameter
424         r0 + r1 - for 64-bit parameter
425 
426         return value - r0 for 32-bit parameter
427                        r0 & r1 - for 64-bit parameter
428 */
429 
430 template <typename T>
TestNeg(Encoder32Test * test)431 bool TestNeg(Encoder32Test *test)
432 {
433     // Initialize
434     test->PreWork<T>();
435     // First type-dependency
436     auto param = test->GetParameter(TypeInfo(T(0)));
437     // Main test call
438     test->GetEncoder()->EncodeNeg(param, param);
439     // Finalize
440     test->PostWork<T>();
441 
442     // If encode unsupported - now print error
443     if (!test->GetEncoder()->GetResult()) {
444         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
445         return false;
446     }
447     // Change this for enable print disasm
448     test->Dump(false);
449 
450     // Main test loop:
451     for (uint64_t i = 0; i < ITERATION; ++i) {
452         // Second type-dependency
453         T tmp = random_gen<T>();
454         // Deduced conflicting types for parameter
455 
456         // Main check - compare parameter and
457         // return value
458         if (!test->CallCode<T>(tmp, -tmp)) {
459             return false;
460         }
461     }
462 
463     if constexpr (std::is_floating_point_v<T>) {
464         T nan = std::numeric_limits<T>::quiet_NaN();
465 
466         if (!test->CallCode<T>(nan, nan)) {
467             return false;
468         }
469     }
470 
471     return true;
472 }
473 
TEST_F(Encoder32Test,NegTest)474 TEST_F(Encoder32Test, NegTest)
475 {
476     EXPECT_TRUE(TestNeg<int8_t>(this));
477     EXPECT_TRUE(TestNeg<int16_t>(this));
478     EXPECT_TRUE(TestNeg<int32_t>(this));
479     EXPECT_TRUE(TestNeg<int64_t>(this));
480     EXPECT_TRUE(TestNeg<float>(this));
481     EXPECT_TRUE(TestNeg<double>(this));
482 }
483 
484 template <typename T>
TestNot(Encoder32Test * test)485 bool TestNot(Encoder32Test *test)
486 {
487     // Initialize
488     test->PreWork<T>();
489     // First type-dependency
490     auto param = test->GetParameter(TypeInfo(T(0)));
491     // Main test call
492     test->GetEncoder()->EncodeNot(param, param);
493     // Finalize
494     test->PostWork<T>();
495 
496     // If encode unsupported - now print error
497     if (!test->GetEncoder()->GetResult()) {
498         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
499         return false;
500     }
501     // Change this for enable print disasm
502     test->Dump(false);
503 
504     // Main test loop:
505     for (uint64_t i = 0; i < ITERATION; ++i) {
506         // Second type-dependency
507         T tmp = random_gen<T>();
508 
509         // Deduced conflicting types for parameter
510 
511         // Main check - compare parameter and
512         // return value
513         if (!test->CallCode<T>(tmp, ~tmp)) {
514             return false;
515         }
516     }
517     return true;
518 }
519 
TEST_F(Encoder32Test,NotTest)520 TEST_F(Encoder32Test, NotTest)
521 {
522     EXPECT_TRUE(TestNot<int8_t>(this));
523     EXPECT_TRUE(TestNot<int16_t>(this));
524     EXPECT_TRUE(TestNot<int32_t>(this));
525     EXPECT_TRUE(TestNot<int64_t>(this));
526 
527     EXPECT_TRUE(TestNot<uint8_t>(this));
528     EXPECT_TRUE(TestNot<uint16_t>(this));
529     EXPECT_TRUE(TestNot<uint32_t>(this));
530     EXPECT_TRUE(TestNot<uint64_t>(this));
531 }
532 
533 template <typename T>
TestMov(Encoder32Test * test)534 bool TestMov(Encoder32Test *test)
535 {
536     // Initialize
537     test->PreWork<T>();
538     // First type-dependency
539     auto param = test->GetParameter(TypeInfo(T(0)));
540     // Main test call
541     test->GetEncoder()->EncodeMov(param, param);
542     // Finalize
543     test->PostWork<T>();
544 
545     // If encode unsupported - now print error
546     if (!test->GetEncoder()->GetResult()) {
547         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
548         return false;
549     }
550     // Change this for enable print disasm
551     test->Dump(false);
552 
553     // Main test loop:
554     for (uint64_t i = 0; i < ITERATION; ++i) {
555         // Second type-dependency
556         T tmp = random_gen<T>();
557         // Deduced conflicting types for parameter
558 
559         // Main check - compare parameter and
560         // return value
561         if (!test->CallCode<T>(tmp, tmp)) {
562             return false;
563         }
564     }
565 
566     if constexpr (std::is_floating_point_v<T>) {
567         T nan = std::numeric_limits<T>::quiet_NaN();
568 
569         if (!test->CallCode<T>(nan, nan)) {
570             return false;
571         }
572     }
573 
574     return true;
575 }
576 
577 template <typename Src, typename Dst>
TestMov2(Encoder32Test * test)578 bool TestMov2(Encoder32Test *test)
579 {
580     static_assert(sizeof(Src) == sizeof(Dst));
581     // Initialize
582     test->PreWork<Src>();
583     // First type-dependency
584     auto input = test->GetParameter(TypeInfo(Src(0)), 0);
585     auto output = test->GetParameter(TypeInfo(Dst(0)), 0);
586     // Main test call
587     test->GetEncoder()->EncodeMov(output, input);
588     // Finalize
589     test->PostWork<Dst>();
590 
591     // If encode unsupported - now print error
592     if (!test->GetEncoder()->GetResult()) {
593         std::cerr << "Unsupported for " << TypeName<Src>() << ", " << TypeName<Dst>() << "\n";
594         return false;
595     }
596     // Change this for enable print disasm
597     test->Dump(false);
598 
599     // Main test loop:
600     for (uint64_t i = 0; i < ITERATION; ++i) {
601         // Second type-dependency
602         Src src = random_gen<Src>();
603         Dst dst = bit_cast<Dst>(src);
604         // Deduced conflicting types for parameter
605 
606         // Main check - compare parameter and
607         // return value
608         if (!test->CallCode<Src, Dst>(src, dst)) {
609             return false;
610         }
611     }
612 
613     if constexpr (std::is_floating_point_v<Src>) {
614         Src nan = std::numeric_limits<Src>::quiet_NaN();
615         Dst dst_nan = bit_cast<Dst>(nan);
616 
617         if (!test->CallCode<Src, Dst>(nan, dst_nan)) {
618             return false;
619         }
620     }
621 
622     return true;
623 }
624 
TEST_F(Encoder32Test,MovTest)625 TEST_F(Encoder32Test, MovTest)
626 {
627     EXPECT_TRUE(TestMov<int8_t>(this));
628     EXPECT_TRUE(TestMov<int16_t>(this));
629     EXPECT_TRUE(TestMov<int32_t>(this));
630     EXPECT_TRUE(TestMov<int64_t>(this));
631     EXPECT_TRUE(TestMov<float>(this));
632     EXPECT_TRUE(TestMov<double>(this));
633 
634     EXPECT_TRUE((TestMov2<float, int32_t>(this)));
635     EXPECT_TRUE((TestMov2<double, int64_t>(this)));
636     EXPECT_TRUE((TestMov2<int32_t, float>(this)));
637     EXPECT_TRUE((TestMov2<int64_t, double>(this)));
638     // TODO (igorban) : add MOVI instructions
639     // & support uint64_t mov
640 }
641 
642 // Jump w/o cc
TEST_F(Encoder32Test,JumpTest)643 TEST_F(Encoder32Test, JumpTest)
644 {
645     // Test for
646     // EncodeJump(LabelHolder::LabelId label)
647     PreWork<uint32_t>();
648 
649     auto param = Target::Current().GetParamReg(0);
650 
651     auto t1 = GetEncoder()->CreateLabel();
652     auto t2 = GetEncoder()->CreateLabel();
653     auto t3 = GetEncoder()->CreateLabel();
654     auto t4 = GetEncoder()->CreateLabel();
655     auto t5 = GetEncoder()->CreateLabel();
656 
657     GetEncoder()->EncodeAdd(param, param, Imm(0x1));
658     GetEncoder()->EncodeJump(t1);
659     GetEncoder()->EncodeMov(param, Imm(0x0));
660     GetEncoder()->EncodeReturn();
661     // T4
662     GetEncoder()->BindLabel(t4);
663     GetEncoder()->EncodeAdd(param, param, Imm(0x1));
664     GetEncoder()->EncodeJump(t5);
665     // Fail value
666     GetEncoder()->EncodeMov(param, Imm(0x0));
667     GetEncoder()->EncodeReturn();
668 
669     // T2
670     GetEncoder()->BindLabel(t2);
671     GetEncoder()->EncodeAdd(param, param, Imm(0x1));
672     GetEncoder()->EncodeJump(t3);
673     // Fail value
674     GetEncoder()->EncodeMov(param, Imm(0x0));
675     GetEncoder()->EncodeReturn();
676     // T3
677     GetEncoder()->BindLabel(t3);
678     GetEncoder()->EncodeAdd(param, param, Imm(0x1));
679     GetEncoder()->EncodeJump(t4);
680     // Fail value
681     GetEncoder()->EncodeMov(param, Imm(0x0));
682     GetEncoder()->EncodeReturn();
683     // T1
684     GetEncoder()->BindLabel(t1);
685     GetEncoder()->EncodeAdd(param, param, Imm(0x1));
686     GetEncoder()->EncodeJump(t2);
687     // Fail value
688     GetEncoder()->EncodeMov(param, Imm(0x0));
689     GetEncoder()->EncodeReturn();
690     // Sucess exit
691     GetEncoder()->BindLabel(t5);
692     PostWork<uint32_t>();
693 
694     if (!GetEncoder()->GetResult()) {
695         std::cerr << "Unsupported \n";
696         return;
697     }
698     Dump(false);
699     for (uint64_t i = 0; i < ITERATION; ++i) {
700         // Second type-dependency
701         auto tmp = random_gen<int32_t>();
702         // Deduced conflicting types for parameter
703         EXPECT_TRUE(CallCode<int32_t>(tmp, tmp + 5));
704     }
705 }
706 
707 template <typename T, bool NotZero = false>
TestBitTestAndBranch(Encoder32Test * test,T value,int pos,uint32_t expected)708 bool TestBitTestAndBranch(Encoder32Test *test, T value, int pos, uint32_t expected)
709 {
710     test->PreWork<T>();
711     auto param = test->GetParameter(TypeInfo(T(0)));
712     auto ret_val = Target::Current().GetReturnReg();
713     auto label = test->GetEncoder()->CreateLabel();
714 
715     if (NotZero) {
716         test->GetEncoder()->EncodeBitTestAndBranch(label, param, pos, true);
717     } else {
718         test->GetEncoder()->EncodeBitTestAndBranch(label, param, pos, false);
719     }
720     test->GetEncoder()->EncodeMov(ret_val, Imm(1));
721     test->GetCallconv()->GenerateEpilogue(FrameInfo::FullPrologue(), []() {});
722 
723     test->GetEncoder()->BindLabel(label);
724     test->GetEncoder()->EncodeMov(ret_val, Imm(0));
725 
726     test->PostWork<T>();
727 
728     if (!test->GetEncoder()->GetResult()) {
729         std::cerr << "Unsupported for " << TypeName<T>() << std::endl;
730         return false;
731     }
732     // Change this for enable print disasm
733     test->Dump(false);
734 
735     if (!test->CallCode<T>(value, expected)) {
736         std::cerr << "Bit " << pos << " of 0x" << std::hex << value << " is not equal to " << std::dec << expected
737                   << std::endl;
738         return false;
739     }
740 
741     return true;
742 }
743 
744 template <typename T, bool NotZero = false>
TestBitTestAndBranch(Encoder32Test * test)745 bool TestBitTestAndBranch(Encoder32Test *test)
746 {
747     size_t max_pos = std::is_same<uint64_t, T>::value ? 64 : 32;
748     for (size_t i = 0; i < max_pos; i++) {
749         T value = static_cast<T>(1) << i;
750         if (!TestBitTestAndBranch<T, NotZero>(test, value, i, NotZero ? 0 : 1)) {
751             return false;
752         }
753         if (!TestBitTestAndBranch<T, NotZero>(test, ~value, i, NotZero ? 1 : 0)) {
754             return false;
755         }
756     }
757     return true;
758 }
759 
760 template <typename T, Condition cc>
TestJumpCC(Encoder32Test * test)761 bool TestJumpCC(Encoder32Test *test)
762 {
763     bool is_signed = std::is_signed<T>::value;
764     // Initialize
765     test->PreWork<T>();
766     // First type-dependency
767     auto param = test->GetParameter(TypeInfo(T(0)));
768     // Main test call
769     auto ret_val = Target::Current().GetReturnReg();
770 
771     auto tsucc = test->GetEncoder()->CreateLabel();
772 
773     test->GetEncoder()->EncodeJump(tsucc, param, cc);
774     test->GetEncoder()->EncodeMov(param, Imm(0x0));
775     test->GetEncoder()->EncodeMov(ret_val, Imm(0x1));
776     test->GetCallconv()->GenerateEpilogue(FrameInfo::FullPrologue(), []() {});
777 
778     test->GetEncoder()->BindLabel(tsucc);
779     test->GetEncoder()->EncodeMov(param, Imm(0x0));
780     test->GetEncoder()->EncodeMov(ret_val, Imm(0x0));
781     // test->GetEncoder()->EncodeReturn(); < encoded in PostWork<T>
782 
783     // Finalize
784     test->PostWork<T>();
785 
786     // If encode unsupported - now print error
787     if (!test->GetEncoder()->GetResult()) {
788         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
789         return false;
790     }
791     // Change this for enable print disasm
792     test->Dump(false);
793 
794     if constexpr (cc == Condition::EQ) {
795         if (!test->CallCode<T>(0, 0)) {
796             std::cerr << "zero EQ test fail \n";
797             return false;
798         }
799     }
800     if constexpr (cc == Condition::NE) {
801         if (!test->CallCode<T>(0, 1)) {
802             std::cerr << "zero EQ test fail \n";
803             return false;
804         }
805     }
806 
807     // Main test loop:
808     for (uint64_t i = 0; i < ITERATION; ++i) {
809         // Second type-dependency
810         T tmp = random_gen<T>();
811         if (tmp == 0) {  // Only non-zero values
812             tmp += 1;
813         }
814         // Deduced conflicting types for parameter
815 
816         if constexpr (cc == Condition::EQ) {
817             if (!test->CallCode<T>(tmp, 1)) {
818                 std::cerr << "non-zero EQ test fail " << tmp << " \n";
819                 return false;
820             }
821         }
822         if constexpr (cc == Condition::NE) {
823             if (!test->CallCode<T>(tmp, 0)) {
824                 std::cerr << "non-zero EQ test fail " << tmp << " \n";
825                 return false;
826             }
827         }
828         // Main check - compare parameter and
829         // return value
830     }
831     return true;
832 }
833 
834 // Jump with cc
TEST_F(Encoder32Test,JumpCCTest)835 TEST_F(Encoder32Test, JumpCCTest)
836 {
837     //  EncodeJump(LabelHolder::LabelId, Reg, Condition)
838     EXPECT_TRUE((TestJumpCC<int8_t, Condition::EQ>(this)));
839     EXPECT_TRUE((TestJumpCC<int8_t, Condition::NE>(this)));
840     EXPECT_TRUE((TestJumpCC<int16_t, Condition::EQ>(this)));
841     EXPECT_TRUE((TestJumpCC<int16_t, Condition::NE>(this)));
842     EXPECT_TRUE((TestJumpCC<int32_t, Condition::EQ>(this)));
843     EXPECT_TRUE((TestJumpCC<int32_t, Condition::NE>(this)));
844     EXPECT_TRUE((TestJumpCC<int64_t, Condition::NE>(this)));
845     EXPECT_TRUE((TestJumpCC<int64_t, Condition::EQ>(this)));
846     EXPECT_TRUE((TestJumpCC<uint8_t, Condition::EQ>(this)));
847     EXPECT_TRUE((TestJumpCC<uint8_t, Condition::NE>(this)));
848     EXPECT_TRUE((TestJumpCC<uint16_t, Condition::EQ>(this)));
849     EXPECT_TRUE((TestJumpCC<uint16_t, Condition::NE>(this)));
850     EXPECT_TRUE((TestJumpCC<uint32_t, Condition::EQ>(this)));
851     EXPECT_TRUE((TestJumpCC<uint32_t, Condition::NE>(this)));
852     EXPECT_TRUE((TestJumpCC<uint64_t, Condition::EQ>(this)));
853     EXPECT_TRUE((TestJumpCC<uint64_t, Condition::NE>(this)));
854 }
855 
856 /*  From operands.h
857 // Condition also used for tell comparison registers type
858 enum Condition {
859  +  EQ,  // equal to 0
860  +  NE,  // not equal to 0
861     // signed
862  -  LT,  // less
863  -  LE,  // less than or equal
864  -  GT,  // greater
865  -  GE,  // greater than or equal
866     // unsigned - checked from registers
867  -  LO,  // less
868  -  LS,  // less than or equal
869  -  HI,  // greater
870  -  HS,  // greater than or equal
871     // Special arch-dependecy
872  -  MI,  // N set            Negative
873  -  PL,  // N clear          Positive or zero
874  -  VS,  // V set            Overflow.
875  -  VC,  // V clear          No overflow.
876  -  AL,  //                  Always.
877  -  NV,  // Behaves as always/al.
878 
879     INVALID_COND
880 }
881 */
882 
TEST_F(Encoder32Test,BitTestAndBranchZero)883 TEST_F(Encoder32Test, BitTestAndBranchZero)
884 {
885     EXPECT_TRUE(TestBitTestAndBranch<uint32_t>(this));
886     EXPECT_TRUE(TestBitTestAndBranch<uint64_t>(this));
887 }
888 
TEST_F(Encoder32Test,BitTestAndBranchNotZero)889 TEST_F(Encoder32Test, BitTestAndBranchNotZero)
890 {
891     constexpr bool notZero = true;
892     EXPECT_TRUE((TestBitTestAndBranch<uint32_t, notZero>(this)));
893     EXPECT_TRUE((TestBitTestAndBranch<uint64_t, notZero>(this)));
894 }
895 
896 template <typename T>
TestLdr(Encoder32Test * test)897 bool TestLdr(Encoder32Test *test)
898 {
899     bool is_signed = std::is_signed<T>::value;
900     // Initialize
901     test->PreWork<T>();
902     // First type-dependency
903     // Some strange code - default parameter is addres (32 bit)
904 
905     auto param = Target::Current().GetReturnReg();
906     // But return value is cutted by loaded value
907     auto ret_val = test->GetParameter(TypeInfo(T(0)));
908 
909     auto mem = MemRef(param);
910     test->GetEncoder()->EncodeLdr(ret_val, is_signed, mem);
911 
912     // Finalize
913     test->PostWork<T>();
914 
915     // If encode unsupported - now print error
916     if (!test->GetEncoder()->GetResult()) {
917         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
918         return false;
919     }
920     // Change this for enable print disasm
921     test->Dump(false);
922 
923     // Main test loop:
924     for (uint64_t i = 0; i < ITERATION; ++i) {
925         // Second type-dependency
926         T tmp = random_gen<T>();
927         T *ptr = &tmp;
928         // Test : param - Pointer to value
929         //        return - value (loaded by ptr)
930         // Value is resulting type, but call is ptr_type
931         if (!test->CallCode<int32_t, T>(reinterpret_cast<int32_t>(ptr), tmp)) {
932             std::cerr << "Ldr test fail " << tmp << " \n";
933             return false;
934         }
935     }
936     return true;
937 }
938 
939 // Load test
TEST_F(Encoder32Test,LoadTest)940 TEST_F(Encoder32Test, LoadTest)
941 {
942     //  EncodeLdr(Reg dst, bool dst_signed, MemRef mem)
943     EXPECT_TRUE((TestLdr<int8_t>(this)));
944     EXPECT_TRUE((TestLdr<int16_t>(this)));
945     EXPECT_TRUE((TestLdr<int32_t>(this)));
946     EXPECT_TRUE((TestLdr<int64_t>(this)));
947     EXPECT_TRUE((TestLdr<uint8_t>(this)));
948     EXPECT_TRUE((TestLdr<uint16_t>(this)));
949     EXPECT_TRUE((TestLdr<uint32_t>(this)));
950     EXPECT_TRUE((TestLdr<uint64_t>(this)));
951     EXPECT_TRUE((TestLdr<float>(this)));
952     EXPECT_TRUE((TestLdr<double>(this)));
953 
954     // TODO(igorban) : additional test for full memory model:
955     //                 + mem(base + index<<scale + disp)
956 }
957 
958 template <typename T>
TestStr(Encoder32Test * test)959 bool TestStr(Encoder32Test *test)
960 {
961     // Initialize
962     test->PreWork<T>();
963     // First type-dependency
964     // Some strange code - default parameter is addres (32 bit)
965     auto param = Target::Current().GetParamReg(0);
966     // Data to be stored:
967     auto stored_value = test->GetParameter(TypeInfo(T(0)), 1);
968 
969 #if (PANDA_TARGET_ARM32_ABI_HARD)
970     if constexpr ((std::is_same<float, T>::value) || std::is_same<double, T>::value) {
971         stored_value = test->GetParameter(TypeInfo(T(0)), 0);
972     }
973 #else
974     if constexpr (std::is_same<float, T>::value) {
975         stored_value = test->GetParameter(TypeInfo(uint32_t(0)), 1);
976         ScopedTmpRegF32 tmp_float(test->GetEncoder());
977         test->GetEncoder()->EncodeMov(tmp_float, stored_value);
978         stored_value = tmp_float;
979     }
980     if constexpr (std::is_same<double, T>::value) {
981         stored_value = test->GetParameter(TypeInfo(uint64_t(0)), 1);
982         ScopedTmpRegF64 tmp_double(test->GetEncoder());
983         test->GetEncoder()->EncodeMov(tmp_double, stored_value);
984         stored_value = tmp_double;
985     }
986 #endif
987 
988     auto mem = MemRef(param);
989     test->GetEncoder()->EncodeStr(stored_value, mem);
990     // Finalize
991     test->PostWork<T>();
992 
993     // If encode unsupported - now print error
994     if (!test->GetEncoder()->GetResult()) {
995         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
996         return false;
997     }
998     // Change this for enable print disasm
999     test->Dump(false);
1000 
1001     // Main test loop:
1002     for (uint64_t i = 0; i < ITERATION; ++i) {
1003         // Second type-dependency
1004         T tmp = random_gen<T>();
1005         T ret_data = 0;
1006         T *ptr = &ret_data;
1007 
1008         // Test : param - Pointer to value
1009         //        return - value (loaded by ptr)
1010         // Value is resulting type, but call is ptr_type
1011         auto result = test->CallCodeStore<T>(reinterpret_cast<int32_t>(ptr), tmp);
1012         // Store must change ret_data value
1013         if (!(ret_data == tmp || (std::is_floating_point_v<T> && std::isnan(ret_data) && std::isnan(tmp)))) {
1014             std::cerr << std::hex << "Ldr test fail " << tmp << " ret_data = " << ret_data << "\n";
1015             return false;
1016         }
1017     }
1018     return true;
1019 }
1020 
1021 // Simple store test
TEST_F(Encoder32Test,StrTest)1022 TEST_F(Encoder32Test, StrTest)
1023 {
1024     //  EncodeStr(Reg src, MemRef mem)
1025     EXPECT_TRUE((TestStr<int8_t>(this)));
1026     EXPECT_TRUE((TestStr<int16_t>(this)));
1027     EXPECT_TRUE((TestStr<int32_t>(this)));
1028     EXPECT_TRUE((TestStr<int64_t>(this)));
1029     EXPECT_TRUE((TestStr<uint8_t>(this)));
1030     EXPECT_TRUE((TestStr<uint16_t>(this)));
1031     EXPECT_TRUE((TestStr<uint32_t>(this)));
1032     EXPECT_TRUE((TestStr<uint64_t>(this)));
1033     EXPECT_TRUE((TestStr<float>(this)));
1034     EXPECT_TRUE((TestStr<double>(this)));
1035 
1036     // TODO(igorban) : additional test for full memory model:
1037     //                 + mem(base + index<<scale + disp)
1038 }
1039 
1040 // Store immediate test
1041 // TEST_F(Encoder32Test, StiTest)
1042 //  EncodeSti(Imm src, MemRef mem)
1043 
1044 // Store zero upper test
1045 // TEST_F(Encoder32Test, StrzTest)
1046 //  EncodeStrz(Reg src, MemRef mem)
1047 
1048 // Return test ???? What here may be tested
1049 // TEST_F(Encoder32Test, ReturnTest)
1050 //  EncodeReturn()
1051 
foo(uint32_t param1,uint32_t param2)1052 bool foo(uint32_t param1, uint32_t param2)
1053 {
1054     // TODO(igorban): use variables
1055     return (param1 == param2);
1056 }
1057 
1058 using funct_ptr = bool (*)(uint32_t param1, uint32_t param2);
1059 
1060 funct_ptr foo_ptr = &foo;
1061 
1062 // Call Test
TEST_F(Encoder32Test,CallTest)1063 TEST_F(Encoder32Test, CallTest)
1064 {
1065     // Initialize
1066     auto link_reg = Target::Current().GetLinkReg();
1067     ArenaVector<Reg> used_regs(GetAllocator()->Adapter());
1068     used_regs.emplace_back(link_reg);
1069 
1070     PreWork<uint32_t>(&used_regs);
1071 
1072     // Call foo
1073     GetEncoder()->MakeCall(reinterpret_cast<void *>(foo_ptr));
1074     // return value - moved to return value
1075     PostWork<uint32_t>();
1076 
1077     // If encode unsupported - now print error
1078     if (!GetEncoder()->GetResult()) {
1079         std::cerr << "Unsupported Call-instruction \n";
1080         return;
1081     }
1082     // Change this for enable print disasm
1083     Dump(false);
1084     // Equality test
1085     auto tmp1 = random_gen<uint32_t>();
1086     uint32_t tmp2 = tmp1;
1087     // first template arg - parameter type, second - return type
1088     auto result = CallCodeCall<uint32_t, bool>(tmp1, tmp2);
1089     // Store must change ret_data value
1090     if (!result) {
1091         std::cerr << std::hex << "Call test fail tmp1=" << tmp1 << " tmp2=" << tmp2 << " result =" << result << "\n";
1092     }
1093     EXPECT_TRUE(result);
1094 
1095     // Main test loop:
1096     for (uint64_t i = 0; i < ITERATION; ++i) {
1097         // Second type-dependency
1098         auto tmp1 = random_gen<uint32_t>();
1099         auto tmp2 = random_gen<uint32_t>();
1100 
1101         // first template arg - parameter type, second - return type
1102         auto result = CallCodeCall<uint32_t, bool>(tmp1, tmp2);
1103         auto ret_data = (tmp1 == tmp2);
1104 
1105         // Store must change ret_data value
1106         if (result != ret_data) {
1107             std::cerr << std::hex << "Call test fail tmp1=" << tmp1 << " tmp2=" << tmp2 << " ret_data = " << ret_data
1108                       << " result =" << result << "\n";
1109         }
1110         EXPECT_EQ(result, ret_data);
1111     }
1112 }
1113 
1114 template <typename T>
TestAbs(Encoder32Test * test)1115 bool TestAbs(Encoder32Test *test)
1116 {
1117     // Initialize
1118     test->PreWork<T>();
1119 
1120     // First type-dependency
1121     auto param = test->GetParameter(TypeInfo(T(0)));
1122 
1123     // Main test call
1124     test->GetEncoder()->EncodeAbs(param, param);
1125 
1126     // Finalize
1127     test->PostWork<T>();
1128 
1129     // If encode unsupported - now print error
1130     if (!test->GetEncoder()->GetResult()) {
1131         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1132         return false;
1133     }
1134     // Change this for enable print disasm
1135     test->Dump(false);
1136 
1137     // Main test loop:
1138     for (uint64_t i = 0; i < ITERATION; ++i) {
1139         // Second type-dependency
1140         T tmp = random_gen<T>();
1141         // Main check - compare parameter and
1142         // return value
1143         if (!test->CallCode(tmp, std::abs(tmp))) {
1144             return false;
1145         }
1146     }
1147 
1148     if constexpr (std::is_floating_point_v<T>) {
1149         T nan = std::numeric_limits<T>::quiet_NaN();
1150 
1151         if (!test->CallCode<T>(nan, nan)) {
1152             return false;
1153         }
1154     }
1155 
1156     return true;
1157 }
1158 
TEST_F(Encoder32Test,AbsTest)1159 TEST_F(Encoder32Test, AbsTest)
1160 {
1161     EXPECT_TRUE(TestAbs<int8_t>(this));
1162     EXPECT_TRUE(TestAbs<int16_t>(this));
1163     EXPECT_TRUE(TestAbs<int32_t>(this));
1164     // TODO (asidorov, igorban) the test failed in release mode
1165     // TestAbs<int64_t>
1166     EXPECT_TRUE(TestAbs<float>(this));
1167     EXPECT_TRUE(TestAbs<double>(this));
1168 }
1169 
1170 template <typename T>
TestSqrt(Encoder32Test * test)1171 bool TestSqrt(Encoder32Test *test)
1172 {
1173     // Initialize
1174     test->PreWork<T>();
1175 
1176     // First type-dependency
1177     auto param = test->GetParameter(TypeInfo(T(0)));
1178 
1179     // Main test call
1180     test->GetEncoder()->EncodeSqrt(param, param);
1181 
1182     // Finalize
1183     test->PostWork<T>();
1184 
1185     // If encode unsupported - now print error
1186     if (!test->GetEncoder()->GetResult()) {
1187         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1188         return false;
1189     }
1190     // Change this for enable print disasm
1191     test->Dump(false);
1192 
1193     // Main test loop:
1194     for (uint64_t i = 0; i < ITERATION; ++i) {
1195         // Second type-dependency
1196         T tmp = random_gen<T>();
1197         // Main check - compare parameter and
1198         // return value
1199         if (!test->CallCode(tmp, std::sqrt(tmp))) {
1200             return false;
1201         }
1202     }
1203 
1204     if constexpr (std::is_floating_point_v<T>) {
1205         T nan = std::numeric_limits<T>::quiet_NaN();
1206 
1207         if (!test->CallCode<T>(nan, nan)) {
1208             return false;
1209         }
1210     }
1211 
1212     return true;
1213 }
1214 
TEST_F(Encoder32Test,SqrtTest)1215 TEST_F(Encoder32Test, SqrtTest)
1216 {
1217     EXPECT_TRUE(TestSqrt<float>(this));
1218     EXPECT_TRUE(TestSqrt<double>(this));
1219 }
1220 
1221 template <typename T>
TestAdd(Encoder32Test * test)1222 bool TestAdd(Encoder32Test *test)
1223 {
1224     // Initialize
1225     test->PreWork<T>();
1226 
1227     // First type-dependency
1228     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
1229     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
1230 
1231     // Main test call
1232     test->GetEncoder()->EncodeAdd(param1, param1, param2);
1233 
1234     // Finalize
1235     test->PostWork<T>();
1236 
1237     // If encode unsupported - now print error
1238     if (!test->GetEncoder()->GetResult()) {
1239         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1240         return false;
1241     }
1242     // Change this for enable print disasm
1243     test->Dump(false);
1244 
1245     // Main test loop:
1246     for (uint64_t i = 0; i < ITERATION; ++i) {
1247         // Second type-dependency
1248         T tmp1 = random_gen<T>();
1249         T tmp2 = random_gen<T>();
1250         // Deduced conflicting types for parameter
1251 
1252         // Main check - compare parameter and
1253         // return value
1254         if (!test->CallCode<T>(tmp1, tmp2, tmp1 + tmp2)) {
1255             return false;
1256         }
1257     }
1258 
1259     if constexpr (std::is_floating_point_v<T>) {
1260         T nan = std::numeric_limits<T>::quiet_NaN();
1261 
1262         if (!test->CallCode<T>(nan, random_gen<T>(), nan)) {
1263             return false;
1264         }
1265         if (!test->CallCode<T>(random_gen<T>(), nan, nan)) {
1266             return false;
1267         }
1268         if (!test->CallCode<T>(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity(), nan)) {
1269             return false;
1270         }
1271         if (!test->CallCode<T>(std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity(), nan)) {
1272             return false;
1273         }
1274     }
1275 
1276     return true;
1277 }
1278 
TEST_F(Encoder32Test,AddTest)1279 TEST_F(Encoder32Test, AddTest)
1280 {
1281     EXPECT_TRUE(TestAdd<int8_t>(this));
1282     EXPECT_TRUE(TestAdd<int16_t>(this));
1283     EXPECT_TRUE(TestAdd<int32_t>(this));
1284     EXPECT_TRUE(TestAdd<int64_t>(this));
1285     EXPECT_TRUE(TestAdd<float>(this));
1286     EXPECT_TRUE(TestAdd<double>(this));
1287 }
1288 
1289 template <typename T>
TestAddImm(Encoder32Test * test)1290 bool TestAddImm(Encoder32Test *test)
1291 {
1292     // Initialize
1293     test->PreWork<T>();
1294 
1295     // First type-dependency
1296     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
1297     T param2 = random_gen<T>();
1298 
1299     // Main test call
1300     test->GetEncoder()->EncodeAdd(param1, param1, Imm(param2));
1301 
1302     // Finalize
1303     test->PostWork<T>();
1304 
1305     // If encode unsupported - now print error
1306     if (!test->GetEncoder()->GetResult()) {
1307         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1308         return false;
1309     }
1310     // Change this for enable print disasm
1311     test->Dump(false);
1312 
1313     // Main test loop:
1314     for (uint64_t i = 0; i < ITERATION; ++i) {
1315         // Second type-dependency
1316         T tmp1 = random_gen<T>();
1317         // Deduced conflicting types for parameter
1318 
1319         // Main check - compare parameter and
1320         // return value
1321         if (!test->CallCode<T>(tmp1, tmp1 + param2)) {
1322             return false;
1323         }
1324     }
1325     return true;
1326 }
1327 
TEST_F(Encoder32Test,AddImmTest)1328 TEST_F(Encoder32Test, AddImmTest)
1329 {
1330     EXPECT_TRUE(TestAddImm<int8_t>(this));
1331     EXPECT_TRUE(TestAddImm<int16_t>(this));
1332     EXPECT_TRUE(TestAddImm<int32_t>(this));
1333     EXPECT_TRUE(TestAddImm<int64_t>(this));
1334 }
1335 
1336 template <typename T>
TestSub(Encoder32Test * test)1337 bool TestSub(Encoder32Test *test)
1338 {
1339     // Initialize
1340     test->PreWork<T>();
1341 
1342     // First type-dependency
1343     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
1344     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
1345 
1346     // Main test call
1347     test->GetEncoder()->EncodeSub(param1, param1, param2);
1348 
1349     // Finalize
1350     test->PostWork<T>();
1351 
1352     // If encode unsupported - now print error
1353     if (!test->GetEncoder()->GetResult()) {
1354         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1355         return false;
1356     }
1357     // Change this for enable print disasm
1358     test->Dump(false);
1359 
1360     // Main test loop:
1361     for (uint64_t i = 0; i < ITERATION; ++i) {
1362         // Second type-dependency
1363         T tmp1 = random_gen<T>();
1364         T tmp2 = random_gen<T>();
1365         // Deduced conflicting types for parameter
1366 
1367         // Main check - compare parameter and
1368         // return value
1369         if (!test->CallCode<T>(tmp1, tmp2, tmp1 - tmp2)) {
1370             return false;
1371         }
1372     }
1373 
1374     if constexpr (std::is_floating_point_v<T>) {
1375         T nan = std::numeric_limits<T>::quiet_NaN();
1376 
1377         if (!test->CallCode<T>(nan, random_gen<T>(), nan)) {
1378             return false;
1379         }
1380         if (!test->CallCode<T>(random_gen<T>(), nan, nan)) {
1381             return false;
1382         }
1383         if (!test->CallCode<T>(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity(), nan)) {
1384             return false;
1385         }
1386         if (!test->CallCode<T>(-std::numeric_limits<T>::infinity(), -std::numeric_limits<T>::infinity(), nan)) {
1387             return false;
1388         }
1389     }
1390     return true;
1391 }
1392 
1393 /*
1394         Sub 2 denormolized values - c++ return wrong result:
1395         // param1=1.66607e-39 param2=9.56199e-39 and result=-4.0369e-39 curr_result=-7.89592e-39
1396         // In binary : param1=1188945 param2=6823664 result=2150364478 curr_result=2153118367
1397 */
1398 
TEST_F(Encoder32Test,SubTest)1399 TEST_F(Encoder32Test, SubTest)
1400 {
1401     EXPECT_TRUE(TestSub<int8_t>(this));
1402     EXPECT_TRUE(TestSub<int16_t>(this));
1403     EXPECT_TRUE(TestSub<int32_t>(this));
1404     EXPECT_TRUE(TestSub<int64_t>(this));
1405     EXPECT_TRUE(TestSub<float>(this));
1406     EXPECT_TRUE(TestSub<double>(this));
1407 }
1408 
1409 template <typename T>
TestSubImm(Encoder32Test * test)1410 bool TestSubImm(Encoder32Test *test)
1411 {
1412     // Initialize
1413     test->PreWork<T>();
1414 
1415     // First type-dependency
1416     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
1417     T param2 = random_gen<T>();
1418 
1419     // Main test call
1420     test->GetEncoder()->EncodeSub(param1, param1, Imm(param2));
1421 
1422     // Finalize
1423     test->PostWork<T>();
1424 
1425     // If encode unsupported - now print error
1426     if (!test->GetEncoder()->GetResult()) {
1427         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1428         return false;
1429     }
1430     // Change this for enable print disasm
1431     test->Dump(false);
1432 
1433     // Main test loop:
1434     for (uint64_t i = 0; i < ITERATION; ++i) {
1435         // Second type-dependency
1436         T tmp1 = random_gen<T>();
1437         // Deduced conflicting types for parameter
1438 
1439         // Main check - compare parameter and
1440         // return value
1441         if (!test->CallCode<T>(tmp1, tmp1 - param2)) {
1442             return false;
1443         }
1444     }
1445     return true;
1446 }
1447 
TEST_F(Encoder32Test,SubImmTest)1448 TEST_F(Encoder32Test, SubImmTest)
1449 {
1450     EXPECT_TRUE(TestSubImm<int8_t>(this));
1451     EXPECT_TRUE(TestSubImm<int16_t>(this));
1452     // TODO (asidorov, igorban) the test failed in release mode
1453     // TestSubImm<int32_t>
1454     EXPECT_TRUE(TestSubImm<int64_t>(this));
1455     // TestSubImm<float>
1456     // TestSubImm<double>
1457 }
1458 
1459 template <typename T>
TestMul(Encoder32Test * test)1460 bool TestMul(Encoder32Test *test)
1461 {
1462     // Initialize
1463     test->PreWork<T>();
1464 
1465     // First type-dependency
1466     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
1467     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
1468 
1469     // Main test call
1470     test->GetEncoder()->EncodeMul(param1, param1, param2);
1471 
1472     // Finalize
1473     test->PostWork<T>();
1474 
1475     // If encode unsupported - now print error
1476     if (!test->GetEncoder()->GetResult()) {
1477         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1478         return false;
1479     }
1480     // Change this for enable print disasm
1481     test->Dump(false);
1482 
1483     // Main test loop:
1484     for (uint64_t i = 0; i < ITERATION; ++i) {
1485         // Second type-dependency
1486         T tmp1 = random_gen<T>();
1487         T tmp2 = random_gen<T>();
1488         // Deduced conflicting types for parameter
1489 
1490         // Main check - compare parameter and
1491         // return value
1492         if (!test->CallCode<T>(tmp1, tmp2, tmp1 * tmp2)) {
1493             return false;
1494         }
1495     }
1496 
1497     if constexpr (std::is_floating_point_v<T>) {
1498         T nan = std::numeric_limits<T>::quiet_NaN();
1499 
1500         if (!test->CallCode<T>(nan, random_gen<T>(), nan)) {
1501             return false;
1502         }
1503         if (!test->CallCode<T>(random_gen<T>(), nan, nan)) {
1504             return false;
1505         }
1506         if (!test->CallCode<T>(0.0, std::numeric_limits<T>::infinity(), nan)) {
1507             return false;
1508         }
1509         if (!test->CallCode<T>(std::numeric_limits<T>::infinity(), 0.0, nan)) {
1510             return false;
1511         }
1512     }
1513 
1514     return true;
1515 }
1516 
TEST_F(Encoder32Test,MulTest)1517 TEST_F(Encoder32Test, MulTest)
1518 {
1519     EXPECT_TRUE(TestMul<int8_t>(this));
1520     EXPECT_TRUE(TestMul<int16_t>(this));
1521     EXPECT_TRUE(TestMul<int32_t>(this));
1522     EXPECT_TRUE(TestMul<int64_t>(this));
1523     EXPECT_TRUE(TestMul<float>(this));
1524     EXPECT_TRUE(TestMul<double>(this));
1525 }
1526 
1527 template <typename T>
TestMin(Encoder32Test * test)1528 bool TestMin(Encoder32Test *test)
1529 {
1530     // Initialize
1531     test->PreWork<T>();
1532 
1533     // First type-dependency
1534     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
1535     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
1536 
1537     // Main test call
1538     test->GetEncoder()->EncodeMin(param1, std::is_signed_v<T>, param1, param2);
1539 
1540     // Finalize
1541     test->PostWork<T>();
1542 
1543     // If encode unsupported - now print error
1544     if (!test->GetEncoder()->GetResult()) {
1545         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1546         return false;
1547     }
1548     // Change this for enable print disasm
1549     test->Dump(false);
1550 
1551     // Main test loop:
1552     for (uint64_t i = 0; i < ITERATION; ++i) {
1553         // Second type-dependency
1554         T tmp1 = random_gen<T>();
1555         T tmp2 = random_gen<T>();
1556         // Deduced conflicting types for parameter
1557 
1558         T result {0};
1559         if (std::is_floating_point_v<T> && (std::isnan(tmp1) || std::isnan(tmp2))) {
1560             result = std::numeric_limits<T>::quiet_NaN();
1561         } else {
1562             // We do that, because auto check -0.0 and +0.0 std::max give incorrect result
1563             if (std::fabs(tmp1) < 1e-300 && std::fabs(tmp2) < 1e-300) {
1564                 continue;
1565             }
1566             result = std::min(tmp1, tmp2);
1567         }
1568 
1569         // Main check - compare parameter and
1570         // return value
1571         if (!test->CallCode<T>(tmp1, tmp2, result)) {
1572             return false;
1573         }
1574     }
1575 
1576     if constexpr (std::is_floating_point_v<T>) {
1577         T nan = std::numeric_limits<T>::quiet_NaN();
1578 
1579         if (!test->CallCode<T>(nan, random_gen<T>(), nan)) {
1580             return false;
1581         }
1582         if (!test->CallCode<T>(random_gen<T>(), nan, nan)) {
1583             return false;
1584         }
1585         // use static_cast to make sure correct float/double type is applied
1586         if (!test->CallCode<T>(static_cast<T>(-0.0), static_cast<T>(+0.0), static_cast<T>(-0.0))) {
1587             return false;
1588         }
1589         if (!test->CallCode<T>(static_cast<T>(+0.0), static_cast<T>(-0.0), static_cast<T>(-0.0))) {
1590             return false;
1591         }
1592     }
1593 
1594     return true;
1595 }
1596 
TEST_F(Encoder32Test,MinTest)1597 TEST_F(Encoder32Test, MinTest)
1598 {
1599     EXPECT_TRUE(TestMin<int8_t>(this));
1600     EXPECT_TRUE(TestMin<int16_t>(this));
1601     EXPECT_TRUE(TestMin<int32_t>(this));
1602     EXPECT_TRUE(TestMin<int64_t>(this));
1603     EXPECT_TRUE(TestMin<uint8_t>(this));
1604     EXPECT_TRUE(TestMin<uint16_t>(this));
1605     EXPECT_TRUE(TestMin<uint32_t>(this));
1606     EXPECT_TRUE(TestMin<uint64_t>(this));
1607     EXPECT_TRUE(TestMin<float>(this));
1608     EXPECT_TRUE(TestMin<double>(this));
1609 }
1610 
1611 template <typename T>
TestMax(Encoder32Test * test)1612 bool TestMax(Encoder32Test *test)
1613 {
1614     // Initialize
1615     test->PreWork<T>();
1616 
1617     // First type-dependency
1618     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
1619     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
1620 
1621     // Main test call
1622     test->GetEncoder()->EncodeMax(param1, std::is_signed_v<T>, param1, param2);
1623 
1624     // Finalize
1625     test->PostWork<T>();
1626 
1627     // If encode unsupported - now print error
1628     if (!test->GetEncoder()->GetResult()) {
1629         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1630         return false;
1631     }
1632     // Change this for enable print disasm
1633     test->Dump(false);
1634 
1635     // Main test loop:
1636     for (uint64_t i = 0; i < ITERATION; ++i) {
1637         // Second type-dependency
1638         T tmp1 = random_gen<T>();
1639         T tmp2 = random_gen<T>();
1640         // Deduced conflicting types for parameter
1641         T result {0};
1642         if (std::is_floating_point_v<T> && (std::isnan(tmp1) || std::isnan(tmp2))) {
1643             result = std::numeric_limits<T>::quiet_NaN();
1644         } else {
1645             // We do that, because auto check -0.0 and +0.0 std::max give incorrect result
1646             if (std::fabs(tmp1) < 1e-300 && std::fabs(tmp2) < 1e-300) {
1647                 continue;
1648             }
1649             result = std::max(tmp1, tmp2);
1650         }
1651 
1652         // Main check - compare parameter and
1653         // return value
1654         if (!test->CallCode<T>(tmp1, tmp2, result)) {
1655             return false;
1656         }
1657     }
1658 
1659     if constexpr (std::is_floating_point_v<T>) {
1660         T nan = std::numeric_limits<T>::quiet_NaN();
1661 
1662         if (!test->CallCode<T>(nan, random_gen<T>(), nan)) {
1663             return false;
1664         }
1665         if (!test->CallCode<T>(random_gen<T>(), nan, nan)) {
1666             return false;
1667         }
1668         // use static_cast to make sure correct float/double type is applied
1669         if (!test->CallCode<T>(static_cast<T>(-0.0), static_cast<T>(+0.0), static_cast<T>(+0.0))) {
1670             return false;
1671         }
1672         if (!test->CallCode<T>(static_cast<T>(+0.0), static_cast<T>(-0.0), static_cast<T>(+0.0))) {
1673             return false;
1674         }
1675     }
1676 
1677     return true;
1678 }
1679 
TEST_F(Encoder32Test,MaxTest)1680 TEST_F(Encoder32Test, MaxTest)
1681 {
1682     EXPECT_TRUE(TestMax<int8_t>(this));
1683     EXPECT_TRUE(TestMax<int16_t>(this));
1684     EXPECT_TRUE(TestMax<int32_t>(this));
1685     EXPECT_TRUE(TestMax<int64_t>(this));
1686     EXPECT_TRUE(TestMax<uint8_t>(this));
1687     EXPECT_TRUE(TestMax<uint16_t>(this));
1688     EXPECT_TRUE(TestMax<uint32_t>(this));
1689     EXPECT_TRUE(TestMax<uint64_t>(this));
1690     EXPECT_TRUE(TestMax<float>(this));
1691     EXPECT_TRUE(TestMax<double>(this));
1692 }
1693 
1694 template <typename T>
TestShl(Encoder32Test * test)1695 bool TestShl(Encoder32Test *test)
1696 {
1697     // Initialize
1698     test->PreWork<T>();
1699 
1700     // First type-dependency
1701     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
1702     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
1703 
1704     // Main test call
1705     test->GetEncoder()->EncodeShl(param1, param1, param2);
1706 
1707     // Finalize
1708     test->PostWork<T>();
1709 
1710     // If encode unsupported - now print error
1711     if (!test->GetEncoder()->GetResult()) {
1712         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1713         return false;
1714     }
1715     // Change this for enable print disasm
1716     test->Dump(false);
1717 
1718     // Main test loop:
1719     for (uint64_t i = 0; i < ITERATION; ++i) {
1720         // Second type-dependency
1721         T tmp1 = random_gen<T>();
1722         T tmp2 {0};
1723         if constexpr (std::is_same_v<T, int64_t>) {
1724             tmp2 = random_gen<uint8_t>() % DOUBLE_WORD_SIZE;
1725         } else {
1726             tmp2 = random_gen<uint8_t>() % WORD_SIZE;
1727         }
1728         // Deduced conflicting types for parameter
1729 
1730         // Main check - compare parameter and
1731         // return value
1732         bool result {false};
1733 
1734         if constexpr (std::is_same_v<T, int8_t> || std::is_same_v<T, int16_t>) {
1735             result = test->CallCode<T>(tmp1, tmp2, tmp1 << (tmp2 & (CHAR_BIT * sizeof(T) - 1)));
1736         } else {
1737             result = test->CallCode<T>(tmp1, tmp2, tmp1 << tmp2);
1738         }
1739 
1740         if (!result) {
1741             return false;
1742         }
1743     }
1744     return true;
1745 }
1746 
TEST_F(Encoder32Test,ShlTest)1747 TEST_F(Encoder32Test, ShlTest)
1748 {
1749     EXPECT_TRUE(TestShl<int8_t>(this));
1750     EXPECT_TRUE(TestShl<int16_t>(this));
1751     EXPECT_TRUE(TestShl<int32_t>(this));
1752     EXPECT_TRUE(TestShl<int64_t>(this));
1753 }
1754 
1755 template <typename T>
TestShr(Encoder32Test * test)1756 bool TestShr(Encoder32Test *test)
1757 {
1758     // Initialize
1759     test->PreWork<T>();
1760 
1761     // First type-dependency
1762     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
1763     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
1764 
1765     // Main test call
1766     test->GetEncoder()->EncodeShr(param1, param1, param2);
1767 
1768     // Finalize
1769     test->PostWork<T>();
1770 
1771     // If encode unsupported - now print error
1772     if (!test->GetEncoder()->GetResult()) {
1773         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1774         return false;
1775     }
1776     // Change this for enable print disasm
1777     test->Dump(false);
1778 
1779     // Main test loop:
1780     for (uint64_t i = 0; i < ITERATION; ++i) {
1781         // Second type-dependency
1782         T tmp1 = random_gen<T>();
1783         T tmp2 {0};
1784         if constexpr (sizeof(T) == sizeof(int64_t)) {
1785             tmp2 = random_gen<uint8_t>() % DOUBLE_WORD_SIZE;
1786         } else {
1787             tmp2 = random_gen<uint8_t>() % WORD_SIZE;
1788         }
1789         // Deduced conflicting types for parameter
1790 
1791         // Main check - compare parameter and
1792         // return value
1793         bool result {false};
1794 
1795         if constexpr (sizeof(T) == sizeof(int64_t)) {
1796             result = test->CallCode<T>(tmp1, tmp2, (static_cast<uint64_t>(tmp1)) >> tmp2);
1797         } else {
1798             result =
1799                 test->CallCode<T>(tmp1, tmp2, (static_cast<uint32_t>(tmp1)) >> (tmp2 & (CHAR_BIT * sizeof(T) - 1)));
1800         }
1801 
1802         if (!result) {
1803             return false;
1804         }
1805     }
1806     return true;
1807 }
1808 
TEST_F(Encoder32Test,ShrTest)1809 TEST_F(Encoder32Test, ShrTest)
1810 {
1811     EXPECT_TRUE(TestShr<int8_t>(this));
1812     EXPECT_TRUE(TestShr<int16_t>(this));
1813     EXPECT_TRUE(TestShr<int32_t>(this));
1814     EXPECT_TRUE(TestShr<int64_t>(this));
1815     EXPECT_TRUE(TestShr<uint8_t>(this));
1816     EXPECT_TRUE(TestShr<uint16_t>(this));
1817     EXPECT_TRUE(TestShr<uint32_t>(this));
1818     EXPECT_TRUE(TestShr<uint64_t>(this));
1819 }
1820 
1821 template <typename T>
TestAShr(Encoder32Test * test)1822 bool TestAShr(Encoder32Test *test)
1823 {
1824     // Initialize
1825     test->PreWork<T>();
1826 
1827     // First type-dependency
1828     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
1829     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
1830 
1831     // Main test call
1832     test->GetEncoder()->EncodeAShr(param1, param1, param2);
1833 
1834     // Finalize
1835     test->PostWork<T>();
1836 
1837     // If encode unsupported - now print error
1838     if (!test->GetEncoder()->GetResult()) {
1839         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1840         return false;
1841     }
1842     // Change this for enable print disasm
1843     test->Dump(false);
1844 
1845     // Main test loop:
1846     for (uint64_t i = 0; i < ITERATION; ++i) {
1847         // Second type-dependency
1848         T tmp1 = random_gen<T>();
1849         T tmp2 {0};
1850         if constexpr (std::is_same_v<T, int64_t>) {
1851             tmp2 = random_gen<uint8_t>() % DOUBLE_WORD_SIZE;
1852         } else {
1853             tmp2 = random_gen<uint8_t>() % WORD_SIZE;
1854         }
1855         // Deduced conflicting types for parameter
1856 
1857         // Main check - compare parameter and
1858         // return value
1859         bool result {false};
1860 
1861         if constexpr (std::is_same_v<T, int8_t> || std::is_same_v<T, int16_t>) {
1862             result = test->CallCode<T>(tmp1, tmp2, tmp1 >> (tmp2 & (CHAR_BIT * sizeof(T) - 1)));
1863         } else {
1864             result = test->CallCode<T>(tmp1, tmp2, tmp1 >> tmp2);
1865         }
1866 
1867         if (!result) {
1868             return false;
1869         }
1870     }
1871     return true;
1872 }
1873 
TEST_F(Encoder32Test,AShrTest)1874 TEST_F(Encoder32Test, AShrTest)
1875 {
1876     EXPECT_TRUE(TestAShr<int8_t>(this));
1877     EXPECT_TRUE(TestAShr<int16_t>(this));
1878     EXPECT_TRUE(TestAShr<int32_t>(this));
1879     EXPECT_TRUE(TestAShr<int64_t>(this));
1880 }
1881 
1882 template <typename T>
TestAnd(Encoder32Test * test)1883 bool TestAnd(Encoder32Test *test)
1884 {
1885     // Initialize
1886     test->PreWork<T>();
1887 
1888     // First type-dependency
1889     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
1890     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
1891 
1892     // Main test call
1893     test->GetEncoder()->EncodeAnd(param1, param1, param2);
1894 
1895     // Finalize
1896     test->PostWork<T>();
1897 
1898     // If encode unsupported - now print error
1899     if (!test->GetEncoder()->GetResult()) {
1900         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1901         return false;
1902     }
1903     // Change this for enable print disasm
1904     test->Dump(false);
1905 
1906     // Main test loop:
1907     for (uint64_t i = 0; i < ITERATION; ++i) {
1908         // Second type-dependency
1909         T tmp1 = random_gen<T>();
1910         T tmp2 = random_gen<T>();
1911         // Deduced conflicting types for parameter
1912 
1913         // Main check - compare parameter and
1914         // return value
1915         if (!test->CallCode<T>(tmp1, tmp2, tmp1 & tmp2)) {
1916             return false;
1917         }
1918     }
1919     return true;
1920 }
1921 
TEST_F(Encoder32Test,AndTest)1922 TEST_F(Encoder32Test, AndTest)
1923 {
1924     EXPECT_TRUE(TestAnd<int8_t>(this));
1925     EXPECT_TRUE(TestAnd<int16_t>(this));
1926     EXPECT_TRUE(TestAnd<int32_t>(this));
1927     EXPECT_TRUE(TestAnd<int64_t>(this));
1928 }
1929 
1930 template <typename T>
TestOr(Encoder32Test * test)1931 bool TestOr(Encoder32Test *test)
1932 {
1933     // Initialize
1934     test->PreWork<T>();
1935 
1936     // First type-dependency
1937     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
1938     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
1939 
1940     // Main test call
1941     test->GetEncoder()->EncodeOr(param1, param1, param2);
1942 
1943     // Finalize
1944     test->PostWork<T>();
1945 
1946     // If encode unsupported - now print error
1947     if (!test->GetEncoder()->GetResult()) {
1948         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1949         return false;
1950     }
1951     // Change this for enable print disasm
1952     test->Dump(false);
1953 
1954     // Main test loop:
1955     for (uint64_t i = 0; i < ITERATION; ++i) {
1956         // Second type-dependency
1957         T tmp1 = random_gen<T>();
1958         T tmp2 = random_gen<T>();
1959         // Deduced conflicting types for parameter
1960 
1961         // Main check - compare parameter and
1962         // return value
1963         if (!test->CallCode<T>(tmp1, tmp2, tmp1 | tmp2)) {
1964             return false;
1965         }
1966     }
1967     return true;
1968 }
1969 
TEST_F(Encoder32Test,OrTest)1970 TEST_F(Encoder32Test, OrTest)
1971 {
1972     EXPECT_TRUE(TestOr<int8_t>(this));
1973     EXPECT_TRUE(TestOr<int16_t>(this));
1974     EXPECT_TRUE(TestOr<int32_t>(this));
1975     EXPECT_TRUE(TestOr<int64_t>(this));
1976 }
1977 
1978 template <typename T>
TestXor(Encoder32Test * test)1979 bool TestXor(Encoder32Test *test)
1980 {
1981     // Initialize
1982     test->PreWork<T>();
1983 
1984     // First type-dependency
1985     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
1986     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
1987 
1988     // Main test call
1989     test->GetEncoder()->EncodeXor(param1, param1, param2);
1990 
1991     // Finalize
1992     test->PostWork<T>();
1993 
1994     // If encode unsupported - now print error
1995     if (!test->GetEncoder()->GetResult()) {
1996         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
1997         return false;
1998     }
1999     // Change this for enable print disasm
2000     test->Dump(false);
2001 
2002     // Main test loop:
2003     for (uint64_t i = 0; i < ITERATION; ++i) {
2004         // Second type-dependency
2005         T tmp1 = random_gen<T>();
2006         T tmp2 = random_gen<T>();
2007         // Deduced conflicting types for parameter
2008 
2009         // Main check - compare parameter and
2010         // return value
2011         if (!test->CallCode<T>(tmp1, tmp2, tmp1 ^ tmp2)) {
2012             return false;
2013         }
2014     }
2015     return true;
2016 }
2017 
TEST_F(Encoder32Test,XorTest)2018 TEST_F(Encoder32Test, XorTest)
2019 {
2020     EXPECT_TRUE(TestXor<int8_t>(this));
2021     EXPECT_TRUE(TestXor<int16_t>(this));
2022     EXPECT_TRUE(TestXor<int32_t>(this));
2023     EXPECT_TRUE(TestXor<int64_t>(this));
2024 }
2025 
2026 template <typename T>
TestShlImm(Encoder32Test * test)2027 bool TestShlImm(Encoder32Test *test)
2028 {
2029     // Initialize
2030     test->PreWork<T>();
2031 
2032     // First type-dependency
2033     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
2034     int64_t param2 {0};
2035     if constexpr (std::is_same_v<T, int64_t>) {
2036         param2 = random_gen<uint8_t>() % DOUBLE_WORD_SIZE;
2037     } else {
2038         param2 = random_gen<uint8_t>() % WORD_SIZE;
2039     }
2040 
2041     // Main test call
2042     test->GetEncoder()->EncodeShl(param1, param1, Imm(param2));
2043 
2044     // Finalize
2045     test->PostWork<T>();
2046 
2047     // If encode unsupported - now print error
2048     if (!test->GetEncoder()->GetResult()) {
2049         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
2050         return false;
2051     }
2052     // Change this for enable print disasm
2053     test->Dump(false);
2054 
2055     // Main test loop:
2056     for (uint64_t i = 0; i < ITERATION; ++i) {
2057         // Second type-dependency
2058         T tmp1 = random_gen<T>();
2059 
2060         // Deduced conflicting types for parameter
2061 
2062         // Main check - compare parameter and
2063         // return value
2064         bool result {false};
2065 
2066         if constexpr (std::is_same_v<T, int8_t> || std::is_same_v<T, int16_t>) {
2067             result = test->CallCode<T>(tmp1, tmp1 << (param2 & (CHAR_BIT * sizeof(T) - 1)));
2068         } else {
2069             result = test->CallCode<T>(tmp1, tmp1 << param2);
2070         }
2071 
2072         if (!result) {
2073             return false;
2074         }
2075     }
2076     return true;
2077 }
2078 
TEST_F(Encoder32Test,ShlImmTest)2079 TEST_F(Encoder32Test, ShlImmTest)
2080 {
2081     EXPECT_TRUE(TestShlImm<int8_t>(this));
2082     EXPECT_TRUE(TestShlImm<int16_t>(this));
2083     EXPECT_TRUE(TestShlImm<int32_t>(this));
2084     EXPECT_TRUE(TestShlImm<int64_t>(this));
2085 }
2086 
2087 template <typename T>
TestShrImm(Encoder32Test * test)2088 bool TestShrImm(Encoder32Test *test)
2089 {
2090     // Initialize
2091     test->PreWork<T>();
2092 
2093     // First type-dependency
2094     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
2095     int64_t param2 {0};
2096     if constexpr (sizeof(T) == sizeof(int64_t)) {
2097         param2 = random_gen<uint8_t>() % DOUBLE_WORD_SIZE;
2098     } else {
2099         param2 = random_gen<uint8_t>() % WORD_SIZE;
2100     }
2101 
2102     // Main test call
2103     test->GetEncoder()->EncodeShr(param1, param1, Imm(param2));
2104 
2105     // Finalize
2106     test->PostWork<T>();
2107 
2108     // If encode unsupported - now print error
2109     if (!test->GetEncoder()->GetResult()) {
2110         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
2111         return false;
2112     }
2113     // Change this for enable print disasm
2114     test->Dump(false);
2115 
2116     // Main test loop:
2117     for (uint64_t i = 0; i < ITERATION; ++i) {
2118         // Second type-dependency
2119         T tmp1 = random_gen<T>();
2120 
2121         // Deduced conflicting types for parameter
2122 
2123         // Main check - compare parameter and
2124         // return value
2125         bool result {false};
2126 
2127         if constexpr (sizeof(T) == sizeof(int64_t)) {
2128             result = test->CallCode<T>(tmp1, (static_cast<uint64_t>(tmp1)) >> param2);
2129         } else {
2130             result = test->CallCode<T>(tmp1, (static_cast<uint32_t>(tmp1)) >> (param2 & (CHAR_BIT * sizeof(T) - 1)));
2131         }
2132 
2133         if (!result) {
2134             return false;
2135         }
2136     }
2137     return true;
2138 }
2139 
TEST_F(Encoder32Test,ShrImmTest)2140 TEST_F(Encoder32Test, ShrImmTest)
2141 {
2142     EXPECT_TRUE(TestShrImm<int8_t>(this));
2143     EXPECT_TRUE(TestShrImm<int16_t>(this));
2144     EXPECT_TRUE(TestShrImm<int32_t>(this));
2145     EXPECT_TRUE(TestShrImm<int64_t>(this));
2146     EXPECT_TRUE(TestShrImm<uint8_t>(this));
2147     EXPECT_TRUE(TestShrImm<uint16_t>(this));
2148     EXPECT_TRUE(TestShrImm<uint32_t>(this));
2149     EXPECT_TRUE(TestShrImm<uint64_t>(this));
2150 }
2151 
2152 template <typename T>
TestCmp(Encoder32Test * test)2153 bool TestCmp(Encoder32Test *test)
2154 {
2155     static_assert(std::is_integral_v<T>);
2156     // Initialize
2157     test->PreWork<T>();
2158 
2159     // First type-dependency
2160     auto output = test->GetParameter(TypeInfo(int32_t(0)), 0);
2161     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
2162     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
2163 
2164     // Main test call
2165     test->GetEncoder()->EncodeCmp(output, param1, param2, std::is_signed_v<T> ? Condition::LT : Condition::LO);
2166 
2167     // Finalize
2168     test->PostWork<T>();
2169 
2170     // If encode unsupported - now print error
2171     if (!test->GetEncoder()->GetResult()) {
2172         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
2173         return false;
2174     }
2175     // Change this for enable print disasm
2176     test->Dump(false);
2177 
2178     // Main test loop:
2179     for (uint64_t i = 0; i < ITERATION; ++i) {
2180         // Second type-dependency
2181         T tmp1 = random_gen<T>();
2182         T tmp2 = random_gen<T>();
2183         // Deduced conflicting types for parameter
2184 
2185         auto compare = [](T a, T b) -> int32_t { return a < b ? -1 : a > b ? 1 : 0; };
2186         int32_t result {compare(tmp1, tmp2)};
2187 
2188         // Main check - compare parameter and
2189         // return value
2190         if (!test->CallCode<T, int32_t>(tmp1, tmp2, result)) {
2191             return false;
2192         }
2193     }
2194 
2195     return true;
2196 }
2197 
2198 template <typename T>
TestFcmp(Encoder32Test * test,bool is_fcmpg)2199 bool TestFcmp(Encoder32Test *test, bool is_fcmpg)
2200 {
2201     static_assert(std::is_floating_point_v<T>);
2202     // Initialize
2203     test->PreWork<T>();
2204 
2205     // First type-dependency
2206     auto output = test->GetParameter(TypeInfo(int32_t(0)), 0);
2207     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
2208     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
2209 
2210     // Main test call
2211     test->GetEncoder()->EncodeCmp(output, param1, param2, is_fcmpg ? Condition::MI : Condition::LT);
2212 
2213     // Finalize
2214     test->PostWork<int32_t>();
2215 
2216     // If encode unsupported - now print error
2217     if (!test->GetEncoder()->GetResult()) {
2218         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
2219         return false;
2220     }
2221     // Change this for enable print disasm
2222     test->Dump(false);
2223 
2224     // Main test loop:
2225     for (uint64_t i = 0; i < ITERATION; ++i) {
2226         // Second type-dependency
2227         T tmp1 = random_gen<T>();
2228         T tmp2 = random_gen<T>();
2229         // Deduced conflicting types for parameter
2230 
2231         auto compare = [](T a, T b) -> int32_t { return a < b ? -1 : a > b ? 1 : 0; };
2232 
2233         int32_t result {0};
2234         if (std::isnan(tmp1) || std::isnan(tmp2)) {
2235             result = is_fcmpg ? 1 : -1;
2236         } else {
2237             result = compare(tmp1, tmp2);
2238         }
2239 
2240         // Main check - compare parameter and
2241         // return value
2242         if (!test->CallCode<T, int32_t>(tmp1, tmp2, result)) {
2243             return false;
2244         }
2245     }
2246 
2247     if constexpr (std::is_floating_point_v<T>) {
2248         T nan = std::numeric_limits<T>::quiet_NaN();
2249 
2250         if (!test->CallCode<T, int32_t>(nan, 5.0, is_fcmpg ? 1 : -1)) {
2251             return false;
2252         }
2253         if (!test->CallCode<T, int32_t>(5.0, nan, is_fcmpg ? 1 : -1)) {
2254             return false;
2255         }
2256         if (!test->CallCode<T, int32_t>(nan, nan, is_fcmpg ? 1 : -1)) {
2257             return false;
2258         }
2259     }
2260 
2261     return true;
2262 }
2263 
TEST_F(Encoder32Test,CmpTest)2264 TEST_F(Encoder32Test, CmpTest)
2265 {
2266     EXPECT_TRUE(TestCmp<int8_t>(this));
2267     EXPECT_TRUE(TestCmp<int16_t>(this));
2268     EXPECT_TRUE(TestCmp<int32_t>(this));
2269     EXPECT_TRUE(TestCmp<int64_t>(this));
2270     EXPECT_TRUE(TestCmp<uint8_t>(this));
2271     EXPECT_TRUE(TestCmp<uint16_t>(this));
2272     EXPECT_TRUE(TestCmp<uint32_t>(this));
2273     EXPECT_TRUE(TestCmp<uint64_t>(this));
2274     EXPECT_TRUE(TestFcmp<float>(this, false));
2275     EXPECT_TRUE(TestFcmp<double>(this, false));
2276     EXPECT_TRUE(TestFcmp<float>(this, true));
2277     EXPECT_TRUE(TestFcmp<double>(this, true));
2278 }
2279 
2280 template <typename T>
TestCmp64(Encoder32Test * test)2281 bool TestCmp64(Encoder32Test *test)
2282 {
2283     static_assert(sizeof(T) == sizeof(int64_t));
2284     // Initialize
2285     test->PreWork<T>();
2286 
2287     // First type-dependency
2288     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
2289     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
2290 
2291     // Main test call
2292     test->GetEncoder()->EncodeCmp(param1, param1, param2, std::is_signed_v<T> ? Condition::LT : Condition::LO);
2293 
2294     // Finalize
2295     test->PostWork<T>();
2296 
2297     // If encode unsupported - now print error
2298     if (!test->GetEncoder()->GetResult()) {
2299         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
2300         return false;
2301     }
2302     // Change this for enable print disasm
2303     test->Dump(false);
2304 
2305     uint64_t hi = static_cast<uint64_t>(0xABCDEFU) << (BITS_PER_BYTE * sizeof(uint32_t));
2306 
2307     // Main test loop:
2308     for (uint64_t i = 0; i < ITERATION; ++i) {
2309         uint32_t lo = random_gen<T>();
2310 
2311         // Second type-dependency
2312         T tmp1 = hi | (lo + 1U);
2313         T tmp2 = hi | lo;
2314         // Deduced conflicting types for parameter
2315 
2316         auto compare = [](T a, T b) -> int32_t { return a < b ? -1 : a > b ? 1 : 0; };
2317 
2318         // Main check - compare parameter and
2319         // return value
2320         if (!test->CallCode<T, int32_t>(tmp1, tmp2, compare(tmp1, tmp2))) {
2321             return false;
2322         }
2323     }
2324     return true;
2325 }
2326 
TEST_F(Encoder32Test,Cmp64Test)2327 TEST_F(Encoder32Test, Cmp64Test)
2328 {
2329     EXPECT_TRUE(TestCmp64<int64_t>(this));
2330     EXPECT_TRUE(TestCmp64<uint64_t>(this));
2331 }
2332 
2333 template <typename T>
TestCompare(Encoder32Test * test)2334 bool TestCompare(Encoder32Test *test)
2335 {
2336     // Initialize
2337     test->PreWork<T>();
2338 
2339     // First type-dependency
2340     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
2341     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
2342 
2343     // Main test call
2344     test->GetEncoder()->EncodeCompare(param1, param1, param2, std::is_signed_v<T> ? Condition::GE : Condition::HS);
2345 
2346     // Finalize
2347     test->PostWork<T>();
2348 
2349     // If encode unsupported - now print error
2350     if (!test->GetEncoder()->GetResult()) {
2351         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
2352         return false;
2353     }
2354     // Change this for enable print disasm
2355     test->Dump(false);
2356 
2357     // Main test loop:
2358     for (uint64_t i = 0; i < ITERATION; ++i) {
2359         // Second type-dependency
2360         T tmp1 = random_gen<T>();
2361         T tmp2 = random_gen<T>();
2362         // Deduced conflicting types for parameter
2363 
2364         auto compare = [](T a, T b) -> int32_t { return a >= b; };
2365 
2366         // Main check - compare parameter and
2367         // return value
2368         if (!test->CallCode<T, int32_t>(tmp1, tmp2, compare(tmp1, tmp2))) {
2369             return false;
2370         }
2371     }
2372     return true;
2373 }
2374 
TEST_F(Encoder32Test,CompareTest)2375 TEST_F(Encoder32Test, CompareTest)
2376 {
2377     EXPECT_TRUE(TestCompare<int8_t>(this));
2378     EXPECT_TRUE(TestCompare<int16_t>(this));
2379     EXPECT_TRUE(TestCompare<int32_t>(this));
2380     EXPECT_TRUE(TestCompare<int64_t>(this));
2381     EXPECT_TRUE(TestCompare<uint8_t>(this));
2382     EXPECT_TRUE(TestCompare<uint16_t>(this));
2383     EXPECT_TRUE(TestCompare<uint32_t>(this));
2384     EXPECT_TRUE(TestCompare<uint64_t>(this));
2385 }
2386 
2387 template <typename T>
TestCompare64(Encoder32Test * test)2388 bool TestCompare64(Encoder32Test *test)
2389 {
2390     static_assert(sizeof(T) == sizeof(int64_t));
2391     // Initialize
2392     test->PreWork<T>();
2393 
2394     // First type-dependency
2395     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
2396     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
2397 
2398     // Main test call
2399     test->GetEncoder()->EncodeCompare(param1, param1, param2, std::is_signed_v<T> ? Condition::LT : Condition::LO);
2400 
2401     // Finalize
2402     test->PostWork<T>();
2403 
2404     // If encode unsupported - now print error
2405     if (!test->GetEncoder()->GetResult()) {
2406         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
2407         return false;
2408     }
2409     // Change this for enable print disasm
2410     test->Dump(false);
2411 
2412     uint64_t hi = static_cast<uint64_t>(0xABCDEFU) << (BITS_PER_BYTE * sizeof(uint32_t));
2413 
2414     // Main test loop:
2415     for (uint64_t i = 0; i < ITERATION; ++i) {
2416         uint32_t lo = random_gen<T>();
2417 
2418         // Second type-dependency
2419         T tmp1 = hi | (lo + 1U);
2420         T tmp2 = hi | lo;
2421         // Deduced conflicting types for parameter
2422 
2423         auto compare = [](T a, T b) -> int32_t { return a < b; };
2424 
2425         // Main check - compare parameter and
2426         // return value
2427         if (!test->CallCode<T, int32_t>(tmp1, tmp2, compare(tmp1, tmp2))) {
2428             return false;
2429         }
2430     }
2431     return true;
2432 }
2433 
TEST_F(Encoder32Test,Compare64Test)2434 TEST_F(Encoder32Test, Compare64Test)
2435 {
2436     EXPECT_TRUE(TestCompare64<int64_t>(this));
2437     EXPECT_TRUE(TestCompare64<uint64_t>(this));
2438 }
2439 
2440 template <typename Src, typename Dst>
TestCast(Encoder32Test * test)2441 bool TestCast(Encoder32Test *test)
2442 {
2443     auto link_reg = Target::Current().GetLinkReg();
2444 
2445     // Initialize
2446     test->PreWork<Src>();
2447     static_cast<aarch32::Aarch32Encoder *>(test->GetEncoder())->GetMasm()->Push(aarch32::VixlReg(link_reg));
2448 
2449     // First type-dependency
2450     auto input = test->GetParameter(TypeInfo(Src(0)), 0);
2451     auto output = test->GetParameter(TypeInfo(Dst(0)), 0);
2452     // Main test call
2453     test->GetEncoder()->EncodeCast(output, std::is_signed_v<Dst>, input, std::is_signed_v<Src>);
2454 
2455     // Finalize
2456     static_cast<aarch32::Aarch32Encoder *>(test->GetEncoder())->GetMasm()->Pop(aarch32::VixlReg(link_reg));
2457     test->PostWork<Dst>();
2458 
2459     // If encode unsupported - now print error
2460     if (!test->GetEncoder()->GetResult()) {
2461         std::cerr << "Unsupported for " << TypeName<Src>() << ", " << TypeName<Dst>() << "\n";
2462         return false;
2463     }
2464     // Change this for enable print disasm
2465     test->Dump(false);
2466 
2467     // Main test loop:
2468     for (uint64_t i = 0; i < ITERATION; ++i) {
2469         // Using max size type: type result "Dst" or 32bit to check result,
2470         // because in our ISA min type is 32bit.
2471         // Only integers less thаn 32bit.
2472         typedef typename std::conditional<(sizeof(Dst) * BYTE_SIZE) >= WORD_SIZE, Dst, uint32_t>::type DstExt;
2473 
2474         // Second type-dependency
2475         auto src = random_gen<Src>();
2476         DstExt dst = static_cast<DstExt>(static_cast<Dst>(src));
2477 
2478         // Other cast for float->int
2479         if (std::is_floating_point<Src>() && !std::is_floating_point<Dst>()) {
2480             auto MIN_INT = std::numeric_limits<Dst>::min();
2481             auto MAX_INT = std::numeric_limits<Dst>::max();
2482             auto FLOAT_MIN_INT = static_cast<Src>(MIN_INT);
2483             auto FLOAT_MAX_INT = static_cast<Src>(MAX_INT);
2484 
2485             if (src > FLOAT_MIN_INT) {
2486                 if (src < FLOAT_MAX_INT) {
2487                     dst = static_cast<Dst>(src);
2488                 } else {
2489                     dst = MAX_INT;
2490                 }
2491             } else if (std::isnan(src)) {
2492                 dst = 0;
2493             } else {
2494                 dst = MIN_INT;
2495             }
2496         }
2497 
2498         // Deduced conflicting types for parameter
2499 
2500         // Main check - compare parameter and
2501         // return value
2502         if (!test->CallCode<Src, DstExt>(src, dst)) {
2503             return false;
2504         }
2505     }
2506 
2507     if constexpr (std::is_floating_point_v<Src> && std::is_integral_v<Dst>) {
2508         Src nan = std::numeric_limits<Src>::quiet_NaN();
2509         typedef typename std::conditional<(sizeof(Dst) * BYTE_SIZE) >= WORD_SIZE, Dst, uint32_t>::type DstExt;
2510 
2511         if (!test->CallCode<Src, DstExt>(nan, Dst(0))) {
2512             return false;
2513         }
2514     }
2515 
2516     return true;
2517 }
2518 
TEST_F(Encoder32Test,CastTest)2519 TEST_F(Encoder32Test, CastTest)
2520 {
2521     // Test int8
2522     EXPECT_TRUE((TestCast<int8_t, int8_t>(this)));
2523     EXPECT_TRUE((TestCast<int8_t, int16_t>(this)));
2524     EXPECT_TRUE((TestCast<int8_t, int32_t>(this)));
2525     EXPECT_TRUE((TestCast<int8_t, int64_t>(this)));
2526 
2527     EXPECT_TRUE((TestCast<int8_t, uint8_t>(this)));
2528     EXPECT_TRUE((TestCast<int8_t, uint16_t>(this)));
2529     EXPECT_TRUE((TestCast<int8_t, uint32_t>(this)));
2530     EXPECT_TRUE((TestCast<int8_t, uint64_t>(this)));
2531     EXPECT_TRUE((TestCast<int8_t, float>(this)));
2532     EXPECT_TRUE((TestCast<int8_t, double>(this)));
2533 
2534     EXPECT_TRUE((TestCast<uint8_t, int8_t>(this)));
2535     EXPECT_TRUE((TestCast<uint8_t, int16_t>(this)));
2536     EXPECT_TRUE((TestCast<uint8_t, int32_t>(this)));
2537     EXPECT_TRUE((TestCast<uint8_t, int64_t>(this)));
2538 
2539     EXPECT_TRUE((TestCast<uint8_t, uint8_t>(this)));
2540     EXPECT_TRUE((TestCast<uint8_t, uint16_t>(this)));
2541     EXPECT_TRUE((TestCast<uint8_t, uint32_t>(this)));
2542     EXPECT_TRUE((TestCast<uint8_t, uint64_t>(this)));
2543     EXPECT_TRUE((TestCast<uint8_t, float>(this)));
2544     EXPECT_TRUE((TestCast<uint8_t, double>(this)));
2545 
2546     // Test int16
2547     EXPECT_TRUE((TestCast<int16_t, int8_t>(this)));
2548     EXPECT_TRUE((TestCast<int16_t, int16_t>(this)));
2549     EXPECT_TRUE((TestCast<int16_t, int32_t>(this)));
2550     EXPECT_TRUE((TestCast<int16_t, int64_t>(this)));
2551 
2552     EXPECT_TRUE((TestCast<int16_t, uint8_t>(this)));
2553     EXPECT_TRUE((TestCast<int16_t, uint16_t>(this)));
2554     EXPECT_TRUE((TestCast<int16_t, uint32_t>(this)));
2555     EXPECT_TRUE((TestCast<int16_t, uint64_t>(this)));
2556     EXPECT_TRUE((TestCast<int16_t, float>(this)));
2557     EXPECT_TRUE((TestCast<int16_t, double>(this)));
2558 
2559     EXPECT_TRUE((TestCast<uint16_t, int8_t>(this)));
2560     EXPECT_TRUE((TestCast<uint16_t, int16_t>(this)));
2561     EXPECT_TRUE((TestCast<uint16_t, int32_t>(this)));
2562     EXPECT_TRUE((TestCast<uint16_t, int64_t>(this)));
2563 
2564     EXPECT_TRUE((TestCast<uint16_t, uint8_t>(this)));
2565     EXPECT_TRUE((TestCast<uint16_t, uint16_t>(this)));
2566     EXPECT_TRUE((TestCast<uint16_t, uint32_t>(this)));
2567     EXPECT_TRUE((TestCast<uint16_t, uint64_t>(this)));
2568     EXPECT_TRUE((TestCast<uint16_t, float>(this)));
2569     EXPECT_TRUE((TestCast<uint16_t, double>(this)));
2570 
2571     // Test int32
2572     EXPECT_TRUE((TestCast<int32_t, int8_t>(this)));
2573     EXPECT_TRUE((TestCast<int32_t, int16_t>(this)));
2574     EXPECT_TRUE((TestCast<int32_t, int32_t>(this)));
2575     EXPECT_TRUE((TestCast<int32_t, int64_t>(this)));
2576 
2577     EXPECT_TRUE((TestCast<int32_t, uint8_t>(this)));
2578     EXPECT_TRUE((TestCast<int32_t, uint16_t>(this)));
2579     EXPECT_TRUE((TestCast<int32_t, uint32_t>(this)));
2580     EXPECT_TRUE((TestCast<int32_t, uint64_t>(this)));
2581     EXPECT_TRUE((TestCast<int32_t, float>(this)));
2582     EXPECT_TRUE((TestCast<int32_t, double>(this)));
2583 
2584     EXPECT_TRUE((TestCast<uint32_t, int8_t>(this)));
2585     EXPECT_TRUE((TestCast<uint32_t, int16_t>(this)));
2586     EXPECT_TRUE((TestCast<uint32_t, int32_t>(this)));
2587     EXPECT_TRUE((TestCast<uint32_t, int64_t>(this)));
2588 
2589     EXPECT_TRUE((TestCast<uint32_t, uint8_t>(this)));
2590     EXPECT_TRUE((TestCast<uint32_t, uint16_t>(this)));
2591     EXPECT_TRUE((TestCast<uint32_t, uint32_t>(this)));
2592     EXPECT_TRUE((TestCast<uint32_t, uint64_t>(this)));
2593     EXPECT_TRUE((TestCast<uint32_t, float>(this)));
2594     EXPECT_TRUE((TestCast<uint32_t, double>(this)));
2595 
2596     // Test int64
2597     EXPECT_TRUE((TestCast<int64_t, int8_t>(this)));
2598     EXPECT_TRUE((TestCast<int64_t, int16_t>(this)));
2599     EXPECT_TRUE((TestCast<int64_t, int32_t>(this)));
2600     EXPECT_TRUE((TestCast<int64_t, int64_t>(this)));
2601 
2602     EXPECT_TRUE((TestCast<int64_t, uint8_t>(this)));
2603     EXPECT_TRUE((TestCast<int64_t, uint16_t>(this)));
2604     EXPECT_TRUE((TestCast<int64_t, uint32_t>(this)));
2605     EXPECT_TRUE((TestCast<int64_t, uint64_t>(this)));
2606     EXPECT_TRUE((TestCast<int64_t, float>(this)));
2607     EXPECT_TRUE((TestCast<int64_t, double>(this)));
2608 
2609     EXPECT_TRUE((TestCast<uint64_t, int8_t>(this)));
2610     EXPECT_TRUE((TestCast<uint64_t, int16_t>(this)));
2611     EXPECT_TRUE((TestCast<uint64_t, int32_t>(this)));
2612     EXPECT_TRUE((TestCast<uint64_t, int64_t>(this)));
2613 
2614     EXPECT_TRUE((TestCast<uint64_t, uint8_t>(this)));
2615     EXPECT_TRUE((TestCast<uint64_t, uint16_t>(this)));
2616     EXPECT_TRUE((TestCast<uint64_t, uint32_t>(this)));
2617     EXPECT_TRUE((TestCast<uint64_t, uint64_t>(this)));
2618     EXPECT_TRUE((TestCast<uint64_t, float>(this)));
2619     EXPECT_TRUE((TestCast<uint64_t, double>(this)));
2620 
2621     // Test float32/64 -> float32/64
2622     EXPECT_TRUE((TestCast<float, float>(this)));
2623     EXPECT_TRUE((TestCast<double, double>(this)));
2624     EXPECT_TRUE((TestCast<float, double>(this)));
2625     EXPECT_TRUE((TestCast<double, float>(this)));
2626 
2627     // We DON'T support cast from float32/64 to int8/16.
2628 
2629     // Test float32
2630     EXPECT_TRUE((TestCast<float, int32_t>(this)));
2631     EXPECT_TRUE((TestCast<float, int64_t>(this)));
2632     EXPECT_TRUE((TestCast<float, uint32_t>(this)));
2633     EXPECT_TRUE((TestCast<float, uint64_t>(this)));
2634 
2635     // Test float64
2636     EXPECT_TRUE((TestCast<double, int32_t>(this)));
2637     EXPECT_TRUE((TestCast<double, int64_t>(this)));
2638     EXPECT_TRUE((TestCast<double, uint32_t>(this)));
2639     EXPECT_TRUE((TestCast<double, uint64_t>(this)));
2640 }
2641 
2642 template <typename T>
TestDiv(Encoder32Test * test)2643 bool TestDiv(Encoder32Test *test)
2644 {
2645     bool is_signed = std::is_signed<T>::value;
2646 
2647     auto link_reg = Target::Current().GetLinkReg();
2648     ArenaVector<Reg> used_regs(test->GetAllocator()->Adapter());
2649     used_regs.emplace_back(link_reg);
2650     test->PreWork<T>(&used_regs);
2651 
2652     // First type-dependency
2653     auto param_1 = test->GetParameter(TypeInfo(T(0)), 0);
2654     auto param_2 = test->GetParameter(TypeInfo(T(0)), 1);
2655 
2656     // Main test call
2657     test->GetEncoder()->EncodeDiv(param_1, is_signed, param_1, param_2);
2658 
2659     // Finalize
2660     test->PostWork<T>();
2661 
2662     // If encode unsupported - now print error
2663     if (!test->GetEncoder()->GetResult()) {
2664         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
2665         return false;
2666     }
2667     // Change this for enable print disasm
2668     test->Dump(false);
2669 
2670     // Main test loop:
2671     for (uint64_t i = 0; i < ITERATION; ++i) {
2672         // Second type-dependency
2673         T tmp1 = random_gen<T>();
2674         T tmp2 = random_gen<T>();
2675         if (tmp2 == 0) {
2676             tmp2 += 1;
2677         }
2678         // Main check - compare parameter and
2679         // return value
2680         if (!test->CallCode<T>(tmp1, tmp2, (tmp1 / tmp2))) {
2681             return false;
2682         }
2683     }
2684 
2685     if constexpr (std::is_floating_point_v<T>) {
2686         T nan = std::numeric_limits<T>::quiet_NaN();
2687 
2688         if (!test->CallCode<T>(nan, random_gen<T>(), nan)) {
2689             return false;
2690         }
2691         if (!test->CallCode<T>(random_gen<T>(), nan, nan)) {
2692             return false;
2693         }
2694         if (!test->CallCode<T>(0.0, 0.0, nan)) {
2695             return false;
2696         }
2697         if (!test->CallCode<T>(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity(), nan)) {
2698             return false;
2699         }
2700         if (!test->CallCode<T>(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity(), nan)) {
2701             return false;
2702         }
2703     }
2704 
2705     return true;
2706 }
2707 
TEST_F(Encoder32Test,DivTest)2708 TEST_F(Encoder32Test, DivTest)
2709 {
2710     EXPECT_TRUE(TestDiv<int8_t>(this));
2711     EXPECT_TRUE(TestDiv<int16_t>(this));
2712     EXPECT_TRUE(TestDiv<int32_t>(this));
2713     EXPECT_TRUE(TestDiv<int64_t>(this));
2714     EXPECT_TRUE(TestDiv<uint8_t>(this));
2715     EXPECT_TRUE(TestDiv<uint16_t>(this));
2716     EXPECT_TRUE(TestDiv<uint32_t>(this));
2717     EXPECT_TRUE(TestDiv<uint64_t>(this));
2718     EXPECT_TRUE(TestDiv<float>(this));
2719     EXPECT_TRUE(TestDiv<double>(this));
2720 }
2721 
2722 template <typename T>
TestMod(Encoder32Test * test)2723 bool TestMod(Encoder32Test *test)
2724 {
2725     bool is_signed = std::is_signed<T>::value;
2726 
2727     auto link_reg = Target::Current().GetLinkReg();
2728     ArenaVector<Reg> used_regs(test->GetAllocator()->Adapter());
2729     used_regs.emplace_back(link_reg);
2730 
2731     test->PreWork<T>(&used_regs);
2732 
2733     static_cast<aarch32::Aarch32Encoder *>(test->GetEncoder())->GetMasm()->PushRegister(vixl::aarch32::r10);
2734     static_cast<aarch32::Aarch32Encoder *>(test->GetEncoder())->GetMasm()->PushRegister(vixl::aarch32::r11);
2735 
2736     // First type-dependency
2737     auto param_1 = test->GetParameter(TypeInfo(T(0)), 0);
2738     auto param_2 = test->GetParameter(TypeInfo(T(0)), 1);
2739 
2740     // Main test call
2741     test->GetEncoder()->EncodeMod(param_1, is_signed, param_1, param_2);
2742 
2743     static_cast<aarch32::Aarch32Encoder *>(test->GetEncoder())->GetMasm()->Pop(vixl::aarch32::r11);
2744     static_cast<aarch32::Aarch32Encoder *>(test->GetEncoder())->GetMasm()->Pop(vixl::aarch32::r10);
2745 
2746     // Finalize
2747     test->PostWork<T>();
2748 
2749     // If encode unsupported - now print error
2750     if (!test->GetEncoder()->GetResult()) {
2751         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
2752         return false;
2753     }
2754     // Change this for enable print disasm
2755     test->Dump(false);
2756 
2757     // Main test loop:
2758     for (uint64_t i = 0; i < ITERATION; ++i) {
2759         // Second type-dependency
2760         T tmp1 = random_gen<T>();
2761         T tmp2 = random_gen<T>();
2762         if (tmp2 == 0) {
2763             tmp2 += 1;
2764         }
2765         // Main check - compare parameter and
2766         // return value
2767         if constexpr (std::is_same<float, T>::value) {
2768             if (!test->CallCode<T>(tmp1, tmp2, fmodf(tmp1, tmp2))) {
2769                 return false;
2770             }
2771         } else if constexpr (std::is_same<double, T>::value) {
2772             if (!test->CallCode<T>(tmp1, tmp2, fmod(tmp1, tmp2))) {
2773                 return false;
2774             }
2775         } else {
2776             if (!test->CallCode<T>(tmp1, tmp2, static_cast<T>(tmp1 % tmp2))) {
2777                 return false;
2778             }
2779         }
2780     }
2781 
2782     if constexpr (std::is_floating_point_v<T>) {
2783         T nan = std::numeric_limits<T>::quiet_NaN();
2784 
2785         if (!test->CallCode<T>(nan, random_gen<T>(), nan)) {
2786             return false;
2787         }
2788         if (!test->CallCode<T>(random_gen<T>(), nan, nan)) {
2789             return false;
2790         }
2791         if (!test->CallCode<T>(0.0, 0.0, nan)) {
2792             return false;
2793         }
2794         if (!test->CallCode<T>(std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity(), nan)) {
2795             return false;
2796         }
2797         if (!test->CallCode<T>(-std::numeric_limits<T>::infinity(), std::numeric_limits<T>::infinity(), nan)) {
2798             return false;
2799         }
2800     }
2801 
2802     return true;
2803 }
2804 
TEST_F(Encoder32Test,ModTest)2805 TEST_F(Encoder32Test, ModTest)
2806 {
2807     EXPECT_TRUE(TestMod<int8_t>(this));
2808     EXPECT_TRUE(TestMod<int16_t>(this));
2809     EXPECT_TRUE(TestMod<int32_t>(this));
2810     EXPECT_TRUE(TestMod<int64_t>(this));
2811     EXPECT_TRUE(TestMod<uint8_t>(this));
2812     EXPECT_TRUE(TestMod<uint16_t>(this));
2813     EXPECT_TRUE(TestMod<uint32_t>(this));
2814     EXPECT_TRUE(TestMod<uint64_t>(this));
2815     EXPECT_TRUE(TestMod<float>(this));
2816     EXPECT_TRUE(TestMod<double>(this));
2817 }
2818 
2819 // MemCopy Test
2820 // TEST_F(Encoder32Test, MemCopyTest) {
2821 //  EncodeMemCopy(MemRef mem_from, MemRef mem_to, size_t size)
2822 
2823 // MemCopyz Test
2824 // TEST_F(Encoder32Test, MemCopyzTest) {
2825 //  EncodeMemCopyz(MemRef mem_from, MemRef mem_to, size_t size)
2826 
2827 template <int ID, typename T>
TestParam(Encoder32Test * test)2828 bool TestParam(Encoder32Test *test)
2829 {
2830     // int32_t uint64_t int32_t  int64_t         int32_t int32_t
2831     //   r0    r2+r3   stack0  stack2(align)   stack4
2832     using funct_ptr = uint64_t (*)(uint32_t, uint64_t, int32_t, int64_t, int32_t, int32_t);
2833 
2834     bool is_signed = std::is_signed<T>::value;
2835 
2836     constexpr TypeInfo params[6] = {INT32_TYPE, INT64_TYPE, INT32_TYPE, INT64_TYPE, INT32_TYPE, INT32_TYPE};
2837     TypeInfo curr_param = params[ID];
2838 
2839     auto param_info = test->GetCallconv()->GetParameterInfo(0);
2840     auto par = param_info->GetNativeParam(params[0]);
2841     // First ret value geted
2842     for (int i = 1; i <= ID; ++i) {
2843         par = param_info->GetNativeParam(params[i]);
2844     }
2845 
2846     auto link_reg = Target::Current().GetLinkReg();
2847     ArenaVector<Reg> used_regs(test->GetAllocator()->Adapter());
2848     used_regs.emplace_back(link_reg);
2849     test->PreWork<T>(&used_regs);
2850 
2851     auto ret_reg = test->GetParameter(curr_param, 0);
2852 
2853     auto fl = test->GetEncoder()->GetFrameLayout();
2854 
2855     // Main test call
2856     if (std::holds_alternative<Reg>(par)) {
2857         test->GetEncoder()->EncodeMov(ret_reg, std::get<Reg>(par));
2858     } else {
2859         auto mem = MemRef(Target::Current().GetStackReg(),
2860                           fl.GetFrameSize<CFrameLayout::BYTES>() + std::get<uint8_t>(par) * fl.GetSlotSize());
2861         test->GetEncoder()->EncodeLdr(ret_reg, is_signed, mem);
2862     }
2863 
2864     // If encode unsupported - now print error
2865     if (!test->GetEncoder()->GetResult()) {
2866         std::cerr << "Unsupported parameter with " << ID << "\n";
2867         return false;
2868     }
2869 
2870     // Finalize
2871     test->PostWork<T>();
2872 
2873     // Change this for enable print disasm
2874     test->Dump(false);
2875 
2876     auto size = test->GetCallconv()->GetCodeSize();
2877     void *offset = (static_cast<uint8_t *>(test->GetCallconv()->GetCodeEntry()));
2878     void *ptr = test->GetCodeAllocator()->AllocateCode(size, offset);
2879     auto func = reinterpret_cast<funct_ptr>(ptr);
2880 
2881     // Main test loop:
2882     for (uint64_t i = 0; i < ITERATION; ++i) {
2883         // Second type-dependency
2884         int32_t param_0 = random_gen<uint32_t>();
2885         uint64_t param_1 = random_gen<uint64_t>();
2886         int32_t param_2 = random_gen<int32_t>();
2887         int64_t param_3 = random_gen<int64_t>();
2888         int32_t param_4 = random_gen<int32_t>();
2889         int32_t param_5 = random_gen<int32_t>();
2890 
2891         // Main check - compare parameter and
2892         // return value
2893         const T curr_result = func(param_0, param_1, param_2, param_3, param_4, param_5);
2894         T result;
2895         if constexpr (ID == 0) {
2896             result = param_0;
2897         }
2898         if constexpr (ID == 1) {
2899             result = param_1;
2900         }
2901         if constexpr (ID == 2) {
2902             result = param_2;
2903         }
2904         if constexpr (ID == 3) {
2905             result = param_3;
2906         }
2907         if constexpr (ID == 4) {
2908             result = param_4;
2909         }
2910         if constexpr (ID == 5) {
2911             result = param_5;
2912         }
2913         if (curr_result != result) {
2914             return false;
2915         };
2916     }
2917     return true;
2918 }
2919 
TEST_F(Encoder32Test,ReadParams)2920 TEST_F(Encoder32Test, ReadParams)
2921 {
2922     EXPECT_TRUE((TestParam<0, int32_t>(this)));
2923     EXPECT_TRUE((TestParam<1, uint64_t>(this)));
2924     EXPECT_TRUE((TestParam<2, int32_t>(this)));
2925     EXPECT_TRUE((TestParam<3, int64_t>(this)));
2926     EXPECT_TRUE((TestParam<4, int32_t>(this)));
2927     EXPECT_TRUE((TestParam<5, int32_t>(this)));
2928 }
2929 
2930 template <typename T, Condition cc>
TestSelect(Encoder32Test * test)2931 bool TestSelect(Encoder32Test *test)
2932 {
2933     auto masm = static_cast<aarch32::Aarch32Encoder *>(test->GetEncoder())->GetMasm();
2934 
2935     // Initialize
2936     test->PreWork<bool>();
2937 
2938     // First type-dependency
2939     auto param0 = test->GetParameter(TypeInfo(T(0)), 0);
2940     auto param1 = test->GetParameter(TypeInfo(T(0)), 1);
2941     auto param2 = Reg(5, TypeInfo(uint32_t(0)));
2942     auto param3 = Reg(6, TypeInfo(uint32_t(0)));
2943 
2944     // Main test call
2945     masm->Push(aarch32::VixlReg(param2));
2946     masm->Push(aarch32::VixlReg(param3));
2947     test->GetEncoder()->EncodeMov(param2, Imm(1));
2948     test->GetEncoder()->EncodeMov(param3, Imm(0));
2949     test->GetEncoder()->EncodeSelect(Reg(param0.GetId(), TypeInfo(uint32_t(0))), param2, param3, param0, param1, cc);
2950     masm->Pop(aarch32::VixlReg(param3));
2951     masm->Pop(aarch32::VixlReg(param2));
2952 
2953     // Finalize
2954     test->PostWork<bool>();
2955 
2956     // If encode unsupported - now print error
2957     if (!test->GetEncoder()->GetResult()) {
2958         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
2959         return false;
2960     }
2961     // Change this for enable print disasm
2962     test->Dump(false);
2963 
2964     // Main test loop:
2965     for (uint64_t i = 0; i < ITERATION; ++i) {
2966         // Second type-dependency
2967         T tmp0 = random_gen<T>();
2968         T tmp1 = random_gen<T>();
2969 
2970         bool res {false};
2971         switch (cc) {
2972             case Condition::LT:
2973             case Condition::LO:
2974                 res = tmp0 < tmp1;
2975                 break;
2976             case Condition::EQ:
2977                 res = tmp0 == tmp1;
2978                 break;
2979             case Condition::NE:
2980                 res = tmp0 != tmp1;
2981                 break;
2982             case Condition::GT:
2983             case Condition::HI:
2984                 res = tmp0 > tmp1;
2985                 break;
2986             default:
2987                 UNREACHABLE();
2988         }
2989 
2990         // Main check - compare parameter and
2991         // return value
2992         if (!test->CallCode<T, bool>(tmp0, tmp1, res)) {
2993             return false;
2994         }
2995     }
2996     return true;
2997 }
2998 
TEST_F(Encoder32Test,SelectTest)2999 TEST_F(Encoder32Test, SelectTest)
3000 {
3001     EXPECT_TRUE((TestSelect<uint32_t, Condition::LO>(this)));
3002     EXPECT_TRUE((TestSelect<uint32_t, Condition::EQ>(this)));
3003     EXPECT_TRUE((TestSelect<uint32_t, Condition::NE>(this)));
3004     EXPECT_TRUE((TestSelect<uint32_t, Condition::HI>(this)));
3005 
3006     EXPECT_TRUE((TestSelect<uint64_t, Condition::LO>(this)));
3007     EXPECT_TRUE((TestSelect<uint64_t, Condition::EQ>(this)));
3008     EXPECT_TRUE((TestSelect<uint64_t, Condition::NE>(this)));
3009     EXPECT_TRUE((TestSelect<uint64_t, Condition::HI>(this)));
3010 
3011     EXPECT_TRUE((TestSelect<int32_t, Condition::LT>(this)));
3012     EXPECT_TRUE((TestSelect<int32_t, Condition::EQ>(this)));
3013     EXPECT_TRUE((TestSelect<int32_t, Condition::NE>(this)));
3014     EXPECT_TRUE((TestSelect<int32_t, Condition::GT>(this)));
3015 
3016     EXPECT_TRUE((TestSelect<int64_t, Condition::LT>(this)));
3017     EXPECT_TRUE((TestSelect<int64_t, Condition::EQ>(this)));
3018     EXPECT_TRUE((TestSelect<int64_t, Condition::NE>(this)));
3019     EXPECT_TRUE((TestSelect<int64_t, Condition::GT>(this)));
3020 }
3021 
3022 template <typename T, Condition cc, bool is_imm>
TestSelectTest(Encoder32Test * test)3023 bool TestSelectTest(Encoder32Test *test)
3024 {
3025     // Initialize
3026     test->PreWork<T>();
3027 
3028     auto param0 = test->GetParameter(TypeInfo(T(0)), 0);
3029     auto param1 = test->GetParameter(TypeInfo(T(0)), 1);
3030     auto param2 = Reg(5, TypeInfo(uint32_t(0)));
3031     auto param3 = Reg(6, TypeInfo(uint32_t(0)));
3032     [[maybe_unused]] T imm_value {};
3033     if (is_imm) {
3034         imm_value = random_gen<T>() & MAX_IMM_VALUE;
3035     }
3036 
3037     // Main test call
3038     test->GetEncoder()->EncodeMov(param2, Imm(1));
3039     test->GetEncoder()->EncodeMov(param3, Imm(0));
3040 
3041     if constexpr (is_imm) {
3042         test->GetEncoder()->EncodeSelectTest(Reg(param0.GetId(), TypeInfo(uint32_t(0))), param2, param3, param0,
3043                                              Imm(imm_value), cc);
3044     } else {
3045         test->GetEncoder()->EncodeSelectTest(Reg(param0.GetId(), TypeInfo(uint32_t(0))), param2, param3, param0, param1,
3046                                              cc);
3047     }
3048 
3049     // Finalize
3050     test->PostWork<bool>();
3051 
3052     // If encode unsupported - now print error
3053     if (!test->GetEncoder()->GetResult()) {
3054         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
3055         return false;
3056     }
3057     // Change this for enable print disasm
3058     test->Dump(false);
3059 
3060     // Main test loop:
3061     for (uint64_t i = 0; i < ITERATION; ++i) {
3062         T tmp0 = random_gen<T>();
3063         T tmp1 = random_gen<T>();
3064 
3065         T second_operand;
3066         if constexpr (is_imm) {
3067             second_operand = imm_value;
3068         } else {
3069             second_operand = tmp1;
3070         }
3071 
3072         T and_res = tmp0 & second_operand;
3073         bool res = cc == Condition::TST_EQ ? and_res == 0 : and_res != 0;
3074 
3075         // Main check - compare parameter and return value
3076         if (!test->CallCode<T, bool>(tmp0, tmp1, res)) {
3077             return false;
3078         }
3079     }
3080     return true;
3081 }
3082 
TEST_F(Encoder32Test,SelectTestTest)3083 TEST_F(Encoder32Test, SelectTestTest)
3084 {
3085     EXPECT_TRUE((TestSelectTest<uint32_t, Condition::TST_EQ, false>(this)));
3086     EXPECT_TRUE((TestSelectTest<uint32_t, Condition::TST_NE, false>(this)));
3087     EXPECT_TRUE((TestSelectTest<uint32_t, Condition::TST_EQ, true>(this)));
3088     EXPECT_TRUE((TestSelectTest<uint32_t, Condition::TST_NE, true>(this)));
3089 
3090     EXPECT_TRUE((TestSelectTest<int32_t, Condition::TST_EQ, false>(this)));
3091     EXPECT_TRUE((TestSelectTest<int32_t, Condition::TST_NE, false>(this)));
3092     EXPECT_TRUE((TestSelectTest<int32_t, Condition::TST_EQ, true>(this)));
3093     EXPECT_TRUE((TestSelectTest<int32_t, Condition::TST_NE, true>(this)));
3094 
3095     EXPECT_TRUE((TestSelectTest<uint64_t, Condition::TST_EQ, false>(this)));
3096     EXPECT_TRUE((TestSelectTest<uint64_t, Condition::TST_NE, false>(this)));
3097 
3098     EXPECT_TRUE((TestSelectTest<int64_t, Condition::TST_EQ, false>(this)));
3099     EXPECT_TRUE((TestSelectTest<int64_t, Condition::TST_NE, false>(this)));
3100 }
3101 
3102 template <typename T, Condition cc>
TestCompareTest(Encoder32Test * test)3103 bool TestCompareTest(Encoder32Test *test)
3104 {
3105     // Initialize
3106     test->PreWork<T>();
3107 
3108     // First type-dependency
3109     auto param1 = test->GetParameter(TypeInfo(T(0)), 0);
3110     auto param2 = test->GetParameter(TypeInfo(T(0)), 1);
3111 
3112     // Main test call
3113     test->GetEncoder()->EncodeCompareTest(param1, param1, param2, cc);
3114 
3115     // Finalize
3116     test->PostWork<T>();
3117 
3118     // If encode unsupported - now print error
3119     if (!test->GetEncoder()->GetResult()) {
3120         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
3121         return false;
3122     }
3123     // Change this for enable print disasm
3124     test->Dump(false);
3125 
3126     // Main test loop:
3127     for (uint64_t i = 0; i < ITERATION; ++i) {
3128         // Second type-dependency
3129         T tmp1 = random_gen<T>();
3130         T tmp2 = random_gen<T>();
3131         // Deduced conflicting types for parameter
3132 
3133         auto compare = [](T a, T b) -> int32_t { return cc == Condition::TST_EQ ? (a & b) == 0 : (a & b) != 0; };
3134 
3135         // Main check - compare parameter and
3136         // return value
3137         if (!test->CallCode<T, int32_t>(tmp1, tmp2, compare(tmp1, tmp2))) {
3138             return false;
3139         }
3140     }
3141     return true;
3142 }
3143 
TEST_F(Encoder32Test,CompareTestTest)3144 TEST_F(Encoder32Test, CompareTestTest)
3145 {
3146     EXPECT_TRUE((TestCompareTest<int32_t, Condition::TST_EQ>(this)));
3147     EXPECT_TRUE((TestCompareTest<int32_t, Condition::TST_NE>(this)));
3148     EXPECT_TRUE((TestCompareTest<int64_t, Condition::TST_EQ>(this)));
3149     EXPECT_TRUE((TestCompareTest<int64_t, Condition::TST_NE>(this)));
3150     EXPECT_TRUE((TestCompareTest<uint32_t, Condition::TST_EQ>(this)));
3151     EXPECT_TRUE((TestCompareTest<uint32_t, Condition::TST_NE>(this)));
3152     EXPECT_TRUE((TestCompareTest<uint64_t, Condition::TST_EQ>(this)));
3153     EXPECT_TRUE((TestCompareTest<uint64_t, Condition::TST_NE>(this)));
3154 }
3155 
3156 template <typename T, Condition cc, bool is_imm>
TestJumpTest(Encoder32Test * test)3157 bool TestJumpTest(Encoder32Test *test)
3158 {
3159     // Initialize
3160     test->PreWork<T>();
3161 
3162     // First type-dependency
3163     auto param0 = test->GetParameter(TypeInfo(T(0)), 0);
3164     auto param1 = test->GetParameter(TypeInfo(T(0)), 1);
3165     auto ret_val = Target::Current().GetParamReg(0);
3166 
3167     auto true_branch = test->GetEncoder()->CreateLabel();
3168     auto end = test->GetEncoder()->CreateLabel();
3169     [[maybe_unused]] T imm_value = random_gen<T>() & MAX_IMM_VALUE;
3170 
3171     // Main test call
3172     if constexpr (is_imm) {
3173         test->GetEncoder()->EncodeJumpTest(true_branch, param0, Imm(imm_value), cc);
3174     } else {
3175         test->GetEncoder()->EncodeJumpTest(true_branch, param0, param1, cc);
3176     }
3177     test->GetEncoder()->EncodeMov(ret_val, Imm(0));
3178     test->GetEncoder()->EncodeJump(end);
3179 
3180     test->GetEncoder()->BindLabel(true_branch);
3181     test->GetEncoder()->EncodeMov(ret_val, Imm(1));
3182 
3183     test->GetEncoder()->BindLabel(end);
3184     // Finalize
3185     test->PostWork<T>();
3186 
3187     // If encode unsupported - now print error
3188     if (!test->GetEncoder()->GetResult()) {
3189         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
3190         return false;
3191     }
3192     // Change this for enable print disasm
3193     test->Dump(false);
3194 
3195     // Main test loop:
3196     for (uint64_t i = 0; i < ITERATION; ++i) {
3197         // Second type-dependency
3198         T tmp1 = random_gen<T>();
3199         T tmp2;
3200         if constexpr (is_imm) {
3201             tmp2 = imm_value;
3202         } else {
3203             tmp2 = random_gen<T>();
3204         }
3205         // Deduced conflicting types for parameter
3206 
3207         auto compare = [](T a, T b) -> int32_t { return cc == Condition::TST_EQ ? (a & b) == 0 : (a & b) != 0; };
3208 
3209         // Main check - compare parameter and
3210         // return value
3211         if (!test->CallCode<T, int32_t>(tmp1, tmp2, compare(tmp1, tmp2))) {
3212             return false;
3213         }
3214     }
3215     return true;
3216 }
3217 
TEST_F(Encoder32Test,JumpTestTest)3218 TEST_F(Encoder32Test, JumpTestTest)
3219 {
3220     EXPECT_TRUE((TestJumpTest<int32_t, Condition::TST_EQ, false>(this)));
3221     EXPECT_TRUE((TestJumpTest<int32_t, Condition::TST_NE, false>(this)));
3222     EXPECT_TRUE((TestJumpTest<int64_t, Condition::TST_EQ, false>(this)));
3223     EXPECT_TRUE((TestJumpTest<int64_t, Condition::TST_NE, false>(this)));
3224     EXPECT_TRUE((TestJumpTest<uint32_t, Condition::TST_EQ, false>(this)));
3225     EXPECT_TRUE((TestJumpTest<uint32_t, Condition::TST_NE, false>(this)));
3226     EXPECT_TRUE((TestJumpTest<uint64_t, Condition::TST_EQ, false>(this)));
3227     EXPECT_TRUE((TestJumpTest<uint64_t, Condition::TST_NE, false>(this)));
3228 
3229     EXPECT_TRUE((TestJumpTest<int32_t, Condition::TST_EQ, true>(this)));
3230     EXPECT_TRUE((TestJumpTest<int32_t, Condition::TST_NE, true>(this)));
3231     EXPECT_TRUE((TestJumpTest<uint32_t, Condition::TST_EQ, true>(this)));
3232     EXPECT_TRUE((TestJumpTest<uint32_t, Condition::TST_NE, true>(this)));
3233 }
3234 
3235 template <typename T, bool is_acquire>
TestLoadExclusive(Encoder32Test * test,uint64_t input_word,T expected_result)3236 bool TestLoadExclusive(Encoder32Test *test, uint64_t input_word, T expected_result)
3237 {
3238     uint64_t buffer[3] = {0xFFFFFFFFFFFFFFFFULL, input_word, 0xFFFFFFFFFFFFFFFFULL};
3239     test->PreWork<T>();
3240     auto param_0 = test->GetParameter(TypeInfo(uintptr_t(0)), 0);
3241     auto result = test->GetParameter(TypeInfo(T(0)), 0);
3242     test->GetEncoder()->EncodeLdrExclusive(result, param_0, is_acquire);
3243     test->PostWork<T>();
3244 
3245     if (!test->GetEncoder()->GetResult()) {
3246         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
3247         return false;
3248     }
3249     // Change this for enable print disasm
3250     test->Dump(false);
3251     auto word_addr = reinterpret_cast<uintptr_t>(&buffer[1]);
3252     return test->CallCode<uintptr_t, T>(word_addr, expected_result);
3253 }
3254 
3255 template <typename T, bool is_release>
TestStoreExclusiveFailed(Encoder32Test * test)3256 bool TestStoreExclusiveFailed(Encoder32Test *test)
3257 {
3258     uint64_t buffer = 0xFFFFFFFFFFFFFFFFULL;
3259     test->PreWork<T>();
3260     auto param_0 = test->GetParameter(TypeInfo(uintptr_t(0)), 0);
3261     auto tmp_reg = test->GetParameter(TypeInfo(T(0)), 1);
3262     auto result = test->GetParameter(TypeInfo(uint32_t(0)), 0);
3263 
3264     // perform store without load - should fail as there are no exlusive monitor
3265     test->GetEncoder()->EncodeStrExclusive(result, tmp_reg, param_0, is_release);
3266     test->PostWork<T>();
3267 
3268     if (!test->GetEncoder()->GetResult()) {
3269         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
3270         return false;
3271     }
3272     // Change this for enable print disasm
3273     test->Dump(false);
3274     auto word_addr = reinterpret_cast<uintptr_t>(&buffer);
3275     return test->CallCodeVariadic<uint32_t, uintptr_t, T>(1, word_addr, 0);
3276 }
3277 
3278 template <typename T, bool is_release>
TestStoreExclusive(Encoder32Test * test,T value,uint64_t expected_result)3279 bool TestStoreExclusive(Encoder32Test *test, T value, uint64_t expected_result)
3280 {
3281     // test writes value into buffer[1]
3282     uint64_t buffer[3] = {0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL, 0xFFFFFFFFFFFFFFFFULL};
3283     test->PreWork<T>();
3284     auto param_0 = test->GetParameter(TypeInfo(uintptr_t(0)), 0);
3285     auto param_1 = test->GetParameter(TypeInfo(T(0)), 1);
3286     auto result = test->GetParameter(TypeInfo(uint32_t(0)), 0);
3287     auto retry_label = test->GetEncoder()->CreateLabel();
3288     // use reg 4 instead of actual tmp reg to correctly encode double-word loads
3289     Reg tmp_reg(4, TypeInfo(T(0)));
3290 
3291     test->GetEncoder()->BindLabel(retry_label);
3292     test->GetEncoder()->EncodeLdrExclusive(tmp_reg, param_0, false);
3293     test->GetEncoder()->EncodeStrExclusive(result, param_1, param_0, is_release);
3294     // retry if store exclusive returned non zero value
3295     test->GetEncoder()->EncodeJump(retry_label, result, Condition::NE);
3296 
3297     test->PostWork<T>();
3298 
3299     if (!test->GetEncoder()->GetResult()) {
3300         std::cerr << "Unsupported for " << TypeName<T>() << "\n";
3301         return false;
3302     }
3303     // Change this for enable print disasm
3304     test->Dump(false);
3305     auto word_addr = reinterpret_cast<uintptr_t>(&buffer[1]);
3306     if (!test->CallCodeVariadic<uint32_t, uintptr_t, T>(0, word_addr, value)) {
3307         return false;
3308     }
3309     if (buffer[1] != expected_result) {
3310         std::cerr << "Failed: expected value " << std::hex << expected_result << " differs from actual result "
3311                   << buffer[1] << std::dec << std::endl;
3312         return false;
3313     }
3314     return true;
3315 }
3316 
TEST_F(Encoder32Test,LoadExclusiveTest)3317 TEST_F(Encoder32Test, LoadExclusiveTest)
3318 {
3319     const uint64_t SOURCE_WORD = 0x1122334455667788ULL;
3320     // aarch32 is little-endian, so bytes are actually stored in following order:
3321     // 0x 88 77 66 55 44 33 22 11
3322     EXPECT_TRUE((TestLoadExclusive<uint8_t, false>(this, SOURCE_WORD, 0x88U)));
3323     EXPECT_TRUE((TestLoadExclusive<uint16_t, false>(this, SOURCE_WORD, 0x7788U)));
3324     EXPECT_TRUE((TestLoadExclusive<uint32_t, false>(this, SOURCE_WORD, 0x55667788UL)));
3325     EXPECT_TRUE((TestLoadExclusive<uint64_t, false>(this, SOURCE_WORD, SOURCE_WORD)));
3326 
3327     EXPECT_TRUE((TestLoadExclusive<uint8_t, true>(this, SOURCE_WORD, 0x88U)));
3328     EXPECT_TRUE((TestLoadExclusive<uint16_t, true>(this, SOURCE_WORD, 0x7788U)));
3329     EXPECT_TRUE((TestLoadExclusive<uint32_t, true>(this, SOURCE_WORD, 0x55667788UL)));
3330     EXPECT_TRUE((TestLoadExclusive<uint64_t, true>(this, SOURCE_WORD, SOURCE_WORD)));
3331 }
3332 
TEST_F(Encoder32Test,StoreExclusiveTest)3333 TEST_F(Encoder32Test, StoreExclusiveTest)
3334 {
3335     EXPECT_TRUE((TestStoreExclusiveFailed<uint8_t, false>(this)));
3336     EXPECT_TRUE((TestStoreExclusiveFailed<uint16_t, false>(this)));
3337     EXPECT_TRUE((TestStoreExclusiveFailed<uint32_t, false>(this)));
3338     EXPECT_TRUE((TestStoreExclusiveFailed<uint64_t, false>(this)));
3339 
3340     EXPECT_TRUE((TestStoreExclusiveFailed<uint8_t, true>(this)));
3341     EXPECT_TRUE((TestStoreExclusiveFailed<uint16_t, true>(this)));
3342     EXPECT_TRUE((TestStoreExclusiveFailed<uint32_t, true>(this)));
3343     EXPECT_TRUE((TestStoreExclusiveFailed<uint64_t, true>(this)));
3344 
3345     EXPECT_TRUE((TestStoreExclusive<uint8_t, false>(this, 0x11U, 0xFFFFFFFFFFFFFF11ULL)));
3346     EXPECT_TRUE((TestStoreExclusive<uint16_t, false>(this, 0x1122U, 0xFFFFFFFFFFFF1122ULL)));
3347     EXPECT_TRUE((TestStoreExclusive<uint32_t, false>(this, 0x11223344UL, 0xFFFFFFFF11223344ULL)));
3348     EXPECT_TRUE((TestStoreExclusive<uint64_t, false>(this, 0xAABBCCDD11335577ULL, 0xAABBCCDD11335577ULL)));
3349 
3350     EXPECT_TRUE((TestStoreExclusive<uint8_t, true>(this, 0x11U, 0xFFFFFFFFFFFFFF11ULL)));
3351     EXPECT_TRUE((TestStoreExclusive<uint16_t, true>(this, 0x1122U, 0xFFFFFFFFFFFF1122ULL)));
3352     EXPECT_TRUE((TestStoreExclusive<uint32_t, true>(this, 0x11223344UL, 0xFFFFFFFF11223344ULL)));
3353     EXPECT_TRUE((TestStoreExclusive<uint64_t, false>(this, 0xAABBCCDD11335577ULL, 0xAABBCCDD11335577ULL)));
3354 }
3355 
TEST_F(Encoder32Test,Registers)3356 TEST_F(Encoder32Test, Registers)
3357 {
3358     auto target = GetEncoder()->GetTarget();
3359     {
3360         {
3361             ScopedTmpReg tmp1(GetEncoder(), true);
3362             ASSERT_EQ(tmp1.GetReg(), target.GetLinkReg());
3363             ScopedTmpReg tmp2(GetEncoder(), true);
3364             ASSERT_NE(tmp2.GetReg(), target.GetLinkReg());
3365         }
3366         ScopedTmpReg tmp3(GetEncoder(), true);
3367         ASSERT_EQ(tmp3.GetReg(), target.GetLinkReg());
3368     }
3369     {
3370         ScopedTmpReg tmp1(GetEncoder(), false);
3371         ASSERT_NE(tmp1.GetReg(), target.GetLinkReg());
3372     }
3373 }
3374 }  // namespace panda::compiler
3375