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