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