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