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