/** * Copyright (c) 2021-2022 Huawei Device Co., Ltd. * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #include #include #include #include #include "mem/code_allocator.h" #include "mem/pool_manager.h" #include "target/amd64/target.h" #include "mem/base_mem_stats.h" template const char *TypeName(void); template const char *TypeName(T) { return TypeName(); } #define CLASS_NAME(type) \ template <> \ const char *TypeName(void) \ { \ return #type; \ } CLASS_NAME(int8_t) CLASS_NAME(int16_t) CLASS_NAME(int32_t) CLASS_NAME(int64_t) CLASS_NAME(uint8_t) CLASS_NAME(uint16_t) CLASS_NAME(uint32_t) CLASS_NAME(uint64_t) CLASS_NAME(float) CLASS_NAME(double) const uint64_t SEED = 0x1234; #ifndef PANDA_NIGHTLY_TEST_ON const uint64_t ITERATION = 40; #else const uint64_t ITERATION = 4000; #endif static inline auto random_generator = std::mt19937_64(SEED); // Max and min exponent on the basus of two float and double static const float MIN_EXP_BASE2_FLOAT = std::log2(FLT_MIN); static const float MAX_EXP_BASE2_FLOAT = std::log2(FLT_MAX) - 1.0; static const double MIN_EXP_BASE2_DOUBLE = std::log2(DBL_MIN); static const double MAX_EXP_BASE2_DOUBLE = std::log2(DBL_MAX) - 1.0; // Masks for generete denormal float numbers static const uint32_t MASK_DENORMAL_FLOAT = 0x807FFFFF; static const uint64_t MASK_DENORMAL_DOUBLE = 0x800FFFFFFFFFFFFF; template static T random_gen() { auto gen {random_generator()}; if constexpr (std::is_integral_v) { return gen; } else { switch (gen % 20U) { case (0U): return std::numeric_limits::quiet_NaN(); case (1U): return std::numeric_limits::infinity(); case (2U): return -std::numeric_limits::infinity(); case (3U): { if constexpr (std::is_same_v) { return panda::bit_cast(gen & MASK_DENORMAL_FLOAT); } else { return panda::bit_cast(gen & MASK_DENORMAL_DOUBLE); } } default: break; } // Uniform distribution floating value std::uniform_real_distribution dis_num(1.0, 2.0); int8_t sign = (gen % 2) == 0 ? 1 : -1; if constexpr (std::is_same_v) { std::uniform_real_distribution dis(MIN_EXP_BASE2_FLOAT, MAX_EXP_BASE2_FLOAT); return sign * dis_num(random_generator) * std::pow(2.0F, dis(random_generator)); } else if constexpr (std::is_same_v) { std::uniform_real_distribution dis(MIN_EXP_BASE2_DOUBLE, MAX_EXP_BASE2_DOUBLE); return sign * dis_num(random_generator) * std::pow(2.0, dis(random_generator)); } UNREACHABLE(); } } namespace panda::compiler { class Encoder64Test : public ::testing::Test { public: Encoder64Test() { panda::mem::MemConfig::Initialize(64_MB, 64_MB, 64_MB, 32_MB); PoolManager::Initialize(); allocator_ = new ArenaAllocator(SpaceType::SPACE_TYPE_COMPILER); encoder_ = Encoder::Create(allocator_, Arch::X86_64, false); encoder_->InitMasm(); regfile_ = RegistersDescription::Create(allocator_, Arch::X86_64); callconv_ = CallingConvention::Create(allocator_, encoder_, regfile_, Arch::X86_64); encoder_->SetRegfile(regfile_); mem_stats_ = new BaseMemStats(); code_alloc_ = new (std::nothrow) CodeAllocator(mem_stats_); } ~Encoder64Test() { Logger::Destroy(); encoder_->~Encoder(); delete allocator_; delete code_alloc_; delete mem_stats_; PoolManager::Finalize(); panda::mem::MemConfig::Finalize(); } void Reset() { encoder_->~Encoder(); encoder_ = Encoder::Create(allocator_, Arch::X86_64, false); encoder_->InitMasm(); } CodeAllocator *GetCodeAllocator() { return code_alloc_; } void ResetCodeAllocator(void *ptr, size_t size) { os::mem::MapRange mem_range(static_cast(ptr), size); mem_range.MakeReadWrite(); delete code_alloc_; code_alloc_ = new (std::nothrow) CodeAllocator(mem_stats_); } ArenaAllocator *GetAllocator() { return allocator_; } Encoder *GetEncoder() { return encoder_; } RegistersDescription *GetRegfile() { return regfile_; } CallingConvention *GetCallconv() { return callconv_; } size_t GetCursor() { return curr_cursor_; } // Warning! Do not use multiply times with different types! Reg GetParameter(TypeInfo type, int id = 0) { ASSERT(id < 4); if (type.IsFloat()) { return Reg(id, type); } return Target::Current().GetParamReg(id, type); } void PreWork() { // Curor need to encode multiply tests due one execution curr_cursor_ = 0; encoder_->SetCursorOffset(0); callconv_->GeneratePrologue(FrameInfo::FullPrologue()); } template void PostWork() { if constexpr (std::is_integral_v) { auto param = Target::Current().GetParamReg(0); auto return_reg = Target::Current().GetReturnReg(); GetEncoder()->EncodeMov(return_reg, param); } callconv_->GenerateEpilogue(FrameInfo::FullPrologue(), []() {}); encoder_->Finalize(); } void Dump(bool enabled) { if (enabled) { auto size = callconv_->GetCodeSize() - curr_cursor_; for (uint32_t i = curr_cursor_; i < curr_cursor_ + size;) { i = encoder_->DisasmInstr(std::cout, i, 0); std::cout << std::endl; } } } template bool CallCode(const T ¶m, const U &result) { // Using max size type: type result "U" or 32bit to check result, // because in our ISA min type is 32bit. // Only integers less thаn 32bit. typedef typename std::conditional<(sizeof(U) * BYTE_SIZE) >= WORD_SIZE, U, uint32_t>::type U_exp; typedef typename std::conditional<(sizeof(T) * BYTE_SIZE) >= WORD_SIZE, T, uint32_t>::type T_exp; using funct_ptr = U_exp (*)(T_exp data); auto size = callconv_->GetCodeSize() - curr_cursor_; void *offset = (static_cast(callconv_->GetCodeEntry())); void *ptr = code_alloc_->AllocateCode(size, offset); auto func = reinterpret_cast(ptr); // Extend types less then 32bit (i8, i16) const U_exp curr_result = func(static_cast(param)); ResetCodeAllocator(ptr, size); bool ret = false; if constexpr (std::is_floating_point_v || std::is_floating_point_v) { ret = (curr_result == result && std::signbit(curr_result) == std::signbit(result)) || (std::isnan(curr_result) && std::isnan(result)); } else { ret = (curr_result - result == 0); } if (!ret) { std::cerr << std::hex << "Failed CallCode for param=" << param << " and result=" << result << " current_reslt=" << curr_result << "\n"; if constexpr (std::is_floating_point_v || std::is_floating_point_v) { std::cerr << "In binary :"; if constexpr (std::is_same::value) { std::cerr << " param=" << bit_cast(param); } else if constexpr (std::is_same::value) { std::cerr << " param=" << bit_cast(param); } if constexpr (std::is_same::value) { std::cerr << " reslt=" << bit_cast(result); std::cerr << " current_reslt=" << bit_cast(curr_result); } else if constexpr (std::is_same::value) { std::cerr << " result=" << bit_cast(result); std::cerr << " current_reslt=" << bit_cast(curr_result); } std::cerr << "\n"; } Dump(true); } return ret; } template bool CallCode(const T ¶m1, const T ¶m2, const U &result) { typedef typename std::conditional<(sizeof(U) * BYTE_SIZE) >= WORD_SIZE, U, uint32_t>::type U_exp; typedef typename std::conditional<(sizeof(T) * BYTE_SIZE) >= WORD_SIZE, T, uint32_t>::type T_exp; using funct_ptr = U_exp (*)(T_exp param1, T_exp param2); auto size = callconv_->GetCodeSize() - curr_cursor_; void *offset = (static_cast(callconv_->GetCodeEntry())); void *ptr = code_alloc_->AllocateCode(size, offset); auto func = reinterpret_cast(ptr); const U_exp curr_result = func(static_cast(param1), static_cast(param2)); ResetCodeAllocator(ptr, size); bool ret = false; if constexpr (std::is_same::value || std::is_same::value) { ret = (curr_result == result && std::signbit(curr_result) == std::signbit(result)) || (std::isnan(curr_result) && std::isnan(result)); } else { ret = (curr_result - result == 0); } if (!ret) { std::cerr << "Failed CallCode for param1=" << param1 << " param2=" << param2 << " and result=" << result << " current_result=" << curr_result << "\n"; if constexpr (std::is_floating_point_v || std::is_floating_point_v) { std::cerr << "In binary :"; if constexpr (std::is_same::value) { std::cerr << " param1=" << bit_cast(param1) << " param2=" << bit_cast(param2); } else if constexpr (std::is_same::value) { std::cerr << " param1=" << bit_cast(param1) << " param2=" << bit_cast(param2); } if constexpr (std::is_same::value) { std::cerr << " reslt=" << bit_cast(result); std::cerr << " current_reslt=" << bit_cast(curr_result); } else if constexpr (std::is_same::value) { std::cerr << " result=" << bit_cast(result); std::cerr << " current_result=" << bit_cast(curr_result); } std::cerr << "\n"; } Dump(true); } return ret; } template bool CallCode(const T ¶m, const T &result) { typedef typename std::conditional<(sizeof(T) * BYTE_SIZE) >= WORD_SIZE, T, uint32_t>::type T_exp; using funct_ptr = T_exp (*)(T_exp data); auto size = callconv_->GetCodeSize() - curr_cursor_; void *offset = (static_cast(callconv_->GetCodeEntry())); void *ptr = code_alloc_->AllocateCode(size, offset); auto func = reinterpret_cast(ptr); const T_exp curr_result = func(static_cast(param)); ResetCodeAllocator(ptr, size); bool ret = false; if constexpr (std::is_same::value || std::is_same::value) { ret = (curr_result == result && std::signbit(curr_result) == std::signbit(result)) || (std::isnan(curr_result) && std::isnan(result)); } else { ret = (curr_result - result == 0); } if (!ret) { std::cerr << std::hex << "Failed CallCode for param=" << param << " and result=" << result << " current_result=" << curr_result << "\n"; if constexpr (std::is_floating_point_v) { std::cerr << "In binary :"; if constexpr (std::is_same::value) { std::cerr << " param=" << bit_cast(param); std::cerr << " reslt=" << bit_cast(result); std::cerr << " curr_reslt=" << bit_cast(curr_result); } else if constexpr (std::is_same::value) { std::cerr << " param=" << bit_cast(param); std::cerr << " curr_result=" << bit_cast(curr_result); } std::cerr << "\n"; } Dump(true); } return ret; } template bool CallCode(const T ¶m1, const T ¶m2, const T &result) { typedef typename std::conditional<(sizeof(T) * BYTE_SIZE) >= WORD_SIZE, T, uint32_t>::type T_exp; using funct_ptr = T_exp (*)(T_exp param1, T_exp param2); auto size = callconv_->GetCodeSize() - curr_cursor_; void *offset = (static_cast(callconv_->GetCodeEntry())); void *ptr = code_alloc_->AllocateCode(size, offset); auto func = reinterpret_cast(ptr); const T_exp curr_result = func(static_cast(param1), static_cast(param2)); ResetCodeAllocator(ptr, size); bool ret = false; if constexpr (std::is_same::value || std::is_same::value) { ret = (curr_result == result && std::signbit(curr_result) == std::signbit(result)) || (std::isnan(curr_result) && std::isnan(result)); } else { ret = (curr_result - result == 0); } if (!ret) { std::cerr << "Failed CallCode for param1=" << param1 << " param2=" << param2 << " and result=" << result << " curr_result=" << curr_result << "\n"; if constexpr (std::is_floating_point_v) { std::cerr << "In binary :"; if constexpr (std::is_same::value) { std::cerr << " param1=" << bit_cast(param1) << " param2=" << bit_cast(param2); std::cerr << " reslt=" << bit_cast(result) << " curr_result=" << bit_cast(curr_result); } else if constexpr (std::is_same::value) { std::cerr << " param1=" << bit_cast(param1) << " param2=" << bit_cast(param2); std::cerr << " result=" << bit_cast(result) << " curr_result=" << bit_cast(curr_result); } std::cerr << "\n"; } Dump(true); } return ret; } template T CallCodeStore(uint64_t address, T param) { using funct_ptr = T (*)(uint64_t param1, T param2); auto size = callconv_->GetCodeSize() - curr_cursor_; void *offset = (static_cast(callconv_->GetCodeEntry())); void *ptr = code_alloc_->AllocateCode(size, offset); auto func = reinterpret_cast(ptr); const T curr_result = func(address, param); ResetCodeAllocator(ptr, size); return curr_result; } template U CallCodeCall(T param1, T param2) { using funct_ptr = U (*)(T param1, T param2); auto size = callconv_->GetCodeSize() - curr_cursor_; void *offset = (static_cast(callconv_->GetCodeEntry())); void *ptr = code_alloc_->AllocateCode(size, offset); auto func = reinterpret_cast(ptr); const U curr_result = func(param1, param2); ResetCodeAllocator(ptr, size); return curr_result; } private: ArenaAllocator *allocator_ {nullptr}; Encoder *encoder_ {nullptr}; RegistersDescription *regfile_ {nullptr}; CallingConvention *callconv_ {nullptr}; CodeAllocator *code_alloc_ {nullptr}; BaseMemStats *mem_stats_ {nullptr}; size_t curr_cursor_ {0}; }; template bool TestNeg(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param = test->GetParameter(TypeInfo(T(0))); // Main test call test->GetEncoder()->EncodeNeg(param, param); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp = random_gen(); // Deduced conflicting types for parameter // Main check - compare parameter and // return value if (!test->CallCode(tmp, -tmp)) { return false; } } if constexpr (std::is_floating_point_v) { T nan = std::numeric_limits::quiet_NaN(); if (!test->CallCode(nan, nan)) { return false; } } return true; } TEST_F(Encoder64Test, NegTest) { EXPECT_TRUE(TestNeg(this)); EXPECT_TRUE(TestNeg(this)); EXPECT_TRUE(TestNeg(this)); EXPECT_TRUE(TestNeg(this)); } template bool TestNot(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param = test->GetParameter(TypeInfo(T(0))); // Main test call test->GetEncoder()->EncodeNot(param, param); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp = random_gen(); // Deduced conflicting types for parameter // Main check - compare parameter and // return value if (!test->CallCode(tmp, ~tmp)) { return false; } } return true; } TEST_F(Encoder64Test, NotTest) { EXPECT_TRUE(TestNot(this)); EXPECT_TRUE(TestNot(this)); EXPECT_TRUE(TestNot(this)); EXPECT_TRUE(TestNot(this)); } template bool TestMov(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param = test->GetParameter(TypeInfo(T(0))); // Main test call test->GetEncoder()->EncodeMov(param, param); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp = random_gen(); // Deduced conflicting types for parameter // Main check - compare parameter and // return value if (!test->CallCode(tmp, tmp)) { return false; } } if constexpr (std::is_floating_point_v) { T nan = std::numeric_limits::quiet_NaN(); if (!test->CallCode(nan, nan)) { return false; } } return true; } template bool TestMov2(Encoder64Test *test) { static_assert(sizeof(Src) == sizeof(Dst)); // Initialize test->PreWork(); // First type-dependency auto input = test->GetParameter(TypeInfo(Src(0)), 0); auto output = test->GetParameter(TypeInfo(Dst(0)), 0); // Main test call test->GetEncoder()->EncodeMov(output, input); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << ", " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency Src src = random_gen(); Dst dst = bit_cast(src); // Deduced conflicting types for parameter // Main check - compare parameter and // return value if (!test->CallCode(src, dst)) { return false; } } if constexpr (std::is_floating_point_v) { Src nan = std::numeric_limits::quiet_NaN(); Dst dst_nan = bit_cast(nan); if (!test->CallCode(nan, dst_nan)) { return false; } } return true; } TEST_F(Encoder64Test, MovTest) { EXPECT_TRUE(TestMov(this)); EXPECT_TRUE(TestMov(this)); EXPECT_TRUE(TestMov(this)); EXPECT_TRUE(TestMov(this)); EXPECT_TRUE((TestMov2(this))); EXPECT_TRUE((TestMov2(this))); EXPECT_TRUE((TestMov2(this))); EXPECT_TRUE((TestMov2(this))); // TODO (igorban) : add MOVI instructions // & support uint64_t mov } // Jump w/o cc TEST_F(Encoder64Test, JumpTest) { // Test for // EncodeJump(LabelHolder::LabelId label) PreWork(); auto param = Target::Current().GetParamReg(0); auto t1 = GetEncoder()->CreateLabel(); auto t2 = GetEncoder()->CreateLabel(); auto t3 = GetEncoder()->CreateLabel(); auto t4 = GetEncoder()->CreateLabel(); auto t5 = GetEncoder()->CreateLabel(); GetEncoder()->EncodeAdd(param, param, Imm(0x1)); GetEncoder()->EncodeJump(t1); GetEncoder()->EncodeMov(param, Imm(0x0)); GetEncoder()->EncodeReturn(); // T4 GetEncoder()->BindLabel(t4); GetEncoder()->EncodeAdd(param, param, Imm(0x1)); GetEncoder()->EncodeJump(t5); // Fail value GetEncoder()->EncodeMov(param, Imm(0x0)); GetEncoder()->EncodeReturn(); // T2 GetEncoder()->BindLabel(t2); GetEncoder()->EncodeAdd(param, param, Imm(0x1)); GetEncoder()->EncodeJump(t3); // Fail value GetEncoder()->EncodeMov(param, Imm(0x0)); GetEncoder()->EncodeReturn(); // T3 GetEncoder()->BindLabel(t3); GetEncoder()->EncodeAdd(param, param, Imm(0x1)); GetEncoder()->EncodeJump(t4); // Fail value GetEncoder()->EncodeMov(param, Imm(0x0)); GetEncoder()->EncodeReturn(); // T1 GetEncoder()->BindLabel(t1); GetEncoder()->EncodeAdd(param, param, Imm(0x1)); GetEncoder()->EncodeJump(t2); // Fail value GetEncoder()->EncodeMov(param, Imm(0x0)); GetEncoder()->EncodeReturn(); // Sucess exit GetEncoder()->BindLabel(t5); PostWork(); if (!GetEncoder()->GetResult()) { std::cerr << "Unsupported \n"; return; } Dump(false); for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency auto tmp = random_gen(); // Deduced conflicting types for parameter EXPECT_TRUE(CallCode(tmp, tmp + 5)); } } template bool TestBitTestAndBranch(Encoder64Test *test, T value, int pos, uint32_t expected) { test->PreWork(); auto param = test->GetParameter(TypeInfo(T(0))); auto ret_val = Target::Current().GetParamReg(0); auto label = test->GetEncoder()->CreateLabel(); auto end = test->GetEncoder()->CreateLabel(); if (NotZero) { test->GetEncoder()->EncodeBitTestAndBranch(label, param, pos, true); } else { test->GetEncoder()->EncodeBitTestAndBranch(label, param, pos, false); } test->GetEncoder()->EncodeMov(ret_val, Imm(1)); test->GetEncoder()->EncodeJump(end); test->GetEncoder()->BindLabel(label); test->GetEncoder()->EncodeMov(ret_val, Imm(0)); test->GetEncoder()->BindLabel(end); test->PostWork(); if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << std::endl; return false; } // Change this for enable print disasm test->Dump(false); if (!test->CallCode(value, expected)) { std::cerr << "Bit " << pos << " of 0x" << std::hex << value << " is not equal to " << std::dec << expected << std::endl; return false; } return true; } template bool TestBitTestAndBranch(Encoder64Test *test) { size_t max_pos = std::is_same::value ? 64 : 32; for (size_t i = 0; i < max_pos; i++) { T value = static_cast(1) << i; if (!TestBitTestAndBranch(test, value, i, NotZero ? 0 : 1)) { return false; } if (!TestBitTestAndBranch(test, ~value, i, NotZero ? 1 : 0)) { return false; } } return true; } template bool TestAddOverflow(Encoder64Test *test, T value1, T value2, T expected) { test->PreWork(); auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); auto ret_val = Target::Current().GetParamReg(0, TypeInfo(T(0))); auto label = test->GetEncoder()->CreateLabel(); auto end = test->GetEncoder()->CreateLabel(); if (is_equal) { test->GetEncoder()->EncodeAddOverflow(label, ret_val, param1, param2, Condition::VS); test->GetEncoder()->EncodeJump(end); test->GetEncoder()->BindLabel(label); test->GetEncoder()->EncodeMov(ret_val, Imm(0)); } else { test->GetEncoder()->EncodeAddOverflow(end, ret_val, param1, param2, Condition::VC); test->GetEncoder()->EncodeMov(ret_val, Imm(0)); } test->GetEncoder()->BindLabel(end); test->PostWork(); if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << std::endl; return false; } // Change this for enable print disasm test->Dump(false); if (!test->CallCode(value1, value2, expected)) { std::cerr << "AddOverflow " << " of 0x" << std::hex << value1 << " and 0x" << std::hex << value2 << " is not equal to " << std::dec << expected << std::endl; return false; } return true; } template bool TestAddOverflow(Encoder64Test *test) { T min = std::numeric_limits::min(); T max = std::numeric_limits::max(); std::array values = {0, 2, 5, -7, -10, max, min}; for (uint32_t i = 0; i < values.size(); ++i) { for (uint32_t j = 0; j < values.size(); ++j) { T a0 = values[i]; T a1 = values[j]; T expected; if ((a0 > 0 && a1 > max - a0) || (a0 < 0 && a1 < min - a0)) { expected = 0; } else { expected = a0 + a1; } if (!TestAddOverflow(test, a0, a1, expected) || !TestAddOverflow(test, a0, a1, expected)) { return false; } } } return true; } template bool TestSubOverflow(Encoder64Test *test, T value1, T value2, T expected) { test->PreWork(); auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); auto ret_val = Target::Current().GetParamReg(0, TypeInfo(T(0))); auto label = test->GetEncoder()->CreateLabel(); auto end = test->GetEncoder()->CreateLabel(); if (is_equal) { test->GetEncoder()->EncodeSubOverflow(label, ret_val, param1, param2, Condition::VS); test->GetEncoder()->EncodeJump(end); test->GetEncoder()->BindLabel(label); test->GetEncoder()->EncodeMov(ret_val, Imm(0)); } else { test->GetEncoder()->EncodeSubOverflow(end, ret_val, param1, param2, Condition::VC); test->GetEncoder()->EncodeMov(ret_val, Imm(0)); } test->GetEncoder()->BindLabel(end); test->PostWork(); if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << std::endl; return false; } // Change this for enable print disasm test->Dump(false); if (!test->CallCode(value1, value2, expected)) { std::cerr << "SubOverflow " << " of 0x" << std::hex << value1 << " and 0x" << std::hex << value2 << " is not equal to " << std::dec << expected << std::endl; return false; } return true; } template bool TestSubOverflow(Encoder64Test *test) { T min = std::numeric_limits::min(); T max = std::numeric_limits::max(); std::array values = {0, 2, 5, -7, -10, max, min}; for (uint32_t i = 0; i < values.size(); ++i) { for (uint32_t j = 0; j < values.size(); ++j) { T a0 = values[i]; T a1 = values[j]; T expected; if ((a1 > 0 && a0 < min + a1) || (a1 < 0 && a0 > max + a1)) { expected = 0; } else { expected = a0 - a1; } if (!TestSubOverflow(test, a0, a1, expected) || !TestSubOverflow(test, a0, a1, expected)) { return false; } } } return true; } template bool TestJumpCC(Encoder64Test *test) { bool is_signed = std::is_signed::value; // Initialize test->PreWork(); // First type-dependency auto param = test->GetParameter(TypeInfo(T(0))); // Main test call auto ret_val = Target::Current().GetParamReg(0); auto tsucc = test->GetEncoder()->CreateLabel(); auto end = test->GetEncoder()->CreateLabel(); test->GetEncoder()->EncodeJump(tsucc, param, cc); test->GetEncoder()->EncodeMov(param, Imm(0x0)); test->GetEncoder()->EncodeMov(ret_val, Imm(0x1)); test->GetEncoder()->EncodeJump(end); test->GetEncoder()->BindLabel(tsucc); test->GetEncoder()->EncodeMov(param, Imm(0x0)); test->GetEncoder()->EncodeMov(ret_val, Imm(0x0)); test->GetEncoder()->BindLabel(end); // test->GetEncoder()->EncodeReturn(); < encoded in PostWork // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); if constexpr (cc == Condition::EQ) { if (!test->CallCode(0, 0)) { std::cerr << "zero EQ test fail \n"; return false; } } if constexpr (cc == Condition::NE) { if (!test->CallCode(0, 1)) { std::cerr << "zero EQ test fail \n"; return false; } } // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp = random_gen(); if (tmp == 0) { // Only non-zero values tmp += 1; } // Deduced conflicting types for parameter if constexpr (cc == Condition::EQ) { if (!test->CallCode(tmp, 1)) { std::cerr << "non-zero EQ test fail " << tmp << " \n"; return false; } } if constexpr (cc == Condition::NE) { if (!test->CallCode(tmp, 0)) { std::cerr << "non-zero EQ test fail " << tmp << " \n"; return false; } } // Main check - compare parameter and // return value } return true; } // Jump with cc TEST_F(Encoder64Test, JumpCCTest) { EXPECT_TRUE((TestJumpCC(this))); EXPECT_TRUE((TestJumpCC(this))); EXPECT_TRUE((TestJumpCC(this))); EXPECT_TRUE((TestJumpCC(this))); EXPECT_TRUE((TestJumpCC(this))); EXPECT_TRUE((TestJumpCC(this))); EXPECT_TRUE((TestJumpCC(this))); EXPECT_TRUE((TestJumpCC(this))); // TestJumpCC // TestJumpCC // TestJumpCC // TestJumpCC } TEST_F(Encoder64Test, BitTestAndBranchZero) { EXPECT_TRUE(TestBitTestAndBranch(this)); EXPECT_TRUE(TestBitTestAndBranch(this)); } TEST_F(Encoder64Test, BitTestAndBranchNotZero) { constexpr bool notZero = true; EXPECT_TRUE((TestBitTestAndBranch(this))); EXPECT_TRUE((TestBitTestAndBranch(this))); } TEST_F(Encoder64Test, AddOverflow) { EXPECT_TRUE((TestAddOverflow(this))); EXPECT_TRUE((TestAddOverflow(this))); } TEST_F(Encoder64Test, SubOverflow) { EXPECT_TRUE((TestSubOverflow(this))); EXPECT_TRUE((TestSubOverflow(this))); } template bool TestLdr(Encoder64Test *test) { bool is_signed = std::is_signed::value; // Initialize test->PreWork(); // First type-dependency // Some strange code - default parameter is addres (32 bit) auto param = Target::Current().GetParamReg(0); // But return value is cutted by loaded value auto ret_val = test->GetParameter(TypeInfo(T(0))); auto mem = MemRef(param); test->GetEncoder()->EncodeLdr(ret_val, is_signed, mem); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp = random_gen(); T *ptr = &tmp; // Test : param - Pointer to value // return - value (loaded by ptr) // Value is resulting type, but call is ptr_type if (!test->CallCode(reinterpret_cast(ptr), tmp)) { std::cerr << "Ldr test fail " << tmp << " \n"; return false; } } return true; } // Load test TEST_F(Encoder64Test, LoadTest) { EXPECT_TRUE((TestLdr(this))); EXPECT_TRUE((TestLdr(this))); EXPECT_TRUE((TestLdr(this))); EXPECT_TRUE((TestLdr(this))); EXPECT_TRUE((TestLdr(this))); EXPECT_TRUE((TestLdr(this))); // TODO(igorban) : additional test for full memory model: // + mem(base + index< bool TestStr(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param = test->GetParameter(TypeInfo(int64_t(0)), 0); // Data to be stored: auto stored_value = test->GetParameter(TypeInfo(T(0)), 1); if constexpr ((std::is_same::value) || std::is_same::value) { stored_value = test->GetParameter(TypeInfo(T(0)), 0); } auto mem = MemRef(param); test->GetEncoder()->EncodeStr(stored_value, mem); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp = random_gen(); T ret_data = 0; T *ptr = &ret_data; // Test : param - Pointer to value // return - value (loaded by ptr) // Value is resulting type, but call is ptr_type auto result = test->CallCodeStore(reinterpret_cast(ptr), tmp); // Store must change ret_data value if (!(ret_data == tmp || (std::is_floating_point_v && std::isnan(ret_data) && std::isnan(tmp)))) { std::cerr << std::hex << "Ldr test fail " << tmp << " ret_data = " << ret_data << "\n"; return false; } } return true; } // Simple store test TEST_F(Encoder64Test, StrTest) { EXPECT_TRUE((TestStr(this))); EXPECT_TRUE((TestStr(this))); EXPECT_TRUE((TestStr(this))); EXPECT_TRUE((TestStr(this))); EXPECT_TRUE((TestStr(this))); EXPECT_TRUE((TestStr(this))); // TODO(igorban) : additional test for full memory model: // + mem(base + index<MakeCall(reinterpret_cast(foo_ptr)); // return value - moved to return value PostWork(); // If encode unsupported - now print error if (!GetEncoder()->GetResult()) { std::cerr << "Unsupported Call-instruction \n"; return; } // Change this for enable print disasm Dump(false); // Equality test auto tmp1 = random_gen(); uint32_t tmp2 = tmp1; // first template arg - parameter type, second - return type auto result = CallCodeCall(tmp1, tmp2); // Store must change ret_data value if (!result) { std::cerr << std::hex << "Call test fail tmp1=" << tmp1 << " tmp2=" << tmp2 << " result =" << result << "\n"; } EXPECT_TRUE(result); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency auto tmp1 = random_gen(); auto tmp2 = random_gen(); // first template arg - parameter type, second - return type auto result = CallCodeCall(tmp1, tmp2); auto ret_data = (tmp1 == tmp2); // Store must change ret_data value if (result != ret_data) { std::cerr << std::hex << "Call test fail tmp1=" << tmp1 << " tmp2=" << tmp2 << " ret_data = " << ret_data << " result =" << result << "\n"; } EXPECT_EQ(result, ret_data); } } template bool TestAbs(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param = test->GetParameter(TypeInfo(T(0))); // Main test call test->GetEncoder()->EncodeAbs(param, param); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp = random_gen(); // Main check - compare parameter and // return value if (!test->CallCode(tmp, std::abs(tmp))) { return false; } } if constexpr (std::is_floating_point_v) { T nan = std::numeric_limits::quiet_NaN(); if (!test->CallCode(nan, nan)) { return false; } } return true; } TEST_F(Encoder64Test, AbsTest) { EXPECT_TRUE(TestAbs(this)); EXPECT_TRUE(TestAbs(this)); EXPECT_TRUE(TestAbs(this)); EXPECT_TRUE(TestAbs(this)); } template bool TestAdd(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeAdd(param1, param1, param2); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency auto tmp1 = random_gen(); auto tmp2 = random_gen(); // Deduced conflicting types for parameter T result {0}; if constexpr (std::is_integral_v) { result = T(std::make_unsigned_t(tmp1) + tmp2); } else { result = tmp1 + tmp2; } // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, result)) { return false; } } if constexpr (std::is_floating_point_v) { T nan = std::numeric_limits::quiet_NaN(); if (!test->CallCode(nan, random_gen(), nan)) { return false; } if (!test->CallCode(random_gen(), nan, nan)) { return false; } if (!test->CallCode(-std::numeric_limits::infinity(), std::numeric_limits::infinity(), nan)) { return false; } if (!test->CallCode(std::numeric_limits::infinity(), -std::numeric_limits::infinity(), nan)) { return false; } } return true; } TEST_F(Encoder64Test, AddTest) { EXPECT_TRUE(TestAdd(this)); EXPECT_TRUE(TestAdd(this)); EXPECT_TRUE(TestAdd(this)); EXPECT_TRUE(TestAdd(this)); } template bool TestAddImm(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); T param2 = random_gen(); // Main test call test->GetEncoder()->EncodeAdd(param1, param1, Imm(param2)); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); // Deduced conflicting types for parameter T result {0}; if constexpr (std::is_integral_v) { result = T(std::make_unsigned_t(tmp1) + param2); } else { result = tmp1 + param2; } // Main check - compare parameter and // return value if (!test->CallCode(tmp1, result)) { return false; } } return true; } TEST_F(Encoder64Test, AddImmTest) { EXPECT_TRUE(TestAddImm(this)); EXPECT_TRUE(TestAddImm(this)); // TestAddImm // TestAddImm } template bool TestSub(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeSub(param1, param1, param2); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 = random_gen(); // Deduced conflicting types for parameter T result {0}; if constexpr (std::is_integral_v) { result = T(std::make_unsigned_t(tmp1) - tmp2); } else { result = tmp1 - tmp2; } // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, result)) { return false; } } if constexpr (std::is_floating_point_v) { T nan = std::numeric_limits::quiet_NaN(); if (!test->CallCode(nan, random_gen(), nan)) { return false; } if (!test->CallCode(random_gen(), nan, nan)) { return false; } if (!test->CallCode(std::numeric_limits::infinity(), std::numeric_limits::infinity(), nan)) { return false; } if (!test->CallCode(-std::numeric_limits::infinity(), -std::numeric_limits::infinity(), nan)) { return false; } } return true; } TEST_F(Encoder64Test, SubTest) { EXPECT_TRUE(TestSub(this)); EXPECT_TRUE(TestSub(this)); EXPECT_TRUE(TestSub(this)); EXPECT_TRUE(TestSub(this)); } template bool TestSubImm(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); T param2 = random_gen(); // Main test call test->GetEncoder()->EncodeSub(param1, param1, Imm(param2)); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); // Deduced conflicting types for parameter T result {0}; if constexpr (std::is_integral_v) { result = T(std::make_unsigned_t(tmp1) - param2); } else { result = tmp1 - param2; } // Main check - compare parameter and // return value if (!test->CallCode(tmp1, result)) { return false; } } return true; } TEST_F(Encoder64Test, SubImmTest) { EXPECT_TRUE(TestSubImm(this)); EXPECT_TRUE(TestSubImm(this)); // TestSubImm // TestSubImm } template bool TestMul(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeMul(param1, param1, param2); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 = random_gen(); // Deduced conflicting types for parameter T result {0}; if constexpr (std::is_integral_v) { result = T(std::make_unsigned_t(tmp1) * tmp2); } else { result = tmp1 * tmp2; } // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, result)) { return false; } } if constexpr (std::is_floating_point_v) { T nan = std::numeric_limits::quiet_NaN(); if (!test->CallCode(nan, random_gen(), nan)) { return false; } if (!test->CallCode(random_gen(), nan, nan)) { return false; } if (!test->CallCode(0.0, std::numeric_limits::infinity(), nan)) { return false; } if (!test->CallCode(std::numeric_limits::infinity(), 0.0, nan)) { return false; } } return true; } TEST_F(Encoder64Test, MulTest) { EXPECT_TRUE(TestMul(this)); EXPECT_TRUE(TestMul(this)); EXPECT_TRUE(TestMul(this)); EXPECT_TRUE(TestMul(this)); } template bool TestMin(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeMin(param1, std::is_signed_v, param1, param2); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 = random_gen(); // Deduced conflicting types for parameter T result {0}; if (std::is_floating_point_v && (std::isnan(tmp1) || std::isnan(tmp2))) { result = std::numeric_limits::quiet_NaN(); } else { result = std::min(tmp1, tmp2); } // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, result)) { return false; } } if constexpr (std::is_floating_point_v) { T nan = std::numeric_limits::quiet_NaN(); if (!test->CallCode(nan, random_gen(), nan)) { return false; } if (!test->CallCode(random_gen(), nan, nan)) { return false; } // use static_cast to make sure correct float/double type is applied if (!test->CallCode(static_cast(-0.0), static_cast(+0.0), static_cast(-0.0))) { return false; } if (!test->CallCode(static_cast(+0.0), static_cast(-0.0), static_cast(-0.0))) { return false; } } return true; } TEST_F(Encoder64Test, MinTest) { EXPECT_TRUE(TestMin(this)); EXPECT_TRUE(TestMin(this)); EXPECT_TRUE(TestMin(this)); EXPECT_TRUE(TestMin(this)); EXPECT_TRUE(TestMin(this)); EXPECT_TRUE(TestMin(this)); } template bool TestMax(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeMax(param1, std::is_signed_v, param1, param2); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 = random_gen(); // Deduced conflicting types for parameter T result {0}; if (std::is_floating_point_v && (std::isnan(tmp1) || std::isnan(tmp2))) { result = std::numeric_limits::quiet_NaN(); } else { result = std::max(tmp1, tmp2); } // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, result)) { return false; } } if constexpr (std::is_floating_point_v) { T nan = std::numeric_limits::quiet_NaN(); if (!test->CallCode(nan, random_gen(), nan)) { return false; } if (!test->CallCode(random_gen(), nan, nan)) { return false; } // use static_cast to make sure correct float/double type is applied if (!test->CallCode(static_cast(-0.0), static_cast(+0.0), static_cast(+0.0))) { return false; } if (!test->CallCode(static_cast(+0.0), static_cast(-0.0), static_cast(+0.0))) { return false; } } return true; } TEST_F(Encoder64Test, MaxTest) { EXPECT_TRUE(TestMax(this)); EXPECT_TRUE(TestMax(this)); EXPECT_TRUE(TestMax(this)); EXPECT_TRUE(TestMax(this)); EXPECT_TRUE(TestMax(this)); EXPECT_TRUE(TestMax(this)); } template bool TestShl(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeShl(param1, param1, param2); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 {0}; if constexpr (std::is_same_v) { tmp2 = random_gen() % DOUBLE_WORD_SIZE; } else { tmp2 = random_gen() % WORD_SIZE; } // Deduced conflicting types for parameter // Main check - compare parameter and // return value bool result {false}; if constexpr (std::is_same_v || std::is_same_v) { result = test->CallCode(tmp1, tmp2, std::make_unsigned_t(tmp1) << (tmp2 & (CHAR_BIT * sizeof(T) - 1))); } else { result = test->CallCode(tmp1, tmp2, std::make_unsigned_t(tmp1) << tmp2); } if (!result) { return false; } } return true; } TEST_F(Encoder64Test, ShlTest) { EXPECT_TRUE(TestShl(this)); EXPECT_TRUE(TestShl(this)); } template bool TestShr(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeShr(param1, param1, param2); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 {0}; if constexpr (sizeof(T) == sizeof(int64_t)) { tmp2 = random_gen() % DOUBLE_WORD_SIZE; } else { tmp2 = random_gen() % WORD_SIZE; } // Deduced conflicting types for parameter // Main check - compare parameter and // return value bool result {false}; if constexpr (sizeof(T) == sizeof(int64_t)) { result = test->CallCode(tmp1, tmp2, (static_cast(tmp1)) >> tmp2); } else { result = test->CallCode(tmp1, tmp2, (static_cast(tmp1)) >> (tmp2 & (CHAR_BIT * sizeof(T) - 1))); } if (!result) { return false; } } return true; } TEST_F(Encoder64Test, ShrTest) { EXPECT_TRUE(TestShr(this)); EXPECT_TRUE(TestShr(this)); EXPECT_TRUE(TestShr(this)); EXPECT_TRUE(TestShr(this)); } template bool TestAShr(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeAShr(param1, param1, param2); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 {0}; if constexpr (std::is_same_v) { tmp2 = random_gen() % DOUBLE_WORD_SIZE; } else { tmp2 = random_gen() % WORD_SIZE; } // Deduced conflicting types for parameter // Main check - compare parameter and // return value bool result {false}; if constexpr (std::is_same_v || std::is_same_v) { result = test->CallCode(tmp1, tmp2, tmp1 >> (tmp2 & (CHAR_BIT * sizeof(T) - 1))); } else { result = test->CallCode(tmp1, tmp2, tmp1 >> tmp2); } if (!result) { return false; } } return true; } TEST_F(Encoder64Test, AShrTest) { EXPECT_TRUE(TestAShr(this)); EXPECT_TRUE(TestAShr(this)); } template bool TestAnd(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeAnd(param1, param1, param2); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 = random_gen(); // Deduced conflicting types for parameter // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, tmp1 & tmp2)) { return false; } } return true; } TEST_F(Encoder64Test, AndTest) { EXPECT_TRUE(TestAnd(this)); EXPECT_TRUE(TestAnd(this)); } template bool TestOr(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeOr(param1, param1, param2); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 = random_gen(); // Deduced conflicting types for parameter // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, tmp1 | tmp2)) { return false; } } return true; } TEST_F(Encoder64Test, OrTest) { EXPECT_TRUE(TestOr(this)); EXPECT_TRUE(TestOr(this)); } template bool TestXor(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeXor(param1, param1, param2); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 = random_gen(); // Deduced conflicting types for parameter // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, tmp1 ^ tmp2)) { return false; } } return true; } TEST_F(Encoder64Test, XorTest) { EXPECT_TRUE(TestXor(this)); EXPECT_TRUE(TestXor(this)); } template bool TestShlImm(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); int64_t param2 {0}; if constexpr (std::is_same_v) { param2 = random_gen() % DOUBLE_WORD_SIZE; } else { param2 = random_gen() % WORD_SIZE; } // Main test call test->GetEncoder()->EncodeShl(param1, param1, Imm(param2)); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); // Deduced conflicting types for parameter // Main check - compare parameter and // return value bool result {false}; if constexpr (std::is_same_v || std::is_same_v) { result = test->CallCode(tmp1, std::make_unsigned_t(tmp1) << (param2 & (CHAR_BIT * sizeof(T) - 1))); } else { result = test->CallCode(tmp1, std::make_unsigned_t(tmp1) << param2); } if (!result) { return false; } } return true; } TEST_F(Encoder64Test, ShlImmTest) { EXPECT_TRUE(TestShlImm(this)); EXPECT_TRUE(TestShlImm(this)); } template bool TestShrImm(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); int64_t param2 {0}; if constexpr (sizeof(T) == sizeof(int64_t)) { param2 = random_gen() % DOUBLE_WORD_SIZE; } else { param2 = random_gen() % WORD_SIZE; } // Main test call test->GetEncoder()->EncodeShr(param1, param1, Imm(param2)); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); // Deduced conflicting types for parameter // Main check - compare parameter and // return value bool result {false}; if constexpr (sizeof(T) == sizeof(int64_t)) { result = test->CallCode(tmp1, (static_cast(tmp1)) >> param2); } else { result = test->CallCode(tmp1, (static_cast(tmp1)) >> (param2 & (CHAR_BIT * sizeof(T) - 1))); } if (!result) { return false; } } return true; } TEST_F(Encoder64Test, ShrImmTest) { EXPECT_TRUE(TestShrImm(this)); EXPECT_TRUE(TestShrImm(this)); EXPECT_TRUE(TestShrImm(this)); EXPECT_TRUE(TestShrImm(this)); } template bool TestCmp(Encoder64Test *test) { static_assert(std::is_integral_v); // Initialize test->PreWork(); // First type-dependency auto output = test->GetParameter(TypeInfo(int32_t(0)), 0); auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeCmp(output, param1, param2, std::is_signed_v ? Condition::LT : Condition::LO); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 = random_gen(); // Deduced conflicting types for parameter auto compare = [](T a, T b) -> int32_t { return a < b ? -1 : a > b ? 1 : 0; }; int32_t result {compare(tmp1, tmp2)}; // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, result)) { return false; } } return true; } template bool TestFcmp(Encoder64Test *test, bool is_fcmpg) { static_assert(std::is_floating_point_v); // Initialize test->PreWork(); // First type-dependency auto output = test->GetParameter(TypeInfo(int32_t(0)), 0); auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeCmp(output, param1, param2, is_fcmpg ? Condition::MI : Condition::LT); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 = random_gen(); // Deduced conflicting types for parameter auto compare = [](T a, T b) -> int32_t { return a < b ? -1 : a > b ? 1 : 0; }; int32_t result {0}; if (std::isnan(tmp1) || std::isnan(tmp2)) { result = is_fcmpg ? 1 : -1; } else { result = compare(tmp1, tmp2); } // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, result)) { return false; } } if constexpr (std::is_floating_point_v) { T nan = std::numeric_limits::quiet_NaN(); if (!test->CallCode(nan, 5.0, is_fcmpg ? 1 : -1)) { return false; } if (!test->CallCode(5.0, nan, is_fcmpg ? 1 : -1)) { return false; } if (!test->CallCode(nan, nan, is_fcmpg ? 1 : -1)) { return false; } } return true; } TEST_F(Encoder64Test, CmpTest) { EXPECT_TRUE(TestCmp(this)); EXPECT_TRUE(TestCmp(this)); EXPECT_TRUE(TestCmp(this)); EXPECT_TRUE(TestCmp(this)); EXPECT_TRUE(TestFcmp(this, false)); EXPECT_TRUE(TestFcmp(this, false)); EXPECT_TRUE(TestFcmp(this, true)); EXPECT_TRUE(TestFcmp(this, true)); } template bool TestCmp64(Encoder64Test *test) { static_assert(sizeof(T) == sizeof(int64_t)); // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeCmp(param1, param1, param2, std::is_signed_v ? Condition::LT : Condition::LO); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); uint64_t hi = static_cast(0xABCDEFU) << (BITS_PER_BYTE * sizeof(uint32_t)); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { uint32_t lo = random_gen(); // Second type-dependency T tmp1 = hi | (lo + 1U); T tmp2 = hi | lo; // Deduced conflicting types for parameter auto compare = [](T a, T b) -> int32_t { return a < b ? -1 : a > b ? 1 : 0; }; // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, compare(tmp1, tmp2))) { return false; } } return true; } TEST_F(Encoder64Test, Cmp64Test) { EXPECT_TRUE(TestCmp64(this)); EXPECT_TRUE(TestCmp64(this)); } template bool TestCompare(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); auto ret_val = std::is_floating_point_v ? Target::Current().GetReturnReg() : Target::Current().GetParamReg(0); // Main test call test->GetEncoder()->EncodeCompare(ret_val, param1, param2, std::is_signed_v ? Condition::GE : Condition::HS); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 = random_gen(); // Deduced conflicting types for parameter auto compare = [](T a, T b) -> int32_t { return a >= b; }; // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, compare(tmp1, tmp2))) { return false; } } return true; } TEST_F(Encoder64Test, CompareTest) { EXPECT_TRUE(TestCompare(this)); EXPECT_TRUE(TestCompare(this)); EXPECT_TRUE(TestCompare(this)); EXPECT_TRUE(TestCompare(this)); EXPECT_TRUE(TestCompare(this)); EXPECT_TRUE(TestCompare(this)); } template bool TestCompare64(Encoder64Test *test) { static_assert(sizeof(T) == sizeof(int64_t)); // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeCompare(param1, param1, param2, std::is_signed_v ? Condition::LT : Condition::LO); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); uint64_t hi = static_cast(0xABCDEFU) << (BITS_PER_BYTE * sizeof(uint32_t)); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { uint32_t lo = random_gen(); // Second type-dependency T tmp1 = hi | (lo + 1U); T tmp2 = hi | lo; // Deduced conflicting types for parameter auto compare = [](T a, T b) -> int32_t { return a < b; }; // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, compare(tmp1, tmp2))) { return false; } } return true; } TEST_F(Encoder64Test, Compare64Test) { EXPECT_TRUE(TestCompare64(this)); EXPECT_TRUE(TestCompare64(this)); } template bool TestCast(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto input = test->GetParameter(TypeInfo(Src(0)), 0); auto output = test->GetParameter(TypeInfo(Dst(0)), 0); // Main test call test->GetEncoder()->EncodeCast(output, std::is_signed_v, input, std::is_signed_v); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << ", " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Using max size type: type result "Dst" or 32bit to check result, // because in our ISA min type is 32bit. // Only integers less thаn 32bit. typedef typename std::conditional<(sizeof(Dst) * BYTE_SIZE) >= WORD_SIZE, Dst, uint32_t>::type DstExt; // Second type-dependency auto src = random_gen(); DstExt dst {}; if (std::is_floating_point() && !std::is_floating_point()) { auto MIN_INT = std::numeric_limits::min(); auto MAX_INT = std::numeric_limits::max(); auto FLOAT_MIN_INT = static_cast(MIN_INT); auto FLOAT_MAX_INT = static_cast(MAX_INT); if (src > FLOAT_MIN_INT) { if (src < FLOAT_MAX_INT) { dst = static_cast(src); } else { dst = MAX_INT; } } else if (std::isnan(src)) { dst = 0; } else { dst = MIN_INT; } } else { dst = static_cast(static_cast(src)); } // Deduced conflicting types for parameter // Main check - compare parameter and // return value if (!test->CallCode(src, dst)) { return false; } } if constexpr (std::is_floating_point_v && std::is_integral_v) { Src nan = std::numeric_limits::quiet_NaN(); typedef typename std::conditional<(sizeof(Dst) * BYTE_SIZE) >= WORD_SIZE, Dst, uint32_t>::type DstExt; if (!test->CallCode(nan, Dst(0))) { return false; } } return true; } TEST_F(Encoder64Test, CastTest) { // Test int8 EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); // Test int16 EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); // Test int32 EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); // Test int64 EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); // Test float/double EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); // We DON'T support cast from float32/64 to int8/16. EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); EXPECT_TRUE((TestCast(this))); } template bool TestDiv(Encoder64Test *test) { bool is_signed = std::is_signed::value; test->PreWork(); // First type-dependency auto param_1 = test->GetParameter(TypeInfo(T(0)), 0); auto param_2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeDiv(param_1, is_signed, param_1, param_2); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 = random_gen(); if (tmp2 == 0) { tmp2 += 1; } // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, (tmp1 / tmp2))) { return false; } } if constexpr (std::is_floating_point_v) { T nan = std::numeric_limits::quiet_NaN(); if (!test->CallCode(nan, random_gen(), nan)) { return false; } if (!test->CallCode(random_gen(), nan, nan)) { return false; } if (!test->CallCode(0.0, 0.0, nan)) { return false; } if (!test->CallCode(std::numeric_limits::infinity(), std::numeric_limits::infinity(), nan)) { return false; } if (!test->CallCode(-std::numeric_limits::infinity(), std::numeric_limits::infinity(), nan)) { return false; } } return true; } TEST_F(Encoder64Test, DivTest) { EXPECT_TRUE(TestDiv(this)); EXPECT_TRUE(TestDiv(this)); EXPECT_TRUE(TestDiv(this)); EXPECT_TRUE(TestDiv(this)); EXPECT_TRUE(TestDiv(this)); EXPECT_TRUE(TestDiv(this)); } template bool TestMod(Encoder64Test *test) { bool is_signed = std::is_signed::value; test->PreWork(); // First type-dependency auto param_1 = test->GetParameter(TypeInfo(T(0)), 0); auto param_2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeMod(param_1, is_signed, param_1, param_2); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 = random_gen(); if (tmp2 == 0) { tmp2 += 1; } // Main check - compare parameter and // return value if constexpr (std::is_same::value) { if (!test->CallCode(tmp1, tmp2, fmodf(tmp1, tmp2))) { return false; } } else if constexpr (std::is_same::value) { if (!test->CallCode(tmp1, tmp2, fmod(tmp1, tmp2))) { return false; } } else { if (!test->CallCode(tmp1, tmp2, static_cast(tmp1 % tmp2))) { return false; } } } if constexpr (std::is_floating_point_v) { T nan = std::numeric_limits::quiet_NaN(); if (!test->CallCode(nan, random_gen(), nan)) { return false; } if (!test->CallCode(random_gen(), nan, nan)) { return false; } if (!test->CallCode(0.0, 0.0, nan)) { return false; } if (!test->CallCode(std::numeric_limits::infinity(), std::numeric_limits::infinity(), nan)) { return false; } if (!test->CallCode(-std::numeric_limits::infinity(), std::numeric_limits::infinity(), nan)) { return false; } } return true; } TEST_F(Encoder64Test, ModTest) { EXPECT_TRUE(TestMod(this)); EXPECT_TRUE(TestMod(this)); EXPECT_TRUE(TestMod(this)); EXPECT_TRUE(TestMod(this)); EXPECT_TRUE(TestMod(this)); EXPECT_TRUE(TestMod(this)); } // MemCopy Test // TEST_F(Encoder64Test, MemCopyTest) { // EncodeMemCopy(MemRef mem_from, MemRef mem_to, size_t size) // MemCopyz Test // TEST_F(Encoder64Test, MemCopyzTest) { // EncodeMemCopyz(MemRef mem_from, MemRef mem_to, size_t size) template bool TestParam(Encoder64Test *test) { // int32_t uint64_t int32_t int64_t int32_t int32_t // r0 r2+r3 stack0 stack2(align) stack4 using funct_ptr = uint64_t (*)(uint32_t, uint64_t, int32_t, int64_t, int32_t, int32_t); bool is_signed = std::is_signed::value; constexpr TypeInfo params[6] = {INT32_TYPE, INT64_TYPE, INT32_TYPE, INT64_TYPE, INT32_TYPE, INT32_TYPE}; TypeInfo curr_param = params[ID]; auto param_info = test->GetCallconv()->GetParameterInfo(0); auto par = param_info->GetNativeParam(params[0]); // First ret value geted for (int i = 1; i <= ID; ++i) { par = param_info->GetNativeParam(params[i]); } test->PreWork(); auto ret_reg = test->GetParameter(curr_param, 0); // Main test call if (std::holds_alternative(par)) { test->GetEncoder()->EncodeMov(ret_reg, std::get(par)); } else { auto mem = MemRef(Target::Current().GetStackReg(), std::get(par) * sizeof(uint32_t)); test->GetEncoder()->EncodeLdr(ret_reg, is_signed, mem); } // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported parameter with " << ID << "\n"; return false; } // Finalize test->PostWork(); // Change this for enable print disasm test->Dump(false); auto size = test->GetCallconv()->GetCodeSize() - test->GetCursor(); void *offset = (static_cast(test->GetCallconv()->GetCodeEntry())); void *ptr = test->GetCodeAllocator()->AllocateCode(size, offset); auto func = reinterpret_cast(ptr); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency int32_t param_0 = random_gen(); uint64_t param_1 = random_gen(); int32_t param_2 = random_gen(); int64_t param_3 = random_gen(); int32_t param_4 = random_gen(); int32_t param_5 = random_gen(); // Main check - compare parameter and // return value const T curr_result = func(param_0, param_1, param_2, param_3, param_4, param_5); T result; if constexpr (ID == 0) { result = param_0; } if constexpr (ID == 1) { result = param_1; } if constexpr (ID == 2) { result = param_2; } if constexpr (ID == 3) { result = param_3; } if constexpr (ID == 4) { result = param_4; } if constexpr (ID == 5) { result = param_5; } if (curr_result != result) { return false; }; } return true; } TEST_F(Encoder64Test, ReadParams) { EXPECT_TRUE((TestParam<0, int32_t>(this))); EXPECT_TRUE((TestParam<1, uint64_t>(this))); EXPECT_TRUE((TestParam<2, int32_t>(this))); EXPECT_TRUE((TestParam<3, int64_t>(this))); EXPECT_TRUE((TestParam<4, int32_t>(this))); EXPECT_TRUE((TestParam<5, int32_t>(this))); } template bool TestSelect(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param0 = test->GetParameter(TypeInfo(T(0)), 0); auto param1 = test->GetParameter(TypeInfo(T(0)), 1); auto param2 = test->GetParameter(TypeInfo(uint32_t(0)), 2); auto param3 = test->GetParameter(TypeInfo(uint32_t(0)), 3); // Main test call test->GetEncoder()->EncodeMov(param2, Imm(1)); test->GetEncoder()->EncodeMov(param3, Imm(0)); test->GetEncoder()->EncodeSelect(Reg(param0.GetId(), TypeInfo(uint32_t(0))), param2, param3, param0, param1, cc); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp0 = random_gen(); T tmp1 = random_gen(); bool res {false}; switch (cc) { case Condition::LT: case Condition::LO: res = tmp0 < tmp1; break; case Condition::EQ: res = tmp0 == tmp1; break; case Condition::NE: res = tmp0 != tmp1; break; case Condition::GT: case Condition::HI: res = tmp0 > tmp1; break; default: UNREACHABLE(); } // Main check - compare parameter and // return value if (!test->CallCode(tmp0, tmp1, res)) { return false; } } return true; } TEST_F(Encoder64Test, SelectTest) { EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); EXPECT_TRUE((TestSelect(this))); } template bool TestSelectTest(Encoder64Test *test) { // Initialize test->PreWork(); auto param0 = test->GetParameter(TypeInfo(T(0)), 0); auto param1 = test->GetParameter(TypeInfo(T(0)), 1); auto param2 = test->GetParameter(TypeInfo(uint32_t(0)), 2); auto param3 = test->GetParameter(TypeInfo(uint32_t(0)), 3); [[maybe_unused]] T imm_value = random_gen(); // Main test call test->GetEncoder()->EncodeMov(param2, Imm(1)); test->GetEncoder()->EncodeMov(param3, Imm(0)); if constexpr (is_imm) { test->GetEncoder()->EncodeSelectTest(Reg(param0.GetId(), TypeInfo(uint32_t(0))), param2, param3, param0, Imm(imm_value), cc); } else { test->GetEncoder()->EncodeSelectTest(Reg(param0.GetId(), TypeInfo(uint32_t(0))), param2, param3, param0, param1, cc); } // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { T tmp0 = random_gen(); T tmp1; if constexpr (is_imm) { tmp1 = imm_value; } else { tmp1 = random_gen(); } T and_res = tmp0 & tmp1; bool res = cc == Condition::TST_EQ ? and_res == 0 : and_res != 0; // Main check - compare parameter and return value if (!test->CallCode(tmp0, tmp1, res)) { return false; } } return true; } TEST_F(Encoder64Test, SelectTestTest) { EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); EXPECT_TRUE((TestSelectTest(this))); } template bool TestCompareTest(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param1 = test->GetParameter(TypeInfo(T(0)), 0); auto param2 = test->GetParameter(TypeInfo(T(0)), 1); // Main test call test->GetEncoder()->EncodeCompareTest(param1, param1, param2, cc); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2 = random_gen(); // Deduced conflicting types for parameter auto compare = [](T a, T b) -> int32_t { return cc == Condition::TST_EQ ? (a & b) == 0 : (a & b) != 0; }; // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, compare(tmp1, tmp2))) { return false; } } return true; } TEST_F(Encoder64Test, CompareTestTest) { EXPECT_TRUE((TestCompareTest(this))); EXPECT_TRUE((TestCompareTest(this))); EXPECT_TRUE((TestCompareTest(this))); EXPECT_TRUE((TestCompareTest(this))); EXPECT_TRUE((TestCompareTest(this))); EXPECT_TRUE((TestCompareTest(this))); EXPECT_TRUE((TestCompareTest(this))); EXPECT_TRUE((TestCompareTest(this))); } template bool TestJumpTest(Encoder64Test *test) { // Initialize test->PreWork(); // First type-dependency auto param0 = test->GetParameter(TypeInfo(T(0)), 0); auto param1 = test->GetParameter(TypeInfo(T(0)), 1); auto ret_val = Target::Current().GetParamReg(0); auto true_branch = test->GetEncoder()->CreateLabel(); auto end = test->GetEncoder()->CreateLabel(); [[maybe_unused]] T imm_value = random_gen(); // Main test call if constexpr (is_imm) { test->GetEncoder()->EncodeJumpTest(true_branch, param0, Imm(imm_value), cc); } else { test->GetEncoder()->EncodeJumpTest(true_branch, param0, param1, cc); } test->GetEncoder()->EncodeMov(ret_val, Imm(0)); test->GetEncoder()->EncodeJump(end); test->GetEncoder()->BindLabel(true_branch); test->GetEncoder()->EncodeMov(ret_val, Imm(1)); test->GetEncoder()->BindLabel(end); // Finalize test->PostWork(); // If encode unsupported - now print error if (!test->GetEncoder()->GetResult()) { std::cerr << "Unsupported for " << TypeName() << "\n"; return false; } // Change this for enable print disasm test->Dump(false); // Main test loop: for (uint64_t i = 0; i < ITERATION; ++i) { // Second type-dependency T tmp1 = random_gen(); T tmp2; if constexpr (is_imm) { tmp2 = imm_value; } else { tmp2 = random_gen(); } // Deduced conflicting types for parameter auto compare = [](T a, T b) -> int32_t { return cc == Condition::TST_EQ ? (a & b) == 0 : (a & b) != 0; }; // Main check - compare parameter and // return value if (!test->CallCode(tmp1, tmp2, compare(tmp1, tmp2))) { return false; } } return true; } TEST_F(Encoder64Test, JumpTestTest) { EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); EXPECT_TRUE((TestJumpTest(this))); } } // namespace panda::compiler