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