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