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